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