Add a new function buffer_add_n(), and the corresponding
[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         strcpy( gb->buf + gb->n_used, data );
246         gb->n_used = total_len;
247         return total_len;
248 }
249
250 /** Append a specified number of characters to a growing_buffer.
251     If the characters so appended include an embedded nul, the results
252     are likely to be unhappy.
253 */
254 int buffer_add_n(growing_buffer* gb, const char* data, size_t n) {
255         if(!(gb && data)) return 0;
256
257         if(n == 0) return 0;
258
259         int total_len = n + gb->n_used;
260
261         if( total_len >= gb->size ) {
262                 if( buffer_expand( gb, total_len ) )
263                         return -1;
264         }
265
266         memcpy( gb->buf + gb->n_used, data, n );
267         gb->buf[total_len] = '\0';
268         gb->n_used = total_len;
269         return total_len;
270 }
271
272
273 int buffer_reset( growing_buffer *gb){
274         if( gb == NULL ) { return -1; }
275         if( gb->buf == NULL ) { return -1; }
276         osrf_clearbuf( gb->buf, gb->size );
277         gb->n_used = 0;
278         gb->buf[ 0 ] = '\0';
279         return gb->n_used;
280 }
281
282 /* Return a pointer to the text within a growing_buffer, */
283 /* while destroying the growing_buffer itself.           */
284
285 char* buffer_release( growing_buffer* gb) {
286         char* s = gb->buf;
287         s[gb->n_used] = '\0';
288         free( gb );
289         return s;
290 }
291
292 /* Destroy a growing_buffer and the text it contains */
293
294 int buffer_free( growing_buffer* gb ) {
295         if( gb == NULL ) 
296                 return 0;
297         free( gb->buf );
298         free( gb );
299         return 1;
300 }
301
302 char* buffer_data( const growing_buffer *gb) {
303         return strdup( gb->buf );
304 }
305
306 char buffer_chomp(growing_buffer* gb) {
307         char c = '\0';
308     if(gb && gb->n_used > 0) {
309             gb->n_used--;
310                 c = gb->buf[gb->n_used];
311             gb->buf[gb->n_used] = '\0';
312     }
313     return c;
314 }
315
316
317 int buffer_add_char(growing_buffer* gb, char c ) {
318         if(gb && gb->buf) {
319
320                 int total_len = gb->n_used + 1;
321
322                 if( total_len >= gb->size ) {
323                         if( buffer_expand( gb, total_len ) )
324                                 return -1;
325                 }
326         
327                 gb->buf[ gb->n_used ]   = c;
328                 gb->buf[ ++gb->n_used ] = '\0';
329         }
330         
331         return gb->n_used;
332 }
333
334
335 char* uescape( const char* string, int size, int full_escape ) {
336
337         if( NULL == string )
338                 return NULL;
339         
340         growing_buffer* buf = buffer_init(size + 64);
341         int clen = 0;
342         int idx = 0;
343         unsigned long int c = 0x0;
344
345         while (string[idx]) {
346
347                 c = 0x0;
348
349                 if ((unsigned char)string[idx] >= 0x80) { // not ASCII
350
351                         if ((unsigned char)string[idx] >= 0xC0 && (unsigned char)string[idx] <= 0xF4) { // starts a UTF8 string
352
353                                 clen = 1;
354                                 if (((unsigned char)string[idx] & 0xF0) == 0xF0) {
355                                         clen = 3;
356                                         c = (unsigned char)string[idx] ^ 0xF0;
357
358                                 } else if (((unsigned char)string[idx] & 0xE0) == 0xE0) {
359                                         clen = 2;
360                                         c = (unsigned char)string[idx] ^ 0xE0;
361
362                                 } else if (((unsigned char)string[idx] & 0xC0) == 0xC0) {
363                                         clen = 1;
364                                         c = (unsigned char)string[idx] ^ 0xC0;
365                                 }
366
367                                 for (;clen;clen--) {
368
369                                         idx++; // look at the next byte
370                                         c = (c << 6) | ((unsigned char)string[idx] & 0x3F); // add this byte worth
371
372                                 }
373
374                                 buffer_fadd(buf, "\\u%04x", c);
375
376                         } else {
377                                 buffer_free(buf);
378                                 return NULL;
379                         }
380
381                 } else {
382                         c = string[idx];
383
384                         /* escape the usual suspects */
385                         if(full_escape) {
386                                 switch(c) {
387                                         case '"':
388                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
389                                                 OSRF_BUFFER_ADD_CHAR(buf, '"');
390                                                 break;
391         
392                                         case '\b':
393                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
394                                                 OSRF_BUFFER_ADD_CHAR(buf, 'b');
395                                                 break;
396         
397                                         case '\f':
398                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
399                                                 OSRF_BUFFER_ADD_CHAR(buf, 'f');
400                                                 break;
401         
402                                         case '\t':
403                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
404                                                 OSRF_BUFFER_ADD_CHAR(buf, 't');
405                                                 break;
406         
407                                         case '\n':
408                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
409                                                 OSRF_BUFFER_ADD_CHAR(buf, 'n');
410                                                 break;
411         
412                                         case '\r':
413                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
414                                                 OSRF_BUFFER_ADD_CHAR(buf, 'r');
415                                                 break;
416
417                                         case '\\':
418                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
419                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
420                                                 break;
421
422                                         default:
423                                                 if( c < 32 ) buffer_fadd(buf, "\\u%04x", c);
424                                                 else OSRF_BUFFER_ADD_CHAR(buf, c);
425                                 }
426
427                         } else {
428                                 OSRF_BUFFER_ADD_CHAR(buf, c);
429                         }
430                 }
431
432                 idx++;
433         }
434
435         return buffer_release(buf);
436 }
437
438
439 // A function to turn a process into a daemon 
440 int daemonize( void ) {
441         pid_t f = fork();
442
443         if (f == -1) {
444                 osrfLogError( OSRF_LOG_MARK, "Failed to fork!" );
445                 return -1;
446
447         } else if (f == 0) { // We're in the child now...
448                 
449                 // Change directories.  Otherwise whatever directory
450                 // we're in couldn't be deleted until the program
451                 // terminated -- possibly causing some inconvenience.
452                 chdir( "/" );
453
454                 /* create new session */
455                 setsid();
456
457                 // Now that we're no longer attached to a terminal,
458                 // we don't want any traffic on the standard streams
459                 freopen( "/dev/null", "r", stdin );
460                 freopen( "/dev/null", "w", stdout );
461                 freopen( "/dev/null", "w", stderr );
462                 
463                 return 0;
464
465         } else { // We're in the parent...
466                 _exit(0);
467         }
468 }
469
470
471 /* Return 1 if the string represents an integer,  */
472 /* as recognized by strtol(); Otherwise return 0. */
473
474 int stringisnum(const char* s) {
475         char* w;
476         strtol(s, &w, 10);
477         return *w ? 0 : 1;
478 }
479         
480
481
482 char* file_to_string(const char* filename) {
483
484         if(!filename) return NULL;
485
486         FILE * file = fopen( filename, "r" );
487         if( !file ) {
488                 osrfLogError( OSRF_LOG_MARK, "Unable to open file [%s]", filename );
489                 return NULL;
490         }
491
492         size_t num_read;
493         char buf[ BUFSIZ + 1 ];
494         growing_buffer* gb = buffer_init(sizeof(buf));
495
496         while( ( num_read = fread( buf, 1, sizeof(buf) - 1, file) ) ) {
497                 buf[ num_read ] = '\0';
498                 buffer_add(gb, buf);
499         }
500
501         fclose(file);
502
503         return buffer_release(gb);
504 }
505
506
507 char* md5sum( const char* text, ... ) {
508
509         struct md5_ctx ctx;
510         unsigned char digest[16];
511
512         MD5_start (&ctx);
513
514         VA_LIST_TO_STRING(text);
515
516         int i;
517         for ( i=0 ; i != strlen(VA_BUF) ; i++ )
518                 MD5_feed (&ctx, VA_BUF[i]);
519
520         MD5_stop (&ctx, digest);
521
522         char buf[16];
523         char final[256];
524         osrf_clearbuf(final, sizeof(final));
525
526         for ( i=0 ; i<16 ; i++ ) {
527                 snprintf(buf, sizeof(buf), "%02x", digest[i]);
528                 strcat( final, buf );
529         }
530
531         return strdup(final);
532
533 }
534
535 int osrfUtilsCheckFileDescriptor( int fd ) {
536
537         fd_set tmpset;
538         FD_ZERO(&tmpset);
539         FD_SET(fd, &tmpset);
540
541         struct timeval tv;
542         tv.tv_sec = 0;
543         tv.tv_usec = 0;
544
545         if( select(fd + 1, &tmpset, NULL, NULL, &tv) == -1 ) {
546                 if( errno == EBADF ) return -1;
547         }
548
549         return 0;
550 }
551