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