Setting tag codemux-0.1-15
[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, struct in_addr *addr)
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 = *addr;
209   sa.sin_port = htons(portNum);
210
211   /* bind the sock */
212   if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
213     close(sock);
214     return(-1);
215   }
216   
217   /* start listening */
218   if (listen(sock, 32) == -1) {
219     close(sock);
220     return(-1);
221   }
222   
223   return(sock);
224 }
225 /*-----------------------------------------------------------------*/
226 int
227 CreatePrivateAcceptSocket(int portNum, int nonBlocking, struct in_addr *addr)
228 {
229   return CreatePrivateAcceptSocketEx(portNum, nonBlocking, addr);
230 }
231 /*-----------------------------------------------------------------*/
232 char *
233 StrdupLower(const char *orig)
234 {
235   char *temp;
236   int i;
237
238   if ((temp = xstrdup(orig)) == NULL)
239     NiceExit(-1, "no memory in strduplower");
240   for (i = 0; temp[i]; i++) {
241     if (isupper((int) temp[i]))
242       temp[i] = tolower(temp[i]);
243   }
244   return(temp);
245 }
246
247 /*-----------------------------------------------------------------*/
248 void 
249 StrcpyLower(char *dest, const char *src)
250 {
251   /* copy 'src' to 'dest' in lower cases. 
252      'dest' should have enough free space to hold src */
253   int i;
254
255   for (i = 0; src[i]; i++) {
256     dest[i] = (isupper((int) src[i])) ? tolower(src[i]) : src[i];
257   }
258
259   /* mark it as NULL */
260   dest[i] = 0;
261 }
262 /*-----------------------------------------------------------------*/
263 void
264 NiceExitBack(int val, char *reason, char *file, int line)
265 {
266   char buf[1024];
267   time_t currT = time(NULL);
268
269   sprintf(buf, "[%s, %d] %.24s %s\n", file, line, ctime(&currT), reason);
270   fprintf(stderr, "%s", buf);
271   exit(val);
272 }
273 /*-----------------------------------------------------------------*/
274 /* Logging & Trace support */
275
276 /* buffer threshold : when the size hits this value, it flushes its content
277    to the file  */
278 #define LOG_BYTES_THRESHOLD (32*1024)
279
280 /* this flag indicates that it preserves the base file name for the current
281    one, and changes its name when it actually closes it off */
282 #define CHANGE_FILE_NAME_ON_SAVE 0x01 
283
284 /* size of the actual log buffer */
285 #define LOG_BYTES_MAX       (2*LOG_BYTES_THRESHOLD)
286
287 /* log/trace book keeping information */
288 typedef struct ExtendedLog {
289   char buffer[LOG_BYTES_MAX]; /* 64 KB */
290   int  bytes;           /* number of bytes written */
291   int  filesize;        /* number of bytes written into this file so far */
292   int  fd;              /* file descriptor */
293   char* sig;            /* base file name */
294   int flags;            /* flags */
295   time_t nextday;
296 } ExtendedLog, *PExtendedLog;
297
298 /* maximum single file size */
299 int maxSingleLogSize = 100 * (1024*1024);
300
301 static time_t
302 GetNextLogFileName(char* file, int size, const char* sig)
303 {
304 #define COMPRESS_EXT1 ".bz2"
305 #define COMPRESS_EXT2 ".gz"
306
307   struct tm cur_tm;
308   time_t cur_t;
309   int idx = 0;
310
311   cur_t = time(NULL);
312   cur_tm = *gmtime(&cur_t);
313
314   for (;;) {
315     /* check if .bz2 exists */
316     snprintf(file, size, "%s.%04d%02d%02d_%03d%s", 
317              sig, cur_tm.tm_year+1900, cur_tm.tm_mon+1, cur_tm.tm_mday, 
318              idx, COMPRESS_EXT1);
319
320     if (access(file, F_OK) == 0) {
321       idx++;
322       continue;
323     }
324
325     /* check if .gz exists */
326     snprintf(file, size, "%s.%04d%02d%02d_%03d%s", 
327              sig, cur_tm.tm_year+1900, cur_tm.tm_mon+1, cur_tm.tm_mday, 
328              idx++, COMPRESS_EXT2);
329
330     if (access(file, F_OK) == 0) 
331       continue;
332
333     /* strip the extension and see if the (uncompressed) file exists */
334     file[strlen(file) - sizeof(COMPRESS_EXT2) + 1] = 0;
335     if (access(file, F_OK) != 0)
336       break;
337   }
338   
339   /* calculate & return the next day */
340   cur_t -= (3600*cur_tm.tm_hour + 60*cur_tm.tm_min + cur_tm.tm_sec);
341   return cur_t + 60*60*24;
342
343 #undef COMPRESS_EXT1
344 #undef COMPRESS_EXT2
345 }
346 /*-----------------------------------------------------------------*/
347 static void
348 FlushBuffer(HANDLE file) 
349 {
350   /* write data into the file */
351   ExtendedLog* pel = (ExtendedLog *)file;
352   int written;
353
354   if (pel == NULL || pel->fd < 0)
355     return;
356   
357   if ((written = write(pel->fd, pel->buffer, pel->bytes)) > 0) {
358     pel->bytes -= written;
359
360     /* if it hasn't written all data, then we need to move memory */
361     if (pel->bytes > 0) 
362       memmove(pel->buffer, pel->buffer + written, pel->bytes);
363     pel->buffer[pel->bytes] = 0;
364     pel->filesize += written;
365   }
366   
367   /* if the filesize is bigger than maxSignleLogSize, then close it off */
368   if (pel->filesize >= maxSingleLogSize) 
369     OpenLogF(file);
370 }
371
372 /*-----------------------------------------------------------------*/
373 HANDLE
374 CreateLogFHandle(const char* signature, int change_file_name_on_save)
375 {
376   ExtendedLog* pel;
377   char *temp;
378
379   /* check if the file can be created */
380   if ((temp = strrchr(signature, '/')) != NULL) {
381     int dirlen = temp - signature + 1;
382     char pardir[dirlen+1];
383
384     memcpy(pardir, signature, dirlen);
385     pardir[dirlen] = 0;
386     if (access(pardir, W_OK) != 0) 
387       return NULL;
388   } else {
389     /* assume it's the current directory */
390     if (access("./", W_OK) != 0) 
391       return NULL;
392   }
393
394   if ((pel = (ExtendedLog *)xcalloc(1, sizeof(ExtendedLog))) == NULL)
395     NiceExit(-1, "failed");
396
397   pel->fd = -1;
398   pel->sig = xstrdup(signature);
399   if (pel->sig == NULL)
400     NiceExit(-1, "signature copying failed");
401   if (change_file_name_on_save)
402     pel->flags |= CHANGE_FILE_NAME_ON_SAVE;
403
404   return pel;
405 }
406
407
408 /*-----------------------------------------------------------------*/
409 int
410 OpenLogF(HANDLE file)
411 {
412   char filename[1024];
413   ExtendedLog* pel = (ExtendedLog *)file;
414
415   if (pel == NULL)
416     return -1;
417
418   if (pel->fd != -1) 
419     close(pel->fd);
420
421   pel->nextday = GetNextLogFileName(filename, sizeof(filename), pel->sig);
422
423   /* change the file name only at saving time 
424      use pel->sig as current file name         */
425   if (pel->flags & CHANGE_FILE_NAME_ON_SAVE) {
426     if (access(pel->sig, F_OK) == 0) 
427       rename(pel->sig, filename);
428     strcpy(filename, pel->sig);
429   }
430
431   /* file opening */
432   if ((pel->fd = open(filename, O_RDWR | O_CREAT | O_APPEND, 
433                       S_IRUSR | S_IWUSR)) == -1) {
434     char errMessage[2048];
435     sprintf(errMessage, "couldn't open the extended log file %s\n", filename);
436     NiceExit(-1, errMessage);
437   }
438
439   /* reset the file size */
440   pel->filesize = 0;
441   return 0;
442 }
443
444 /*-----------------------------------------------------------------*/
445 int 
446 WriteLog(HANDLE file, const char* data, int size, int forceFlush)
447 {
448   ExtendedLog* pel = (ExtendedLog *)file;
449
450   /* if an error might occur, then stop here */
451   if (pel == NULL || pel->fd < 0 || size > LOG_BYTES_MAX)
452     return -1;
453
454   if (data != NULL) {
455     /* flush the previous data, if this data would overfill the buffer */
456     if (pel->bytes + size >= LOG_BYTES_MAX) 
457       FlushBuffer(file);
458
459     /* write into the buffer */
460     memcpy(pel->buffer + pel->bytes, data, size);
461     pel->bytes += size;
462   }
463
464   /* need to flush ? */
465   if ((forceFlush && (pel->bytes > 0)) || (pel->bytes >= LOG_BYTES_THRESHOLD))
466     FlushBuffer(file);
467
468   return 0;
469 }
470 /*-----------------------------------------------------------------*/
471 void
472 DailyReopenLogF(HANDLE file) 
473 {
474   /* check if current file is a day long,
475      opens another for today's file         */
476   ExtendedLog* pel = (ExtendedLog *)file;
477
478   if (pel && (time(NULL) >= pel->nextday)) {
479     FlushLogF(file);               /* flush */
480     OpenLogF(file);                /* close previous one & reopen today's */
481   }
482 }
483 /*-----------------------------------------------------------------*/
484 int 
485 HandleToFileNo(HANDLE file)
486 {
487   ExtendedLog* pel = (ExtendedLog *)file;
488
489   return (pel != NULL) ? pel->fd : -1;
490 }
491 /*-----------------------------------------------------------------*/
492 unsigned int 
493 HashString(const char *name, unsigned int hash, int endOnQuery, 
494            int skipLastIfDot)
495 {
496   /* if endOnQuery, we stop the hashing when we hit the question mark.
497      if skipLastIfDot, we check the last component of the path to see
498      if it includes a dot, and it so, we skip it. if both are specified,
499      we first try the query, and if that exists, we don't trim the path */
500
501   int i;
502   int len;
503   char *temp;
504
505   if (name == NULL)
506     return 0;
507
508   len = strlen(name);
509   if (endOnQuery && (temp = strchr(name, '?')) != NULL)
510     len = temp - name;
511   else if (skipLastIfDot) {
512     /* first, find last component by searching backward */
513     if ((temp = strrchr(name, '/')) != NULL) {
514       /* now search forward for the dot */
515       if (strchr(temp, '.') != NULL)
516         len = temp - name;
517     }
518   }
519
520   for (i = 0; i < len; i ++)
521     hash += (_rotl(hash, 19) + name[i]);
522
523   return hash;
524 }