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