Annotation of parser3/src/main/pa_common.C, revision 1.91
1.15 paf 1: /** @file
1.16 paf 2: Parser: commonly functions.
3:
1.8 paf 4: Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
1.85 paf 5: Author: Alexander Petrosyan <paf@design.ru>(http://paf.design.ru)
1.16 paf 6:
1.91 ! paf 7: $Id: pa_common.C,v 1.90 2001/11/14 13:07:41 paf Exp $
1.1 paf 8: */
9:
10: #include "pa_common.h"
1.2 paf 11: #include "pa_types.h"
1.4 paf 12: #include "pa_exception.h"
1.14 paf 13: #include "pa_pool.h"
14: #include "pa_globals.h"
15: #include "pa_value.h"
16: #include "pa_hash.h"
17: #include "pa_string.h"
1.1 paf 18:
1.82 paf 19: #ifndef _O_TEXT
20: # define _O_TEXT 0
21: #endif
22: #ifndef _O_BINARY
23: # define _O_BINARY 0
1.47 paf 24: #endif
1.80 paf 25: #ifndef O_TRUNC
26: # define O_TRUNC 0
27: #endif
28:
1.86 paf 29: static char *strnchr(char *buf, size_t size, char c) {
30: for(; size-->0; buf++) {
31: if(*buf==c)
32: return buf;
33: }
34:
35: return 0;
36: }
37:
38: void fix_line_breaks(char *buf, size_t& size) {
1.87 paf 39: //_asm int 3;
1.86 paf 40: const char * const eob=buf+size;
1.87 paf 41: char *dest=buf;
1.72 parser 42: // fix DOS: \r\n -> \n
43: // fix Macintosh: \r -> \n
1.87 paf 44: char *bol=buf;
1.86 paf 45: while(char *eol=strnchr(bol, eob -bol, '\r')) {
1.72 parser 46: size_t len=eol-bol;
47: if(dest!=bol)
48: memcpy(dest, bol, len);
49: dest+=len;
50: *dest++='\n';
51:
1.87 paf 52: if(&eol[1]<eob && eol[1]=='\n') { // \r,\n = DOS
1.72 parser 53: bol=eol+2;
54: size--;
55: } else // \r,not \n = Macintosh
56: bol=eol+1;
57: }
58: // last piece without \r, including terminating 0
59: if(dest!=bol)
1.87 paf 60: memcpy(dest, bol, eob-bol);
1.72 parser 61: }
1.18 paf 62:
1.28 paf 63: char *file_read_text(Pool& pool, const String& file_spec, bool fail_on_read_problem) {
1.72 parser 64: void *result; size_t size;
65: return file_read(pool, file_spec, result, size, true, fail_on_read_problem)?(char *)result:0;
1.34 paf 66: }
67: bool file_read(Pool& pool, const String& file_spec,
1.87 paf 68: void*& data, size_t& data_size, bool as_text,
1.44 paf 69: bool fail_on_read_problem,
70: size_t offset, size_t limit) {
1.64 parser 71: const char *fname=file_spec.cstr(String::UL_FILE_SPEC);
1.62 parser 72: //printf("file_read(%s)\n", fname);
1.33 paf 73: int f;
1.2 paf 74: struct stat finfo;
1.33 paf 75:
76: // first open, next stat:
1.45 paf 77: // directory update of NTFS hard links performed on open.
1.33 paf 78: // ex:
79: // a.html:^test[] and b.html hardlink to a.html
80: // user inserts ! before ^test in a.html
81: // directory entry of b.html in NTFS not updated at once,
1.35 paf 82: // they delay update till open, so we would receive "!^test[" string
83: // if would do stat, next open.
1.34 paf 84: if(
1.72 parser 85: (f=open(fname, O_RDONLY|(as_text?_O_BINARY/*_O_TEXT*/:_O_BINARY)))>=0 &&
1.34 paf 86: stat(fname, &finfo)==0) {
1.2 paf 87: /*if(exclusive)
88: flock(f, LOCK_EX);*/
1.44 paf 89: size_t max_size=limit?min(offset+limit, finfo.st_size)-offset:finfo.st_size;
1.87 paf 90: int read_size;
1.47 paf 91: if(!max_size) { // eof
1.83 paf 92: if(as_text) {
93: data=pool.malloc(1);
94: *(char*)data=0;
95: } else
96: data=0;
1.47 paf 97: read_size=0;
1.59 parser 98: } else {
1.78 paf 99: data=pool.malloc(max_size+(as_text?1:0), 3);
1.59 parser 100: if(offset)
101: lseek(f, offset, SEEK_SET);
102: read_size=read(f, data, max_size);
1.47 paf 103: }
1.2 paf 104: /*if(exclusive)
105: flock(f, LOCK_UN);*/
106: close(f);
1.59 parser 107: if(!max_size) // eof
108: return true;
1.32 paf 109:
1.87 paf 110: if(read_size<0 || read_size>max_size)
1.73 parser 111: throw Exception(0, 0,
1.33 paf 112: &file_spec,
1.44 paf 113: "read failed: actually read %d bytes count not in [0..%lu] valid range",
114: read_size, (unsigned long)max_size); //never
1.87 paf 115:
116: data_size=read_size;
117: if(as_text) {
118: fix_line_breaks((char *)data, data_size);
119: // note: after fixing
120: ((char*&)data)[data_size]=0;
121: }
1.72 parser 122: return true;
1.2 paf 123: }
1.4 paf 124: if(fail_on_read_problem)
1.73 parser 125: throw Exception(0, 0,
1.33 paf 126: &file_spec,
1.54 parser 127: "read failed: %s (%d), actual filename '%s'",
128: strerror(errno), errno, fname);
1.34 paf 129: return false;
1.8 paf 130: }
131:
1.63 parser 132: static void create_dir_for_file(const String& file_spec) {
133: size_t pos_after=1;
134: int pos_before;
135: while((pos_before=file_spec.pos("/", 1, pos_after))>=0) {
1.64 parser 136: mkdir(file_spec.mid(0, pos_before).cstr(String::UL_FILE_SPEC), 0775);
1.63 parser 137: pos_after=pos_before+1;
138: }
139: }
140:
1.18 paf 141: void file_write(Pool& pool,
1.28 paf 142: const String& file_spec,
1.34 paf 143: const void *data, size_t size,
1.80 paf 144: bool as_text,
145: bool do_append/*,
1.20 paf 146: bool exclusive*/) {
1.64 parser 147: const char *fname=file_spec.cstr(String::UL_FILE_SPEC);
1.28 paf 148: int f;
1.80 paf 149: if(access(fname, W_OK)!=0) // no
1.63 parser 150: create_dir_for_file(file_spec);
1.50 paf 151:
1.80 paf 152: if((f=open(fname,
153: O_CREAT|O_RDWR
154: |(as_text?_O_TEXT:_O_BINARY)
155: |(do_append?O_APPEND:O_TRUNC), 0666))>=0) {
156: /*if(exclusive)
157: flock(f, LOCK_EX);*/
158:
159: if(size) write(f, data, size);
160: #if O_TRUNC==0
161: ftruncate(f, size);
1.18 paf 162: #endif
1.80 paf 163: /*if(exclusive)
164: flock(f, LOCK_UN);*/
165: close(f);
166: } else
167: throw Exception(0, 0,
168: &file_spec,
169: "write failed: %s (%d), actual filename '%s'",
170: strerror(errno), errno, fname);
1.30 paf 171: }
172:
1.63 parser 173: // throws nothing! [this is required in file_move & file_delete]
1.50 paf 174: static void rmdir(const String& file_spec, size_t pos_after) {
175: int pos_before;
176: if((pos_before=file_spec.pos("/", 1, pos_after))>=0)
177: rmdir(file_spec, pos_before+1);
178:
1.64 parser 179: rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::UL_FILE_SPEC));
1.50 paf 180: }
1.30 paf 181: void file_delete(Pool& pool, const String& file_spec) {
1.64 parser 182: const char *fname=file_spec.cstr(String::UL_FILE_SPEC);
1.54 parser 183: if(unlink(fname)!=0)
1.73 parser 184: throw Exception(0, 0,
1.33 paf 185: &file_spec,
1.54 parser 186: "unlink failed: %s (%d), actual filename '%s'",
187: strerror(errno), errno, fname);
1.50 paf 188:
189: rmdir(file_spec, 1);
1.60 parser 190: }
191: void file_move(Pool& pool, const String& old_spec, const String& new_spec) {
1.64 parser 192: const char *old_spec_cstr=old_spec.cstr(String::UL_FILE_SPEC);
193: const char *new_spec_cstr=new_spec.cstr(String::UL_FILE_SPEC);
1.63 parser 194:
195: create_dir_for_file(new_spec);
196:
1.60 parser 197: if(rename(old_spec_cstr, new_spec_cstr)!=0)
1.73 parser 198: throw Exception(0, 0,
1.60 parser 199: &old_spec,
200: "rename failed: %s (%d), actual filename '%s' to '%s'",
201: strerror(errno), errno, old_spec_cstr, new_spec_cstr);
1.63 parser 202:
203: rmdir(old_spec, 1);
1.31 paf 204: }
205:
1.51 paf 206:
207: static bool entry_readable(const String& file_spec, bool need_dir) {
1.64 parser 208: const char *fname=file_spec.cstr(String::UL_FILE_SPEC);
1.51 paf 209: struct stat finfo;
210: if(access(fname, R_OK)==0 && stat(fname, &finfo)==0) {
1.73 parser 211: bool is_dir=finfo.st_mode&S_IFDIR != 0;
1.51 paf 212: return is_dir==need_dir;
213: }
214: return false;
215: }
1.31 paf 216: bool file_readable(const String& file_spec) {
1.51 paf 217: return entry_readable(file_spec, false);
218: }
219: bool dir_readable(const String& file_spec) {
220: return entry_readable(file_spec, true);
1.65 parser 221: }
222: String *file_readable(const String& path, const String& name) {
223: String *result=new(path.pool()) String(path);
224: *result << "/";
225: *result << name;
226: return file_readable(*result)?result:0;
1.43 paf 227: }
228: bool file_executable(const String& file_spec) {
1.64 parser 229: return access(file_spec.cstr(String::UL_FILE_SPEC), X_OK)==0;
1.44 paf 230: }
231:
1.64 parser 232: bool file_stat(const String& file_spec,
1.58 parser 233: size_t& rsize,
234: time_t& ratime,
235: time_t& rmtime,
1.64 parser 236: time_t& rctime,
237: bool fail_on_read_problem) {
1.44 paf 238: Pool& pool=file_spec.pool();
1.64 parser 239: const char *fname=file_spec.cstr(String::UL_FILE_SPEC);
1.44 paf 240: struct stat finfo;
241: if(stat(fname, &finfo)!=0)
1.64 parser 242: if(fail_on_read_problem)
1.73 parser 243: throw Exception(0, 0,
1.67 parser 244: &file_spec,
245: "getting file size failed: %s (%d), real filename '%s'",
246: strerror(errno), errno, fname);
1.64 parser 247: else
248: return false;
1.58 parser 249: rsize=finfo.st_size;
250: ratime=finfo.st_atime;
251: rmtime=finfo.st_mtime;
252: rctime=finfo.st_ctime;
1.64 parser 253: return true;
1.18 paf 254: }
255:
1.8 paf 256: char *getrow(char **row_ref, char delim) {
257: char *result=*row_ref;
258: if(result) {
259: *row_ref=strchr(result, delim);
260: if(*row_ref)
261: *((*row_ref)++)=0;
262: else if(!*result)
263: return 0;
264: }
265: return result;
266: }
267:
1.23 paf 268: char *lsplit(char *string, char delim) {
269: if(string) {
270: char *v=strchr(string, delim);
1.8 paf 271: if(v) {
272: *v=0;
273: return v+1;
274: }
275: }
276: return 0;
277: }
278:
279: char *lsplit(char **string_ref, char delim) {
280: char *result=*string_ref;
281: char *next=lsplit(*string_ref, delim);
282: *string_ref=next;
283: return result;
1.9 paf 284: }
285:
286: char *rsplit(char *string, char delim) {
1.18 paf 287: if(string) {
1.9 paf 288: char *v=strrchr(string, delim);
1.18 paf 289: if(v) {
1.9 paf 290: *v=0;
291: return v+1;
292: }
293: }
294: return NULL;
1.10 paf 295: }
296:
1.37 paf 297: /// @todo less stupid type detection
1.10 paf 298: char *format(Pool& pool, double value, char *fmt) {
1.78 paf 299: char *result=(char *)pool.malloc(MAX_NUMBER, 4);
1.10 paf 300: if(fmt)
301: if(strpbrk(fmt, "diouxX"))
302: if(strpbrk(fmt, "ouxX"))
1.33 paf 303: snprintf(result, MAX_NUMBER, fmt, (uint)value );
1.10 paf 304: else
1.33 paf 305: snprintf(result, MAX_NUMBER, fmt, (int)value );
1.10 paf 306: else
307: snprintf(result, MAX_NUMBER, fmt, value);
308: else
1.33 paf 309: snprintf(result, MAX_NUMBER, "%d", (int)value);
1.10 paf 310:
311: return result;
1.12 paf 312: }
313:
1.36 paf 314: size_t stdout_write(const void *buf, size_t size) {
1.12 paf 315: #ifdef WIN32
316: do{
317: int chunk_written=fwrite(buf, 1, min(8*0x400, size), stdout);
318: if(chunk_written<=0)
319: break;
320: size-=chunk_written;
1.36 paf 321: buf=((const char*)buf)+chunk_written;
1.12 paf 322: } while(size>0);
323:
324: return size;
325: #else
1.13 paf 326: return fwrite(buf, 1, size, stdout);
1.12 paf 327: #endif
1.2 paf 328: }
1.14 paf 329:
1.77 parser 330: char *unescape_chars(Pool& pool, const char *cp, int len) {
1.78 paf 331: char *s=(char *)pool.malloc(len + 1, 5);
1.14 paf 332: enum EscapeState {
1.33 paf 333: EscapeRest,
334: EscapeFirst,
1.14 paf 335: EscapeSecond
336: } escapeState=EscapeRest;
337: int escapedValue=0;
338: int srcPos=0;
339: int dstPos=0;
340: while(srcPos < len) {
341: int ch=cp[srcPos];
342: switch(escapeState) {
343: case EscapeRest:
344: if(ch=='%') {
345: escapeState=EscapeFirst;
346: } else if(ch=='+') {
347: s[dstPos++]=' ';
348: } else {
349: s[dstPos++]=ch;
350: }
351: break;
352: case EscapeFirst:
353: escapedValue=hex_value[ch] << 4;
354: escapeState=EscapeSecond;
355: break;
356: case EscapeSecond:
357: escapedValue +=hex_value[ch];
358: s[dstPos++]=escapedValue;
359: escapeState=EscapeRest;
360: break;
361: }
362: srcPos++;
363: }
364: s[dstPos]=0;
365: return s;
366: }
367:
1.52 paf 368: /// used by attributed_meaning_to_string / append_attribute_subattribute
1.40 paf 369: struct Attributed_meaning_info {
1.52 paf 370: String *header; // header line being constructed
371: String::Untaint_lang lang; // language in which to append to that line
1.40 paf 372: };
1.17 paf 373: static void append_attribute_subattribute(const Hash::Key& akey, Hash::Val *avalue,
1.14 paf 374: void *info) {
375: if(akey==VALUE_NAME)
376: return;
377:
1.40 paf 378: Attributed_meaning_info& ami=*static_cast<Attributed_meaning_info *>(info);
379:
1.14 paf 380: // ...; charset=windows1251
1.48 paf 381: *ami.header << "; ";
1.70 parser 382: ami.header->append(akey, ami.lang);
1.48 paf 383: *ami.header << "=";
1.70 parser 384: ami.header->append(static_cast<Value *>(avalue)->as_string(), ami.lang);
1.14 paf 385: }
1.49 paf 386: const String& attributed_meaning_to_string(Value& meaning,
387: String::Untaint_lang lang) {
1.20 paf 388: String &result=*new(meaning.pool()) String(meaning.pool());
1.75 parser 389: if(Hash *hash=meaning.get_hash(0)) {
1.14 paf 390: // $value(value) $subattribute(subattribute value)
391: if(Value *value=static_cast<Value *>(hash->get(*value_name)))
1.40 paf 392: result.append(value->as_string(), lang, true);
1.14 paf 393:
1.40 paf 394: Attributed_meaning_info attributed_meaning_info={
1.52 paf 395: &result,
396: lang
1.40 paf 397: };
398: hash->for_each(append_attribute_subattribute, &attributed_meaning_info);
1.14 paf 399: } else // result value
1.40 paf 400: result.append(meaning.as_string(), lang, true);
1.14 paf 401:
402: return result;
1.24 paf 403: }
404:
405: #ifdef WIN32
406: void back_slashes_to_slashes(char *s) {
407: if(s)
408: for(; *s; s++)
409: if(*s=='\\')
410: *s='/';
411: }
1.42 paf 412: /*
413: void slashes_to_back_slashes(char *s) {
414: if(s)
415: for(; *s; s++)
416: if(*s=='/')
417: *s='\\';
418: }
419: */
1.24 paf 420: #endif
1.41 paf 421:
422: bool StrEqNc(const char *s1, const char *s2, bool strict) {
423: while(true) {
424: if(!(*s1)) {
425: if(!(*s2))
426: return true;
427: else
428: return !strict;
429: } else if(!(*s2))
430: return !strict;
431: if(isalpha(*s1)) {
432: if(tolower(*s1) !=tolower(*s2))
433: return false;
434: } else if((*s1) !=(*s2))
435: return false;
436: s1++;
437: s2++;
438: }
1.57 parser 439: }
440:
1.84 paf 441: static bool isLeap(int year) {
1.57 parser 442: return !(
443: (year % 4) || ((year % 400) && !(year % 100))
444: );
445: }
446:
447: int getMonthDays(int year, int month) {
448: int monthDays[]={
449: 31,
450: isLeap(year) ? 29 : 28,
451: 31,
452: 30,
453: 31,
454: 30,
455: 31,
456: 31,
457: 30,
458: 31,
459: 30,
460: 31
461: };
462: return monthDays[month];
1.41 paf 463: }
1.69 parser 464:
465: void remove_crlf(char *start, char *end) {
466: for(char *p=start; p<end; p++)
467: switch(*p) {
468: case '\n': *p='|'; break;
469: case '\r': *p=' '; break;
470: }
1.91 ! paf 471: }
! 472:
! 473:
! 474: /// must be last in this file
! 475: #undef vsnprintf
! 476: int __vsnprintf(char *b, size_t s, const char *f, va_list l) {
! 477: if(!s)
! 478: return 0;
! 479:
! 480: int r;
! 481: // note: on win32& maybe somewhere else
! 482: // vsnprintf do not writes terminating 0 in 'buffer full' case, reducing
! 483: --s;
! 484: #if _MSC_VER
! 485: /*
! 486: win32:
! 487: mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001APR\1033\vccore.chm::/html/_crt__vsnprintf.2c_._vsnwprintf.htm
! 488:
! 489: if the number of bytes to write exceeds buffer, then count bytes are written and –1 is returned
! 490: */
! 491: r=_vsnprintf(b, s, f, l);
! 492: if(r<0)
! 493: r=s;
! 494: #else
! 495: r=vsnprintf(b, s, f, l);
! 496: /*
! 497: solaris:
! 498: man vsnprintf
! 499:
! 500: The snprintf() function returns the number of characters
! 501: formatted, that is, the number of characters that would have
! 502: been written to the buffer if it were large enough. If the
! 503: value of n is 0 on a call to snprintf(), an unspecified
! 504: value less than 1 is returned.
! 505: */
! 506:
! 507: if(r<0)
! 508: r=0;
! 509: else if(r>s)
! 510: r=s;
! 511: #endif
! 512: b[r]=0;
! 513: return r;
! 514: }
! 515:
! 516: int __snprintf(char *b, size_t s, const char *f, ...) {
! 517: va_list l;
! 518: va_start(l, f);
! 519: int r=__vsnprintf(b, s, f, l);
! 520: va_end(l);
! 521: return r;
1.74 parser 522: }
E-mail: