]> git.ipfire.org Git - thirdparty/sarg.git/blame - util.c
Remove the flooding of the console with a debug message
[thirdparty/sarg.git] / util.c
CommitLineData
25697a35 1/*
94ff9470 2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
67302a9e 3 * 1998, 2013
25697a35
GS
4 *
5 * SARG donations:
6 * please look at http://sarg.sourceforge.net/donations.php
1164c474
FM
7 * Support:
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
25697a35
GS
9 * ---------------------------------------------------------------------
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
24 *
25 */
26
27// #define LEGACY_MY_ATOLL
28// #define LEGACY_TESTVALIDUSERCHAR
29
30#include "include/conf.h"
5f3cfd1d 31#include "include/defs.h"
25697a35 32
e6414a9d 33#if defined(HAVE_BACKTRACE)
ac422f9b 34#define USE_GETWORD_BACKTRACE 1
e6414a9d
FM
35#else
36#define USE_GETWORD_BACKTRACE 0
37#endif
38
25697a35 39static char mtab1[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
48864d28
FM
40
41//! The list of the HTTP codes to exclude from the report.
42static char *excludecode=NULL;
25697a35 43
728f6a04
FM
44extern char *CurrentLocale;
45
e6414a9d
FM
46#if USE_GETWORD_BACKTRACE
47static void getword_backtrace(void)
48{
9bd92830
FM
49 void *buffer[5];
50 int i, n;
51 char **calls;
52
53 n=backtrace(buffer,sizeof(buffer)/sizeof(buffer[0]));
54 if (n<=0) return;
55 calls=backtrace_symbols(buffer,n);
56 if (calls) {
57 debuga(_("getword backtrace:\n"));
58 for (i=0 ; i<n ; i++) {
59 fprintf(stderr,"SARG: %d:%s\n",i+1,calls[i]);
60 }
61 free(calls);
62 }
e6414a9d
FM
63}
64#endif //USE_GETWORD_BACKTRACE
65
9c7c6346 66void getword_start(struct getwordstruct *gwarea, const char *line)
25697a35 67{
9bd92830
FM
68 gwarea->beginning=line;
69 gwarea->current=line;
70 gwarea->modified=0;
9c7c6346 71}
25697a35 72
9c7c6346 73void getword_restart(struct getwordstruct *gwarea)
25697a35 74{
9bd92830
FM
75 if (gwarea->modified) {
76 debuga(_("Cannot parse again the line as it was modified\n"));
77 exit(EXIT_FAILURE);
78 }
79 gwarea->current=gwarea->beginning;
9c7c6346 80}
25697a35 81
06b39c87 82int getword(char *word, int limit, struct getwordstruct *gwarea, char stop)
9c7c6346 83{
9bd92830 84 int x;
25697a35 85
9bd92830
FM
86 for(x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
87 if(x>=limit) {
22276ff3 88 debuga(_("End of word not found in getword after %d bytes.\n"),x);
c55a1bcd
FM
89 debuga(_("Line=\"%s\"\n"),gwarea->beginning);
90 debuga(_("Record=\"%s\"\n"),gwarea->current);
91 debuga(_("searching for \'x%x\'\n"),stop);
92 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
9bd92830 93 word[(limit>0) ? limit-1 : 0]='\0';
e6414a9d 94#if USE_GETWORD_BACKTRACE
007905af 95 getword_backtrace();
e6414a9d 96#endif
9bd92830
FM
97 return(-1);
98 }
99 word[x] = gwarea->current[x];
100 }
25697a35 101
9bd92830
FM
102 word[x] = '\0';
103 if (gwarea->current[x]) ++x;
104 gwarea->current+=x;
105 return(0);
25697a35
GS
106}
107
06b39c87 108int getword_limit(char *word, int limit, struct getwordstruct *gwarea, char stop)
e5b2c6f0 109{
9bd92830 110 int x;
e5b2c6f0 111
9bd92830
FM
112 limit--;
113 for(x=0; x<limit && gwarea->current[x] && gwarea->current[x] != stop ;x++) {
114 word[x] = gwarea->current[x];
115 }
116 word[x] = '\0';
117 gwarea->current+=x;
118 while (*gwarea->current && *gwarea->current != stop) gwarea->current++;
119 if (*gwarea->current) ++gwarea->current;
120 return(0);
e5b2c6f0
FM
121}
122
06b39c87 123int getword_multisep(char *word, int limit, struct getwordstruct *gwarea, char stop)
4bcb77cf 124{
9bd92830 125 int x;
4bcb77cf 126
9bd92830
FM
127 for(x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
128 if(x>=limit) {
22276ff3 129 debuga(_("End of word not found in getword_multisep after %d bytes.\n"),x);
c55a1bcd
FM
130 debuga(_("Line=\"%s\"\n"),gwarea->beginning);
131 debuga(_("Record=\"%s\"\n"),gwarea->current);
132 debuga(_("searching for \'x%x\'\n"),stop);
133 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
9bd92830 134 if (limit>0) word[limit-1]='\0';
e6414a9d 135#if USE_GETWORD_BACKTRACE
9bd92830 136 getword_backtrace();
e6414a9d 137#endif
9bd92830
FM
138 //exit(EXIT_FAILURE);
139 return(-1);
140 }
141 word[x] = gwarea->current[x];
142 }
4bcb77cf 143
9bd92830
FM
144 word[x] = '\0';
145 while (gwarea->current[x] && gwarea->current[x]==stop) ++x;
146 gwarea->current+=x;
147 return(0);
4bcb77cf
FM
148}
149
06b39c87 150int getword_skip(int limit, struct getwordstruct *gwarea, char stop)
076cbab8 151{
9bd92830 152 int x;
076cbab8 153
9bd92830
FM
154 for(x=0;(gwarea->current[x] && (gwarea->current[x] != stop ));x++) {
155 if(x>=limit) {
22276ff3 156 debuga(_("End of word not found in getword_skip after %d bytes.\n"),x);
c55a1bcd
FM
157 debuga(_("Line=\"%s\"\n"),gwarea->beginning);
158 debuga(_("Record=\"%s\"\n"),gwarea->current);
159 debuga(_("searching for \'x%x\'\n"),stop);
160 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
e6414a9d 161#if USE_GETWORD_BACKTRACE
9bd92830 162 getword_backtrace();
e6414a9d 163#endif
9bd92830
FM
164 return(-1);
165 }
166 }
076cbab8 167
9bd92830
FM
168 if (gwarea->current[x]) ++x;
169 gwarea->current+=x;
170 return(0);
076cbab8
FM
171}
172
06b39c87 173int getword_atoll(long long int *number, struct getwordstruct *gwarea, char stop)
25697a35 174{
9bd92830
FM
175 int x;
176 int sign=+1;
816a2597 177 int digit;
9bd92830
FM
178
179 if (gwarea->current[0] == '-') {
180 gwarea->current++;
181 sign=-1;
182 } else if (gwarea->current[0] == '+') {
183 gwarea->current++;
184 }
185 *number=0LL;
186 for(x=0;isdigit(gwarea->current[x]);x++) {
816a2597
FM
187 digit=gwarea->current[x]-'0';
188 if (*number >= (LLONG_MAX-digit)/10) {
189 debuga(_("Integer overflow detected in getword_atoll in line %s\n"),gwarea->beginning);
190 return(-1);
191 }
192 *number=(*number * 10) + digit;
9bd92830
FM
193 }
194 if(gwarea->current[x] && gwarea->current[x]!=stop) {
22276ff3 195 debuga(_("End of number not found in getword_atoll after %d bytes.\n"),x);
c55a1bcd
FM
196 debuga(_("Line=\"%s\"\n"),gwarea->beginning);
197 debuga(_("Record=\"%s\"\n"),gwarea->current);
198 debuga(_("searching for \'x%x\'\n"),stop);
199 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
e6414a9d 200#if USE_GETWORD_BACKTRACE
9bd92830 201 getword_backtrace();
e6414a9d 202#endif
9bd92830
FM
203 return(-1);
204 }
205 *number*=sign;
25697a35 206
9bd92830
FM
207 if (gwarea->current[x]) ++x;
208 gwarea->current+=x;
209 return(0);
0a4e18e1 210}
25697a35 211
bd8b7715
FM
212int getword_atoi(int *number, struct getwordstruct *gwarea, char stop)
213{
214 int x;
215 int sign=+1;
216 int digit;
217
218 if (gwarea->current[0] == '-') {
219 gwarea->current++;
220 sign=-1;
221 } else if (gwarea->current[0] == '+') {
222 gwarea->current++;
223 }
224 *number=0;
225 for(x=0;isdigit(gwarea->current[x]);x++) {
226 digit=gwarea->current[x]-'0';
227 if (*number > (INT_MAX-digit)/10) {
228 debuga(_("Integer overflow detected in getword_atoi in line %s\n"),gwarea->beginning);
229 return(-1);
230 }
231 *number=(*number * 10) + digit;
232 }
233 if(gwarea->current[x] && gwarea->current[x]!=stop) {
22276ff3 234 debuga(_("End of number not found in getword_atoi after %d bytes.\n"),x);
c55a1bcd
FM
235 debuga(_("Line=\"%s\"\n"),gwarea->beginning);
236 debuga(_("Record=\"%s\"\n"),gwarea->current);
237 debuga(_("searching for \'x%x\'\n"),stop);
238 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
bd8b7715
FM
239#if USE_GETWORD_BACKTRACE
240 getword_backtrace();
241#endif
242 return(-1);
243 }
244 *number*=sign;
245
246 if (gwarea->current[x]) ++x;
247 gwarea->current+=x;
248 return(0);
249}
250
2c4bc22b
FM
251int getword_atol(long int *number, struct getwordstruct *gwarea, char stop)
252{
253 long int x;
254 long int sign=+1;
255 int digit;
256
257 if (gwarea->current[0] == '-') {
258 gwarea->current++;
259 sign=-1;
260 } else if (gwarea->current[0] == '+') {
261 gwarea->current++;
262 }
263 *number=0;
264 for(x=0;isdigit(gwarea->current[x]);x++) {
265 digit=gwarea->current[x]-'0';
266 if (*number > (LONG_MAX-digit)/10) {
267 debuga(_("Integer overflow detected in getword_atol in line %s\n"),gwarea->beginning);
268 return(-1);
269 }
270 *number=(*number * 10) + digit;
271 }
272 if(gwarea->current[x] && gwarea->current[x]!=stop) {
22276ff3 273 debuga(_("End of number not found in getword_atol after %ld bytes.\n"),x);
c7851516
FM
274 debuga(_("Line=\"%s\"\n"),gwarea->beginning);
275 debuga(_("Record=\"%s\"\n"),gwarea->current);
276 debuga(_("searching for \'x%x\'\n"),stop);
277 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
2c4bc22b
FM
278#if USE_GETWORD_BACKTRACE
279 getword_backtrace();
280#endif
281 return(-1);
282 }
283 *number*=sign;
284
285 if (gwarea->current[x]) ++x;
286 gwarea->current+=x;
287 return(0);
288}
289
290int getword_atolu(unsigned long int *number, struct getwordstruct *gwarea, char stop)
291{
292 unsigned long int x;
293 int digit;
294
295 if (gwarea->current[0] == '-') {
c7851516
FM
296 debuga(_("getword_atolu got a negative number.\n"));
297 debuga(_("Line=\"%s\"\n"),gwarea->beginning);
298 debuga(_("Record=\"%s\"\n"),gwarea->current);
2c4bc22b
FM
299 return(-1);
300 }
301 if (gwarea->current[0] == '+') {
302 gwarea->current++;
303 }
304 *number=0;
305 for(x=0;isdigit(gwarea->current[x]);x++) {
306 digit=gwarea->current[x]-'0';
307 if (*number > (ULONG_MAX-digit)/10) {
308 debuga(_("Integer overflow detected in getword_atolu in line %s\n"),gwarea->beginning);
309 return(-1);
310 }
311 *number=(*number * 10) + digit;
312 }
313 if(gwarea->current[x] && gwarea->current[x]!=stop) {
22276ff3 314 debuga(_("End of number not found in getword_atolu after %ld bytes.\n"),x);
c7851516
FM
315 debuga(_("Line=\"%s\"\n"),gwarea->beginning);
316 debuga(_("Record=\"%s\"\n"),gwarea->current);
317 debuga(_("searching for \'x%x\'\n"),stop);
318 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
2c4bc22b
FM
319#if USE_GETWORD_BACKTRACE
320 getword_backtrace();
321#endif
322 return(-1);
323 }
324
325 if (gwarea->current[x]) ++x;
326 gwarea->current+=x;
327 return(0);
328}
329
25697a35 330
06b39c87 331int getword_ptr(char *orig_line,char **word, struct getwordstruct *gwarea, char stop)
e5b2c6f0 332{
9bd92830
FM
333 /*!
334 \note Why pass the original buffer to the function ? Because we must modify it to
335 insert the terminating ASCII zero for the word we return and that's not compatible
336 with getword_restart(). Moreover, getword_start() sometime works on constant strings
337 so this function require the original buffer to detect any missuse.
338 */
339 int x;
340 int sep;
341 int start;
342
343 if (orig_line && orig_line!=gwarea->beginning) {
344 debuga(_("Invalid buffer passed to getword_ptr\n"));
345 return(-1);
346 }
347
348 start=(gwarea->current-gwarea->beginning);
349 if (word && orig_line) *word=orig_line+start;
350 for(x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++);
351 sep=(gwarea->current[x]!='\0');
352 if (word && orig_line) orig_line[start+x] = '\0';
353 if (sep) ++x;
354 gwarea->current+=x;
355 gwarea->modified=1;
356 return(0);
e5b2c6f0
FM
357}
358
48864d28 359#define MAXLLL 30 //!< Maximum number of digits in long long (a guess).
25697a35
GS
360long long int my_atoll (const char *nptr)
361{
9bd92830
FM
362 long long int returnval=0LL;
363 int max_digits = MAXLLL ;
25697a35 364
9bd92830
FM
365 // Soak up all the white space
366 while (isspace( *nptr )) {
367 nptr++;
368 }
25697a35 369
9bd92830
FM
370 //For each character left to right
371 //change the character to a single digit
372 //multiply what we had before by 10 and add the new digit
25697a35 373
9bd92830
FM
374 while (--max_digits && isdigit( *nptr ))
375 {
376 returnval = ( returnval * 10 ) + ( *nptr++ - '0' ) ;
377 }
25697a35 378
9bd92830 379 return returnval;
0a4e18e1 380}
25697a35 381
e6414a9d 382int is_absolute(const char *path)
6798f0a7 383{
9bd92830 384 if (*path=='/') return(1);
2f8edd60 385#ifdef _WIN32
9bd92830 386 if (isalpha(path[0]) && path[1]==':') return(1);
6798f0a7 387#endif
9bd92830 388 return(0);
6798f0a7 389}
25697a35 390
32e71fa4 391void my_mkdir(const char *name)
25697a35 392{
9bd92830
FM
393 char w0[MAXLEN];
394 int i;
395 int chars;
396
397 if(!is_absolute(name)) {
398 debuga(_("Invalid path (%s). Please, use absolute paths only.\n"),name);
9bd92830
FM
399 exit(EXIT_FAILURE);
400 }
401
402 chars=0;
403 for (i=0 ; name[i] ; i++) {
404 if (i>=sizeof(w0)) {
405 debuga(_("directory name too long: %s\n"),name);
406 exit(EXIT_FAILURE);
407 }
408 if (chars>0 && name[i] == '/') {
409 w0[i] = '\0';
affa72c5
FM
410 if (access(w0, R_OK) != 0) {
411 if (mkdir(w0,0755)) {
412 debuga(_("Cannot create directory %s - %s\n"),w0,strerror(errno));
9bd92830
FM
413 exit(EXIT_FAILURE);
414 }
415 }
416 }
417 if (name[i] != '/') chars++;
418 w0[i] = name[i];
419 }
420
affa72c5
FM
421 if (access(name, R_OK) != 0) {
422 if (mkdir(name,0755)) {
423 debuga(_("Cannot create directory %s - %s\n"),name,strerror(errno));
9bd92830
FM
424 exit(EXIT_FAILURE);
425 }
426 }
25697a35
GS
427}
428
429
e5b2c6f0 430void my_lltoa(unsigned long long int n, char *s, int ssize, int len)
25697a35 431{
9bd92830
FM
432 int i;
433 int slen = 0;
434 int j;
435 char c;
436
437 ssize--;
438 if (len>ssize) {
439 debuga(_("The requested number of digits passed to my_lltoa (%d) is bigger than the output buffer size (%d)\n"),len,ssize);
440 abort();
441 }
442
443 do {
444 s[slen++] = (n % 10) + '0';
445 } while ((n /= 10) > 0 && slen<ssize);
446 s[slen] = '\0';
447
448 for (i = 0, j = slen-1; i<j; i++, j--) {
449 c = s[i];
450 s[i] = s[j];
451 s[j] = c;
452 }
453
454 if(len>slen) {
455 i=len-slen;
456 for(j=slen; j>=0; j--)
457 s[j+i]=s[j];
458 for(j=0 ; j<i ; j++)
459 s[j]='0';
460 }
25697a35
GS
461}
462
fa6552b0 463int month2num(const char *month)
25697a35 464{
9bd92830 465 int m;
25697a35 466
9bd92830
FM
467 for(m=0 ; m<12 && strcmp(mtab1[m],month) != 0; m++);
468 return(m);
fa6552b0 469}
25697a35 470
fa6552b0
FM
471int builddia(int day, int month, int year)
472{
9bd92830 473 return(year*10000+month*100+day);
25697a35
GS
474}
475
944cf283
FM
476/*!
477Compare two dates.
478
479\param date1 The first date to compare.
480\param date2 The second date to compare.
481
482\retval -1 If date1<date2.
483\retval 0 If date1==date2.
484\retval 1 if date1>date2.
485*/
486int compare_date(struct tm *date1,struct tm *date2)
487{
488 if (date1->tm_year<date2->tm_year) return(-1);
489 if (date1->tm_year>date2->tm_year) return(1);
490 if (date1->tm_mon<date2->tm_mon) return(-1);
491 if (date1->tm_mon>date2->tm_mon) return(1);
492 if (date1->tm_mday<date2->tm_mday) return(-1);
493 if (date1->tm_mday>date2->tm_mday) return(1);
494 if (date1->tm_hour<date2->tm_hour) return(-1);
495 if (date1->tm_hour>date2->tm_hour) return(1);
496 if (date1->tm_min<date2->tm_min) return(-1);
497 if (date1->tm_min>date2->tm_min) return(1);
498 if (date1->tm_sec<date2->tm_sec) return(-1);
499 if (date1->tm_sec>date2->tm_sec) return(1);
500 return(0);
501}
25697a35 502
60ec7f09 503void buildymd(const char *dia, const char *mes, const char *ano, char *wdata,int wdata_size)
25697a35 504{
9bd92830 505 int nmes;
25697a35 506
9bd92830 507 nmes=month2num(mes);
60ec7f09 508 snprintf(wdata,wdata_size,"%04d%02d%02d",atoi(ano),nmes+1,atoi(dia));
25697a35
GS
509}
510
511
fa6552b0 512int conv_month(const char *month)
25697a35 513{
9bd92830 514 int x;
25697a35 515
9bd92830
FM
516 for(x=0; x<12 && strncmp(mtab1[x],month,3)!=0; x++);
517 return(x+1);
25697a35
GS
518}
519
520
fa6552b0 521const char *conv_month_name(int month)
25697a35 522{
9bd92830 523 static char str[4];
25697a35 524
9bd92830
FM
525 if (month<1 || month>12) {
526 snprintf(str,sizeof(str),"%03d",month);
527 return(str);
528 }
529 return(mtab1[month-1]);
25697a35
GS
530}
531
9f93fec3
FM
532/*!
533Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
534
535\param msg The printf like message to format.
536\param ... The arguments to format in the message.
537*/
d2fe0c32 538void debuga(const char *msg,...)
25697a35 539{
9bd92830 540 va_list ap;
25697a35 541
9bd92830
FM
542 fputs(_("SARG: "),stderr);
543 va_start(ap,msg);
544 vfprintf(stderr,msg,ap);
545 va_end(ap);
25697a35
GS
546}
547
548
9f93fec3
FM
549/*!
550Write a debug message to stderr. The message is prefixed by "SARG: (info)".
551
552\param msg The printf like message to format.
553\param ... The arguments to format in the message.
554*/
555void debugaz(const char *msg,...)
25697a35 556{
9f93fec3
FM
557 va_list ap;
558
559 fputs(_("SARG: (info) "),stderr);
560 va_start(ap,msg);
561 vfprintf(stderr,msg,ap);
562 va_end(ap);
25697a35
GS
563}
564
565
25697a35 566char *fixnum(long long int value, int n)
25697a35 567{
1b81f396 568#define MAXLEN_FIXNUM 256
9bd92830
FM
569 char num[MAXLEN_FIXNUM]="";
570 char buf[MAXLEN_FIXNUM * 2];
571 char *pbuf;
572 static char ret[MAXLEN_FIXNUM * 2];
573 char *pret;
574 register int i, j, k;
575 int numlen;
d5a8da9d 576 static char abbrev[30]="";
9bd92830
FM
577
578 my_lltoa(value, num, sizeof(num), 0);
579
580 if(DisplayedValues==DISPLAY_ABBREV) {
581 numlen = strlen(num);
582 if(numlen <= 3)
d5a8da9d 583 strcpy(abbrev,num);
844f18f2
FM
584 else if (numlen%3 == 1) {
585 abbrev[0]=num[0];
586 abbrev[1]=(UseComma) ? ',' : '.';
587 abbrev[2]=num[1];
588 abbrev[3]=num[2];
d5a8da9d 589 abbrev[4]='\0';
9bd92830 590 }
844f18f2
FM
591 else if (numlen%3 == 2) {
592 abbrev[0]=num[0];
593 abbrev[1]=num[1];
594 abbrev[2]=(UseComma) ? ',' : '.';
595 abbrev[3]=num[2];
596 abbrev[4]=num[3];
d5a8da9d 597 abbrev[5]='\0';
9bd92830 598 }
844f18f2
FM
599 else if (numlen%3 == 0) {
600 abbrev[0]=num[0];
601 abbrev[1]=num[1];
602 abbrev[2]=num[2];
603 abbrev[3]=(UseComma) ? ',' : '.';
604 abbrev[4]=num[3];
605 abbrev[5]=num[4];
d5a8da9d 606 abbrev[6]='\0';
844f18f2
FM
607 }
608 if (n) {
609 if (numlen <= 3) {
610 //no prefix
611 }
612 else if (numlen <= 6)
d5a8da9d 613 strcat(abbrev,"K");
844f18f2 614 else if (numlen <= 9)
d5a8da9d 615 strcat(abbrev,"M");
844f18f2 616 else if (numlen <= 12)
d5a8da9d 617 strcat(abbrev,"G");
844f18f2 618 else if (numlen <= 15)
d5a8da9d 619 strcat(abbrev,"T");
844f18f2
FM
620 else if (numlen >= 18)
621 strcat(abbrev,"P");
622 else if (numlen <= 21)
623 strcat(abbrev,"E");
624 else if (numlen <= 24)
625 strcat(abbrev,"Z");
626 else if (numlen <= 27)
627 strcat(abbrev,"Y");
628 else
629 strcat(abbrev,"???");
9bd92830 630 }
9bd92830
FM
631 return(abbrev);
632 }
633
634 bzero(buf, MAXLEN_FIXNUM*2);
635
636 pbuf = buf;
637 pret = ret;
638 k = 0;
639
640 for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
641 if ( k == 2 && i != 0 ) {
642 k = 0;
643 pbuf[j++] = num[i];
644 pbuf[j++] = (UseComma) ? ',' : '.';
645 continue;
646 }
647 pbuf[j] = num[i];
648 j++;
649 k++;
650 }
651
652 pret[0]='\0';
653
654 for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
655 pret[j] = pbuf[i];
656
657 pret[j] = '\0';
658
659 return pret;
25697a35
GS
660}
661
662
d6e703cc 663char *fixnum2(long long int value, int n)
d6e703cc 664{
32e71fa4 665#define MAXLEN_FIXNUM2 1024
9bd92830
FM
666 char num[MAXLEN_FIXNUM2];
667 char buf[MAXLEN_FIXNUM2 * 2];
668 char *pbuf;
669 static char ret[MAXLEN_FIXNUM2 * 2];
670 char *pret;
671 register int i, j, k;
2357ef77 672
9bd92830
FM
673 my_lltoa(value, num, sizeof(num), 0);
674 bzero(buf, MAXLEN_FIXNUM2*2);
d6e703cc 675
9bd92830
FM
676 pbuf = buf;
677 pret = ret;
678 k = 0;
d6e703cc 679
9bd92830
FM
680 for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
681 if ( k == 2 && i != 0 ) {
682 k = 0;
683 pbuf[j++] = num[i];
684 pbuf[j++] = (UseComma) ? ',' : '.';
685 continue;
686 }
687 pbuf[j] = num[i];
688 j++;
689 k++;
690 }
d6e703cc 691
9bd92830 692 pret[0]='\0';
d6e703cc 693
9bd92830
FM
694 for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
695 pret[j] = pbuf[i];
d6e703cc 696
9bd92830 697 pret[j] = '\0';
d6e703cc 698
9bd92830 699 return pret;
d6e703cc
FM
700}
701
702
25697a35
GS
703char *buildtime(long long int elap)
704{
60ec7f09 705 long int num = elap / 1000LL;
9bd92830
FM
706 int hor = 0;
707 int min = 0;
708 int sec = 0;
60ec7f09 709 static char buf[20];
25697a35 710
60ec7f09
FM
711 hor=num / 3600L;
712 min=(num % 3600L) / 60L;
713 sec=num % 60L;
714 snprintf(buf,sizeof(buf),"%02d:%02d:%02d",hor,min,sec);
25697a35 715
9bd92830 716 return(buf);
25697a35
GS
717}
718
719
15d3cb5c
FM
720/*!
721Get the date stored in the <tt>sarg-date</tt> file of a directory with the connection data.
722
723\param dirname The directory to look for the connection directory.
724\param name The name of the directory whose <tt>sarg-date</tt> file must be read.
725\param data The buffer to store the content of the file. It must be more than 80
726bytes long.
727
728\retval 0 No error.
729\retval -1 File not found.
730*/
731int obtdate(const char *dirname, const char *name, char *data)
25697a35 732{
9bd92830
FM
733 FILE *fp_in;
734 char wdir[MAXLEN];
25697a35 735
60ec7f09
FM
736 if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-date",dirname,name)>=sizeof(wdir)) {
737 debuga(_("Buffer to small to store %s%s/sarg-date"),dirname,name);
738 exit(EXIT_FAILURE);
739 }
9bd92830 740 if ((fp_in = fopen(wdir, "rt")) == 0) {
60ec7f09
FM
741 if (snprintf(wdir,sizeof(wdir),"%s%s/date",dirname,name)>=sizeof(wdir)) {
742 debuga(_("Buffer to small to store %s%s/date"),dirname,name);
743 exit(EXIT_FAILURE);
744 }
9bd92830
FM
745 if ((fp_in = fopen(wdir, "rt")) == 0) {
746 data[0]='\0';
15d3cb5c 747 return(-1);
9bd92830
FM
748 }
749 }
25697a35 750
9bd92830
FM
751 if (!fgets(data,80,fp_in)) {
752 debuga(_("Failed to read the date in %s\n"),wdir);
753 exit(EXIT_FAILURE);
754 }
755 fclose(fp_in);
756 fixendofline(data);
25697a35 757
15d3cb5c 758 return(0);
25697a35
GS
759}
760
761
a1de61fe 762void formatdate(char *date,int date_size,int year,int month,int day,int hour,int minute,int second,int dst)
9e41ca7e 763{
9bd92830
FM
764 struct tm ltm;
765 time_t unixtime;
766 struct tm *fulltm;
9e41ca7e 767
9bd92830
FM
768 memset(&ltm,0,sizeof(ltm));
769 if (year>=1900) ltm.tm_year=year-1900;
770 if (month>=1 && month<=12) ltm.tm_mon=month-1;
771 if (day>=1 && day<=31) ltm.tm_mday=day;
772 if (hour>=0 && hour<24) ltm.tm_hour=hour;
773 if (minute>=0 && minute<60) ltm.tm_min=minute;
774 if (second>=0 && second<60) ltm.tm_sec=second;
775 ltm.tm_isdst=dst;
776 unixtime=mktime(&ltm); //fill the missing entries
777 fulltm=localtime(&unixtime);
778 //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
779 strftime(date,date_size,"%c",fulltm);
9e41ca7e
FM
780}
781
782
fa6552b0 783void computedate(int year,int month,int day,struct tm *t)
9426efec 784{
9bd92830
FM
785 memset(t,0,sizeof(*t));
786 t->tm_year=year-1900;
787 t->tm_mon=month-1;
788 t->tm_mday=day;
9426efec
FM
789}
790
791
d25d4e6a 792int obtuser(const char *dirname, const char *name)
25697a35 793{
9bd92830
FM
794 FILE *fp_in;
795 char wdir[MAXLEN];
796 char tuser[20];
797 int nuser;
25697a35 798
60ec7f09
FM
799 if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-users",dirname,name)>=sizeof(wdir)) {
800 debuga(_("Buffer too small to store %s%s/sarg-users"),dirname,name);
801 exit(EXIT_FAILURE);
802 }
9bd92830 803 if((fp_in=fopen(wdir,"r"))==NULL) {
60ec7f09
FM
804 if (snprintf(wdir,sizeof(wdir),"%s%s/users",dirname,name)>=sizeof(wdir)) {
805 debuga(_("Buffer too small to store %s%s/users"),dirname,name);
806 exit(EXIT_FAILURE);
807 }
9bd92830
FM
808 if((fp_in=fopen(wdir,"r"))==NULL) {
809 return(0);
810 }
811 }
25697a35 812
9bd92830
FM
813 if (!fgets(tuser,sizeof(tuser),fp_in)) {
814 debuga(_("Failed to read the number of users in %s\n"),wdir);
815 exit(EXIT_FAILURE);
816 }
817 fclose(fp_in);
818 nuser=atoi(tuser);
25697a35 819
9bd92830 820 return(nuser);
25697a35
GS
821}
822
823
ea275279 824void obttotal(const char *dirname, const char *name, int nuser, long long int *tbytes, long long int *media)
25697a35 825{
9bd92830
FM
826 FILE *fp_in;
827 char *buf;
828 char wdir[MAXLEN];
829 char user[MAX_USER_LEN];
830 char sep;
831 struct getwordstruct gwarea;
832 longline line;
833
834 *tbytes=0;
835 *media=0;
836
60ec7f09
FM
837 if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-general",dirname,name)>=sizeof(wdir)) {
838 debuga(_("Buffer too small to store %s%s/sarg-general"),dirname,name);
839 exit(EXIT_FAILURE);
840 }
9bd92830 841 if ((fp_in = fopen(wdir, "r")) == 0) {
60ec7f09
FM
842 if (snprintf(wdir,sizeof(wdir),"%s%s/general",dirname,name)>=sizeof(wdir)) {
843 debuga(_("Buffer too small to store %s%s/general"),dirname,name);
844 exit(EXIT_FAILURE);
845 }
9bd92830
FM
846 if ((fp_in = fopen(wdir, "r")) == 0) {
847 return;
848 }
849 }
850
851 if ((line=longline_create())==NULL) {
852 debuga(_("Not enough memory to read the file %s\n"),wdir);
853 exit(EXIT_FAILURE);
854 }
855
856 while((buf=longline_read(fp_in,line))!=NULL) {
857 if (strncmp(buf,"TOTAL\t",6) == 0)
858 sep='\t'; //new file
859 else if (strncmp(buf,"TOTAL ",6) == 0)
860 sep=' '; //old file
861 else
862 continue;
863 getword_start(&gwarea,buf);
864 if (getword(user,sizeof(user),&gwarea,sep)<0) {
865 debuga(_("There is a invalid user in file %s\n"),wdir);
866 exit(EXIT_FAILURE);
867 }
868 if(strcmp(user,"TOTAL") != 0)
869 continue;
870 if (getword_skip(MAXLEN,&gwarea,sep)<0) {
871 debuga(_("There a broken total number of access in file %s\n"),wdir);
872 exit(EXIT_FAILURE);
873 }
874 if (getword_atoll(tbytes,&gwarea,sep)<0) {
875 debuga(_("There is a broken number of bytes in file %s\n"),wdir);
876 exit(EXIT_FAILURE);
877 }
878 break;
879 }
880 fclose(fp_in);
881 longline_destroy(&line);
882
883 if (nuser <= 0)
884 return;
885
886 *media=*tbytes / nuser;
887 return;
25697a35
GS
888}
889
fa6552b0 890int getperiod_fromsarglog(const char *arqtt,struct periodstruct *period)
25697a35 891{
9bd92830
FM
892 const char *str;
893 int day0, month0, year0, hour0, minute0;
894 int day1, month1, year1, hour1, minute1;
9bd92830
FM
895 int i;
896
897 memset(period,0,sizeof(*period));
898
899 str=arqtt;
900 while((str=strstr(str,"sarg-"))!=NULL) {
901 str+=5;
902 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
903 day0=(str[0]-'0')*10+(str[1]-'0');
cbe0740f 904 if (day0<1 || day0>31) continue;
9bd92830 905 str+=2;
cbe0740f
FM
906 month0=(str[0]-'0')*10+(str[1]-'0')-1;
907 if (month0<0 || month0>11) continue;
ddd6dbdc 908 str+=2;
9bd92830
FM
909 year0=0;
910 for (i=0 ; isdigit(str[i]) && i<4 ; i++) year0=year0*10+(str[i]-'0');
911 if (i!=4) continue;
912 str+=4;
913 if (str[0]!='_') continue;
914 str++;
915
916 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
917 hour0=(str[0]-'0')*10+(str[1]-'0');
918 str+=2;
919 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
920 minute0=(str[0]-'0')*10+(str[1]-'0');
921 str+=2;
922
923 if (*str != '-') continue;
924 str++;
925
926 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
927 day1=(str[0]-'0')*10+(str[1]-'0');
cbe0740f 928 if (day1<1 || day1>31) continue;
9bd92830 929 str+=2;
cbe0740f
FM
930 month1=(str[0]-'0')*10+(str[1]-'0')-1;
931 if (month1<0 || month1>11) continue;
ddd6dbdc 932 str+=2;
9bd92830
FM
933 year1=0;
934 for (i=0 ; isdigit(str[i]) && i<4 ; i++) year1=year1*10+(str[i]-'0');
935 if (i!=4) continue;
936 str+=4;
937
938 if (str[0]!='_') continue;
939 str++;
940
941 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
942 hour1=(str[0]-'0')*10+(str[1]-'0');
943 str+=2;
944 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
945 minute1=(str[0]-'0')*10+(str[1]-'0');
946 str+=2;
947
948 period->start.tm_mday=day0;
949 period->start.tm_mon=month0;
950 period->start.tm_year=year0-1900;
951 period->start.tm_hour=hour0;
952 period->start.tm_min=minute0;
953 period->end.tm_mday=day1;
954 period->end.tm_mon=month1;
955 period->end.tm_year=year1-1900;
956 period->end.tm_hour=hour1;
957 period->end.tm_min=minute1;
958 return(0);
959 }
960 return(-1);
fa6552b0 961}
48864d28 962
42b117e3
FM
963void getperiod_fromrange(struct periodstruct *period,int dfrom,int duntil)
964{
9bd92830
FM
965 memset(&period->start,0,sizeof(period->start));
966 period->start.tm_mday=dfrom%100;
967 period->start.tm_mon=(dfrom/100)%100-1;
968 period->start.tm_year=(dfrom/10000)-1900;
42b117e3 969
9bd92830
FM
970 memset(&period->end,0,sizeof(period->end));
971 period->end.tm_mday=duntil%100;
972 period->end.tm_mon=(duntil/100)%100-1;
973 period->end.tm_year=(duntil/10000)-1900;
42b117e3
FM
974}
975
cc6af460
FM
976/*!
977Update the \a main period to encompass the period in \a candidate.
978*/
979void getperiod_merge(struct periodstruct *main,struct periodstruct *candidate)
980{
981 int cdate;
982 int mdate;
983
984 mdate=(main->start.tm_year)*10000+(main->start.tm_mon)*100+main->start.tm_mday;
985 cdate=(candidate->start.tm_year)*10000+(candidate->start.tm_mon)*100+candidate->start.tm_mday;
986 if (cdate<mdate) memcpy(&main->start,&candidate->start,sizeof(struct tm));
987
988 mdate=(main->end.tm_year)*10000+(main->end.tm_mon)*100+main->end.tm_mday;
989 cdate=(candidate->end.tm_year)*10000+(candidate->end.tm_mon)*100+candidate->end.tm_mday;
990 if (cdate>mdate) memcpy(&main->end,&candidate->end,sizeof(struct tm));
991}
992
fa6552b0
FM
993int getperiod_buildtext(struct periodstruct *period)
994{
9bd92830
FM
995 int i;
996 int range;
997 char text1[40], text2[40];
998
81a022d8 999 if (df=='u') {
9bd92830 1000 i=strftime(text1, sizeof(text1), "%Y %b %d", &period->start);
81a022d8 1001 } else if(df=='e') {
9bd92830 1002 i=strftime(text1, sizeof(text1), "%d %b %Y", &period->start);
81a022d8 1003 } else /*if (df=='w')*/ {
9bd92830
FM
1004 IndexTree=INDEX_TREE_FILE;
1005 i=strftime(text1, sizeof(text1), "%Y.%U", &period->start);
1006 }
1007 if (i == 0) return(-1);
1008
1009 range=(period->start.tm_year!=period->end.tm_year ||
007905af
FM
1010 period->start.tm_mon!=period->end.tm_mon ||
1011 period->start.tm_mday!=period->end.tm_mday);
9bd92830 1012 if (range) {
81a022d8 1013 if (df=='u') {
9bd92830 1014 i=strftime(text2, sizeof(text2)-i, "%Y %b %d", &period->end);
81a022d8 1015 } else if (df=='e') {
9bd92830
FM
1016 i=strftime(text2, sizeof(text2)-i, "%d %b %Y", &period->end);
1017 } else {
1018 i=strftime(text2, sizeof(text2)-i, "%Y.%U", &period->end);
1019 }
1020 if (i == 0) return(-1);
1021 }
1022
1023 if (range) {
1024 snprintf(period->text,sizeof(period->text),"%s-%s",text1,text2);
1025 snprintf(period->html,sizeof(period->html),"%s&mdash;%s",text1,text2);
1026 } else {
a87d4d11
FM
1027 safe_strcpy(period->text,text1,sizeof(period->text));
1028 safe_strcpy(period->html,text1,sizeof(period->html));
9bd92830
FM
1029 }
1030 return(0);
25697a35
GS
1031}
1032
06ced858 1033static void copy_images(void)
25697a35 1034{
9bd92830
FM
1035 FILE *img_in, *img_ou;
1036 char images[512];
1037 char imgdir[MAXLEN];
1038 char srcfile[MAXLEN];
1039 char dstfile[MAXLEN];
1040 DIR *dirp;
1041 struct dirent *direntp;
1042 char buffer[MAXLEN];
1043 size_t nread;
1044 struct stat info;
1045
1046 if (snprintf(images,sizeof(images),"%simages",outdir)>=sizeof(images)) {
1047 debuga(_("Cannot copy images to target directory %simages\n"),outdir);
1048 exit(EXIT_FAILURE);
1049 }
1050 if (access(images,R_OK)!=0) {
affa72c5
FM
1051 if (mkdir(images,0755)) {
1052 debuga(_("Cannot create directory %s - %s\n"),images,strerror(errno));
1053 exit(EXIT_FAILURE);
1054 }
9bd92830
FM
1055 }
1056
1057 strcpy(imgdir,IMAGEDIR);
1058 dirp = opendir(imgdir);
1059 if(dirp==NULL) {
1060 debuga(_("(util) Can't open directory %s: %s\n"),imgdir,strerror(errno));
1061 return;
1062 }
1063 while ((direntp = readdir( dirp )) != NULL ){
1064 if(direntp->d_name[0]=='.')
1065 continue;
60ec7f09
FM
1066 if (snprintf(srcfile,sizeof(srcfile),"%s/%s",imgdir,direntp->d_name)>=sizeof(srcfile)) {
1067 debuga(_("Buffer too small to store %s/%s"),imgdir,direntp->d_name);
1068 exit(EXIT_FAILURE);
1069 }
9bd92830
FM
1070 if (stat(srcfile,&info)) {
1071 debuga(_("Cannot stat \"%s\" - %s\n"),srcfile,strerror(errno));
1072 continue;
1073 }
1074 if (S_ISREG(info.st_mode)) {
60ec7f09
FM
1075 if (snprintf(dstfile,sizeof(dstfile),"%s/%s",images,direntp->d_name)>=sizeof(dstfile)) {
1076 debuga(_("Buffer too small to store %s/%s"),images,direntp->d_name);
1077 exit(EXIT_FAILURE);
1078 }
9bd92830
FM
1079 img_in = fopen(srcfile, "rb");
1080 if(img_in!=NULL) {
1081 img_ou = fopen(dstfile, "wb");
1082 if(img_ou!=NULL) {
1083 while ((nread = fread(buffer,1,sizeof(buffer),img_in))>0) {
1084 if (fwrite(buffer,1,nread,img_ou)!=nread) {
1085 debuga(_("Failed to copy image %s to %s\n"),srcfile,dstfile);
1086 break;
1087 }
1088 }
507460ae
FM
1089 if (fclose(img_ou)==EOF) {
1090 debuga(_("Error while copying image %s: %s\n"),dstfile,strerror(errno));
1091 exit(EXIT_FAILURE);
1092 }
9bd92830
FM
1093 } else
1094 fprintf(stderr,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open/create file", dstfile, strerror(errno));
1095 fclose(img_in);
1096 } else
1097 fprintf(stderr,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open file", srcfile, strerror(errno));
1098 }
1099 }
1100 (void) closedir(dirp);
1101
1102 return;
06ced858
FM
1103}
1104
0971b2d6
FM
1105/*!
1106 * Check if the proposed file name conforms to the directory structure layed out
1107 * as a file tree. It is used to check if the file name enumerated while scanning
1108 * a directory content may have been created by sarg running with IndexTree set to
1109 * INDEX_TREE_FILE.
1110 */
1111bool IsTreeFileDirName(const char *Name)
1112{
1113 char DateFormat;
1114 int i;
1115
1116 // start year (date format u) or start day (date format e)
1117 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1118
1119 if (isdigit(Name[2]) && isdigit(Name[3]))
1120 {
1121 // date format is either u or w
1122 if (Name[4]=='.')
1123 {
1124 // date format is w
1125 if (!isdigit(Name[5]) || !isdigit(Name[6])) return(false);
1126 return(true);//date format w is confirmed
1127 }
1128
1129 // date format is u
1130 Name+=4;
1131
1132 // start month
1133 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1134 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1135 if (i<0) return(false);
1136 Name+=3;
1137
1138 // start day
1139 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1140 Name+=2;
1141
1142 DateFormat='u';
1143 }
1144 else if (isalpha(Name[2]) && isalpha(Name[3]) && isalpha(Name[4]))
1145 {
1146 // date format is e
1147 Name+=2;
1148
1149 // start month
1150 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1151 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1152 if (i<0) return(false);
1153 Name+=3;
1154
1155 // start day
1156 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1157 Name+=4;
1158
1159 DateFormat='e';
1160 }
1161 else
1162 return(false);
1163
1164 if (Name[0]!='-') return(false);
1165 Name++;
1166
1167 if (DateFormat=='u')
1168 {
1169 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1170 Name+=4;
1171
1172 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1173 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1174 if (i<0) return(false);
1175 Name+=3;
1176
1177 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1178 Name+=2;
1179 }
1180 else //DateFormat=='e'
1181 {
1182 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1183 Name+=2;
1184
1185 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1186 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1187 if (i<0) return(false);
1188 Name+=3;
1189
1190 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1191 Name+=4;
1192 }
1193 /*
16c12388
FM
1194 * The directory name may contains additional characters such as a counter if
1195 * a previous report is never overwritten.
1196 */
1197 return(true);
1198}
1199
1200/*!
1201 * Check if the proposed file name can be the year part of a report tree build with
1202 * IndexTree set to INDEX_TREE_DATE.
1203 */
1204bool IsTreeYearFileName(const char *Name)
1205{
1206 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1207 Name+=4;
1208 if (Name[0]=='-')
1209 {
1210 Name++;
1211 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1212 Name+=4;
1213 }
1214 if (Name[0]) return(false);
1215 return(true);
1216}
1217
1218/*!
1219 * Check if the proposed file name can be the month part of a report tree build with
1220 * IndexTree set to INDEX_TREE_DATE.
1221 */
1222bool IsTreeMonthFileName(const char *Name)
1223{
1224 int m;
1225
1226 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1227 m=(Name[0]-'0')*10+(Name[1]-'0');
1228 if (m<1 || m>12) return(false);
1229 Name+=2;
1230 if (Name[0]=='-')
1231 {
1232 Name++;
1233 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1234 m=(Name[0]-'0')*10+(Name[1]-'0');
1235 if (m<1 || m>12) return(false);
1236 Name+=2;
1237 }
1238 if (Name[0]) return(false);
1239 return(true);
1240}
1241
1242/*!
1243 * Check if the proposed file name can be the day part of a report tree build with
1244 * IndexTree set to INDEX_TREE_DATE.
1245 */
1246bool IsTreeDayFileName(const char *Name)
1247{
1248 int d;
1249
1250 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1251 d=(Name[0]-'0')*10+(Name[1]-'0');
1252 if (d<1 || d>31) return(false);
1253 if (Name[2]=='-')
1254 {
1255 Name+=3;
1256 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1257 d=(Name[0]-'0')*10+(Name[1]-'0');
1258 if (d<1 || d>31) return(false);
1259 }
1260 /*
1261 * The directory name may contains additional characters such as a counter if
1262 * a previous report is never overwritten.
0971b2d6
FM
1263 */
1264 return(true);
1265}
1266
fa6552b0 1267int vrfydir(const struct periodstruct *per1, const char *addr, const char *site, const char *us, const char *form)
06ced858 1268{
9bd92830
FM
1269 FILE *fp_ou;
1270 int num=1, count=0;
1271 char wdir[MAXLEN];
1272 char dirname2[MAXLEN];
1273 int y1, y2;
1274 int m1, m2;
1275 int d1, d2;
1276 int wlen, wlen2;
1277 time_t curtime;
1278 struct tm *loctm;
1279
1280 strcpy(wdir,outdir);
1281 wlen=strlen(wdir);
1282 y1=per1->start.tm_year+1900;
1283 y2=per1->end.tm_year+1900;
1284 m1=per1->start.tm_mon+1;
1285 m2=per1->end.tm_mon+1;
1286 d1=per1->start.tm_mday;
1287 d2=per1->end.tm_mday;
1288 if(IndexTree == INDEX_TREE_DATE) {
1289 wlen+=sprintf(wdir+wlen,"%04d",y1);
1290 if(y1!=y2) wlen+=sprintf(wdir+wlen,"-%04d",y2);
1291 if(access(wdir, R_OK) != 0)
1292 my_mkdir(wdir);
1293
1294 wlen+=sprintf(wdir+wlen,"/%02d",m1);
1295 if(m1 != m2) wlen+=sprintf(wdir+wlen,"-%02d",m2);
1296 if(access(wdir, R_OK) != 0)
1297 my_mkdir(wdir);
1298
1299 wlen+=sprintf(wdir+wlen,"/%02d",d1);
1300 if(d1!=d2) wlen+=sprintf(wdir+wlen,"-%02d",d2);
1301 } else {
81a022d8 1302 if (df == 'u') {
9bd92830 1303 wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%04d%s%02d-%04d%s%02d",y1,
007905af 1304 conv_month_name(m1),d1,y2,conv_month_name(m2),d2);
81a022d8 1305 } else if (df == 'e') {
9bd92830 1306 wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%02d%s%04d-%02d%s%04d",d1,
007905af 1307 conv_month_name(m1),y1,d2,conv_month_name(m2),y2);
81a022d8 1308 } else if (df == 'w') {
9bd92830
FM
1309 wlen2=strftime(wdir+wlen, sizeof(wdir)-wlen, "%Y.%U", &per1->start);
1310 if (wlen2==0) return(-1);
1311 wlen+=wlen2;
1312 }
1313 }
1314
1315 if(us[0] != '\0') {
1316 struct userinfostruct *uinfo=userinfo_find_from_id(us);
1317 if (uinfo) {
1318 strcat(wdir,"-");
1319 strcat(wdir,uinfo->filename);
1320 }
1321 }
1322 if(addr[0] != '\0') {
1323 strcat(wdir,"-");
1324 strcat(wdir,addr);
1325 }
1326 if(site[0] != '\0') {
1327 strcat(wdir,"-");
1328 strcat(wdir,site);
1329 }
1330
1331 strcpy(outdirname,wdir);
1332
1333 if(IndexTree != INDEX_TREE_DATE) {
1334 if(!OverwriteReport) {
1335 while(num) {
1336 if(access(wdir,R_OK) == 0) {
1337 sprintf(wdir,"%s.%d",outdirname,num);
1338 num++;
1339 count++;
1340 } else
1341 break;
1342 }
1343
1344 if(count > 0) {
1345 if(debug)
1346 debuga(_("File %s already exists, moved to %s\n"),outdirname,wdir);
1347 rename(outdirname,wdir);
1348 }
1349 } else {
1350 if(access(outdirname,R_OK) == 0) {
1351 unlinkdir(outdirname,1);
1352 }
1353 }
1354 my_mkdir(outdirname);
1355 } else {
1356 strcpy(dirname2,wdir);
1357 if(!OverwriteReport) {
1358 while(num) {
1359 if(access(wdir,R_OK) == 0) {
1360 sprintf(wdir,"%s.%d",dirname2,num);
1361 num++;
1362 count++;
1363 } else
1364 break;
1365 }
1366
1367 if(count > 0) {
1368 if(debug)
1369 debuga(_("File %s already exists, moved to %s\n"),dirname2,wdir);
1370 rename(dirname2,wdir);
1371 strcpy(dirname2,wdir);
1372 }
1373 } else {
1374 if(access(wdir,R_OK) == 0) {
1375 unlinkdir(wdir,1);
1376 }
1377 }
1378
1379 if(access(wdir, R_OK) != 0)
1380 my_mkdir(wdir);
1381 }
1382
1383 strcpy(dirname2,wdir);
1384
60ec7f09
FM
1385 if (snprintf(wdir,sizeof(wdir),"%s/sarg-date",outdirname)>=sizeof(wdir)) {
1386 debuga(_("Buffer too small to store %s/sarg-date"),outdirname);
1387 exit(EXIT_FAILURE);
1388 }
9bd92830 1389 if ((fp_ou = fopen(wdir, "wt")) == 0) {
d6f0349d 1390 debuga(_("cannot open %s for writing: %s\n"),wdir,strerror(errno));
9bd92830
FM
1391 perror("SARG:");
1392 exit(EXIT_FAILURE);
1393 }
1394 time(&curtime);
1395 //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
1396 loctm=localtime(&curtime);
1397 strftime(wdir,sizeof(wdir),"%Y-%m-%d %H:%M:%S",loctm);
1398 if (fprintf(fp_ou,"%s %d\n",wdir,loctm->tm_isdst)<0) {
1399 debuga(_("Failed to write the date in %s\n"),wdir);
1400 perror("SARG:");
1401 exit(EXIT_FAILURE);
1402 }
1403 if (fclose(fp_ou)==EOF) {
507460ae 1404 debuga(_("Failed to write the date in %s: %s\n"),wdir,strerror(errno));
9bd92830
FM
1405 exit(EXIT_FAILURE);
1406 }
1407
1408 copy_images();
1409 return(0);
25697a35
GS
1410}
1411
a87d4d11
FM
1412/*!
1413 Copy a string without overflowing the buffer. The copied string
1414 is properly terminated by an ASCII zero.
b902df7e 1415
a87d4d11
FM
1416 \param dest The destination buffer.
1417 \param src The source buffer.
1418 \param length The size of the destination buffer. The program is aborted
1419 if the length is negative or zero.
1420*/
1421void safe_strcpy(char *dest,const char *src,int length)
1422{
1423 if (length<=0) {
1424 debuga(_("Invalid buffer length passed to the function to safely copy a string\n"));
1425 exit(EXIT_FAILURE);
1426 }
1427 strncpy(dest,src,length-1);
1428 dest[length-1]='\0';
1429}
1430
25697a35
GS
1431void strip_latin(char *line)
1432{
9bd92830
FM
1433 int i,j;
1434 int skip;
1435
1436 j=0;
1437 skip=0;
1438 for (i=0;line[i];i++){
1439 if (skip){
1440 if (line[i]==';') skip=0;
1441 } else {
1442 if (line[i]=='&')
1443 skip=1;
1444 else
1445 line[j++]=line[i];
1446 }
1447 }
1448 line[j]='\0';
1449 return;
25697a35
GS
1450}
1451
81a022d8 1452void zdate(char *ftime,int ftimesize, char DateFormat)
25697a35 1453{
9bd92830
FM
1454 time_t t;
1455 struct tm *local;
25697a35 1456
9bd92830
FM
1457 t = time(NULL);
1458 local = localtime(&t);
81a022d8 1459 if (DateFormat=='u')
9bd92830 1460 strftime(ftime, ftimesize, "%b/%d/%Y %H:%M", local);
81a022d8 1461 else if (DateFormat=='e')
9bd92830 1462 strftime(ftime, ftimesize, "%d/%b/%Y-%H:%M", local);
81a022d8 1463 else if (DateFormat=='w')
9bd92830
FM
1464 strftime(ftime, ftimesize, "%W-%H-%M", local);
1465 return;
25697a35
GS
1466}
1467
1468
324ba7f3 1469char *fixtime(long long int elap)
25697a35 1470{
60ec7f09 1471 long int num = elap / 1000LL;
9bd92830
FM
1472 int hor = 0;
1473 int min = 0;
1474 int sec = 0;
60ec7f09 1475 static char buf[20];
25697a35 1476
60ec7f09
FM
1477 hor=num / 3600L;
1478 min=(num % 3600L) / 60L;
1479 sec=num % 60L;
25697a35 1480
9bd92830
FM
1481 if(hor==0 && min==0 && sec==0)
1482 strcpy(buf,"0");
1483 else
60ec7f09 1484 snprintf(buf,sizeof(buf),"%d:%02d:%02d",hor,min,sec);
25697a35 1485
9bd92830 1486 return buf;
25697a35
GS
1487}
1488
1489
60ec7f09 1490void date_from(char *date,int date_size, int *dfrom, int *duntil)
25697a35 1491{
9bd92830
FM
1492 int d0=0;
1493 int m0=0;
1494 int y0=0;
1495 int d1=0;
1496 int m1=0;
1497 int y1=0;
1498
1499 if (isdigit(date[0])) {
1500 int next=-1;
1501
1502 if (sscanf(date,"%d/%d/%d%n",&d0,&m0,&y0,&next)!=3 || y0<100 || m0<1 || m0>12 || d0<1 || d0>31 || next<0) {
1503 debuga(_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1504 exit(EXIT_FAILURE);
1505 }
1506 if (date[next]=='-') {
1507 if (sscanf(date+next+1,"%d/%d/%d",&d1,&m1,&y1)!=3 || y1<100 || m1<1 || m1>12 || d1<1 || d1>31) {
1508 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1509 exit(EXIT_FAILURE);
1510 }
1511 } else if (date[next]!='\0') {
1512 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1513 exit(EXIT_FAILURE);
1514 } else {
1515 d1=d0;
1516 m1=m0;
1517 y1=y0;
1518 }
1519 } else {
1520 int i;
1521 time_t Today,t1;
1522 struct tm *Date0,Date1;
1523
1524 if (time(&Today)==(time_t)-1) {
1525 debuga(_("Failed to get the current time\n"));
1526 exit(EXIT_FAILURE);
1527 }
1528 if (sscanf(date,"day-%d",&i)==1) {
1529 if (i<0) {
1530 debuga(_("Invalid number of days in -d parameter\n"));
1531 exit(EXIT_FAILURE);
1532 }
1533 Today-=i*24*60*60;
1534 Date0=localtime(&Today);
1535 if (Date0==NULL) {
1536 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1537 exit(EXIT_FAILURE);
1538 }
1539 y0=y1=Date0->tm_year+1900;
1540 m0=m1=Date0->tm_mon+1;
1541 d0=d1=Date0->tm_mday;
1542 } else if (sscanf(date,"week-%d",&i)==1) {
1543 /*
1544 There is no portable way to find the first day of the week even though the
1545 information is available in the locale. nl_langinfo has the unofficial
1546 parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
1547 undocumented as is their return value and it is discouraged to use them.
1548 Beside, nl_langinfo isn't available on windows and the first day of the
1549 week isn't available at all on that system.
1550 */
1551 const int FirstWeekDay=1;
1552 time_t WeekBegin;
1553
1554 if (i<0) {
1555 debuga(_("Invalid number of weeks in -d parameter\n"));
1556 exit(EXIT_FAILURE);
1557 }
1558 Date0=localtime(&Today);
1559 if (Date0==NULL) {
1560 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1561 exit(EXIT_FAILURE);
1562 }
1563 WeekBegin=Today-((Date0->tm_wday-FirstWeekDay+7)%7)*24*60*60;
1564 WeekBegin-=i*7*24*60*60;
1565 Date0=localtime(&WeekBegin);
1566 if (Date0==NULL) {
1567 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1568 exit(EXIT_FAILURE);
1569 }
1570 y0=Date0->tm_year+1900;
1571 m0=Date0->tm_mon+1;
1572 d0=Date0->tm_mday;
1573 WeekBegin+=6*24*60*60;
1574 Date0=localtime(&WeekBegin);
1575 if (Date0==NULL) {
1576 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1577 exit(EXIT_FAILURE);
1578 }
1579 y1=Date0->tm_year+1900;
1580 m1=Date0->tm_mon+1;
1581 d1=Date0->tm_mday;
1582 } else if (sscanf(date,"month-%d",&i)==1) {
1583 if (i<0) {
1584 debuga(_("Invalid number of months in -d parameter\n"));
1585 exit(EXIT_FAILURE);
1586 }
1587 Date0=localtime(&Today);
1588 if (Date0==NULL) {
1589 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1590 exit(EXIT_FAILURE);
1591 }
1592 if (Date0->tm_mon<i%12) {
1593 y0=Date0->tm_year+1900-i/12-1;
1594 m0=(Date0->tm_mon+12-i%12)%12+1;
1595 d0=1;
1596 } else {
1597 y0=Date0->tm_year+1900-i/12;
1598 m0=Date0->tm_mon-i%12+1;
1599 d0=1;
1600 }
1601 memcpy(&Date1,Date0,sizeof(struct tm));
1602 Date1.tm_isdst=-1;
1603 Date1.tm_mday=1;
1604 if (m0<12) {
1605 Date1.tm_mon=m0;
1606 Date1.tm_year=y0-1900;
1607 } else {
1608 Date1.tm_mon=0;
1609 Date1.tm_year=y0-1900+1;
1610 }
1611 t1=mktime(&Date1);
1612 t1-=24*60*60;
1613 Date0=localtime(&t1);
1614 y1=Date0->tm_year+1900;
1615 m1=Date0->tm_mon+1;
1616 d1=Date0->tm_mday;
1617 } else {
1618 debuga(_("Invalid date range passed on command line\n"));
1619 exit(EXIT_FAILURE);
1620 }
1621 }
1622
1623 *dfrom=y0*10000+m0*100+d0;
1624 *duntil=y1*10000+m1*100+d1;
60ec7f09 1625 snprintf(date,date_size,"%02d/%02d/%04d-%02d/%02d/%04d",d0,m0,y0,d1,m1,y1);
9bd92830 1626 return;
25697a35
GS
1627}
1628
1629
1630char *strlow(char *string)
1631{
9bd92830 1632 char *s;
25697a35 1633
9bd92830
FM
1634 if (string)
1635 {
1636 for (s = string; *s; ++s)
1637 *s = tolower(*s);
1638 }
25697a35 1639
9bd92830 1640 return string;
25697a35
GS
1641}
1642
1643
1644
1645
1646char *strup(char *string)
1647{
9bd92830 1648 char *s;
25697a35 1649
9bd92830
FM
1650 if (string)
1651 {
1652 for (s = string; *s; ++s)
1653 *s = toupper(*s);
1654 }
25697a35 1655
9bd92830 1656 return string;
25697a35
GS
1657}
1658
1659
32e71fa4 1660void removetmp(const char *outdir)
25697a35 1661{
9dc20988
FM
1662 FILE *fp_gen;
1663 char filename[256];
9bd92830
FM
1664
1665 if(!RemoveTempFiles)
1666 return;
1667
1668 if(debug) {
1669 debuga(_("Purging temporary file sarg-general\n"));
1670 }
9dc20988 1671 if (snprintf(filename,sizeof(filename),"%s/sarg-general",outdir)>=sizeof(filename)) {
9bd92830
FM
1672 debuga(_("(removetmp) directory too long to remove %s/sarg-period\n"),outdir);
1673 exit(EXIT_FAILURE);
1674 }
9dc20988 1675 if((fp_gen=fopen(filename,"w"))==NULL){
d6f0349d 1676 debuga(_("(removetmp) Cannot open file %s: %s\n"),filename,strerror(errno));
9bd92830
FM
1677 exit(EXIT_FAILURE);
1678 }
9dc20988
FM
1679 totalger(fp_gen,filename);
1680 if (fclose(fp_gen)==EOF) {
507460ae 1681 debuga(_("Failed to close %s after writing the total line: %s\n"),filename,strerror(errno));
9bd92830
FM
1682 exit(EXIT_FAILURE);
1683 }
25697a35
GS
1684}
1685
48864d28 1686void load_excludecodes(const char *ExcludeCodes)
25697a35 1687{
9bd92830
FM
1688 FILE *fp_in;
1689 char data[80];
1690 int i;
1691 int Stored;
1692 long int MemSize;
1693
1694 if(ExcludeCodes[0] == '\0')
1695 return;
1696
1697 if((fp_in=fopen(ExcludeCodes,"r"))==NULL) {
d6f0349d 1698 debuga(_("(util) Cannot open file %s (exclude_codes): %s\n"),ExcludeCodes,strerror(errno));
9bd92830
FM
1699 exit(EXIT_FAILURE);
1700 }
1701
1702 if (fseek(fp_in, 0, SEEK_END)==-1) {
1703 debuga(_("Failed to move till the end of the excluded codes file %s: %s\n"),ExcludeCodes,strerror(errno));
1704 exit(EXIT_FAILURE);
1705 }
1706 MemSize = ftell(fp_in);
1707 if (MemSize<0) {
1708 debuga(_("Cannot get the size of file %s\n"),ExcludeCodes);
1709 exit(EXIT_FAILURE);
1710 }
1711 if (fseek(fp_in, 0, SEEK_SET)==-1) {
1712 debuga(_("Failed to rewind the excluded codes file %s: %s\n"),ExcludeCodes,strerror(errno));
1713 exit(EXIT_FAILURE);
1714 }
1715
1716 MemSize+=1;
1717 if((excludecode=(char *) malloc(MemSize))==NULL) {
1718 debuga(_("malloc error (%ld)\n"),MemSize);
1719 exit(EXIT_FAILURE);
1720 }
1721 memset(excludecode,0,MemSize);
1722
1723 Stored=0;
1724 while(fgets(data,sizeof(data),fp_in)!=NULL) {
1725 if (data[0]=='#') continue;
1726 for (i=strlen(data)-1 ; i>=0 && (unsigned char)data[i]<=' ' ; i--) data[i]='\0';
1727 if (i<0) continue;
1728 if (Stored+i+2>=MemSize) {
1729 debuga(_("Too many codes to exclude in file %s\n"),ExcludeCodes);
1730 break;
1731 }
1732 strcat(excludecode,data);
1733 strcat(excludecode,";");
1734 Stored+=i+1;
1735 }
1736
1737 fclose(fp_in);
1738 return;
25697a35
GS
1739}
1740
48864d28
FM
1741void free_excludecodes(void)
1742{
9bd92830
FM
1743 if (excludecode) {
1744 free(excludecode);
1745 excludecode=NULL;
1746 }
48864d28
FM
1747}
1748
32e71fa4 1749int vercode(const char *code)
25697a35 1750{
9bd92830
FM
1751 char *cod;
1752 int clen;
48864d28 1753
9bd92830
FM
1754 if (excludecode && excludecode[0]!='\0') {
1755 clen=strlen(code);
1756 cod=excludecode;
1757 while (cod) {
1758 if (strncmp(code,cod,clen)==0 && cod[clen]==';')
1759 return 1;
1760 cod=strchr(cod,';');
1761 if (cod) cod++;
1762 }
1763 }
1764 return 0;
25697a35
GS
1765}
1766
1767void fixnone(char *str)
1768{
9bd92830 1769 int i;
32e71fa4 1770
9bd92830
FM
1771 for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--);
1772 if(i==3 && strncmp(str,"none",4) == 0)
1773 str[0]='\0';
25697a35 1774
9bd92830 1775 return;
25697a35
GS
1776}
1777
2357ef77
FM
1778void fixendofline(char *str)
1779{
9bd92830 1780 int i;
2357ef77 1781
9bd92830 1782 for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--) str[i]=0;
2357ef77
FM
1783}
1784
25697a35 1785#ifdef LEGACY_TESTVALIDUSERCHAR
32e71fa4 1786int testvaliduserchar(const char *user)
25697a35 1787{
9bd92830
FM
1788 int x=0;
1789 int y=0;
25697a35 1790
9bd92830
FM
1791 for (y=0; y<strlen(UserInvalidChar); y++) {
1792 for (x=0; x<strlen(user); x++) {
1793 if(user[x] == UserInvalidChar[y])
1794 return 1;
1795 }
1796 }
1797 return 0;
25697a35
GS
1798}
1799#else
32e71fa4 1800int testvaliduserchar(const char *user)
25697a35 1801{
9bd92830
FM
1802 char * p_UserInvalidChar = UserInvalidChar ;
1803 const char * p_user ;
25697a35 1804
9bd92830
FM
1805 while( *p_UserInvalidChar ) {
1806 p_user = user ;
1807 while ( *p_user ) {
1808 if( *p_UserInvalidChar == *p_user )
1809 return 1;
1810 p_user++ ;
1811 }
1812 p_UserInvalidChar++ ;
1813 }
1814 return 0;
25697a35
GS
1815}
1816#endif
1817
1818int compar( const void *a, const void *b )
9bd92830
FM
1819{
1820 if( *(int *)a > *(int *)b ) return 1;
1821 if( *(int *)a < *(int *)b ) return -1;
1822 return 0;
25697a35
GS
1823}
1824
1825int getnumlist( char *buf, numlist *list, const int len, const int maxvalue )
48864d28 1826{
9bd92830
FM
1827 int i, j, d, flag, r1, r2;
1828 char *pbuf, **bp, *strbufs[ 24 ];
1829
1830 bp = strbufs;
1831 strtok( buf, " \t" );
1832 for( *bp = strtok( NULL, "," ), list->len = 0; *bp; *bp = strtok( NULL, "," ) ) {
1833 if( ++bp >= &strbufs[ 24 ] )
1834 break;
1835 list->len++;
1836 }
1837 if( ! list->len )
1838 return -1;
1839 d = 0;
1840 for( i = 0; i < list->len; i++ ) {
1841 if( strchr( strbufs[ i ], '-' ) != 0 ) {
1842 pbuf = strbufs[ i ];
1843 strtok( pbuf, "-" );
1844 pbuf = strtok( NULL, "\0" );
1845 r1 = atoi( strbufs[ i ] );
1846 if( ( r2 = atoi( pbuf ) ) >= maxvalue || r1 >= r2 )
1847 return -1;
1848 if( i + d + ( r2 - r1 ) + 1 <= len ) {
1849 for( j = r1; j <= r2; j++ )
1850 list->list[ i + d++ ] = j;
1851 d--;
1852 }
1853 }
1854 else
1855 if( ( list->list[ i + d ] = atoi( strbufs[ i ] ) ) >= maxvalue )
1856 return 1;
1857 }
1858 list->len += d;
1859 qsort( list->list, list->len, sizeof( int ), compar );
1860 do {
1861 flag = 0;
1862 for( i = 0; i < list->len - 1; i++ )
1863 if( list->list[ i ] == list->list[ i + 1 ] ) {
1864 for( j = i + 1; j < list->len; j++ )
1865 list->list[ j - 1 ] = list->list[ j ];
1866 list->len--;
1867 flag = 1;
1868 break;
1869 }
1870 } while( flag );
1871 return 0;
25697a35
GS
1872}
1873
dfb337be
FM
1874void show_info(FILE *fp_ou)
1875{
9bd92830 1876 char ftime[127];
dfb337be 1877
9bd92830 1878 if(!ShowSargInfo) return;
81a022d8 1879 zdate(ftime, sizeof(ftime), df);
b3a3c51c
FM
1880 fputs("<div class=\"info\">",fp_ou);
1881 fprintf(fp_ou,_("Generated by <a href='%s'>%s-%s</a> on %s"),URL,PGM,VERSION,ftime);
1882 fputs("</div>\n",fp_ou);
dfb337be
FM
1883}
1884
c0ec9cc7 1885void show_sarg(FILE *fp_ou, int depth)
dfb337be 1886{
9bd92830 1887 int i;
c0ec9cc7 1888
9bd92830
FM
1889 if(!ShowSargLogo) return;
1890 fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou);
1891 for (i=0 ; i<depth ; i++)
1892 fputs("../",fp_ou);
1893 fputs("images/sarg.png\" title=\"SARG, Squid Analysis Report Generator. Logo by Osamu Matsuzaki\" alt=\"Sarg\"></a>&nbsp;Squid Analysis Report Generator</div>\n",fp_ou);
dfb337be
FM
1894}
1895
1896void write_logo_image(FILE *fp_ou)
1897{
9bd92830
FM
1898 if(LogoImage[0]!='\0')
1899 fprintf(fp_ou, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\">&nbsp;%s</div>\n",LogoImage,Width,Height,LogoText);
dfb337be 1900}
491b862f 1901
2e96438d 1902void write_html_head(FILE *fp_ou, int depth, const char *page_title,int javascript)
491b862f 1903{
9bd92830 1904 int i;
2e96438d 1905
9bd92830
FM
1906 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou);
1907 fprintf(fp_ou, "<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet);
1908 if (page_title) fprintf(fp_ou,"<title>%s</title>\n",page_title);
1909 css(fp_ou);
1910 if ((javascript & HTML_JS_SORTTABLE)!=0 && SortTableJs[0]) {
1911 fputs("<script type=\"text/javascript\" src=\"",fp_ou);
1912 if (strncmp(SortTableJs,"../",3)==0) {
1913 for (i=0 ; i<depth ; i++) fputs("../",fp_ou);
1914 }
1915 fputs(SortTableJs,fp_ou);
1916 fputs("\"></script>\n",fp_ou);
1917 }
1918 fputs("</head>\n<body>\n",fp_ou);
7f2382f6
FM
1919}
1920
2e96438d 1921void write_html_header(FILE *fp_ou, int depth, const char *page_title,int javascript)
7f2382f6 1922{
9bd92830
FM
1923 write_html_head(fp_ou,depth,page_title,javascript);
1924 write_logo_image(fp_ou);
1925 show_sarg(fp_ou, depth);
1926 fprintf(fp_ou,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title);
c0ec9cc7
FM
1927}
1928
1929void close_html_header(FILE *fp_ou)
1930{
9bd92830 1931 fputs("</table></div>\n",fp_ou);
c0ec9cc7
FM
1932}
1933
fa6552b0 1934int write_html_trailer(FILE *fp_ou)
c0ec9cc7 1935{
9bd92830
FM
1936 show_info(fp_ou);
1937 if (fputs("</body>\n</html>\n",fp_ou)==EOF) return(-1);
1938 return(0);
dfb337be
FM
1939}
1940
ac422f9b 1941void output_html_string(FILE *fp_ou,const char *str,int maxlen)
dfb337be 1942{
9bd92830
FM
1943 int i=0;
1944
1945 while (*str && (maxlen<=0 || i<maxlen)) {
1946 switch (*str) {
1947 case '&':
1948 fputs("&amp;",fp_ou);
1949 break;
1950 case '<':
1951 fputs("&lt;",fp_ou);
1952 break;
1953 case '>':
1954 fputs("&gt;",fp_ou);
1955 break;
1956 case '"':
1957 fputs("&quot;",fp_ou);
1958 break;
1959 case '\'':
1960 fputs("&#39;",fp_ou);
1961 break;
1962 default:
1963 fputc(*str,fp_ou);
1964 }
1965 str++;
1966 i++;
1967 }
1968 if (maxlen>0 && i>=maxlen)
1969 fputs("&hellip;",fp_ou);
ac422f9b
FM
1970}
1971
1972void output_html_url(FILE *fp_ou,const char *url)
1973{
9bd92830
FM
1974 while (*url) {
1975 if (*url=='&')
1976 fputs("&amp;",fp_ou);
1977 else
1978 fputc(*url,fp_ou);
1979 url++;
1980 }
491b862f
GS
1981}
1982
67a93701
FM
1983/*!
1984 Write a host name inside an A tag of a HTML file. If the host name starts
1985 with a star, it is assumed to be an alias that cannot be put inside a link
1986 so the A tag is not written around the host name.
b902df7e 1987
67a93701
FM
1988 \param fp_ou The handle of the HTML file.
1989 \param url The host to display in the HTML file.
1990 \param maxlen The maximum number of characters to print into the host name.
1991 */
6fa33a32 1992void output_html_link(FILE *fp_ou,const char *url,int maxlen)
67a93701
FM
1993{
1994 if (url[0]==ALIAS_PREFIX) {
1995 // this is an alias, no need for a A tag
1996 output_html_string(fp_ou,url+1,100);
1997 } else {
6fa33a32
FM
1998 if (skip_scheme(url)==url)
1999 fputs("<a href=\"http://",fp_ou);//no scheme in the url, assume http:// to make the link clickable
2000 else
2001 fputs("<a href=\"",fp_ou);//the scheme is in the url, no need to add one
67a93701
FM
2002 output_html_url(fp_ou,url);
2003 fputs("\">",fp_ou);
2004 output_html_string(fp_ou,url,100);
2005 fputs("</a>",fp_ou);
2006 }
2007}
2008
48864d28 2009void url_module(const char *url, char *w2)
25697a35 2010{
9bd92830
FM
2011 int x, y;
2012 char w[255];
25697a35 2013
9bd92830
FM
2014 y=0;
2015 for(x=strlen(url)-1; x>=0; x--) {
2016 if(url[x] == '/' || y>=sizeof(w)-1) break;
2017 w[y++]=url[x];
2018 }
2019 if (x<0) {
2020 w2[0]='\0';
2021 return;
2022 }
25697a35 2023
9bd92830
FM
2024 x=0;
2025 for(y=y-1; y>=0; y--) {
2026 w2[x++]=w[y];
2027 }
2028 w2[x]='\0';
25697a35
GS
2029}
2030
f72b484a
FM
2031/*!
2032Mangle an URL to produce a part that can be used as an anchor in
2033a html <a name=""> tag.
2034
2035\param url The URL to mangle.
2036\param anchor The buffer to write the mangled URL.
2037\param size The size of the buffer.
2038*/
2039void url_to_anchor(const char *url,char *anchor,int size)
e5b2c6f0 2040{
f72b484a
FM
2041 int i,j;
2042 bool skip;
e5b2c6f0 2043
f72b484a
FM
2044 // find url end
2045 for (i=0 ; url[i] && url[i]!='/' && url[i]!='?' ; i++);
2046 i--;
2047 if (i<=0) {
2048 anchor[0]='\0';
2049 return;
2050 }
2051
2052 // only keep really safe characters
2053 skip=false;
2054 j=size-1;
2055 anchor[j]='\0';
2056 while (j>0 && i>=0)
2057 {
2058 if(isalnum(url[i]) || url[i]=='-' || url[i]=='_' || url[i]=='.') {
2059 anchor[--j]=url[i];
2060 skip=false;
9bd92830 2061 } else {
f72b484a
FM
2062 if (!skip) anchor[--j]='_';
2063 skip=true;
2064 }
2065 i--;
2066 }
2067 if (j>0)
2068 {
2069 while ( anchor[j])
2070 {
2071 *anchor=anchor[j];
2072 anchor++;
9bd92830 2073 }
f72b484a 2074 *anchor='\0';
9bd92830 2075 }
e5b2c6f0 2076}
d6e703cc 2077
32e71fa4 2078void version(void)
25697a35 2079{
9bd92830 2080 printf(_("SARG Version: %s\n"),VERSION);
728f6a04
FM
2081#if defined(ENABLE_NLS) && defined(HAVE_LOCALE_H)
2082 if (debug) {
2683bb62
FM
2083 printf(_("\nFor the translation to work, a valid message file should be installed as "
2084 "\"%s/<Locale>/LC_MESSAGES/%s.mo\" where <Locale> is derived from the effective locale.\n"),LOCALEDIR,PACKAGE_NAME);
2085 if (CurrentLocale) {
2086 printf(_("Currently effective locale is \"%s\".\n"),CurrentLocale);
2087 } else {
2088 printf(_("Locale is not set in the environment variable.\n"));
2089 }
2090 // TRANSLATORS: You may change this message to tell the reader that the language is correctly supported.
2091 printf(_("If this message is in English, then your language is not supported or not correctly installed.\n"));
728f6a04
FM
2092 }
2093#endif
9bd92830 2094 exit(EXIT_SUCCESS);
25697a35 2095}
5f3cfd1d
FM
2096
2097char *get_param_value(const char *param,char *line)
2098{
9bd92830 2099 int plen;
2357ef77 2100
9bd92830
FM
2101 while (*line==' ' || *line=='\t') line++;
2102 plen=strlen(param);
2103 if (strncasecmp(line,param,plen)) return(NULL);
2104 if (line[plen]!=' ' && line[plen]!='\t') return(NULL);
2105 line+=plen;
2106 while (*line==' ' || *line=='\t') line++;
2107 return(line);
5f3cfd1d 2108}
936c9905 2109
170a77ea 2110void unlinkdir(const char *dir,bool contentonly)
304a739d 2111{
9bd92830
FM
2112 struct stat st;
2113 DIR *dirp;
2114 struct dirent *direntp;
2115 char dname[MAXLEN];
2116 int err;
2117
2118 dirp=opendir(dir);
2119 if (!dirp) return;
2120 while ((direntp = readdir(dirp)) != NULL) {
2121 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
007905af 2122 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
9bd92830
FM
2123 continue;
2124 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2125 debuga(_("directory name to delete too long: %s/%s\n"),dir,direntp->d_name);
2126 exit(EXIT_FAILURE);
2127 }
463f8e09 2128#ifdef HAVE_LSTAT
9bd92830 2129 err=lstat(dname,&st);
463f8e09 2130#else
9bd92830 2131 err=stat(dname,&st);
463f8e09 2132#endif
9bd92830
FM
2133 if (err) {
2134 debuga(_("cannot stat %s\n"),dname);
2135 exit(EXIT_FAILURE);
2136 }
2137 if (S_ISREG(st.st_mode)) {
2138 if (unlink(dname)) {
7c4264ec 2139 debuga(_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
9bd92830
FM
2140 exit(EXIT_FAILURE);
2141 }
2142 } else if (S_ISDIR(st.st_mode)) {
2143 unlinkdir(dname,0);
2144 } else {
2145 debuga(_("unknown path type %s\n"),dname);
2146 }
2147 }
2148 closedir(dirp);
2149
2150 if (!contentonly) {
2151 if (rmdir(dir)) {
7c4264ec 2152 debuga(_("Cannot delete \"%s\": %s\n"),dir,strerror(errno));
9bd92830
FM
2153 exit(EXIT_FAILURE);
2154 }
2155 }
51465d08 2156}
ac422f9b 2157
170a77ea
FM
2158/*!
2159Delete every file from the temporary directory where sarg is told to store its
2160temporary files.
2161
2162As any stray file left over by a previous run would be included in the report, we
2163must delete every file from the temporary directory before we start processing the logs.
2164
2165But the temporary directory is given by the user either in the configuration file or
2166on the command line. We check that the user didn't give a wrong directory by looking
2167at the files stored in the directory. If a single file is not one of ours, we abort.
2168
2169\param dir The temporary directory to purge.
2170*/
2171void emptytmpdir(const char *dir)
2172{
2173 struct stat st;
2174 DIR *dirp;
2175 struct dirent *direntp;
2176 int dlen;
2177 int elen;
2178 char dname[MAXLEN];
2179 int err;
2180 int i;
2181 static const char *TmpExt[]=
2182 {
2183 ".int_unsort",
2184 ".int_log",
2185 ".day",
2186 "htmlrel.txt",
2187 ".user_unsort",
2188 ".user_log",
2189 ".utmp",
0428e5fd
FM
2190 ".ip",
2191 "lastlog1",
2192 "lastlog"
170a77ea
FM
2193 };
2194
2195 dirp=opendir(dir);
2196 if (!dirp) return;
bd43d81f 2197
170a77ea
FM
2198 // make sure the temporary directory contains only our files
2199 while ((direntp = readdir(dirp)) != NULL) {
2200 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2201 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2202 continue;
2203
2204 // is it one of our files
2205 dlen=strlen(direntp->d_name);
2206 for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
2207 elen=strlen(TmpExt[i]);
2208 if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
2209 }
2210 if (i<0) {
2211 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2212 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2213 "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
2214 exit(EXIT_FAILURE);
2215 }
bd43d81f 2216
170a77ea
FM
2217 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2218 debuga(_("directory name to delete too long: %s/%s\n"),dir,direntp->d_name);
2219 exit(EXIT_FAILURE);
2220 }
bd43d81f 2221
170a77ea
FM
2222#ifdef HAVE_LSTAT
2223 err=lstat(dname,&st);
2224#else
2225 err=stat(dname,&st);
2226#endif
2227 if (err) {
2228 debuga(_("cannot stat \"%s\"\n"),dname);
2229 exit(EXIT_FAILURE);
2230 }
2231 if (S_ISDIR(st.st_mode)) {
2232 unlinkdir(dname,0);
2233 } else if (!S_ISREG(st.st_mode)) {
6884aba6 2234 debuga(_("Unknown path type for \"%s\". Check temporary directory\n"),dname);
170a77ea
FM
2235 exit(EXIT_FAILURE);
2236 }
2237 }
2238 rewinddir(dirp);
2239
2240 // now delete our files
2241 while ((direntp = readdir(dirp)) != NULL) {
2242 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2243 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2244 continue;
2245
2246 // is it one of our files
2247 dlen=strlen(direntp->d_name);
2248 for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
2249 elen=strlen(TmpExt[i]);
2250 if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
2251 }
2252 if (i<0) {
2253 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2254 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2255 "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
2256 exit(EXIT_FAILURE);
2257 }
bd43d81f 2258
170a77ea
FM
2259 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2260 debuga(_("directory name to delete too long: %s/%s\n"),dir,direntp->d_name);
2261 exit(EXIT_FAILURE);
2262 }
2263#ifdef HAVE_LSTAT
2264 err=lstat(dname,&st);
2265#else
2266 err=stat(dname,&st);
2267#endif
2268 if (err) {
2269 debuga(_("cannot stat \"%s\"\n"),dname);
2270 exit(EXIT_FAILURE);
2271 }
2272 if (S_ISREG(st.st_mode)) {
2273 if (unlink(dname)) {
2274 debuga(_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
2275 exit(EXIT_FAILURE);
2276 }
2277 } else {
2278 debuga(_("unknown path type %s\n"),dname);
2279 }
2280 }
2281 closedir(dirp);
2282}
2283
5207d9f8
FM
2284/*!
2285 Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
2286 prefix size.
2287
2288 \param buf The buffer to parse.
2289 \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
a16cb22a 2290 The pointer may be NULL.
5207d9f8
FM
2291 \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
2292 \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
2293 \param nbits The number of prefix bits for an IP address.
2294 \param next The content of the line after the extracted address.
2295
2296 \retval 3 The pattern is a IPv6 address.
2297 \retval 2 The pattern is a IPv4 address.
2298 \retval 1 The patter is a string.
2299 \retval 0 Empty pattern.
2300 */
7819e0d5 2301int extract_address_mask(const char *buf,const char **text,unsigned char *ipv4,unsigned short int *ipv6,int *nbits,const char **next)
5207d9f8
FM
2302{
2303 int i;
2304 int j;
2305 int ip_size;
2306 unsigned int value4, value6;
2307 unsigned short int addr[8];
2308 int addr_len;
0ec4b481 2309 int nibble6_len;
5207d9f8
FM
2310 int mask, max_mask;
2311 int pad_pos;
2312 int pad_len;
08eb52bb
FM
2313 bool bracket=false;
2314 bool port=false;
2315 bool port_num=0;
5207d9f8
FM
2316
2317 // skip leading spaces and tabs
2318 while (*buf && (*buf==' ' || *buf=='\t')) buf++;
bd43d81f 2319
5207d9f8
FM
2320 // find out the nature of the pattern
2321 ip_size=0x60 | 0x04;
0ec4b481
FM
2322 if (*buf=='[') {
2323 bracket=true;
2324 ip_size=0x60;
2325 buf++;
2326 }
5207d9f8
FM
2327 value4=0U;
2328 value6=0U;
2329 addr_len=0;
0ec4b481 2330 nibble6_len=0;
5207d9f8 2331 pad_pos=-1;
84aefd39 2332 for (i=0 ; (unsigned char)buf[i]>' ' && buf[i]!='/' && buf[i]!='?' && (!bracket || buf[i]!=']') && ip_size ; i++) {
5207d9f8
FM
2333 if (ip_size & 0x04) {
2334 if (isdigit(buf[i])) {
08eb52bb
FM
2335 if (port) {
2336 port_num=port_num*10+(buf[i]-'0');
2337 if (port_num>65535) ip_size&=~0x04;
2338 } else {
2339 value4=value4*10+(buf[i]-'0');
2340 if (value4>0xFFU) ip_size&=~0x04;
2341 }
5207d9f8
FM
2342 } else if (buf[i]=='.' && addr_len<4) {
2343 addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
2344 value4=0U;
08eb52bb
FM
2345 } else if (!port && buf[i]==':') {
2346 port=true;
5207d9f8
FM
2347 } else {
2348 ip_size&=~0x04;
2349 }
2350 }
2351 if (ip_size & 0x60) {
2352 if (isdigit(buf[i])) {
2353 value6=(value6<<4)+(buf[i]-'0');
0ec4b481 2354 nibble6_len++;
5207d9f8
FM
2355 if (value6>0xFFFFU) ip_size&=~0x60;
2356 } else if (toupper(buf[i])>='A' && toupper(buf[i])<='F') {
2357 value6=(value6<<4)+(toupper(buf[i])-'A'+10);
0ec4b481 2358 nibble6_len++;
5207d9f8
FM
2359 if (value6>0xFFFFU) ip_size&=~0x60;
2360 } else if (buf[i]==':' && addr_len<8) {
0ec4b481
FM
2361 if (nibble6_len>0) {
2362 addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
2363 nibble6_len=0;
2364 }
5207d9f8 2365 value6=0U;
0ec4b481
FM
2366 if (buf[i+1]==':') {
2367 pad_pos=addr_len;
2368 i++;
2369 }
5207d9f8
FM
2370 } else {
2371 ip_size&=~0x60;
2372 }
2373 }
2374 }
2375 if (i==0) return(0);
2376 if (ip_size & 0x04) {
2377 if (addr_len!=3)
2378 ip_size&=~0x04;
2379 else
2380 addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
2381 }
2382 if (ip_size & 0x60) {
2383 if (pad_pos<0 && addr_len!=7) {
2384 ip_size&=~0x60;
2385 } else if (pad_pos>=0 && addr_len>=7)
2386 ip_size&=~0x60;
0ec4b481 2387 else if (nibble6_len>0)
5207d9f8
FM
2388 addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
2389 }
2390 if (!ip_size) {
a16cb22a
FM
2391 if (text) {
2392 *text=buf;
2393 if (bracket) (*text)--;
2394 }
5207d9f8 2395 while ((unsigned char)buf[i]>' ') i++;
7819e0d5 2396 if (next) *next=buf+i;
5207d9f8
FM
2397 return(1);
2398 }
2399 max_mask=(ip_size & 0x04) ? 4*8 : 8*16;
2400 if (buf[i]=='/') {
2401 i++;
2402 mask=atoi(buf+i);
2403 while (isdigit(buf[i])) i++;
2404 if (mask<0 || mask>max_mask) mask=max_mask;
2405 } else
2406 mask=max_mask;
0ec4b481 2407 if (ip_size & 0x60 && bracket && buf[i]==']') i++;
7819e0d5 2408 if (next) *next=buf+i;
5207d9f8
FM
2409 if (ip_size & 0x04) {
2410 if (nbits) *nbits=mask;
2411 for (i=0 ; i<addr_len ; i++)
2412 ipv4[i]=(unsigned char)addr[i];
2413 return(2);
2414 }
2415
2416 // IPv6 address
2417 if (nbits) *nbits=mask;
2418 i=0;
2419 j=0;
2420 if (pad_pos>=0) {
2421 while (i<pad_pos)
2422 ipv6[j++]=(unsigned short int)addr[i++];
2423 pad_len=8-addr_len;
0ec4b481 2424 while (j<pad_pos+pad_len)
5207d9f8
FM
2425 ipv6[j++]=0;
2426 }
2427 while (i<addr_len)
2428 ipv6[j++]=(unsigned short int)addr[i++];
2429 return(3);
2430}