Annotation of parser3/src/main/pa_common.C, revision 1.91.4.1.2.1
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.4.1.2.1! paf 7: $Id: pa_common.C,v 1.91.4.1 2001/11/23 11:53:03 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)
1.91.4.1 paf 155: |(do_append?O_APPEND:O_TRUNC), 0664))>=0) {
1.80 paf 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.91.4.1.2.1! paf 181: bool file_delete(Pool& pool, const String& file_spec, bool fail_on_read_problem) {
1.64 parser 182: const char *fname=file_spec.cstr(String::UL_FILE_SPEC);
1.54 parser 183: if(unlink(fname)!=0)
1.91.4.1.2.1! paf 184: if(fail_on_read_problem)
! 185: throw Exception(0, 0,
! 186: &file_spec,
! 187: "unlink failed: %s (%d), actual filename '%s'",
! 188: strerror(errno), errno, fname);
! 189: else
! 190: return false;
1.50 paf 191:
192: rmdir(file_spec, 1);
1.91.4.1.2.1! paf 193: return true;
1.60 parser 194: }
195: void file_move(Pool& pool, const String& old_spec, const String& new_spec) {
1.64 parser 196: const char *old_spec_cstr=old_spec.cstr(String::UL_FILE_SPEC);
197: const char *new_spec_cstr=new_spec.cstr(String::UL_FILE_SPEC);
1.63 parser 198:
199: create_dir_for_file(new_spec);
200:
1.60 parser 201: if(rename(old_spec_cstr, new_spec_cstr)!=0)
1.73 parser 202: throw Exception(0, 0,
1.60 parser 203: &old_spec,
204: "rename failed: %s (%d), actual filename '%s' to '%s'",
205: strerror(errno), errno, old_spec_cstr, new_spec_cstr);
1.63 parser 206:
207: rmdir(old_spec, 1);
1.31 paf 208: }
209:
1.51 paf 210:
211: static bool entry_readable(const String& file_spec, bool need_dir) {
1.64 parser 212: const char *fname=file_spec.cstr(String::UL_FILE_SPEC);
1.51 paf 213: struct stat finfo;
214: if(access(fname, R_OK)==0 && stat(fname, &finfo)==0) {
1.73 parser 215: bool is_dir=finfo.st_mode&S_IFDIR != 0;
1.51 paf 216: return is_dir==need_dir;
217: }
218: return false;
219: }
1.31 paf 220: bool file_readable(const String& file_spec) {
1.51 paf 221: return entry_readable(file_spec, false);
222: }
223: bool dir_readable(const String& file_spec) {
224: return entry_readable(file_spec, true);
1.65 parser 225: }
226: String *file_readable(const String& path, const String& name) {
227: String *result=new(path.pool()) String(path);
228: *result << "/";
229: *result << name;
230: return file_readable(*result)?result:0;
1.43 paf 231: }
232: bool file_executable(const String& file_spec) {
1.64 parser 233: return access(file_spec.cstr(String::UL_FILE_SPEC), X_OK)==0;
1.44 paf 234: }
235:
1.64 parser 236: bool file_stat(const String& file_spec,
1.58 parser 237: size_t& rsize,
238: time_t& ratime,
239: time_t& rmtime,
1.64 parser 240: time_t& rctime,
241: bool fail_on_read_problem) {
1.44 paf 242: Pool& pool=file_spec.pool();
1.64 parser 243: const char *fname=file_spec.cstr(String::UL_FILE_SPEC);
1.44 paf 244: struct stat finfo;
245: if(stat(fname, &finfo)!=0)
1.64 parser 246: if(fail_on_read_problem)
1.73 parser 247: throw Exception(0, 0,
1.67 parser 248: &file_spec,
249: "getting file size failed: %s (%d), real filename '%s'",
250: strerror(errno), errno, fname);
1.64 parser 251: else
252: return false;
1.58 parser 253: rsize=finfo.st_size;
254: ratime=finfo.st_atime;
255: rmtime=finfo.st_mtime;
256: rctime=finfo.st_ctime;
1.64 parser 257: return true;
1.18 paf 258: }
259:
1.8 paf 260: char *getrow(char **row_ref, char delim) {
261: char *result=*row_ref;
262: if(result) {
263: *row_ref=strchr(result, delim);
264: if(*row_ref)
265: *((*row_ref)++)=0;
266: else if(!*result)
267: return 0;
268: }
269: return result;
270: }
271:
1.23 paf 272: char *lsplit(char *string, char delim) {
273: if(string) {
274: char *v=strchr(string, delim);
1.8 paf 275: if(v) {
276: *v=0;
277: return v+1;
278: }
279: }
280: return 0;
281: }
282:
283: char *lsplit(char **string_ref, char delim) {
284: char *result=*string_ref;
285: char *next=lsplit(*string_ref, delim);
286: *string_ref=next;
287: return result;
1.9 paf 288: }
289:
290: char *rsplit(char *string, char delim) {
1.18 paf 291: if(string) {
1.9 paf 292: char *v=strrchr(string, delim);
1.18 paf 293: if(v) {
1.9 paf 294: *v=0;
295: return v+1;
296: }
297: }
298: return NULL;
1.10 paf 299: }
300:
1.37 paf 301: /// @todo less stupid type detection
1.10 paf 302: char *format(Pool& pool, double value, char *fmt) {
1.78 paf 303: char *result=(char *)pool.malloc(MAX_NUMBER, 4);
1.10 paf 304: if(fmt)
305: if(strpbrk(fmt, "diouxX"))
306: if(strpbrk(fmt, "ouxX"))
1.33 paf 307: snprintf(result, MAX_NUMBER, fmt, (uint)value );
1.10 paf 308: else
1.33 paf 309: snprintf(result, MAX_NUMBER, fmt, (int)value );
1.10 paf 310: else
311: snprintf(result, MAX_NUMBER, fmt, value);
312: else
1.33 paf 313: snprintf(result, MAX_NUMBER, "%d", (int)value);
1.10 paf 314:
315: return result;
1.12 paf 316: }
317:
1.36 paf 318: size_t stdout_write(const void *buf, size_t size) {
1.12 paf 319: #ifdef WIN32
320: do{
321: int chunk_written=fwrite(buf, 1, min(8*0x400, size), stdout);
322: if(chunk_written<=0)
323: break;
324: size-=chunk_written;
1.36 paf 325: buf=((const char*)buf)+chunk_written;
1.12 paf 326: } while(size>0);
327:
328: return size;
329: #else
1.13 paf 330: return fwrite(buf, 1, size, stdout);
1.12 paf 331: #endif
1.2 paf 332: }
1.14 paf 333:
1.77 parser 334: char *unescape_chars(Pool& pool, const char *cp, int len) {
1.78 paf 335: char *s=(char *)pool.malloc(len + 1, 5);
1.14 paf 336: enum EscapeState {
1.33 paf 337: EscapeRest,
338: EscapeFirst,
1.14 paf 339: EscapeSecond
340: } escapeState=EscapeRest;
341: int escapedValue=0;
342: int srcPos=0;
343: int dstPos=0;
344: while(srcPos < len) {
345: int ch=cp[srcPos];
346: switch(escapeState) {
347: case EscapeRest:
348: if(ch=='%') {
349: escapeState=EscapeFirst;
350: } else if(ch=='+') {
351: s[dstPos++]=' ';
352: } else {
353: s[dstPos++]=ch;
354: }
355: break;
356: case EscapeFirst:
357: escapedValue=hex_value[ch] << 4;
358: escapeState=EscapeSecond;
359: break;
360: case EscapeSecond:
361: escapedValue +=hex_value[ch];
362: s[dstPos++]=escapedValue;
363: escapeState=EscapeRest;
364: break;
365: }
366: srcPos++;
367: }
368: s[dstPos]=0;
369: return s;
370: }
371:
1.52 paf 372: /// used by attributed_meaning_to_string / append_attribute_subattribute
1.40 paf 373: struct Attributed_meaning_info {
1.52 paf 374: String *header; // header line being constructed
375: String::Untaint_lang lang; // language in which to append to that line
1.40 paf 376: };
1.17 paf 377: static void append_attribute_subattribute(const Hash::Key& akey, Hash::Val *avalue,
1.14 paf 378: void *info) {
379: if(akey==VALUE_NAME)
380: return;
381:
1.40 paf 382: Attributed_meaning_info& ami=*static_cast<Attributed_meaning_info *>(info);
383:
1.14 paf 384: // ...; charset=windows1251
1.48 paf 385: *ami.header << "; ";
1.70 parser 386: ami.header->append(akey, ami.lang);
1.48 paf 387: *ami.header << "=";
1.70 parser 388: ami.header->append(static_cast<Value *>(avalue)->as_string(), ami.lang);
1.14 paf 389: }
1.49 paf 390: const String& attributed_meaning_to_string(Value& meaning,
391: String::Untaint_lang lang) {
1.20 paf 392: String &result=*new(meaning.pool()) String(meaning.pool());
1.75 parser 393: if(Hash *hash=meaning.get_hash(0)) {
1.14 paf 394: // $value(value) $subattribute(subattribute value)
395: if(Value *value=static_cast<Value *>(hash->get(*value_name)))
1.40 paf 396: result.append(value->as_string(), lang, true);
1.14 paf 397:
1.40 paf 398: Attributed_meaning_info attributed_meaning_info={
1.52 paf 399: &result,
400: lang
1.40 paf 401: };
402: hash->for_each(append_attribute_subattribute, &attributed_meaning_info);
1.14 paf 403: } else // result value
1.40 paf 404: result.append(meaning.as_string(), lang, true);
1.14 paf 405:
406: return result;
1.24 paf 407: }
408:
409: #ifdef WIN32
410: void back_slashes_to_slashes(char *s) {
411: if(s)
412: for(; *s; s++)
413: if(*s=='\\')
414: *s='/';
415: }
1.42 paf 416: /*
417: void slashes_to_back_slashes(char *s) {
418: if(s)
419: for(; *s; s++)
420: if(*s=='/')
421: *s='\\';
422: }
423: */
1.24 paf 424: #endif
1.41 paf 425:
426: bool StrEqNc(const char *s1, const char *s2, bool strict) {
427: while(true) {
428: if(!(*s1)) {
429: if(!(*s2))
430: return true;
431: else
432: return !strict;
433: } else if(!(*s2))
434: return !strict;
435: if(isalpha(*s1)) {
436: if(tolower(*s1) !=tolower(*s2))
437: return false;
438: } else if((*s1) !=(*s2))
439: return false;
440: s1++;
441: s2++;
442: }
1.57 parser 443: }
444:
1.84 paf 445: static bool isLeap(int year) {
1.57 parser 446: return !(
447: (year % 4) || ((year % 400) && !(year % 100))
448: );
449: }
450:
451: int getMonthDays(int year, int month) {
452: int monthDays[]={
453: 31,
454: isLeap(year) ? 29 : 28,
455: 31,
456: 30,
457: 31,
458: 30,
459: 31,
460: 31,
461: 30,
462: 31,
463: 30,
464: 31
465: };
466: return monthDays[month];
1.41 paf 467: }
1.69 parser 468:
469: void remove_crlf(char *start, char *end) {
470: for(char *p=start; p<end; p++)
471: switch(*p) {
472: case '\n': *p='|'; break;
473: case '\r': *p=' '; break;
474: }
1.91 paf 475: }
476:
477:
478: /// must be last in this file
479: #undef vsnprintf
480: int __vsnprintf(char *b, size_t s, const char *f, va_list l) {
481: if(!s)
482: return 0;
483:
484: int r;
485: // note: on win32& maybe somewhere else
486: // vsnprintf do not writes terminating 0 in 'buffer full' case, reducing
487: --s;
488: #if _MSC_VER
489: /*
490: win32:
491: mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001APR\1033\vccore.chm::/html/_crt__vsnprintf.2c_._vsnwprintf.htm
492:
493: if the number of bytes to write exceeds buffer, then count bytes are written and –1 is returned
494: */
495: r=_vsnprintf(b, s, f, l);
496: if(r<0)
497: r=s;
498: #else
499: r=vsnprintf(b, s, f, l);
500: /*
501: solaris:
502: man vsnprintf
503:
504: The snprintf() function returns the number of characters
505: formatted, that is, the number of characters that would have
506: been written to the buffer if it were large enough. If the
507: value of n is 0 on a call to snprintf(), an unspecified
508: value less than 1 is returned.
509: */
510:
511: if(r<0)
512: r=0;
513: else if(r>s)
514: r=s;
515: #endif
516: b[r]=0;
517: return r;
518: }
519:
520: int __snprintf(char *b, size_t s, const char *f, ...) {
521: va_list l;
522: va_start(l, f);
523: int r=__vsnprintf(b, s, f, l);
524: va_end(l);
525: return r;
1.74 parser 526: }
E-mail: