|
|
| version 1.92, 2001/11/23 12:56:37 | version 1.115, 2002/08/05 13:48:51 |
|---|---|
| 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://paf.design.ru) | Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) |
| $Id$ | |
| */ | */ |
| static const char* IDENT_COMMON_C="$Date$"; | |
| #include "pa_common.h" | #include "pa_common.h" |
| #include "pa_types.h" | #include "pa_types.h" |
| #include "pa_exception.h" | #include "pa_exception.h" |
| Line 16 | Line 16 |
| #include "pa_hash.h" | #include "pa_hash.h" |
| #include "pa_string.h" | #include "pa_string.h" |
| #ifdef WIN32 | |
| # include <windows.h> | |
| #endif | |
| // some maybe-undefined constants | |
| #ifndef _O_TEXT | #ifndef _O_TEXT |
| # define _O_TEXT 0 | # define _O_TEXT 0 |
| #endif | #endif |
| Line 26 | Line 32 |
| # define O_TRUNC 0 | # define O_TRUNC 0 |
| #endif | #endif |
| // 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 | |
| static char *strnchr(char *buf, size_t size, char c) { | static char *strnchr(char *buf, size_t size, char c) { |
| // sanity check | |
| if(!buf) | |
| return 0; | |
| for(; size-->0; buf++) { | for(; size-->0; buf++) { |
| if(*buf==c) | if(*buf==c) |
| return buf; | return buf; |
| Line 81 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 && | if(lock_shared_blocking(f)!=0) { |
| stat(fname, &finfo)==0) { | Exception e("file.lock", |
| /*if(exclusive) | &file_spec, |
| flock(f, LOCK_EX);*/ | "shared lock failed: %s (%d), actual filename '%s'", |
| size_t max_size=limit?min(offset+limit, finfo.st_size)-offset:finfo.st_size; | strerror(errno), errno, fname); |
| int read_size; | unlock(f); |
| close(f); | |
| if(fail_on_read_problem) | |
| throw e; | |
| return false; | |
| } | |
| if(stat(fname, &finfo)!=0) { | |
| Exception e("file.missing", | |
| &file_spec, | |
| "stat failed: %s (%d), actual filename '%s'", | |
| strerror(errno), errno, fname); | |
| unlock(f); | |
| close(f); | |
| if(fail_on_read_problem) | |
| throw e; | |
| return false; | |
| } | |
| #ifdef NO_FOREIGN_GROUP_FILES | |
| if(finfo.st_gid/*foreign?*/!=getegid()) { | |
| Exception e("parser.runtime", | |
| &file_spec, | |
| "parser reading files of foreign group disabled [recompile parser without --disable-foreign-group-files configure option], actual filename '%s'", | |
| fname); | |
| unlock(f); | |
| close(f); | |
| if(fail_on_read_problem) | |
| throw e; | |
| return false; | |
| } | |
| #endif | |
| 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 |
| if(as_text) { | if(as_text) { |
| data=pool.malloc(1); | data=pool.malloc(1); |
| *(char*)data=0; | *(char*)data=0; |
| } else | } else |
| data=0; | data=0; |
| read_size=0; | data_size=0; |
| } else { | } else { |
| data=pool.malloc(max_size+(as_text?1:0), 3); | 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) |
| throw Exception(0, 0, | throw Exception(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 |
| data_size=read_size; | |
| if(as_text) { | if(as_text) { |
| fix_line_breaks((char *)data, data_size); | fix_line_breaks((char *)data, data_size); |
| // note: after fixing | // note: after fixing |
| Line 122 bool file_read(Pool& pool, const String& | Line 205 bool file_read(Pool& pool, const String& |
| return true; | return true; |
| } | } |
| if(fail_on_read_problem) | if(fail_on_read_problem) |
| throw Exception(0, 0, | throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0, |
| &file_spec, | &file_spec, |
| "read failed: %s (%d), actual filename '%s'", | "read failed: %s (%d), actual filename '%s'", |
| strerror(errno), errno, fname); | strerror(errno), errno, fname); |
| Line 138 static void create_dir_for_file(const St | Line 221 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 do_append/*, | bool do_append, |
| bool exclusive*/) { | bool do_block, |
| bool fail_on_lock_problem) { | |
| 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 |
| Line 153 void file_write(Pool& pool, | Line 237 void file_write(Pool& pool, |
| O_CREAT|O_RDWR | O_CREAT|O_RDWR |
| |(as_text?_O_TEXT:_O_BINARY) | |(as_text?_O_TEXT:_O_BINARY) |
| |(do_append?O_APPEND:O_TRUNC), 0664))>=0) { | |(do_append?O_APPEND:O_TRUNC), 0664))>=0) { |
| /*if(exclusive) | if((do_block?lock_exclusive_blocking(f):lock_exclusive_nonblocking(f))!=0) { |
| flock(f, LOCK_EX);*/ | Exception e("file.lock", |
| &file_spec, | |
| "shared lock failed: %s (%d), actual filename '%s'", | |
| strerror(errno), errno, fname); | |
| close(f); | |
| if(fail_on_lock_problem) | |
| throw e; | |
| return false; | |
| } | |
| try { | |
| action(f, context); | |
| } catch(...) { | |
| #if O_TRUNC==0 | |
| if(!do_append) | |
| ftruncate(f, tell(f)); | |
| #endif | |
| unlock(f); | |
| close(f); | |
| /*re*/throw; | |
| } | |
| if(size) write(f, data, size); | |
| #if O_TRUNC==0 | #if O_TRUNC==0 |
| ftruncate(f, size); | if(!do_append) |
| ftruncate(f, tell(f)); | |
| #endif | #endif |
| /*if(exclusive) | unlock(f); |
| flock(f, LOCK_UN);*/ | |
| close(f); | close(f); |
| return true; | |
| } else | } else |
| throw Exception(0, 0, | throw Exception(errno==EACCES?"file.access":0, |
| &file_spec, | &file_spec, |
| "write failed: %s (%d), actual filename '%s'", | "%s failed: %s (%d), actual filename '%s'", |
| strerror(errno), errno, fname); | 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) { | |
| size_t written=write(f, info.data, info.size); | |
| if(errno) | |
| throw Exception(0, | |
| 0, | |
| "write failed: %s (%d)", strerror(errno), errno); | |
| } | |
| } | |
| 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 178 static void rmdir(const String& file_spe | Line 311 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(errno==EACCES?"file.access":errno==ENOENT?"file.missing":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); |
| create_dir_for_file(new_spec); | create_dir_for_file(new_spec); |
| if(rename(old_spec_cstr, new_spec_cstr)!=0) | if(rename(old_spec_cstr, new_spec_cstr)!=0) |
| throw Exception(0, 0, | throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0, |
| &old_spec, | &old_spec, |
| "rename failed: %s (%d), actual filename '%s' to '%s'", | "rename failed: %s (%d), actual filename '%s' to '%s'", |
| strerror(errno), errno, old_spec_cstr, new_spec_cstr); | strerror(errno), errno, old_spec_cstr, new_spec_cstr); |
| Line 208 static bool entry_readable(const String& | Line 345 static bool entry_readable(const String& |
| const char *fname=file_spec.cstr(String::UL_FILE_SPEC); | const char *fname=file_spec.cstr(String::UL_FILE_SPEC); |
| struct stat finfo; | struct stat finfo; |
| if(access(fname, R_OK)==0 && stat(fname, &finfo)==0) { | if(access(fname, R_OK)==0 && stat(fname, &finfo)==0) { |
| bool is_dir=finfo.st_mode&S_IFDIR != 0; | bool is_dir=(finfo.st_mode&S_IFDIR) != 0; |
| return is_dir==need_dir; | return is_dir==need_dir; |
| } | } |
| return false; | return false; |
| Line 240 bool file_stat(const String& file_spec, | Line 377 bool file_stat(const String& file_spec, |
| struct stat finfo; | struct stat finfo; |
| if(stat(fname, &finfo)!=0) | if(stat(fname, &finfo)!=0) |
| if(fail_on_read_problem) | if(fail_on_read_problem) |
| throw Exception(0, 0, | throw Exception("file.missing", |
| &file_spec, | &file_spec, |
| "getting file size failed: %s (%d), real filename '%s'", | "getting file size failed: %s (%d), real filename '%s'", |
| strerror(errno), errno, fname); | strerror(errno), errno, fname); |
| Line 296 char *rsplit(char *string, char delim) { | Line 433 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, 4); | char local_buf[MAX_NUMBER]; |
| size_t size; | |
| if(fmt) | if(fmt) |
| if(strpbrk(fmt, "diouxX")) | if(strpbrk(fmt, "diouxX")) |
| if(strpbrk(fmt, "ouxX")) | if(strpbrk(fmt, "ouxX")) |
| snprintf(result, MAX_NUMBER, fmt, (uint)value ); | size=snprintf(local_buf, sizeof(local_buf), fmt, (uint)value); |
| else | else |
| snprintf(result, MAX_NUMBER, fmt, (int)value ); | size=snprintf(local_buf, sizeof(local_buf), fmt, (int)value); |
| else | else |
| snprintf(result, MAX_NUMBER, fmt, value); | size=snprintf(local_buf, sizeof(local_buf), fmt, value); |
| else | else |
| snprintf(result, MAX_NUMBER, "%d", (int)value); | size=snprintf(local_buf, sizeof(local_buf), "%d", (int)value); |
| return result; | char *pool_buf=(char *)pool.malloc(size+1, 4); |
| memcpy(pool_buf, local_buf, size+1); | |
| return pool_buf; | |
| } | } |
| size_t stdout_write(const void *buf, size_t size) { | size_t stdout_write(const void *buf, size_t size) { |
| Line 520 int __snprintf(char *b, size_t s, const | Line 661 int __snprintf(char *b, size_t s, const |
| va_end(l); | va_end(l); |
| return r; | 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 | |
| } |