|
|
| version 1.77, 2001/10/24 16:33:02 | version 1.104, 2002/02/13 12:05:17 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: commonly functions. | Parser: commonly functions. |
| Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com) | Copyright(c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com) |
| Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf) | Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) |
| $Id$ | $Id$ |
| */ | */ |
| Line 16 | Line 16 |
| #include "pa_hash.h" | #include "pa_hash.h" |
| #include "pa_string.h" | #include "pa_string.h" |
| #ifndef WIN32 | #ifdef WIN32 |
| # ifndef _O_TEXT | # include <windows.h> |
| # define _O_TEXT 0 | |
| # endif | |
| # ifndef _O_BINARY | |
| # define _O_BINARY 0 | |
| # endif | |
| #endif | #endif |
| #if _MSC_VER | // some maybe-undefined constants |
| int __vsnprintf(char *b, size_t s, const char *f, va_list l) { | #ifndef _O_TEXT |
| int r=_vsnprintf(b, --s, f, l); | # define _O_TEXT 0 |
| b[s]=0; | #endif |
| return r; | #ifndef _O_BINARY |
| } | # define _O_BINARY 0 |
| int __snprintf(char *b, size_t s, const char *f, ...) { | #endif |
| va_list l; | #ifndef O_TRUNC |
| va_start(l, f); | # define O_TRUNC 0 |
| int r=__vsnprintf(b, s, f, l); | #endif |
| va_end(l); | |
| return r; | // locking constants |
| } | |
| #ifdef HAVE_FLOCK | |
| static int lock_shared_blocking(int fd) { return flock(fd, LOCK_SH); } | |
| static int lock_exclusive_blocking(int fd) { return flock(fd, LOCK_EX); } | |
| static int lock_exclusive_nonblocking(int fd) { return flock(fd, LOCK_EX || LOCK_NB); } | |
| static int unlock(int fd) { return flock(fd, LOCK_UN); } | |
| #else | |
| #ifdef HAVE__LOCKING | |
| #define FLOCK(operation) lseek(fd, 0, SEEK_SET); return _locking(fd, operation, 1) | |
| static int lock_shared_blocking(int fd) { FLOCK(_LK_LOCK); } | |
| static int lock_exclusive_blocking(int fd) { FLOCK(_LK_LOCK); } | |
| static int lock_exclusive_nonblocking(int fd) { FLOCK(_LK_NBLCK); } | |
| static int unlock(int fd) { FLOCK(_LK_UNLCK); } | |
| #else | |
| #ifdef HAVE_FCNTL | |
| #define FLOCK(cmd, arg) struct flock ls={arg, SEEK_SET}; return fcntl(fd, cmd, &ls) | |
| static int lock_shared_blocking(int fd) { FLOCK(F_SETLKW, F_RDLCK); } | |
| static int lock_exclusive_blocking(int fd) { FLOCK(F_SETLKW, F_WRLCK); } | |
| static int lock_exclusive_nonblocking(int fd) { FLOCK(F_SETLK, F_RDLCK); } | |
| static int unlock(int fd) { FLOCK(F_SETLK, F_UNLCK); } | |
| #else | |
| #ifdef HAVE_LOCKF | |
| #define FLOCK(fd, operation) lseek(fd, 0, SEEK_SET); return lockf(fd, operation, 1) | |
| static int lock_shared_blocking(int fd) { FLOCK(F_LOCK); } // on intel solaris man doesn't have doc on shared blocking | |
| static int lock_exclusive_blocking(int fd) { FLOCK(F_LOCK); } | |
| static int lock_exclusive_nonblocking(int fd) { FLOCK(F_TLOCK); } | |
| static int unlock(int fd) { FLOCK(F_TLOCK); } | |
| #else | |
| #error unable to find file locking func | |
| #endif | |
| #endif | #endif |
| #endif | |
| #endif | |
| static char *strnchr(char *buf, size_t size, char c) { | |
| // sanity check | |
| if(!buf) | |
| return 0; | |
| for(; size-->0; buf++) { | |
| if(*buf==c) | |
| return buf; | |
| } | |
| return 0; | |
| } | |
| void fix_line_breaks(char *src, size_t& size) { | void fix_line_breaks(char *buf, size_t& size) { |
| char *dest=src; | //_asm int 3; |
| const char * const eob=buf+size; | |
| char *dest=buf; | |
| // fix DOS: \r\n -> \n | // fix DOS: \r\n -> \n |
| // fix Macintosh: \r -> \n | // fix Macintosh: \r -> \n |
| char *bol=src; | char *bol=buf; |
| while(char *eol=strchr(bol, '\r')) { | while(char *eol=strnchr(bol, eob -bol, '\r')) { |
| size_t len=eol-bol; | size_t len=eol-bol; |
| if(dest!=bol) | if(dest!=bol) |
| memcpy(dest, bol, len); | memcpy(dest, bol, len); |
| dest+=len; | dest+=len; |
| *dest++='\n'; | *dest++='\n'; |
| if(eol[1]=='\n') { // \r,\n = DOS | if(&eol[1]<eob && eol[1]=='\n') { // \r,\n = DOS |
| bol=eol+2; | bol=eol+2; |
| size--; | size--; |
| } else // \r,not \n = Macintosh | } else // \r,not \n = Macintosh |
| Line 62 void fix_line_breaks(char *src, size_t& | Line 112 void fix_line_breaks(char *src, size_t& |
| } | } |
| // last piece without \r, including terminating 0 | // last piece without \r, including terminating 0 |
| if(dest!=bol) | if(dest!=bol) |
| strcpy(dest, bol); | memcpy(dest, bol, eob-bol); |
| } | } |
| char *file_read_text(Pool& pool, const String& file_spec, bool fail_on_read_problem) { | char *file_read_text(Pool& pool, const String& file_spec, bool fail_on_read_problem) { |
| Line 70 char *file_read_text(Pool& pool, const S | Line 120 char *file_read_text(Pool& pool, const S |
| return file_read(pool, file_spec, result, size, true, fail_on_read_problem)?(char *)result:0; | return file_read(pool, file_spec, result, size, true, fail_on_read_problem)?(char *)result:0; |
| } | } |
| bool file_read(Pool& pool, const String& file_spec, | bool file_read(Pool& pool, const String& file_spec, |
| void*& data, size_t& read_size, bool as_text, | void*& data, size_t& data_size, bool as_text, |
| bool fail_on_read_problem, | bool fail_on_read_problem, |
| size_t offset, size_t limit) { | size_t offset, size_t limit) { |
| const char *fname=file_spec.cstr(String::UL_FILE_SPEC); | const char *fname=file_spec.cstr(String::UL_FILE_SPEC); |
| Line 86 bool file_read(Pool& pool, const String& | Line 136 bool file_read(Pool& pool, const String& |
| // directory entry of b.html in NTFS not updated at once, | // directory entry of b.html in NTFS not updated at once, |
| // they delay update till open, so we would receive "!^test[" string | // they delay update till open, so we would receive "!^test[" string |
| // if would do stat, next open. | // if would do stat, next open. |
| if( | if((f=open(fname, O_RDONLY|(as_text?_O_TEXT:_O_BINARY)))>=0) { |
| (f=open(fname, O_RDONLY|(as_text?_O_BINARY/*_O_TEXT*/:_O_BINARY)))>=0 && | lock_shared_blocking(f); |
| stat(fname, &finfo)==0) { | if(stat(fname, &finfo)!=0) { |
| /*if(exclusive) | Exception e(0, 0, |
| flock(f, LOCK_EX);*/ | &file_spec, |
| size_t max_size=limit?min(offset+limit, finfo.st_size)-offset:finfo.st_size; | "stat failed: %s (%d), actual filename '%s'", |
| strerror(errno), errno, fname); | |
| unlock(f); | |
| close(f); | |
| if(fail_on_read_problem) | |
| throw e; | |
| return false; | |
| } | |
| size_t max_size=limit?min(offset+limit, (size_t)finfo.st_size)-offset:finfo.st_size; | |
| if(!max_size) { // eof | if(!max_size) { // eof |
| data=0; | if(as_text) { |
| read_size=0; | data=pool.malloc(1); |
| *(char*)data=0; | |
| } else | |
| data=0; | |
| data_size=0; | |
| } else { | } else { |
| data=pool.malloc(max_size+(as_text?1:0)); | data=pool.malloc(max_size+(as_text?1:0), 3); |
| if(offset) | if(offset) |
| lseek(f, offset, SEEK_SET); | lseek(f, offset, SEEK_SET); |
| read_size=read(f, data, max_size); | data_size=read(f, data, max_size); |
| } | } |
| /*if(exclusive) | unlock(f); |
| flock(f, LOCK_UN);*/ | |
| close(f); | close(f); |
| if(!max_size) // eof | if(!max_size) // eof |
| return true; | return true; |
| if(read_size>=0 && read_size<=max_size) { | if(int(data_size)<0 || data_size>max_size) |
| if(as_text) | |
| ((char*&)data)[read_size]=0; | |
| } else | |
| throw Exception(0, 0, | throw Exception(0, 0, |
| &file_spec, | &file_spec, |
| "read failed: actually read %d bytes count not in [0..%lu] valid range", | "read failed: actually read %d bytes count not in [0..%lu] valid range", |
| read_size, (unsigned long)max_size); //never | data_size, (unsigned long)max_size); //never |
| if(as_text) | if(as_text) { |
| fix_line_breaks((char *)data, read_size); | fix_line_breaks((char *)data, data_size); |
| // note: after fixing | |
| ((char*&)data)[data_size]=0; | |
| } | |
| return true; | return true; |
| } | } |
| if(fail_on_read_problem) | if(fail_on_read_problem) |
| Line 137 static void create_dir_for_file(const St | Line 198 static void create_dir_for_file(const St |
| } | } |
| } | } |
| void file_write(Pool& pool, | bool file_write_action_under_lock( |
| const String& file_spec, | const String& file_spec, |
| const void *data, size_t size, | const char *action_name, void (*action)(int, void *), void *context, |
| bool as_text/*, | bool as_text, |
| bool exclusive*/) { | bool do_append, |
| bool do_block) { | |
| const char *fname=file_spec.cstr(String::UL_FILE_SPEC); | const char *fname=file_spec.cstr(String::UL_FILE_SPEC); |
| int f; | int f; |
| if(access(fname, W_OK)!=0) {/*no*/ | if(access(fname, W_OK)!=0) // no |
| create_dir_for_file(file_spec); | create_dir_for_file(file_spec); |
| if((f=open(fname, O_WRONLY|O_CREAT|_O_BINARY, 0666))>0) | if((f=open(fname, |
| O_CREAT|O_RDWR | |
| |(as_text?_O_TEXT:_O_BINARY) | |
| |(do_append?O_APPEND:O_TRUNC), 0664))>=0) { | |
| if((do_block?lock_exclusive_blocking(f):lock_exclusive_nonblocking(f))!=0) { | |
| close(f); | close(f); |
| } | return false; |
| if(access(fname, R_OK|W_OK)==0) { | } |
| int mode=O_RDWR|(as_text?_O_TEXT:_O_BINARY) | |
| #ifdef WIN32 | try { |
| |O_TRUNC | action(f, context); |
| #endif | } catch(...) { |
| ; | #if O_TRUNC==0 |
| if((f=open(fname, mode, 0666))>=0) { | if(!do_append) |
| /*if(exclusive) | ftruncate(f, tell(f)); |
| flock(f, LOCK_EX);*/ | |
| if(size) write(f, data, size); | |
| #ifndef WIN32 | |
| ftruncate(f, size); | |
| #endif | #endif |
| /*if(exclusive) | unlock(f); |
| flock(f, LOCK_UN);*/ | |
| close(f); | close(f); |
| return; | /*re*/throw; |
| } | } |
| } | |
| throw Exception(0, 0, | #if O_TRUNC==0 |
| &file_spec, | if(!do_append) |
| "write failed: %s (%d), actual filename '%s'", | ftruncate(f, tell(f)); |
| strerror(errno), errno, fname); | #endif |
| unlock(f); | |
| close(f); | |
| return true; | |
| } else | |
| throw Exception(0, 0, | |
| &file_spec, | |
| "%s failed: %s (%d), actual filename '%s'", | |
| action_name, strerror(errno), errno, fname); | |
| // here should be nothing, see rethrow above | |
| } | |
| #ifndef DOXYGEN | |
| struct File_write_action_info { | |
| const void *data; size_t size; | |
| }; | |
| #endif | |
| static void file_write_action(int f, void *context) { | |
| File_write_action_info& info=*static_cast<File_write_action_info *>(context); | |
| if(info.size) | |
| write(f, info.data, info.size); | |
| } | |
| void file_write( | |
| const String& file_spec, | |
| const void *data, size_t size, | |
| bool as_text, | |
| bool do_append) { | |
| File_write_action_info info={data, size}; | |
| file_write_action_under_lock( | |
| file_spec, | |
| "write", file_write_action, &info, | |
| as_text, | |
| do_append); | |
| } | } |
| // throws nothing! [this is required in file_move & file_delete] | // throws nothing! [this is required in file_move & file_delete] |
| Line 184 static void rmdir(const String& file_spe | Line 276 static void rmdir(const String& file_spe |
| rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::UL_FILE_SPEC)); | rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::UL_FILE_SPEC)); |
| } | } |
| void file_delete(Pool& pool, const String& file_spec) { | bool file_delete(const String& file_spec, bool fail_on_read_problem) { |
| const char *fname=file_spec.cstr(String::UL_FILE_SPEC); | const char *fname=file_spec.cstr(String::UL_FILE_SPEC); |
| if(unlink(fname)!=0) | if(unlink(fname)!=0) |
| throw Exception(0, 0, | if(fail_on_read_problem) |
| &file_spec, | throw Exception(0, 0, |
| "unlink failed: %s (%d), actual filename '%s'", | &file_spec, |
| strerror(errno), errno, fname); | "unlink failed: %s (%d), actual filename '%s'", |
| strerror(errno), errno, fname); | |
| else | |
| return false; | |
| rmdir(file_spec, 1); | rmdir(file_spec, 1); |
| return true; | |
| } | } |
| void file_move(Pool& pool, const String& old_spec, const String& new_spec) { | void file_move(const String& old_spec, const String& new_spec) { |
| const char *old_spec_cstr=old_spec.cstr(String::UL_FILE_SPEC); | const char *old_spec_cstr=old_spec.cstr(String::UL_FILE_SPEC); |
| const char *new_spec_cstr=new_spec.cstr(String::UL_FILE_SPEC); | const char *new_spec_cstr=new_spec.cstr(String::UL_FILE_SPEC); |
| Line 302 char *rsplit(char *string, char delim) { | Line 398 char *rsplit(char *string, char delim) { |
| /// @todo less stupid type detection | /// @todo less stupid type detection |
| char *format(Pool& pool, double value, char *fmt) { | char *format(Pool& pool, double value, char *fmt) { |
| char *result=(char *)pool.malloc(MAX_NUMBER); | char *result=(char *)pool.malloc(MAX_NUMBER, 4); |
| if(fmt) | if(fmt) |
| if(strpbrk(fmt, "diouxX")) | if(strpbrk(fmt, "diouxX")) |
| if(strpbrk(fmt, "ouxX")) | if(strpbrk(fmt, "ouxX")) |
| Line 334 size_t stdout_write(const void *buf, siz | Line 430 size_t stdout_write(const void *buf, siz |
| } | } |
| char *unescape_chars(Pool& pool, const char *cp, int len) { | char *unescape_chars(Pool& pool, const char *cp, int len) { |
| char *s=(char *)pool.malloc(len + 1); | char *s=(char *)pool.malloc(len + 1, 5); |
| enum EscapeState { | enum EscapeState { |
| EscapeRest, | EscapeRest, |
| EscapeFirst, | EscapeFirst, |
| Line 444 bool StrEqNc(const char *s1, const char | Line 540 bool StrEqNc(const char *s1, const char |
| } | } |
| } | } |
| static int isLeap(int year) { | static bool isLeap(int year) { |
| return !( | return !( |
| (year % 4) || ((year % 400) && !(year % 100)) | (year % 4) || ((year % 400) && !(year % 100)) |
| ); | ); |
| Line 475 void remove_crlf(char *start, char *end) | Line 571 void remove_crlf(char *start, char *end) |
| case '\r': *p=' '; break; | case '\r': *p=' '; break; |
| } | } |
| } | } |
| /// must be last in this file | |
| #undef vsnprintf | |
| int __vsnprintf(char *b, size_t s, const char *f, va_list l) { | |
| if(!s) | |
| return 0; | |
| int r; | |
| // note: on win32& maybe somewhere else | |
| // vsnprintf do not writes terminating 0 in 'buffer full' case, reducing | |
| --s; | |
| #if _MSC_VER | |
| /* | |
| win32: | |
| mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001APR\1033\vccore.chm::/html/_crt__vsnprintf.2c_._vsnwprintf.htm | |
| if the number of bytes to write exceeds buffer, then count bytes are written and –1 is returned | |
| */ | |
| r=_vsnprintf(b, s, f, l); | |
| if(r<0) | |
| r=s; | |
| #else | |
| r=vsnprintf(b, s, f, l); | |
| /* | |
| solaris: | |
| man vsnprintf | |
| The snprintf() function returns the number of characters | |
| formatted, that is, the number of characters that would have | |
| been written to the buffer if it were large enough. If the | |
| value of n is 0 on a call to snprintf(), an unspecified | |
| value less than 1 is returned. | |
| */ | |
| if(r<0) | |
| r=0; | |
| else if(r>s) | |
| r=s; | |
| #endif | |
| b[r]=0; | |
| return r; | |
| } | |
| int __snprintf(char *b, size_t s, const char *f, ...) { | |
| va_list l; | |
| va_start(l, f); | |
| int r=__vsnprintf(b, s, f, l); | |
| va_end(l); | |
| return r; | |
| } | |
| int pa_sleep(unsigned long secs, unsigned long usecs) { | |
| for (; usecs >= 1000000; ++secs, usecs -= 1000000); | |
| #ifdef WIN32 | |
| Sleep(secs * 1000 + usecs / 1000); | |
| return 0; | |
| #else | |
| struct timeval t; | |
| t.tv_sec = secs; | |
| t.tv_usec = usecs; | |
| return (select(0, NULL, NULL, NULL, &t) == -1 ? errno : 0); | |
| #endif | |
| } |