Annotation of parser3/src/main/pa_common.C, revision 1.126
1.15 paf 1: /** @file
1.16 paf 2: Parser: commonly functions.
3:
1.102 paf 4: Copyright(c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)
1.101 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.111 paf 6: */
1.16 paf 7:
1.126 ! paf 8: static const char* IDENT_COMMON_C="$Date: 2002/11/25 08:55:12 $";
1.1 paf 9:
10: #include "pa_common.h"
1.4 paf 11: #include "pa_exception.h"
1.14 paf 12: #include "pa_globals.h"
1.126 ! paf 13: #include "pa_hash.h"
! 14: #include "pa_vstring.h"
1.1 paf 15:
1.98 paf 16: #ifdef WIN32
17: # include <windows.h>
1.126 ! paf 18: #else
! 19: # define closesocket close
1.98 paf 20: #endif
21:
1.93 paf 22: // some maybe-undefined constants
23:
1.82 paf 24: #ifndef _O_TEXT
25: # define _O_TEXT 0
26: #endif
27: #ifndef _O_BINARY
28: # define _O_BINARY 0
1.47 paf 29: #endif
1.80 paf 30:
1.93 paf 31: // locking constants
32:
1.99 paf 33: #ifdef HAVE_FLOCK
34:
35: static int lock_shared_blocking(int fd) { return flock(fd, LOCK_SH); }
36: static int lock_exclusive_blocking(int fd) { return flock(fd, LOCK_EX); }
37: static int lock_exclusive_nonblocking(int fd) { return flock(fd, LOCK_EX || LOCK_NB); }
38: static int unlock(int fd) { return flock(fd, LOCK_UN); }
39:
1.98 paf 40: #else
1.99 paf 41: #ifdef HAVE__LOCKING
1.98 paf 42:
1.126 ! paf 43: #define FLOCK(operation) lseek(fd, 0, SEEK_SET); return _locking(fd, operation, 1)
1.99 paf 44: static int lock_shared_blocking(int fd) { FLOCK(_LK_LOCK); }
45: static int lock_exclusive_blocking(int fd) { FLOCK(_LK_LOCK); }
46: static int lock_exclusive_nonblocking(int fd) { FLOCK(_LK_NBLCK); }
47: static int unlock(int fd) { FLOCK(_LK_UNLCK); }
1.93 paf 48:
1.99 paf 49: #else
50: #ifdef HAVE_FCNTL
1.93 paf 51:
1.126 ! paf 52: #define FLOCK(cmd, arg) struct flock ls={arg, SEEK_SET}; return fcntl(fd, cmd, &ls)
1.99 paf 53: static int lock_shared_blocking(int fd) { FLOCK(F_SETLKW, F_RDLCK); }
54: static int lock_exclusive_blocking(int fd) { FLOCK(F_SETLKW, F_WRLCK); }
55: static int lock_exclusive_nonblocking(int fd) { FLOCK(F_SETLK, F_RDLCK); }
56: static int unlock(int fd) { FLOCK(F_SETLK, F_UNLCK); }
1.93 paf 57:
58: #else
59: #ifdef HAVE_LOCKF
1.99 paf 60:
1.126 ! paf 61: #define FLOCK(fd, operation) lseek(fd, 0, SEEK_SET); return lockf(fd, operation, 1)
1.99 paf 62: static int lock_shared_blocking(int fd) { FLOCK(F_LOCK); } // on intel solaris man doesn't have doc on shared blocking
63: static int lock_exclusive_blocking(int fd) { FLOCK(F_LOCK); }
64: static int lock_exclusive_nonblocking(int fd) { FLOCK(F_TLOCK); }
65: static int unlock(int fd) { FLOCK(F_TLOCK); }
66:
1.93 paf 67: #else
1.99 paf 68:
69: #error unable to find file locking func
70:
71: #endif
1.93 paf 72: #endif
73: #endif
74: #endif
75:
1.126 ! paf 76:
! 77: static char* strnchr(char* buf, size_t size, char c) {
1.103 paf 78: // sanity check
79: if(!buf)
80: return 0;
81:
1.126 ! paf 82: for(; size-->0; buf++) {
1.86 paf 83: if(*buf==c)
84: return buf;
85: }
86:
87: return 0;
88: }
89:
1.126 ! paf 90: void fix_line_breaks(char* buf, size_t& size) {
1.87 paf 91: //_asm int 3;
1.126 ! paf 92: const char* const eob=buf+size;
! 93: char* dest=buf;
1.72 parser 94: // fix DOS: \r\n -> \n
95: // fix Macintosh: \r -> \n
1.126 ! paf 96: char* bol=buf;
! 97: while(char* eol=strnchr(bol, eob -bol, '\r')) {
1.72 parser 98: size_t len=eol-bol;
99: if(dest!=bol)
1.126 ! paf 100: memcpy(dest, bol, len);
1.72 parser 101: dest+=len;
1.126 ! paf 102: *dest++='\n';
1.72 parser 103:
1.126 ! paf 104: if(&eol[1]<eob && eol[1]=='\n') { // \r, \n = DOS
1.72 parser 105: bol=eol+2;
1.126 ! paf 106: size--;
! 107: } else // \r, not \n = Macintosh
1.72 parser 108: bol=eol+1;
109: }
110: // last piece without \r, including terminating 0
111: if(dest!=bol)
1.126 ! paf 112: memcpy(dest, bol, eob-bol);
1.72 parser 113: }
1.18 paf 114:
1.126 ! paf 115: char* file_read_text(Pool& pool, const String& file_spec,
! 116: bool fail_on_read_problem,
! 117: Hash *params, Hash** out_fields) {
1.72 parser 118: void *result; size_t size;
1.126 ! paf 119: return file_read(pool, file_spec, result, size, true, params, out_fields, fail_on_read_problem)?(char *)result:0;
! 120: }
! 121:
! 122: //http request stuff
! 123: /* ************************ http stuff *********************** */
! 124:
! 125: static bool set_addr(struct sockaddr_in *addr, const char* host, const short port){
! 126: memset(addr, 0, sizeof(*addr));
! 127: addr->sin_family=AF_INET;
! 128: addr->sin_port=htons(port);
! 129: if(host) {
! 130: if(struct hostent *hostIP=gethostbyname(host))
! 131: memcpy(&addr->sin_addr, hostIP->h_addr, hostIP->h_length);
! 132: else
! 133: return false;
! 134: } else
! 135: addr->sin_addr.s_addr=INADDR_ANY;
! 136: return true;
! 137: }
! 138:
! 139: static void http_read_response(String& response, int sock){
! 140: while(true) {
! 141: char *buf=(char *)response.pool().malloc(MAX_STRING);
! 142: ssize_t size=recv(sock, buf, MAX_STRING, 0);
! 143: if(size<=0)
! 144: break;
! 145: response.APPEND_TAINTED(buf, size, "http response", 0);
! 146: }
! 147: }
! 148:
! 149: /* ********************** request *************************** */
! 150:
! 151: #if defined(SIGALRM) && defined(HAVE_SIGSETJMP) && defined(HAVE_SIGLONGJMP)
! 152: # define WE_CAN_USE_ALARM
! 153: #endif
! 154:
! 155: #ifdef WE_CAN_USE_ALARM
! 156: static sigjmp_buf timeout_env;
! 157: static void timeout_handler(int sig){
! 158: siglongjmp(timeout_env, 1);
! 159: }
! 160: #endif
! 161:
! 162: static void http_request(String& response,
! 163: const String *origin_string,
! 164: const char* host, int port,
! 165: const char* request,
! 166: int timeout){
! 167: if(!host)
! 168: throw Exception("http.host",
! 169: origin_string,
! 170: "zero hostname"); //never
! 171:
! 172: #ifdef WE_CAN_USE_ALARM
! 173: signal(SIGALRM, timeout_handler);
! 174: #endif
! 175: int sock=-1;
! 176: try {
! 177: #ifdef WE_CAN_USE_ALARM
! 178: if(sigsetjmp(timeout_env, 1))
! 179: throw Exception("http.timeout",
! 180: origin_string,
! 181: "timeout occured while retrieving document");
! 182: else {
! 183: alarm(timeout);
! 184: #endif
! 185: struct sockaddr_in dest;
! 186:
! 187: if(!set_addr(&dest, host, port))
! 188: throw Exception("http.host",
! 189: origin_string,
! 190: "can't resolve hostname \"%s\"", host);
! 191:
! 192: if((sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP/*0*/))<0)
! 193: throw Exception("http.connect",
! 194: origin_string,
! 195: "can't make socket : %s (%d)", strerror(errno), errno);
! 196: if(connect(sock, (struct sockaddr *)&dest, sizeof(dest)))
! 197: throw Exception("http.connect",
! 198: origin_string,
! 199: "can't connect to host \"%s\": %s (%d)", host, strerror(errno), errno);
! 200: size_t request_size=strlen(request);
! 201: if(send(sock, request, request_size, 0)!=(ssize_t)request_size)
! 202: throw Exception("http.connect",
! 203: origin_string,
! 204: "error sending request : %s (%d)", strerror(errno), errno);
! 205:
! 206: http_read_response(response, sock);
! 207: if(sock>=0)
! 208: closesocket(sock);
! 209: #ifdef WE_CAN_USE_ALARM
! 210: }
! 211: #endif
! 212: } catch(...) {
! 213: if(sock>=0)
! 214: closesocket(sock);
! 215: #ifdef WE_CAN_USE_ALARM
! 216: alarm(0);
! 217: #endif
! 218: /*re*/throw;
! 219: }
! 220: }
! 221:
! 222: static void headers_foreacher(const Hash::Key& key, Hash::Val *value, void *info)
! 223: {
! 224: String * request=static_cast<String *>(info);
! 225: *(request)<<key<<": "<<*(static_cast<Value *>(value))->get_string()<<"\n";
! 226: }
! 227:
! 228: static void file_read_http(Pool& pool, const String& file_spec,
! 229: void*& data, size_t& data_size,
! 230: Hash *params=0, Hash** out_fields=0) {
! 231: char host[MAX_STRING];
! 232: char* uri;
! 233: int port;
! 234: const char* method="GET";
! 235: int timeout=2;
! 236:
! 237: char* connect_string=file_spec.cstr(String::UL_UNSPECIFIED);
! 238: if(strncmp(connect_string, "http://", 7)!=0)
! 239: throw Exception(0,
! 240: &file_spec,
! 241: "does not start with http://"); //never
! 242: connect_string+=7;
! 243:
! 244: strncpy(host, connect_string, sizeof(host)-1); host[sizeof(host)-1]=0;
! 245: char* host_uri=lsplit(host, '/');
! 246: uri=host_uri?connect_string+(host_uri-1-host):"/";
! 247: char* port_cstr=lsplit(host, ':');
! 248: char* error_pos=0;
! 249: port=port_cstr?strtol(port_cstr, &error_pos, 0):80;
! 250:
! 251: if(params) {
! 252: if(Value *method_element=static_cast<Value *>(params->get(*http_method_name)))
! 253: method=method_element->as_string().cstr();
! 254: if(Value *timeout_element=static_cast<Value *>(params->get(*http_timeout_name)))
! 255: timeout=timeout_element->as_int();
! 256: }
! 257:
! 258: //making request
! 259: String request(pool);
! 260: request<< method <<" "<< uri <<" HTTP/1.0\nHost: "<< host<<"\n";
! 261: if(params)
! 262: if(Value *headers_element=static_cast<Value *>(params->get(*http_headers_name)))
! 263: {
! 264: Hash *headers=headers_element->get_hash(&file_spec);
! 265: headers->for_each(headers_foreacher, &request);
! 266: }
! 267: request<<"\n";
! 268:
! 269: //sending request
! 270: String response(pool);
! 271: http_request(response,
! 272: &file_spec, host, port, request.cstr(String::UL_UNSPECIFIED), timeout);
! 273:
! 274: //processing results
! 275: int pos=response.pos("\r\n\r\n", 4);
! 276: if(pos<1){
! 277: throw Exception("http.response",
! 278: &file_spec,
! 279: "bad response from host - no headers found");
! 280: }
! 281: String header_block=response.mid(0, pos);
! 282: String body=response.mid(pos+4, response.size());
! 283:
! 284: Array aheaders(pool);
! 285: Hash& headers=*new(pool) Hash(pool);
! 286: size_t pos_after_ref=0;
! 287: header_block.split(aheaders, &pos_after_ref, "\r\n", 2);
! 288:
! 289: //processing status code
! 290: const String *status_line=aheaders.get_string(0);
! 291: if(!status_line){
! 292: throw Exception("http.response",
! 293: &file_spec,
! 294: "bad response from host - no status line ");
! 295: }
! 296: //processing headers
! 297: Array astatus(pool);
! 298: pos_after_ref=0;
! 299: status_line->split(astatus, &pos_after_ref, " ", 1);
! 300: const String * status_code=astatus.get_string(1);
! 301:
! 302: for(int i=1;i<aheaders.size();i++) {
! 303: if(const String *line=aheaders.get_string(i)) {
! 304: pos=line->pos(": ", 2);
! 305: if(pos<1)
! 306: throw Exception("http.response",
! 307: &file_spec,
! 308: "bad response from host - bad header \"%s\"", line->cstr());
! 309:
! 310: headers.put(line->mid(0, pos), new(pool) VString(line->mid(pos+2, line->size())));
! 311: } else
! 312: throw Exception("http.response",
! 313: &file_spec,
! 314: "bad response from host - bad headers \"%s\"", header_block.cstr());
! 315: }
! 316:
! 317: // output response
! 318: data=body.cstr(); data_size=body.size();
! 319: if(out_fields) {
! 320: headers.put(*file_status_name, new(pool) VString(*status_code));
! 321: *out_fields=&headers;
! 322: }
1.34 paf 323: }
1.123 paf 324:
325: #ifndef DOXYGEN
326: struct File_read_action_info {
327: void **data; size_t *data_size;
1.126 ! paf 328: };
1.123 paf 329: #endif
1.126 ! paf 330: static void file_read_action(Pool& pool,
! 331: struct stat& finfo,
1.123 paf 332: int f,
1.126 ! paf 333: const String& file_spec, const char* fname, bool as_text,
1.123 paf 334: void *context) {
1.126 ! paf 335: File_read_action_info& info=*static_cast<File_read_action_info *>(context);
1.123 paf 336: if(size_t to_read_size=(size_t)finfo.st_size) {
1.126 ! paf 337: *info.data=pool.malloc(to_read_size+(as_text?1:0), 3);
! 338: *info.data_size=(size_t)read(f, *info.data, to_read_size);
1.123 paf 339:
340: if(ssize_t(*info.data_size)<0 || *info.data_size>to_read_size)
1.126 ! paf 341: throw Exception(0,
1.123 paf 342: &file_spec,
343: "read failed: actually read %lu bytes count not in [0..%lu] valid range",
1.126 ! paf 344: *info.data_size, to_read_size);
1.123 paf 345: } else { // empty file
346: if(as_text) {
1.126 ! paf 347: *info.data=pool.malloc(1);
1.123 paf 348: *(char*)(*info.data)=0;
349: } else
350: *info.data=0;
351: *info.data_size=0;
352: return;
353: }
1.126 ! paf 354: }
! 355: bool file_read(Pool& pool, const String& file_spec,
! 356: void*& data, size_t& data_size,
! 357: bool as_text, Hash *params, Hash** out_fields,
! 358: bool fail_on_read_problem) {
! 359: bool result;
! 360: if(file_spec.starts_with("http://", 7)) {
! 361: // fail on read problem
! 362: file_read_http(pool, file_spec, data, data_size, params, out_fields);
! 363: result=true;
! 364: } else {
! 365: File_read_action_info info={&data, &data_size};
! 366: result=file_read_action_under_lock(pool, file_spec,
! 367: "read", file_read_action, &info,
! 368: as_text, fail_on_read_problem);
! 369: }
1.123 paf 370:
1.126 ! paf 371: if(result && as_text) {
! 372: fix_line_breaks((char *)(data), data_size);
1.123 paf 373: // note: after fixing
1.126 ! paf 374: ((char*&)(data))[data_size]=0;
1.123 paf 375: }
1.126 ! paf 376:
! 377: return result;
1.123 paf 378: }
379:
380: bool file_read_action_under_lock(Pool& pool, const String& file_spec,
1.126 ! paf 381: const char* action_name, File_read_action action, void *context,
! 382: bool as_text,
1.123 paf 383: bool fail_on_read_problem) {
1.126 ! paf 384: const char* fname=file_spec.cstr(String::UL_FILE_SPEC);
1.33 paf 385: int f;
386:
387: // first open, next stat:
1.45 paf 388: // directory update of NTFS hard links performed on open.
1.33 paf 389: // ex:
390: // a.html:^test[] and b.html hardlink to a.html
391: // user inserts ! before ^test in a.html
1.126 ! paf 392: // directory entry of b.html in NTFS not updated at once,
1.35 paf 393: // they delay update till open, so we would receive "!^test[" string
394: // if would do stat, next open.
1.123 paf 395: // later: it seems, even this does not help sometimes
1.98 paf 396: if((f=open(fname, O_RDONLY|(as_text?_O_TEXT:_O_BINARY)))>=0) {
1.123 paf 397: try {
398: if(lock_shared_blocking(f)!=0)
1.126 ! paf 399: throw Exception("file.lock",
1.123 paf 400: &file_spec,
401: "shared lock failed: %s (%d), actual filename '%s'",
1.126 ! paf 402: strerror(errno), errno, fname);
1.123 paf 403:
1.124 paf 404: struct stat finfo;
405: if(stat(fname, &finfo)!=0)
406: throw Exception("file.missing", // hardly possible: we just opened it OK
407: &file_spec,
408: "stat failed: %s (%d), actual filename '%s'",
1.126 ! paf 409: strerror(errno), errno, fname);
1.124 paf 410:
1.105 paf 411: #ifdef NO_FOREIGN_GROUP_FILES
1.123 paf 412: if(finfo.st_gid/*foreign?*/!=getegid()) {
1.126 ! paf 413: throw Exception("parser.runtime",
! 414: &file_spec,
1.123 paf 415: "parser reading files of foreign group disabled [recompile parser without --disable-foreign-group-files configure option], actual filename '%s'",
1.126 ! paf 416: fname);
1.105 paf 417: #endif
1.32 paf 418:
1.126 ! paf 419: action(pool, finfo, f, file_spec, fname, as_text, context);
1.123 paf 420: } catch(...) {
1.126 ! paf 421: unlock(f);close(f);
1.123 paf 422: if(fail_on_read_problem)
423: /*re*/throw;
424: return false;
425: }
1.87 paf 426:
1.126 ! paf 427: unlock(f);close(f);
1.72 parser 428: return true;
1.118 paf 429: } else {
430: if(fail_on_read_problem)
1.126 ! paf 431: throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0,
1.118 paf 432: &file_spec,
1.123 paf 433: "%s failed: %s (%d), actual filename '%s'",
1.126 ! paf 434: action_name, strerror(errno), errno, fname);
1.118 paf 435: return false;
436: }
1.8 paf 437: }
438:
1.63 parser 439: static void create_dir_for_file(const String& file_spec) {
440: size_t pos_after=1;
441: int pos_before;
442: while((pos_before=file_spec.pos("/", 1, pos_after))>=0) {
1.126 ! paf 443: mkdir(file_spec.mid(0, pos_before).cstr(String::UL_FILE_SPEC), 0775);
1.63 parser 444: pos_after=pos_before+1;
445: }
446: }
447:
1.98 paf 448: bool file_write_action_under_lock(
1.28 paf 449: const String& file_spec,
1.126 ! paf 450: const char* action_name, File_write_action action, void *context,
! 451: bool as_text,
! 452: bool do_append,
! 453: bool do_block,
1.110 paf 454: bool fail_on_lock_problem) {
1.126 ! paf 455: const char* fname=file_spec.cstr(String::UL_FILE_SPEC);
1.28 paf 456: int f;
1.80 paf 457: if(access(fname, W_OK)!=0) // no
1.126 ! paf 458: create_dir_for_file(file_spec);
1.50 paf 459:
1.80 paf 460: if((f=open(fname,
461: O_CREAT|O_RDWR
462: |(as_text?_O_TEXT:_O_BINARY)
1.122 paf 463: |(do_append?O_APPEND:0), 0664))>=0) {
1.99 paf 464: if((do_block?lock_exclusive_blocking(f):lock_exclusive_nonblocking(f))!=0) {
1.126 ! paf 465: Exception e("file.lock",
1.110 paf 466: &file_spec,
467: "shared lock failed: %s (%d), actual filename '%s'",
1.126 ! paf 468: strerror(errno), errno, fname);
! 469: close(f);
1.110 paf 470: if(fail_on_lock_problem)
471: throw e;
1.98 paf 472: return false;
473: }
1.96 paf 474:
475: try {
1.126 ! paf 476: action(f, context);
1.96 paf 477: } catch(...) {
1.104 paf 478: if(!do_append)
1.125 paf 479: ftruncate(f, lseek(f, 0, SEEK_CUR)); // one can not use O_TRUNC, read lower
1.126 ! paf 480: unlock(f);close(f);
1.96 paf 481: /*re*/throw;
482: }
1.80 paf 483:
1.104 paf 484: if(!do_append)
1.125 paf 485: ftruncate(f, lseek(f, 0, SEEK_CUR)); // O_TRUNC truncates even exclusevely write-locked file [thanks to Igor Milyakov <virtan@rotabanner.com> for discovering]
1.126 ! paf 486: unlock(f);close(f);
1.98 paf 487: return true;
1.80 paf 488: } else
1.126 ! paf 489: throw Exception(errno==EACCES?"file.access":0,
1.80 paf 490: &file_spec,
1.96 paf 491: "%s failed: %s (%d), actual filename '%s'",
1.126 ! paf 492: action_name, strerror(errno), errno, fname);
1.96 paf 493: // here should be nothing, see rethrow above
494: }
495:
496: #ifndef DOXYGEN
497: struct File_write_action_info {
498: const void *data; size_t size;
1.126 ! paf 499: };
1.96 paf 500: #endif
501: static void file_write_action(int f, void *context) {
1.126 ! paf 502: File_write_action_info& info=*static_cast<File_write_action_info *>(context);
1.113 paf 503: if(info.size) {
1.126 ! paf 504: int written=write(f, info.data, info.size);
1.116 paf 505: if(written<0)
1.126 ! paf 506: throw Exception(0,
! 507: 0,
! 508: "write failed: %s (%d)", strerror(errno), errno);
1.113 paf 509: }
1.96 paf 510: }
511: void file_write(
512: const String& file_spec,
513: const void *data, size_t size,
1.126 ! paf 514: bool as_text,
1.96 paf 515: bool do_append) {
1.126 ! paf 516: File_write_action_info info={data, size};
1.98 paf 517: file_write_action_under_lock(
1.96 paf 518: file_spec,
1.126 ! paf 519: "write", file_write_action, &info,
! 520: as_text,
! 521: do_append);
1.30 paf 522: }
523:
1.63 parser 524: // throws nothing! [this is required in file_move & file_delete]
1.50 paf 525: static void rmdir(const String& file_spec, size_t pos_after) {
526: int pos_before;
527: if((pos_before=file_spec.pos("/", 1, pos_after))>=0)
1.126 ! paf 528: rmdir(file_spec, pos_before+1);
1.50 paf 529:
1.126 ! paf 530: rmdir(file_spec.mid(0, pos_after-1/* / */).cstr(String::UL_FILE_SPEC));
1.50 paf 531: }
1.95 paf 532: bool file_delete(const String& file_spec, bool fail_on_read_problem) {
1.126 ! paf 533: const char* fname=file_spec.cstr(String::UL_FILE_SPEC);
1.54 parser 534: if(unlink(fname)!=0)
1.93 paf 535: if(fail_on_read_problem)
1.126 ! paf 536: throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0,
1.93 paf 537: &file_spec,
538: "unlink failed: %s (%d), actual filename '%s'",
1.126 ! paf 539: strerror(errno), errno, fname);
1.93 paf 540: else
541: return false;
1.50 paf 542:
1.126 ! paf 543: rmdir(file_spec, 1);
1.93 paf 544: return true;
1.60 parser 545: }
1.95 paf 546: void file_move(const String& old_spec, const String& new_spec) {
1.126 ! paf 547: const char* old_spec_cstr=old_spec.cstr(String::UL_FILE_SPEC);
! 548: const char* new_spec_cstr=new_spec.cstr(String::UL_FILE_SPEC);
1.63 parser 549:
1.126 ! paf 550: create_dir_for_file(new_spec);
1.63 parser 551:
1.60 parser 552: if(rename(old_spec_cstr, new_spec_cstr)!=0)
1.126 ! paf 553: throw Exception(errno==EACCES?"file.access":errno==ENOENT?"file.missing":0,
1.60 parser 554: &old_spec,
555: "rename failed: %s (%d), actual filename '%s' to '%s'",
1.126 ! paf 556: strerror(errno), errno, old_spec_cstr, new_spec_cstr);
1.63 parser 557:
1.126 ! paf 558: rmdir(old_spec, 1);
1.31 paf 559: }
560:
1.51 paf 561:
1.126 ! paf 562: bool entry_exists(const char* fname, struct stat *afinfo) {
1.118 paf 563: struct stat lfinfo;
564: bool result=stat(fname, &lfinfo)==0;
565: if(afinfo)
566: *afinfo=lfinfo;
567: return result;
1.119 paf 568: }
569:
570: bool entry_exists(const String& file_spec) {
1.126 ! paf 571: const char* fname=file_spec.cstr(String::UL_FILE_SPEC);
! 572: return entry_exists(fname, 0);
1.118 paf 573: }
574:
1.51 paf 575: static bool entry_readable(const String& file_spec, bool need_dir) {
1.126 ! paf 576: char* fname=file_spec.cstr(String::UL_FILE_SPEC);
1.120 paf 577: if(need_dir) {
1.126 ! paf 578: size_t size=strlen(fname);
1.120 paf 579: while(size) {
1.126 ! paf 580: char c=fname[size-1];
1.120 paf 581: if(c=='/' || c=='\\')
582: fname[--size]=0;
583: else
584: break;
585: }
586: }
1.51 paf 587: struct stat finfo;
1.118 paf 588: if(access(fname, R_OK)==0 && entry_exists(fname, &finfo)) {
1.109 paf 589: bool is_dir=(finfo.st_mode&S_IFDIR) != 0;
1.51 paf 590: return is_dir==need_dir;
591: }
592: return false;
593: }
1.31 paf 594: bool file_readable(const String& file_spec) {
1.126 ! paf 595: return entry_readable(file_spec, false);
1.51 paf 596: }
597: bool dir_readable(const String& file_spec) {
1.126 ! paf 598: return entry_readable(file_spec, true);
1.65 parser 599: }
600: String *file_readable(const String& path, const String& name) {
1.126 ! paf 601: String *result=new(path.pool()) String(path);
! 602: *result << "/";
1.65 parser 603: *result << name;
604: return file_readable(*result)?result:0;
1.43 paf 605: }
606: bool file_executable(const String& file_spec) {
1.64 parser 607: return access(file_spec.cstr(String::UL_FILE_SPEC), X_OK)==0;
1.44 paf 608: }
609:
1.64 parser 610: bool file_stat(const String& file_spec,
1.58 parser 611: size_t& rsize,
1.126 ! paf 612: time_t& ratime,
! 613: time_t& rmtime,
! 614: time_t& rctime,
1.64 parser 615: bool fail_on_read_problem) {
1.126 ! paf 616: Pool& pool=file_spec.pool();
! 617: const char* fname=file_spec.cstr(String::UL_FILE_SPEC);
1.44 paf 618: struct stat finfo;
619: if(stat(fname, &finfo)!=0)
1.64 parser 620: if(fail_on_read_problem)
1.126 ! paf 621: throw Exception("file.missing",
1.67 parser 622: &file_spec,
623: "getting file size failed: %s (%d), real filename '%s'",
1.126 ! paf 624: strerror(errno), errno, fname);
1.64 parser 625: else
626: return false;
1.58 parser 627: rsize=finfo.st_size;
628: ratime=finfo.st_atime;
629: rmtime=finfo.st_mtime;
630: rctime=finfo.st_ctime;
1.64 parser 631: return true;
1.18 paf 632: }
633:
1.126 ! paf 634: char* getrow(char* *row_ref, char delim) {
! 635: char* result=*row_ref;
1.8 paf 636: if(result) {
1.126 ! paf 637: *row_ref=strchr(result, delim);
1.8 paf 638: if(*row_ref)
639: *((*row_ref)++)=0;
640: else if(!*result)
641: return 0;
642: }
643: return result;
644: }
645:
1.126 ! paf 646: char* lsplit(char* string, char delim) {
1.23 paf 647: if(string) {
1.126 ! paf 648: char* v=strchr(string, delim);
1.8 paf 649: if(v) {
650: *v=0;
651: return v+1;
652: }
653: }
654: return 0;
655: }
656:
1.126 ! paf 657: char* lsplit(char* *string_ref, char delim) {
! 658: char* result=*string_ref;
! 659: char* next=lsplit(*string_ref, delim);
1.8 paf 660: *string_ref=next;
661: return result;
1.9 paf 662: }
663:
1.126 ! paf 664: char* rsplit(char* string, char delim) {
1.18 paf 665: if(string) {
1.126 ! paf 666: char* v=strrchr(string, delim);
1.18 paf 667: if(v) {
1.9 paf 668: *v=0;
669: return v+1;
670: }
671: }
672: return NULL;
1.10 paf 673: }
674:
1.37 paf 675: /// @todo less stupid type detection
1.126 ! paf 676: char* format(Pool& pool, double value, char* fmt) {
! 677: char local_buf[MAX_NUMBER];
1.108 paf 678: size_t size;
679:
1.10 paf 680: if(fmt)
681: if(strpbrk(fmt, "diouxX"))
682: if(strpbrk(fmt, "ouxX"))
1.126 ! paf 683: size=snprintf(local_buf, sizeof(local_buf), fmt, (uint)value);
1.10 paf 684: else
1.126 ! paf 685: size=snprintf(local_buf, sizeof(local_buf), fmt, (int)value);
1.10 paf 686: else
1.126 ! paf 687: size=snprintf(local_buf, sizeof(local_buf), fmt, value);
1.10 paf 688: else
1.126 ! paf 689: size=snprintf(local_buf, sizeof(local_buf), "%d", (int)value);
1.10 paf 690:
1.126 ! paf 691: char* pool_buf=(char *)pool.malloc(size+1, 4);
! 692: memcpy(pool_buf, local_buf, size+1);
1.108 paf 693: return pool_buf;
1.12 paf 694: }
695:
1.36 paf 696: size_t stdout_write(const void *buf, size_t size) {
1.12 paf 697: #ifdef WIN32
698: do{
1.126 ! paf 699: int chunk_written=fwrite(buf, 1, min(8*0x400, size), stdout);
1.12 paf 700: if(chunk_written<=0)
701: break;
702: size-=chunk_written;
1.36 paf 703: buf=((const char*)buf)+chunk_written;
1.126 ! paf 704: } while(size>0);
1.12 paf 705:
706: return size;
707: #else
1.126 ! paf 708: return fwrite(buf, 1, size, stdout);
1.12 paf 709: #endif
1.2 paf 710: }
1.14 paf 711:
1.126 ! paf 712: char* unescape_chars(Pool& pool, const char* cp, int len) {
! 713: char* s=(char *)pool.malloc(len + 1, 5);
1.14 paf 714: enum EscapeState {
1.33 paf 715: EscapeRest,
716: EscapeFirst,
1.14 paf 717: EscapeSecond
718: } escapeState=EscapeRest;
719: int escapedValue=0;
720: int srcPos=0;
721: int dstPos=0;
722: while(srcPos < len) {
1.126 ! paf 723: int ch=cp[srcPos];
1.14 paf 724: switch(escapeState) {
725: case EscapeRest:
726: if(ch=='%') {
727: escapeState=EscapeFirst;
728: } else if(ch=='+') {
1.126 ! paf 729: s[dstPos++]=' ';
1.14 paf 730: } else {
731: s[dstPos++]=ch;
732: }
733: break;
734: case EscapeFirst:
735: escapedValue=hex_value[ch] << 4;
736: escapeState=EscapeSecond;
737: break;
738: case EscapeSecond:
1.126 ! paf 739: escapedValue +=hex_value[ch];
1.14 paf 740: s[dstPos++]=escapedValue;
741: escapeState=EscapeRest;
742: break;
743: }
1.126 ! paf 744: srcPos++;
1.14 paf 745: }
746: s[dstPos]=0;
747: return s;
1.24 paf 748: }
749:
750: #ifdef WIN32
1.126 ! paf 751: void back_slashes_to_slashes(char* s) {
1.24 paf 752: if(s)
753: for(; *s; s++)
754: if(*s=='\\')
1.126 ! paf 755: *s='/';
1.24 paf 756: }
1.42 paf 757: /*
1.126 ! paf 758: void slashes_to_back_slashes(char* s) {
1.42 paf 759: if(s)
760: for(; *s; s++)
761: if(*s=='/')
1.126 ! paf 762: *s='\\';
1.42 paf 763: }
764: */
1.24 paf 765: #endif
1.41 paf 766:
1.126 ! paf 767: bool StrEqNc(const char* s1, const char* s2, bool strict) {
1.41 paf 768: while(true) {
769: if(!(*s1)) {
770: if(!(*s2))
771: return true;
772: else
773: return !strict;
774: } else if(!(*s2))
775: return !strict;
776: if(isalpha(*s1)) {
777: if(tolower(*s1) !=tolower(*s2))
778: return false;
779: } else if((*s1) !=(*s2))
780: return false;
1.126 ! paf 781: s1++;
! 782: s2++;
1.41 paf 783: }
1.57 parser 784: }
785:
1.84 paf 786: static bool isLeap(int year) {
1.57 parser 787: return !(
788: (year % 4) || ((year % 400) && !(year % 100))
1.126 ! paf 789: );
1.57 parser 790: }
791:
792: int getMonthDays(int year, int month) {
793: int monthDays[]={
1.126 ! paf 794: 31,
! 795: isLeap(year) ? 29 : 28,
! 796: 31,
! 797: 30,
! 798: 31,
! 799: 30,
! 800: 31,
! 801: 31,
! 802: 30,
! 803: 31,
! 804: 30,
1.57 parser 805: 31
1.126 ! paf 806: };
! 807: return monthDays[month];
1.41 paf 808: }
1.69 parser 809:
1.126 ! paf 810: void remove_crlf(char* start, char* end) {
! 811: for(char* p=start; p<end; p++)
1.69 parser 812: switch(*p) {
1.126 ! paf 813: case '\n': *p='|'; break;
! 814: case '\r': *p=' '; break;
1.69 parser 815: }
1.91 paf 816: }
817:
818:
819: /// must be last in this file
820: #undef vsnprintf
1.126 ! paf 821: int __vsnprintf(char* b, size_t s, const char* f, va_list l) {
1.91 paf 822: if(!s)
823: return 0;
824:
825: int r;
826: // note: on win32& maybe somewhere else
827: // vsnprintf do not writes terminating 0 in 'buffer full' case, reducing
828: --s;
829: #if _MSC_VER
830: /*
831: win32:
832: mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001APR\1033\vccore.chm::/html/_crt__vsnprintf.2c_._vsnwprintf.htm
833:
834: if the number of bytes to write exceeds buffer, then count bytes are written and –1 is returned
835: */
1.126 ! paf 836: r=_vsnprintf(b, s, f, l);
1.91 paf 837: if(r<0)
838: r=s;
839: #else
1.126 ! paf 840: r=vsnprintf(b, s, f, l);
1.91 paf 841: /*
842: solaris:
843: man vsnprintf
844:
845: The snprintf() function returns the number of characters
846: formatted, that is, the number of characters that would have
847: been written to the buffer if it were large enough. If the
848: value of n is 0 on a call to snprintf(), an unspecified
849: value less than 1 is returned.
850: */
851:
852: if(r<0)
853: r=0;
854: else if(r>s)
855: r=s;
856: #endif
857: b[r]=0;
858: return r;
859: }
860:
1.126 ! paf 861: int __snprintf(char* b, size_t s, const char* f, ...) {
1.91 paf 862: va_list l;
1.126 ! paf 863: va_start(l, f);
! 864: int r=__vsnprintf(b, s, f, l);
! 865: va_end(l);
1.91 paf 866: return r;
1.98 paf 867: }
868:
869: int pa_sleep(unsigned long secs, unsigned long usecs) {
1.126 ! paf 870: for (; usecs >= 1000000; ++secs, usecs -= 1000000);
1.98 paf 871:
872: #ifdef WIN32
1.126 ! paf 873: Sleep(secs * 1000 + usecs / 1000);
1.98 paf 874: return 0;
875: #else
876: struct timeval t;
877: t.tv_sec = secs;
878: t.tv_usec = usecs;
1.126 ! paf 879: return (select(0, NULL, NULL, NULL, &t) == -1 ? errno : 0);
1.98 paf 880: #endif
1.74 parser 881: }
E-mail: