- fixed the slow memory leak problem.
[codemux.git] / codemuxlib.c
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <sys/time.h>
4 #include <netinet/in.h>
5 #include <ctype.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include "codemuxlib.h"
14 #include "debug.h"
15
16 /*-----------------------------------------------------------------*/
17 static char *
18 GetNextLineBack(FILE *file, int lower, int stripComments)
19 {
20   /* reads the next non-blank line of the file. strips off any leading
21      space, any comments, and any trailing space.  returns a lowercase
22      version of the line that has been malloc'd */
23   char line[1024];
24
25   while (fgets(line, sizeof(line), file) != NULL) {
26     char *temp;
27     int len;
28
29     /* strip off any comments, leading and trailing whitespace */
30     if (stripComments) {
31       if ((temp = strchr(line, '#')) != NULL)
32         *temp = 0;
33     }
34     len = strlen(line);
35     while (len > 0 && isspace((int) line[len-1])) {
36       len--;
37       line[len] = 0;
38     }
39     temp = line;
40     while (isspace((int) *temp))
41       temp++;
42     if (temp[0] == 0)
43       continue;                 /* blank line, move on */
44
45     if (lower)
46       return(StrdupLower(temp));
47     return(xstrdup(temp));
48   }
49
50   return(NULL);
51 }
52 /*-----------------------------------------------------------------*/
53 char *
54 GetLowerNextLine(FILE *file)
55 {
56   return(GetNextLineBack(file, TRUE, TRUE));
57 }
58 /*-----------------------------------------------------------------*/
59 char *
60 GetNextLine(FILE *file)
61 {
62   return(GetNextLineBack(file, FALSE, TRUE));
63 }
64 /*-----------------------------------------------------------------*/
65 char *
66 GetNextLineNoCommStrip(FILE *file)
67 {
68   return(GetNextLineBack(file, FALSE, FALSE));
69 }
70 /*-----------------------------------------------------------------*/
71 int
72 DoesSuffixMatch(char *start, int len, char *suffix)
73 {
74   int sufLen = strlen(suffix);
75
76   if (len < 1)
77     len = strlen(start);
78   if (len < sufLen)
79     return(FALSE);
80   if (strncasecmp(start+len-sufLen, suffix, sufLen))
81     return(FALSE);
82   return(TRUE);
83 }
84 /*-----------------------------------------------------------------*/
85 int
86 DoesDotlessSuffixMatch(char *start, int len, char *suffix)
87 {
88   /* ignores any dots on end of start, suffix */
89   int sufLen = strlen(suffix);
90
91   if (len < 1)
92     len = strlen(start);
93
94   while (len > 1 && start[len-1] == '.')
95     len--;
96   while (sufLen > 1 && suffix[sufLen-1] == '.')
97     sufLen--;
98
99   if (len < sufLen)
100     return(FALSE);
101   if (strncasecmp(start+len-sufLen, suffix, sufLen))
102     return(FALSE);
103   return(TRUE);
104 }
105 /*-----------------------------------------------------------------*/
106 int
107 WordCount(char *buf)
108 {
109   int count = 0;
110   int wasSpace = TRUE;
111
112   while (*buf != '\0') {
113     int isSpace = isspace(*buf);
114     if (wasSpace && (!isSpace))
115       count++;
116     wasSpace = isSpace;
117     buf++;
118   }
119   return(count);
120 }
121 /*-----------------------------------------------------------------*/
122 char *
123 GetField(const char *start, int whichField)
124 {
125   int currField;
126
127   /* move to first non-blank char */
128   while (isspace(*start))
129     start++;
130
131   if (*start == '\0')
132     return(NULL);
133
134   for (currField = 0; currField < whichField; currField++) {
135     /* move over this field */
136     while (*start != '\0' && (!isspace(*start)))
137       start++;
138     /* move over blanks before next field */
139     while (isspace(*start))
140       start++;
141     if (*start == '\0')
142       return(NULL);
143   }
144   return((char *) start);
145 }
146 /* ---------------------------------------------------------------- */
147 char *
148 GetWord(const char *start, int whichWord)
149 {
150   /* returns a newly allocated string containing the desired word,
151      or NULL if there was a problem */
152   unsigned char *temp;
153   int len = 0;
154   char *res;
155
156   temp = (unsigned char *) GetField(start, whichWord);
157   if (!temp)
158     return(NULL);
159   while (!(temp[len] == '\0' || isspace(temp[len])))
160     len++;
161   if (!len)
162     NiceExit(-1, "internal error");
163   res = (char *)xcalloc(1, len+1);
164   if (!res) 
165     NiceExit(-1, "out of memory");
166   memcpy(res, temp, len);
167   return(res);
168 }
169 /*-----------------------------------------------------------------*/
170 static int 
171 CreatePrivateAcceptSocketEx(int portNum, int nonBlocking, int loopbackOnly)
172 {
173   int doReuse = 1;
174   struct linger doLinger;
175   int sock;
176   struct sockaddr_in sa;
177   
178   /* Create socket. */
179   if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
180     return(-1);
181   
182   /* don't linger on close */
183   doLinger.l_onoff = doLinger.l_linger = 0;
184   if (setsockopt(sock, SOL_SOCKET, SO_LINGER, 
185                  &doLinger, sizeof(doLinger)) == -1) {
186     close(sock);
187     return(-1);
188   }
189   
190   /* reuse addresses */
191   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 
192                  &doReuse, sizeof(doReuse)) == -1) {
193     close(sock);
194     return(-1);
195   }
196
197   if (nonBlocking) {
198     /* make listen socket nonblocking */
199     if (fcntl(sock, F_SETFL, O_NDELAY) == -1) {
200       close(sock);
201       return(-1);
202     }
203   }
204   
205   /* set up info for binding listen */
206   memset(&sa, 0, sizeof(sa));
207   sa.sin_family = AF_INET;
208   sa.sin_addr.s_addr = (loopbackOnly) ? htonl(INADDR_LOOPBACK) 
209                                       : htonl(INADDR_ANY);
210   sa.sin_port = htons(portNum);
211
212   /* bind the sock */
213   if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
214     close(sock);
215     return(-1);
216   }
217   
218   /* start listening */
219   if (listen(sock, 32) == -1) {
220     close(sock);
221     return(-1);
222   }
223   
224   return(sock);
225 }
226 /*-----------------------------------------------------------------*/
227 int
228 CreatePrivateAcceptSocket(int portNum, int nonBlocking)
229 {
230   return CreatePrivateAcceptSocketEx(portNum, nonBlocking, FALSE);
231 }
232 /*-----------------------------------------------------------------*/
233 char *
234 StrdupLower(const char *orig)
235 {
236   char *temp;
237   int i;
238
239   if ((temp = xstrdup(orig)) == NULL)
240     NiceExit(-1, "no memory in strduplower");
241   for (i = 0; temp[i]; i++) {
242     if (isupper((int) temp[i]))
243       temp[i] = tolower(temp[i]);
244   }
245   return(temp);
246 }
247
248 /*-----------------------------------------------------------------*/
249 void 
250 StrcpyLower(char *dest, const char *src)
251 {
252   /* copy 'src' to 'dest' in lower cases. 
253      'dest' should have enough free space to hold src */
254   int i;
255
256   for (i = 0; src[i]; i++) {
257     dest[i] = (isupper((int) src[i])) ? tolower(src[i]) : src[i];
258   }
259
260   /* mark it as NULL */
261   dest[i] = 0;
262 }
263 /*-----------------------------------------------------------------*/
264 void
265 NiceExitBack(int val, char *reason, char *file, int line)
266 {
267   char buf[1024];
268   time_t currT = time(NULL);
269
270   sprintf(buf, "[%s, %d] %.24s %s\n", file, line, ctime(&currT), reason);
271   fprintf(stderr, "%s", buf);
272   exit(val);
273 }
274 /*-----------------------------------------------------------------*/
275 /* Logging & Trace support */
276
277 /* buffer threshold : when the size hits this value, it flushes its content
278    to the file  */
279 #define LOG_BYTES_THRESHOLD (32*1024)
280
281 /* this flag indicates that it preserves the base file name for the current
282    one, and changes its name when it actually closes it off */
283 #define CHANGE_FILE_NAME_ON_SAVE 0x01 
284
285 /* size of the actual log buffer */
286 #define LOG_BYTES_MAX       (2*LOG_BYTES_THRESHOLD)
287
288 /* log/trace book keeping information */
289 typedef struct ExtendedLog {
290   char buffer[LOG_BYTES_MAX]; /* 64 KB */
291   int  bytes;           /* number of bytes written */
292   int  filesize;        /* number of bytes written into this file so far */
293   int  fd;              /* file descriptor */
294   char* sig;            /* base file name */
295   int flags;            /* flags */
296   time_t nextday;
297 } ExtendedLog, *PExtendedLog;
298
299 /* maximum single file size */
300 int maxSingleLogSize = 100 * (1024*1024);
301
302 static time_t
303 GetNextLogFileName(char* file, int size, const char* sig)
304 {
305 #define COMPRESS_EXT1 ".bz2"
306 #define COMPRESS_EXT2 ".gz"
307
308   struct tm cur_tm;
309   time_t cur_t;
310   int idx = 0;
311
312   cur_t = time(NULL);
313   cur_tm = *gmtime(&cur_t);
314
315   for (;;) {
316     /* check if .bz2 exists */
317     snprintf(file, size, "%s.%04d%02d%02d_%03d%s", 
318              sig, cur_tm.tm_year+1900, cur_tm.tm_mon+1, cur_tm.tm_mday, 
319              idx, COMPRESS_EXT1);
320
321     if (access(file, F_OK) == 0) {
322       idx++;
323       continue;
324     }
325
326     /* check if .gz exists */
327     snprintf(file, size, "%s.%04d%02d%02d_%03d%s", 
328              sig, cur_tm.tm_year+1900, cur_tm.tm_mon+1, cur_tm.tm_mday, 
329              idx++, COMPRESS_EXT2);
330
331     if (access(file, F_OK) == 0) 
332       continue;
333
334     /* strip the extension and see if the (uncompressed) file exists */
335     file[strlen(file) - sizeof(COMPRESS_EXT2) + 1] = 0;
336     if (access(file, F_OK) != 0)
337       break;
338   }
339   
340   /* calculate & return the next day */
341   cur_t -= (3600*cur_tm.tm_hour + 60*cur_tm.tm_min + cur_tm.tm_sec);
342   return cur_t + 60*60*24;
343
344 #undef COMPRESS_EXT1
345 #undef COMPRESS_EXT2
346 }
347 /*-----------------------------------------------------------------*/
348 static void
349 FlushBuffer(HANDLE file) 
350 {
351   /* write data into the file */
352   ExtendedLog* pel = (ExtendedLog *)file;
353   int written;
354
355   if (pel == NULL || pel->fd < 0)
356     return;
357   
358   if ((written = write(pel->fd, pel->buffer, pel->bytes)) > 0) {
359     pel->bytes -= written;
360
361     /* if it hasn't written all data, then we need to move memory */
362     if (pel->bytes > 0) 
363       memmove(pel->buffer, pel->buffer + written, pel->bytes);
364     pel->buffer[pel->bytes] = 0;
365     pel->filesize += written;
366   }
367   
368   /* if the filesize is bigger than maxSignleLogSize, then close it off */
369   if (pel->filesize >= maxSingleLogSize) 
370     OpenLogF(file);
371 }
372
373 /*-----------------------------------------------------------------*/
374 HANDLE
375 CreateLogFHandle(const char* signature, int change_file_name_on_save)
376 {
377   ExtendedLog* pel;
378   char *temp;
379
380   /* check if the file can be created */
381   if ((temp = strrchr(signature, '/')) != NULL) {
382     int dirlen = temp - signature + 1;
383     char pardir[dirlen+1];
384
385     memcpy(pardir, signature, dirlen);
386     pardir[dirlen] = 0;
387     if (access(pardir, W_OK) != 0) 
388       return NULL;
389   } else {
390     /* assume it's the current directory */
391     if (access("./", W_OK) != 0) 
392       return NULL;
393   }
394
395   if ((pel = (ExtendedLog *)xcalloc(1, sizeof(ExtendedLog))) == NULL)
396     NiceExit(-1, "failed");
397
398   pel->fd = -1;
399   pel->sig = xstrdup(signature);
400   if (pel->sig == NULL)
401     NiceExit(-1, "signature copying failed");
402   if (change_file_name_on_save)
403     pel->flags |= CHANGE_FILE_NAME_ON_SAVE;
404
405   return pel;
406 }
407
408
409 /*-----------------------------------------------------------------*/
410 int
411 OpenLogF(HANDLE file)
412 {
413   char filename[1024];
414   ExtendedLog* pel = (ExtendedLog *)file;
415
416   if (pel == NULL)
417     return -1;
418
419   if (pel->fd != -1) 
420     close(pel->fd);
421
422   pel->nextday = GetNextLogFileName(filename, sizeof(filename), pel->sig);
423
424   /* change the file name only at saving time 
425      use pel->sig as current file name         */
426   if (pel->flags & CHANGE_FILE_NAME_ON_SAVE) {
427     if (access(pel->sig, F_OK) == 0) 
428       rename(pel->sig, filename);
429     strcpy(filename, pel->sig);
430   }
431
432   /* file opening */
433   if ((pel->fd = open(filename, O_RDWR | O_CREAT | O_APPEND, 
434                       S_IRUSR | S_IWUSR)) == -1) {
435     char errMessage[2048];
436     sprintf(errMessage, "couldn't open the extended log file %s\n", filename);
437     NiceExit(-1, errMessage);
438   }
439
440   /* reset the file size */
441   pel->filesize = 0;
442   return 0;
443 }
444
445 /*-----------------------------------------------------------------*/
446 int 
447 WriteLog(HANDLE file, const char* data, int size, int forceFlush)
448 {
449   ExtendedLog* pel = (ExtendedLog *)file;
450
451   /* if an error might occur, then stop here */
452   if (pel == NULL || pel->fd < 0 || size > LOG_BYTES_MAX)
453     return -1;
454
455   if (data != NULL) {
456     /* flush the previous data, if this data would overfill the buffer */
457     if (pel->bytes + size >= LOG_BYTES_MAX) 
458       FlushBuffer(file);
459
460     /* write into the buffer */
461     memcpy(pel->buffer + pel->bytes, data, size);
462     pel->bytes += size;
463   }
464
465   /* need to flush ? */
466   if ((forceFlush && (pel->bytes > 0)) || (pel->bytes >= LOG_BYTES_THRESHOLD))
467     FlushBuffer(file);
468
469   return 0;
470 }
471 /*-----------------------------------------------------------------*/
472 void
473 DailyReopenLogF(HANDLE file) 
474 {
475   /* check if current file is a day long,
476      opens another for today's file         */
477   ExtendedLog* pel = (ExtendedLog *)file;
478
479   if (pel && (time(NULL) >= pel->nextday)) {
480     FlushLogF(file);               /* flush */
481     OpenLogF(file);                /* close previous one & reopen today's */
482   }
483 }
484 /*-----------------------------------------------------------------*/
485 int 
486 HandleToFileNo(HANDLE file)
487 {
488   ExtendedLog* pel = (ExtendedLog *)file;
489
490   return (pel != NULL) ? pel->fd : -1;
491 }
492 /*-----------------------------------------------------------------*/
493 unsigned int 
494 HashString(const char *name, unsigned int hash, int endOnQuery, 
495            int skipLastIfDot)
496 {
497   /* if endOnQuery, we stop the hashing when we hit the question mark.
498      if skipLastIfDot, we check the last component of the path to see
499      if it includes a dot, and it so, we skip it. if both are specified,
500      we first try the query, and if that exists, we don't trim the path */
501
502   int i;
503   int len;
504   char *temp;
505
506   if (name == NULL)
507     return 0;
508
509   len = strlen(name);
510   if (endOnQuery && (temp = strchr(name, '?')) != NULL)
511     len = temp - name;
512   else if (skipLastIfDot) {
513     /* first, find last component by searching backward */
514     if ((temp = strrchr(name, '/')) != NULL) {
515       /* now search forward for the dot */
516       if (strchr(temp, '.') != NULL)
517         len = temp - name;
518     }
519   }
520
521   for (i = 0; i < len; i ++)
522     hash += (_rotl(hash, 19) + name[i]);
523
524   return hash;
525 }