]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/utils.c
491f168a65d6f7cf1288ee8ff2d1f20a3b7b916b
[OpenSRF.git] / src / libopensrf / utils.c
1 /*
2 Copyright (C) 2005  Georgia Public Library Service 
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 */
14
15 #include <opensrf/utils.h>
16 #include <opensrf/log.h>
17 #include <errno.h>
18
19 static const char hex_chars[]   = "0123456789abcdef";
20 static unsigned char hex_code[7] = "\\u00";
21
22 inline void* safe_malloc( int size ) {
23         void* ptr = (void*) malloc( size );
24         if( ptr == NULL ) {
25                 osrfLogError( OSRF_LOG_MARK, "Out of Memory" );
26                 exit(99);
27         }
28         memset( ptr, 0, size ); // remove this after safe_calloc transition
29         return ptr;
30 }
31
32 inline void* safe_calloc( int size ) {
33         void* ptr = (void*) calloc( 1, size );
34         if( ptr == NULL ) {
35                 osrfLogError( OSRF_LOG_MARK, "Out of Memory" );
36                 exit(99);
37         }
38         return ptr;
39 }
40
41 /****************
42  The following static variables, and the following two functions,
43  overwrite the argv array passed to main().  The purpose is to
44  change the program name as reported by ps and similar utilities.
45
46  Warning: this code makes the non-portable assumption that the
47  strings to which argv[] points are contiguous in memory.  The
48  C Standard makes no such guarantee.
49  ****************/
50 static char** global_argv = NULL;
51 static int global_argv_size = 0;
52
53 int init_proc_title( int argc, char* argv[] ) {
54
55         global_argv = argv;
56
57         int i = 0;
58         while( i < argc ) {
59                 int len = strlen( global_argv[i]);
60                 osrf_clearbuf( global_argv[i], len );
61                 global_argv_size += len;
62                 i++;
63         }
64
65         global_argv_size -= 2;
66         return 0;
67 }
68
69 int set_proc_title( const char* format, ... ) {
70         VA_LIST_TO_STRING(format);
71         osrf_clearbuf( *(global_argv), global_argv_size);
72         return snprintf( *(global_argv), global_argv_size, VA_BUF );
73 }
74
75
76 /* utility method for profiling */
77 double get_timestamp_millis( void ) {
78         struct timeval tv;
79         gettimeofday(&tv, NULL);
80         double time     = (int)tv.tv_sec        + ( ((double)tv.tv_usec / 1000000) );
81         return time;
82 }
83
84
85 /* setting/clearing file flags */
86 int set_fl( int fd, int flags ) {
87         
88         int val;
89
90         if( (val = fcntl( fd, F_GETFL, 0) ) < 0 ) 
91                 return -1;
92
93         val |= flags;
94
95         if( fcntl( fd, F_SETFL, val ) < 0 ) 
96                 return -1;
97
98         return 0;
99 }
100         
101 int clr_fl( int fd, int flags ) {
102         
103         int val;
104
105         if( (val = fcntl( fd, F_GETFL, 0) ) < 0 ) 
106                 return -1;
107
108         val &= ~flags;
109
110         if( fcntl( fd, F_SETFL, val ) < 0 ) 
111                 return -1;
112
113         return 0;
114 }
115
116 long va_list_size(const char* format, va_list args) {
117         int len = 0;
118         len = vsnprintf(NULL, 0, format, args);
119         va_end(args);
120         len += 2;
121         return len;
122 }
123
124
125 char* va_list_to_string(const char* format, ...) {
126
127         long len = 0;
128         va_list args;
129         va_list a_copy;
130
131         va_copy(a_copy, args);
132
133         va_start(args, format);
134         len = va_list_size(format, args);
135
136         char buf[len];
137         osrf_clearbuf(buf, sizeof(buf));
138
139         va_start(a_copy, format);
140         vsnprintf(buf, len - 1, format, a_copy);
141         va_end(a_copy);
142         return strdup(buf);
143 }
144
145 // ---------------------------------------------------------------------------------
146 // Flesh out a ubiqitous growing string buffer
147 // ---------------------------------------------------------------------------------
148
149 growing_buffer* buffer_init(int num_initial_bytes) {
150
151         if( num_initial_bytes > BUFFER_MAX_SIZE ) return NULL;
152
153         size_t len = sizeof(growing_buffer);
154
155         growing_buffer* gb;
156         OSRF_MALLOC(gb, len);
157
158         gb->n_used = 0;/* nothing stored so far */
159         gb->size = num_initial_bytes;
160         OSRF_MALLOC(gb->buf, gb->size + 1);
161
162         return gb;
163 }
164
165
166 /* Expand the internal buffer of a growing_buffer so that it */
167 /* will accommodate a specified string length.  Return 0 if  */
168 /* successful, or 1 otherwise. */
169
170 /* Note that we do not check to see if the buffer is already */
171 /* big enough.  It is the responsibility of the calling      */
172 /* function to call this only when necessary. */
173
174 static int buffer_expand( growing_buffer* gb, size_t total_len ) {
175
176         // Make sure the request is not excessive
177         
178         if( total_len >= BUFFER_MAX_SIZE ) {
179                 fprintf(stderr, "Buffer reached MAX_SIZE of %lu",
180                                 (unsigned long) BUFFER_MAX_SIZE );
181                 buffer_free( gb );
182                 return 1;
183         }
184
185         // Pick a big enough buffer size, but don't exceed a maximum
186         
187         while( total_len >= gb->size ) {
188                 gb->size *= 2;
189         }
190
191         if( gb->size > BUFFER_MAX_SIZE )
192                 gb->size = BUFFER_MAX_SIZE;
193
194         // Allocate and populate the new buffer
195         
196         char* new_data;
197         OSRF_MALLOC( new_data, gb->size );
198         memcpy( new_data, gb->buf, gb->n_used );
199         new_data[ gb->n_used ] = '\0';
200
201         // Replace the old buffer
202         
203         free( gb->buf );
204         gb->buf = new_data;
205         return 0;
206 }
207
208
209 int buffer_fadd(growing_buffer* gb, const char* format, ... ) {
210
211         if(!gb || !format) return 0; 
212
213         long len = 0;
214         va_list args;
215         va_list a_copy;
216
217         va_copy(a_copy, args);
218
219         va_start(args, format);
220         len = va_list_size(format, args);
221
222         char buf[len];
223         osrf_clearbuf(buf, sizeof(buf));
224
225         va_start(a_copy, format);
226         vsnprintf(buf, len - 1, format, a_copy);
227         va_end(a_copy);
228
229         return buffer_add(gb, buf);
230
231 }
232
233
234 int buffer_add(growing_buffer* gb, const char* data) {
235         if(!(gb && data)) return 0;
236
237         int data_len = strlen( data );
238
239         if(data_len == 0) return 0;
240
241         int total_len = data_len + gb->n_used;
242
243         if( total_len >= gb->size ) {
244                 if( buffer_expand( gb, total_len ) )
245                         return -1;
246         }
247
248         strcpy( gb->buf + gb->n_used, data );
249         gb->n_used = total_len;
250         return total_len;
251 }
252
253
254 int buffer_reset( growing_buffer *gb){
255         if( gb == NULL ) { return -1; }
256         if( gb->buf == NULL ) { return -1; }
257         osrf_clearbuf( gb->buf, gb->size );
258         gb->n_used = 0;
259         gb->buf[ 0 ] = '\0';
260         return gb->n_used;
261 }
262
263 /* Return a pointer to the text within a growing_buffer, */
264 /* while destroying the growing_buffer itself.           */
265
266 char* buffer_release( growing_buffer* gb) {
267         char* s = gb->buf;
268         s[gb->n_used] = '\0';
269         free( gb );
270         return s;
271 }
272
273 /* Destroy a growing_buffer and the text it contains */
274
275 int buffer_free( growing_buffer* gb ) {
276         if( gb == NULL ) 
277                 return 0;
278         free( gb->buf );
279         free( gb );
280         return 1;
281 }
282
283 char* buffer_data( const growing_buffer *gb) {
284         return strdup( gb->buf );
285 }
286
287 int buffer_chomp(growing_buffer* gb) {
288         if( gb == NULL ) { return -1; }
289     if(gb->n_used > 0) {
290             gb->n_used--;
291             gb->buf[gb->n_used] = '\0';
292     }
293     return gb->n_used;
294 }
295
296
297 /*
298 #define OSRF_BUFFER_ADD_CHAR(gb, c)\
299         do {\
300                 if(gb) {\
301                         if(gb->n_used < gb->size - 1)\
302                                 gb->buf[gb->n_used++] = c;\
303                         else\
304                                 buffer_add_char(gb, c);\
305                 }\
306         }while(0)
307         */
308
309 int buffer_add_char(growing_buffer* gb, char c ) {
310         if(gb && gb->buf) {
311
312                 int total_len = gb->n_used + 1;
313
314                 if( total_len >= gb->size ) {
315                         if( buffer_expand( gb, total_len ) )
316                                 return -1;
317                 }
318         
319                 gb->buf[ gb->n_used ]   = c;
320                 gb->buf[ ++gb->n_used ] = '\0';
321         }
322         
323         return gb->n_used;
324 }
325
326
327 char* uescape( const char* string, int size, int full_escape ) {
328
329         if( NULL == string )
330                 return NULL;
331         
332         growing_buffer* buf = buffer_init(size + 64);
333         int clen = 0;
334         int idx = 0;
335         unsigned long int c = 0x0;
336
337         while (string[idx]) {
338
339                 c = 0x0;
340
341                 if ((unsigned char)string[idx] >= 0x80) { // not ASCII
342
343                         if ((unsigned char)string[idx] >= 0xC0 && (unsigned char)string[idx] <= 0xF4) { // starts a UTF8 string
344
345                                 clen = 1;
346                                 if (((unsigned char)string[idx] & 0xF0) == 0xF0) {
347                                         clen = 3;
348                                         c = (unsigned char)string[idx] ^ 0xF0;
349
350                                 } else if (((unsigned char)string[idx] & 0xE0) == 0xE0) {
351                                         clen = 2;
352                                         c = (unsigned char)string[idx] ^ 0xE0;
353
354                                 } else if (((unsigned char)string[idx] & 0xC0) == 0xC0) {
355                                         clen = 1;
356                                         c = (unsigned char)string[idx] ^ 0xC0;
357                                 }
358
359                                 for (;clen;clen--) {
360
361                                         idx++; // look at the next byte
362                                         c = (c << 6) | ((unsigned char)string[idx] & 0x3F); // add this byte worth
363
364                                 }
365
366                                 buffer_fadd(buf, "\\u%04x", c);
367
368                         } else {
369                                 buffer_free(buf);
370                                 return NULL;
371                         }
372
373                 } else {
374                         c = string[idx];
375
376                         /* escape the usual suspects */
377                         if(full_escape) {
378                                 switch(c) {
379                                         case '"':
380                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
381                                                 OSRF_BUFFER_ADD_CHAR(buf, '"');
382                                                 break;
383         
384                                         case '\b':
385                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
386                                                 OSRF_BUFFER_ADD_CHAR(buf, 'b');
387                                                 break;
388         
389                                         case '\f':
390                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
391                                                 OSRF_BUFFER_ADD_CHAR(buf, 'f');
392                                                 break;
393         
394                                         case '\t':
395                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
396                                                 OSRF_BUFFER_ADD_CHAR(buf, 't');
397                                                 break;
398         
399                                         case '\n':
400                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
401                                                 OSRF_BUFFER_ADD_CHAR(buf, 'n');
402                                                 break;
403         
404                                         case '\r':
405                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
406                                                 OSRF_BUFFER_ADD_CHAR(buf, 'r');
407                                                 break;
408
409                                         case '\\':
410                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
411                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
412                                                 break;
413
414                                         default:
415                                                 if( c < 32 ) buffer_fadd(buf, "\\u%04x", c);
416                                                 else OSRF_BUFFER_ADD_CHAR(buf, c);
417                                 }
418
419                         } else {
420                                 OSRF_BUFFER_ADD_CHAR(buf, c);
421                         }
422                 }
423
424                 idx++;
425         }
426
427         return buffer_release(buf);
428 }
429
430 int buffer_append_uescape( growing_buffer* buf, const char* string ) {
431
432         if(NULL == string)
433                 return 0;       // Nothing to add?  Nothing to do
434
435         if( NULL == buf )
436                 return -1;      // Nothing to add to
437
438         int idx = 0;
439         unsigned long int c;
440
441         while (string[idx]) {
442
443                 c = 0x0;
444
445                 if ((unsigned char)string[idx] >= 0x80) { // not ASCII
446
447                         if ((unsigned char)string[idx] >= 0xC0 && (unsigned char)string[idx] <= 0xF4) { // starts a UTF8 string
448
449                                 int clen = 1;
450                                 if (((unsigned char)string[idx] & 0xF0) == 0xF0) {
451                                         clen = 3;
452                                         c = (unsigned char)string[idx] ^ 0xF0;
453
454                                 } else if (((unsigned char)string[idx] & 0xE0) == 0xE0) {
455                                         clen = 2;
456                                         c = (unsigned char)string[idx] ^ 0xE0;
457
458                                 } else if (((unsigned char)string[idx] & 0xC0) == 0xC0) {
459                                         clen = 1;
460                                         c = (unsigned char)string[idx] ^ 0xC0;
461                                 }
462
463                                 for (;clen;clen--) {
464
465                                         idx++; // look at the next byte
466                                         c = (c << 6) | ((unsigned char)string[idx] & 0x3F); // add this byte worth
467                                 }
468
469                                 buffer_fadd(buf, "\\u%04x", c);
470
471                         } else {
472                                 return idx + 1;
473                         }
474
475                 } else if(string[idx] >= ' ' ) { // printable ASCII character
476
477                         c = string[idx];
478                         switch(c) {
479                                 case '"':
480                                 case '\\':
481                                         OSRF_BUFFER_ADD_CHAR(buf, '\\');
482                                         OSRF_BUFFER_ADD_CHAR(buf, c);
483                                         break;
484
485                 default:
486                                 OSRF_BUFFER_ADD_CHAR(buf, c);
487             }
488
489                 } else {
490                         c = string[idx];
491
492                         /* escape the usual suspects */
493                         switch(c) {
494                                 case '\b':
495                                         OSRF_BUFFER_ADD_CHAR(buf, '\\');
496                                         OSRF_BUFFER_ADD_CHAR(buf, 'b');
497                                         break;
498         
499                                 case '\f':
500                                         OSRF_BUFFER_ADD_CHAR(buf, '\\');
501                                         OSRF_BUFFER_ADD_CHAR(buf, 'f');
502                                         break;
503         
504                                 case '\t':
505                                         OSRF_BUFFER_ADD_CHAR(buf, '\\');
506                                         OSRF_BUFFER_ADD_CHAR(buf, 't');
507                                         break;
508         
509                                 case '\n':
510                                         OSRF_BUFFER_ADD_CHAR(buf, '\\');
511                                         OSRF_BUFFER_ADD_CHAR(buf, 'n');
512                                         break;
513         
514                                 case '\r':
515                                         OSRF_BUFFER_ADD_CHAR(buf, '\\');
516                                         OSRF_BUFFER_ADD_CHAR(buf, 'r');
517                                         break;
518
519                                 default:
520                                 {
521                                         // Represent as \u followed by four hex characters
522                                         hex_code[ 4 ] = hex_chars[ c >> 4 ];    // high nybble
523                                         hex_code[ 5 ] = hex_chars[ c & 0x0F ];  // low nybble
524                                         hex_code[ 6 ] = '\0';
525                                         OSRF_BUFFER_ADD(buf, (char *) hex_code);
526                                         break;
527                                 }
528                         }
529                 }
530
531                 idx++;
532         }
533
534         return 0;
535 }
536
537 // A function to turn a process into a daemon 
538 int daemonize( void ) {
539         pid_t f = fork();
540
541         if (f == -1) {
542                 osrfLogError( OSRF_LOG_MARK, "Failed to fork!" );
543                 return -1;
544
545         } else if (f == 0) { // We're in the child now...
546                 
547                 // Change directories.  Otherwise whatever directory
548                 // we're in couldn't be deleted until the program
549                 // terminated -- possibly causing some inconvenience.
550                 chdir( "/" );
551
552                 /* create new session */
553                 setsid();
554
555                 // Now that we're no longer attached to a terminal,
556                 // we don't want any traffic on the standard streams
557                 freopen( "/dev/null", "r", stdin );
558                 freopen( "/dev/null", "w", stdout );
559                 freopen( "/dev/null", "w", stderr );
560                 
561                 return 0;
562
563         } else { // We're in the parent...
564                 _exit(0);
565         }
566 }
567
568
569 /* Return 1 if the string represents an integer,  */
570 /* as recognized by strtol(); Otherwise return 0. */
571
572 int stringisnum(const char* s) {
573         char* w;
574         strtol(s, &w, 10);
575         return *w ? 0 : 1;
576 }
577         
578
579
580 char* file_to_string(const char* filename) {
581
582         if(!filename) return NULL;
583
584         FILE * file = fopen( filename, "r" );
585         if( !file ) {
586                 osrfLogError( OSRF_LOG_MARK, "Unable to open file [%s]", filename );
587                 return NULL;
588         }
589
590         size_t num_read;
591         char buf[ BUFSIZ + 1 ];
592         growing_buffer* gb = buffer_init(sizeof(buf));
593
594         while( ( num_read = fread( buf, 1, sizeof(buf) - 1, file) ) ) {
595                 buf[ num_read ] = '\0';
596                 buffer_add(gb, buf);
597         }
598
599         fclose(file);
600
601         return buffer_release(gb);
602 }
603
604
605 char* md5sum( const char* text, ... ) {
606
607         struct md5_ctx ctx;
608         unsigned char digest[16];
609
610         MD5_start (&ctx);
611
612         VA_LIST_TO_STRING(text);
613
614         int i;
615         for ( i=0 ; i != strlen(VA_BUF) ; i++ )
616                 MD5_feed (&ctx, VA_BUF[i]);
617
618         MD5_stop (&ctx, digest);
619
620         char buf[16];
621         char final[256];
622         osrf_clearbuf(final, sizeof(final));
623
624         for ( i=0 ; i<16 ; i++ ) {
625                 snprintf(buf, sizeof(buf), "%02x", digest[i]);
626                 strcat( final, buf );
627         }
628
629         return strdup(final);
630
631 }
632
633 int osrfUtilsCheckFileDescriptor( int fd ) {
634
635         fd_set tmpset;
636         FD_ZERO(&tmpset);
637         FD_SET(fd, &tmpset);
638
639         struct timeval tv;
640         tv.tv_sec = 0;
641         tv.tv_usec = 0;
642
643         if( select(fd + 1, &tmpset, NULL, NULL, &tv) == -1 ) {
644                 if( errno == EBADF ) return -1;
645         }
646
647         return 0;
648 }
649