]> git.ipfire.org Git - thirdparty/sarg.git/blame - index.c
Remove deleted C file from po source list
[thirdparty/sarg.git] / index.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#include "include/conf.h"
5f3cfd1d 28#include "include/defs.h"
25697a35 29
9afddeae
FM
30#ifdef HAVE_LSTAT
31#define MY_LSTAT lstat
32#else
33#define MY_LSTAT stat
34#endif
35
36
b25b96fe
FM
37static void make_date_index(void);
38static void make_file_index(void);
06ced858
FM
39static void file_index_to_date_index(const char *entry);
40static void date_index_to_file_index(const char *entry);
41
32e71fa4 42void make_index(void)
25697a35 43{
9bd92830
FM
44 DIR *dirp;
45 struct dirent *direntp;
46 char wdir[MAXLEN];
47
48 if(LastLog > 0) mklastlog(outdir);
49
50 if(Index == INDEX_NO) {
a2d39aed 51 sprintf(wdir,"%s"INDEX_HTML_FILE,outdir);
08f9b029
FM
52 if(access(wdir, R_OK) == 0) {
53 if (unlink(wdir)) {
7c4264ec 54 debuga(_("Cannot delete \"%s\": %s\n"),wdir,strerror(errno));
08f9b029
FM
55 exit(EXIT_FAILURE);
56 }
57 }
9bd92830
FM
58 return;
59 }
60
56b97279
FM
61 if(debug) {
62 // TRANSLATORS: The %s is the name of the html index file (index.html).
63 debuga(_("Making %s\n"),INDEX_HTML_FILE);
64 }
9bd92830
FM
65
66 // convert any old report hierarchy
67 if ((dirp = opendir(outdir)) == NULL) {
cd012e70 68 debuga(_("Cannot open directory \"%s\": %s\n"),outdir,strerror(errno));
9bd92830
FM
69 exit(EXIT_FAILURE);
70 }
71 while ((direntp = readdir( dirp )) != NULL) {
72 if(isdigit(direntp->d_name[0]) && isdigit(direntp->d_name[1])) {
73 if(IndexTree == INDEX_TREE_DATE)
74 file_index_to_date_index(direntp->d_name);
75 else
76 date_index_to_file_index(direntp->d_name);
77 }
78 }
79 closedir(dirp);
80
81 if(IndexTree == INDEX_TREE_DATE) {
82 make_date_index();
83 } else {
84 make_file_index();
85 }
b25b96fe 86}
25697a35 87
9afddeae
FM
88/*!
89 * Get the effective size of a regular file or directory.
90 *
91 * \param statb The structure filled by lstat(2).
92 *
93 * \return The size occupied on the disk (more or less).
94 *
95 * The actual size occupied on disk by a file or a directory table is not a
96 * trivial computation. It must take into account sparse files, compression,
97 * deduplication and probably many more.
98 *
99 * Here, we assume the file takes a whole number of blocks (which is not the
100 * case of ReiserFS); the block size is constant (which is not the case of
101 * ZFS); every data block is stored in one individal block (no deduplication as
102 * is done by btrfs); data are not compressed (unlike ReiserFS and ZFS).
103 *
104 * As we are dealing with directories containing mostly text and a few
105 * compressed pictures, we don't worry about sparse files with lot of zeros
106 * that would take less blocks than the actual file size.
107 */
108static long long int get_file_size(struct stat *statb)
109{
31990f84 110#ifdef __linux__
9afddeae
FM
111 long long int blocks;
112
113 //return(statb->st_size);//the size of the file content
114 //return(statb->st_blocks*512);//what is the purpose of this size?
115 if (statb->st_blksize==0) return(statb->st_size);
116 blocks=(statb->st_size+statb->st_blksize-1)/statb->st_blksize;
117 return(blocks*statb->st_blksize);//how many bytes occupied on disk
31990f84
FM
118#else
119 return(statb->st_size);
120#endif
9afddeae
FM
121}
122
123/*!
124 * Get the size of a directory.
125 *
126 * The size is the size of the directory content excluding the directory table.
127 * The "du" tool on Linux returns the content size including the directory
128 * table.
129 *
130 * \param path The directory whose size is computed. This is a buffer that must be
131 * big enough to contains the deepest path as directory entries are appended to
132 * the string this buffer contains.
133 * \param path_size The number of bytes available in the \a path buffer.
134 *
135 * \return The number of bytes occupied by the directory content.
136 */
137static long long int get_size(char *path,int path_size)
138{
139 int path_len;
140 DIR *dirp;
141 struct dirent *direntp;
142 struct stat statb;
143 int name_len;
144 long long int total_size=0;
145 char *dir_list=NULL;
146 int dir_filled=0;
147 int dir_allocated=0;
148
149 path_len=strlen(path);
150 if (path_len+2>=path_size) {
041018b6
FM
151 debuga(_("Path too long: "));
152 debuga_more("%s\n",path);
9afddeae
FM
153 exit(EXIT_FAILURE);
154 }
155 if ((dirp=opendir(path))==NULL) {
96dadc9f 156 debuga(_("Cannot open directory \"%s\": %s\n"),path,strerror(errno));
9afddeae
FM
157 exit(EXIT_FAILURE);
158 }
159 path[path_len++]='/';
160 while ((direntp=readdir(dirp))!=NULL) {
161 if (direntp->d_name[0]=='.' && (direntp->d_name[1]=='\0' || (direntp->d_name[1]=='.' && direntp->d_name[2]=='\0'))) continue;
162 name_len=strlen(direntp->d_name);
163 if (path_len+name_len+1>=path_size) {
041018b6
FM
164 debuga(_("Path too long "));
165 debuga_more("%s%s\n",path,direntp->d_name);
9afddeae
FM
166 exit(EXIT_FAILURE);
167 }
168 strcpy(path+path_len,direntp->d_name);
169 if (MY_LSTAT(path,&statb) == -1) {
170 debuga(_("Failed to get the file statistics of %s: %s\n"),path,strerror(errno));
171 continue;
172 }
173 if (S_ISDIR(statb.st_mode))
174 {
175 if (!dir_list || dir_filled+name_len>=dir_allocated)
176 {
177 int size=3*(name_len+1);//make room for three file names like this one
178 if (size<256) size=256;
179 dir_allocated+=size;
180 dir_list=realloc(dir_list,dir_allocated);
181 if (!dir_list) {
182 debuga(_("Not enough memory to recurse into subdirectory\n"));
183 exit(EXIT_FAILURE);
184 }
185 }
186 strcpy(dir_list+dir_filled,direntp->d_name);
187 dir_filled+=name_len+1;
188 total_size+=get_file_size(&statb);
189 }
190 else if (S_ISREG(statb.st_mode))
191 {
192 total_size+=get_file_size(&statb);
193 }
194 }
195 closedir(dirp);
196
197 if (dir_list)
198 {
199 int start=0;
200
201 while (start<dir_filled)
202 {
203 name_len=strlen(dir_list+start);
204 strcpy(path+path_len,dir_list+start);
205 total_size+=get_size(path,path_size);
206 start+=name_len+1;
207 }
208 free(dir_list);
209 }
210
211 path[path_len-1]='\0';//restore original string
212 return (total_size);
213}
214
215/*!
216 * Rebuild the html index file for a day when the reports are grouped in a date tree.
217 *
218 * \param monthdir The buffer containing the path where the html index file must be rebuild.
219 * The buffer must be big enough to contain the deepest path in that directory as the buffer is
220 * used to concatenate the directory entries.
221 * \param monthdir_size The size, in byte, of the \a monthdir buffer.
222 * \param order A postive number to sort the index file in positive order. A negative value sort it
223 * in decreasing order.
224 * \param yearnum The string naming the year in the date tree.
225 * \param monthnum The string naming the month in the date tree.
226 *
227 * \return The approximate size occupied by the directory.
228 */
229static long long int make_date_index_day(char *monthdir,int monthdir_size,int order,const char *yearnum,const char *monthnum)
230{
231 int monthdir_len;
232 int ndays;
233 DIR *dirp3;
234 struct dirent *direntp;
235 struct stat statb;
236 int i;
237 int daysort[31*31];
238 int d1, d2, day;
239 FILE *fp_ou;
240 char title[80];
241 char daynum[10];
242 int d;
243 long long int total_size=0;
244 long long int sub_size;
245 int name_len;
246
247 ndays=0;
248 if ((dirp3 = opendir(monthdir)) == NULL) {
cd012e70 249 debuga(_("Cannot open directory \"%s\": %s\n"),monthdir,strerror(errno));
9afddeae
FM
250 exit(EXIT_FAILURE);
251 }
252 monthdir_len=strlen(monthdir);
253 if (monthdir_len+strlen(INDEX_HTML_FILE)+2>=monthdir_size) {
041018b6
FM
254 debuga(_("Path too long: "));
255 debuga_more("%s/%s\n",monthdir,INDEX_HTML_FILE);
9afddeae
FM
256 exit(EXIT_FAILURE);
257 }
258 monthdir[monthdir_len++]='/';
259 while ((direntp = readdir( dirp3 )) != NULL) {
260 if (direntp->d_name[0]=='.' && (direntp->d_name[1]=='\0' || (direntp->d_name[1]=='.' && direntp->d_name[2]=='\0'))) continue;
261 name_len=strlen(direntp->d_name);
262 if (monthdir_len+name_len+1>=monthdir_size) {
041018b6
FM
263 debuga(_("Path too long: "));
264 debuga_more("%s%s\n",monthdir,direntp->d_name);
9afddeae
FM
265 exit(EXIT_FAILURE);
266 }
267 strcpy(monthdir+monthdir_len,direntp->d_name);
268 if (MY_LSTAT(monthdir,&statb) == -1) {
269 debuga(_("Failed to get the file statistics of %s: %s\n"),monthdir,strerror(errno));
270 continue;
271 }
272 if (S_ISDIR(statb.st_mode))
273 {
274 if(!isdigit(direntp->d_name[0]) && !isdigit(direntp->d_name[1])) continue;
275 i=-1;
276 if (sscanf(direntp->d_name,"%d%n",&d1,&i)!=1 || d1<1 || d1>31 || i<0) continue;
277 if (direntp->d_name[i]=='-') {
278 if (sscanf(direntp->d_name+i+1,"%d",&d2)!=1 || d2<1 || d2>31) continue;
279 } else if (direntp->d_name[i]!='\0') {
280 continue;
281 } else {
282 d2=0;
283 }
284 if (ndays>=sizeof(daysort)/sizeof(daysort[0])) {
285 debuga(_("Too many day directories in %s\nSupernumerary entries are ignored\n"),monthdir);
286 break;
287 }
288 day=(d1 << 5) | d2;
289 for (i=ndays ; i>0 && day<daysort[i-1] ; i--) {
290 daysort[i]=daysort[i-1];
291 }
292 daysort[i]=day;
293 ndays++;
294 total_size+=get_file_size(&statb);
295 }
296 else if (S_ISREG(statb.st_mode))
297 {
298 total_size+=get_file_size(&statb);
299 }
300 }
301 closedir(dirp3);
302
303 strcpy(monthdir+monthdir_len,INDEX_HTML_FILE);
304 if((fp_ou=fopen(monthdir,"w"))==NULL) {
96dadc9f 305 debuga(_("Cannot open file \"%s\": %s\n"),monthdir,strerror(errno));
9afddeae
FM
306 exit(EXIT_FAILURE);
307 }
308 snprintf(title,sizeof(title),ngettext("SARG: report for %s/%s","SARG: reports for %s/%s",ndays),yearnum,monthnum);
309 write_html_header(fp_ou,2,title,HTML_JS_NONE);
310 close_html_header(fp_ou);
311 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\">\n<tr><td></td><td></td></tr>\n",fp_ou);
312 fprintf(fp_ou,"<tr><th class=\"header_l\">%s/%s/%s</th>",_("YEAR"),_("MONTH"),_("DAYS"));
313 if (IndexFields & INDEXFIELDS_DIRSIZE)
314 fprintf(fp_ou,"<th class=\"header_l\">%s</th>",_("SIZE"));
315 fputs("</tr>\n",fp_ou);
316 for (d=0 ; d<ndays ; d++) {
317 if (order>0)
318 day=daysort[d];
319 else
320 day=daysort[ndays-1-d];
321 d1=(day >> 5) & 0x1F;
322 if ((day & 0x1F) != 0) {
323 d2=day & 0x1F;
324 sprintf(daynum,"%02d-%02d",d1,d2);
325 } else {
326 sprintf(daynum,"%02d",d1);
327 }
328 strcpy(monthdir+monthdir_len,daynum);
329 sub_size=get_size(monthdir,monthdir_size);
330
331 fprintf(fp_ou,"<tr><td class=\"data2\"><a href=\"%s/%s\">%s %s %s</a></td>",daynum,INDEX_HTML_FILE,yearnum,monthnum,daynum);
332 if (IndexFields & INDEXFIELDS_DIRSIZE)
333 {
334 char size_str[40];
335
336 strncpy(size_str,fixnum(sub_size,1),sizeof(size_str)-1);
337 size_str[sizeof(size_str)-1]='\0';
338 fprintf(fp_ou,"<td class=\"data2\">%s</td>",size_str);
339 }
340 fputs("</tr>\n",fp_ou);
341 total_size+=sub_size;
342 }
343 fputs("</table></div>\n",fp_ou);
344 monthdir[monthdir_len-1]='\0';
342bd723 345 write_html_trailer(fp_ou);
9afddeae
FM
346 if (fclose(fp_ou)==EOF) {
347 debuga(_("Write error in %s/%s: %s\n"),monthdir,INDEX_HTML_FILE,strerror(errno));
348 exit(EXIT_FAILURE);
349 }
350 return(total_size);
351}
352
353/*!
354 * Get the name of a month based on its number.
355 *
356 * \param month The month number starting from one.
357 * \param month_name The buffer to store the month name.
358 * \param month_size The size of the \a month_name buffer.
359 */
360static void name_month(int month,char *month_name,int month_size)
361{
362 const char *m[12]={N_("January"),N_("February"),N_("March"),N_("April"),N_("May"),N_("June"),N_("July"),
363 N_("August"),N_("September"),N_("October"),N_("November"),N_("December")};
364
365 if (month<1 || month>12) {
366 debuga(_("The internal list of month names is invalid. Please report this bug to the translator.\n"));
367 exit(EXIT_FAILURE);
368 }
369 strncpy(month_name,_(m[month-1]),month_size-1);
370 month_name[month_size-1]='\0';
371}
372
373/*!
374 * Rebuild the html index file for a month when the reports are grouped in a date tree.
375 *
376 * \param yeardir The buffer containing the path where the html index file must be rebuild.
377 * The buffer must be big enough to contain the deepest path in that directory as the buffer is
378 * used to concatenate the directory entries.
379 * \param yeardir_size The size, in byte, of the \a yeardir buffer.
380 * \param order A postive number to sort the index file in positive order. A negative value sort it
381 * in decreasing order.
382 * \param yearnum The string naming the year in the date tree.
383 *
384 * \return The approximate size occupied by the directory.
385 */
386static long long int make_date_index_month(char *yeardir,int yeardir_size,int order,const char *yearnum)
387{
388 int yeardir_len;
389 int nmonths;
390 DIR *dirp2;
391 struct dirent *direntp;
392 struct stat statb;
393 int i;
394 int monthsort[144];
395 int m1, m2, month;
396 FILE *fp_ou;
397 char title[80];
398 char monthname1[9], monthname2[9];
399 char nmonth[30];
400 char monthnum[10];
401 int m;
402 long long int total_size=0;
403 long long int sub_size;
404 int name_len;
405
406 nmonths=0;
407 if ((dirp2 = opendir(yeardir)) == NULL) {
cd012e70 408 debuga(_("Cannot open directory \"%s\": %s\n"),yeardir,strerror(errno));
9afddeae
FM
409 exit(EXIT_FAILURE);
410 }
411 yeardir_len=strlen(yeardir);
412 if (yeardir_len+strlen(INDEX_HTML_FILE)+2>=yeardir_size) {
041018b6
FM
413 debuga(_("Path too long: "));
414 debuga_more("%s/%s\n",yeardir,INDEX_HTML_FILE);
9afddeae
FM
415 exit(EXIT_FAILURE);
416 }
417 yeardir[yeardir_len++]='/';
418 while ((direntp = readdir( dirp2 )) != NULL) {
419 if (direntp->d_name[0]=='.' && (direntp->d_name[1]=='\0' || (direntp->d_name[1]=='.' && direntp->d_name[2]=='\0'))) continue;
420 name_len=strlen(direntp->d_name);
421 if (yeardir_len+name_len+1>=yeardir_size) {
041018b6
FM
422 debuga(_("Path too long: "));
423 debuga_more("%s%s\n",yeardir,direntp->d_name);
9afddeae
FM
424 exit(EXIT_FAILURE);
425 }
426 strcpy(yeardir+yeardir_len,direntp->d_name);
427 if (MY_LSTAT(yeardir,&statb) == -1) {
428 debuga(_("Failed to get the file statistics of %s: %s\n"),yeardir,strerror(errno));
429 continue;
430 }
431 if (S_ISDIR(statb.st_mode))
432 {
433 if(!isdigit(direntp->d_name[0]) || !isdigit(direntp->d_name[1])) continue;
434 i=-1;
435 if (sscanf(direntp->d_name,"%d%n",&m1,&i)!=1 || m1<1 || m1>12 || i<0) continue;
436 if (direntp->d_name[i]=='-') {
437 if (sscanf(direntp->d_name+i+1,"%d",&m2)!=1 || m2<1 || m2>12) continue;
438 } else if (direntp->d_name[i]!='\0') {
439 continue;
440 } else {
441 m2=0;
442 }
443 if (nmonths>=sizeof(monthsort)/sizeof(monthsort[0])) {
444 debuga(_("Too many month directories in %s\nSupernumerary entries are ignored\n"),yeardir);
445 break;
446 }
447 month=(m1<<4) | m2;
448 for (i=nmonths ; i>0 && month<monthsort[i-1] ; i--) {
449 monthsort[i]=monthsort[i-1];
450 }
451 monthsort[i]=month;
452 nmonths++;
453 total_size+=get_file_size(&statb);
454 }
455 else if (S_ISREG(statb.st_mode))
456 {
457 total_size+=get_file_size(&statb);
458 }
459 }
460 closedir(dirp2);
461
462 strcpy(yeardir+yeardir_len,INDEX_HTML_FILE);
463 if((fp_ou=fopen(yeardir,"w"))==NULL) {
96dadc9f 464 debuga(_("Cannot open file \"%s\": %s\n"),yeardir,strerror(errno));
9afddeae
FM
465 exit(EXIT_FAILURE);
466 }
467 snprintf(title,sizeof(title),ngettext("SARG: report for %s","SARG: reports for %s",nmonths),yearnum);
468 write_html_header(fp_ou,1,title,HTML_JS_NONE);
469 close_html_header(fp_ou);
470 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\">\n<tr><td></td><td></td></tr>\n",fp_ou);
471 fprintf(fp_ou,"<tr><th class=\"header_l\">%s/%s</th>",_("YEAR"),_("MONTH"));
472 if (IndexFields & INDEXFIELDS_DIRSIZE)
473 fprintf(fp_ou,"<th class=\"header_l\">%s</th>",_("SIZE"));
474 fputs("</tr>\n",fp_ou);
475 for (m=0 ; m<nmonths ; m++) {
476 if (order>0)
477 month=monthsort[m];
478 else
479 month=monthsort[nmonths-1-m];
480 m1=(month >> 4) & 0x0F;
481 if ((month & 0x0F) != 0) {
482 m2=month & 0x0F;
483 sprintf(monthnum,"%02d-%02d",m1,m2);
484 name_month(m1,monthname1,sizeof(monthname1));
485 name_month(m2,monthname2,sizeof(monthname2));
486 sprintf(nmonth,"%s-%s",monthname1,monthname2);
487 } else {
488 sprintf(monthnum,"%02d",m1);
489 name_month(m1,nmonth,sizeof(nmonth));
490 }
491 if (yeardir_len+strlen(monthnum)+1>=yeardir_size) {
492 yeardir[yeardir_len]='\0';
041018b6
FM
493 debuga(_("Path too long: "));
494 debuga_more("%s%s\n",yeardir,monthnum);
9afddeae
FM
495 exit(EXIT_FAILURE);
496 }
497 strcpy(yeardir+yeardir_len,monthnum);
498 sub_size=make_date_index_day(yeardir,yeardir_size,order,yearnum,nmonth);
499
500 fprintf(fp_ou,"<tr><td class=\"data2\"><a href=\"%s/%s\">%s %s</a></td>",monthnum,INDEX_HTML_FILE,yearnum,nmonth);
501 if (IndexFields & INDEXFIELDS_DIRSIZE)
502 {
503 char size_str[40];
504
505 strncpy(size_str,fixnum(sub_size,1),sizeof(size_str)-1);
506 size_str[sizeof(size_str)-1]='\0';
507 fprintf(fp_ou,"<td class=\"data2\">%s</td>",size_str);
508 }
509 fputs("</tr>\n",fp_ou);
510 total_size+=sub_size;
511 }
512 fputs("</table></div>\n",fp_ou);
513 yeardir[yeardir_len-1]='\0';
342bd723 514 write_html_trailer(fp_ou);
9afddeae
FM
515 if (fclose(fp_ou)==EOF) {
516 debuga(_("Write error in %s/%s: %s\n"),yeardir,INDEX_HTML_FILE,strerror(errno));
517 exit(EXIT_FAILURE);
518 }
519 return(total_size);
520}
521
522/*!
523 * Rebuild a date index tree in the output directory.
524 */
b25b96fe
FM
525static void make_date_index(void)
526{
9afddeae
FM
527 FILE *fp_ou;
528 DIR *dirp;
9bd92830 529 struct dirent *direntp;
9bd92830
FM
530 char yearindex[MAXLEN];
531 char yeardir[MAXLEN];
532 char yearnum[10];
9bd92830
FM
533 int yearsort[150];
534 int nyears;
535 int year;
9afddeae 536 int i, y;
9bd92830 537 int order;
9afddeae
FM
538 int yeardirlen;
539 long long int total_size;
9bd92830
FM
540
541 nyears=0;
542 if ((dirp = opendir(outdir)) == NULL) {
cd012e70 543 debuga(_("Cannot open directory \"%s\": %s\n"),outdir,strerror(errno));
9bd92830
FM
544 exit(EXIT_FAILURE);
545 }
546 while ((direntp = readdir( dirp )) != NULL) {
9afddeae 547 if (!isdigit(direntp->d_name[0]) || !isdigit(direntp->d_name[1]) ||
007905af 548 !isdigit(direntp->d_name[2]) || !isdigit(direntp->d_name[3])) continue;
9afddeae
FM
549 year=atoi(direntp->d_name) << 10;
550 if (direntp->d_name[4]=='-')
551 {
552 if (!isdigit(direntp->d_name[5]) || !isdigit(direntp->d_name[6]) ||
553 !isdigit(direntp->d_name[7]) || !isdigit(direntp->d_name[8])) continue;
554 if (direntp->d_name[9]) continue;
555 year|=atoi(direntp->d_name+5);
556 }
557 else
558 {
559 if (direntp->d_name[4]) continue;
560 }
9bd92830
FM
561 if (nyears>=sizeof(yearsort)/sizeof(yearsort[0])) {
562 /*
563 If too many years are listed in the directory, we ignore the earliest years. The yearsort array
564 is big enough to accomodate the most ambitious use of sarg but this safety is added to prevent
565 a crash should the directory be polluted by other entries.
566 */
567 if (year>yearsort[0]) {
568 for (i=1 ; i<nyears && year>yearsort[i] ; i++)
569 yearsort[i-1]=yearsort[i];
570 yearsort[i-1]=year;
571 }
572 } else {
573 for (i=nyears ; i>0 && year<yearsort[i-1] ; i--) {
574 yearsort[i]=yearsort[i-1];
575 }
576 yearsort[i]=year;
577 nyears++;
578 }
579 }
580 closedir( dirp );
581
582 order=(strcmp(IndexSortOrder,"A") == 0) ? 1 : -1;
583
9afddeae
FM
584 if (snprintf(yearindex,sizeof(yearindex),"%s"INDEX_HTML_FILE,outdir)>=sizeof(yearindex)) {
585 debuga(_("Resulting index file name too long: %s/%s"),outdir,INDEX_HTML_FILE);
586 exit(EXIT_FAILURE);
587 }
9bd92830 588 if((fp_ou=fopen(yearindex,"w"))==NULL) {
96dadc9f 589 debuga(_("Cannot open file \"%s\": %s\n"),yearindex,strerror(errno));
9bd92830
FM
590 exit(EXIT_FAILURE);
591 }
592 write_html_header(fp_ou,0,ngettext("SARG report","SARG reports",nyears),HTML_JS_NONE);
593 close_html_header(fp_ou);
9aaa3361
FM
594 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\">\n",fp_ou);
595 fprintf(fp_ou,"<tr><th class=\"header_l\">%s</th>",_("YEAR"));
596 if (IndexFields & INDEXFIELDS_DIRSIZE)
597 fprintf(fp_ou,"<th class=\"header_l\">%s</th>",_("SIZE"));
598 fputs("</tr>\n",fp_ou);
9afddeae
FM
599
600 yeardirlen=strlen(outdir);
601 if (yeardirlen>=sizeof(yeardir)) {
041018b6
FM
602 debuga(_("Path too long: "));
603 debuga_more("%s",outdir);
9afddeae
FM
604 exit(EXIT_FAILURE);
605 }
606 strcpy(yeardir,outdir);
607
9bd92830
FM
608 for (y=0 ; y<nyears ; y++) {
609 if (order>0)
610 year=yearsort[y];
611 else
612 year=yearsort[nyears-1-y];
9afddeae
FM
613 if ((year & 0x3FF)==0)
614 sprintf(yearnum,"%04d",year>>10);
615 else
616 sprintf(yearnum,"%04d-%04d",year>>10,year & 0x3FF);
617 strcpy(yeardir+yeardirlen,yearnum);
618 total_size=make_date_index_month(yeardir,sizeof(yeardir),order,yearnum);
619
56b97279 620 fprintf(fp_ou,"<tr><td class=\"data2\"><a href=\"%s/%s\">%s</a></td>",yearnum,INDEX_HTML_FILE,yearnum);
9aaa3361 621 if (IndexFields & INDEXFIELDS_DIRSIZE)
9afddeae
FM
622 {
623 char size_str[40];
9bd92830 624
9afddeae
FM
625 strncpy(size_str,fixnum(total_size,1),sizeof(size_str)-1);
626 size_str[sizeof(size_str)-1]='\0';
627 fprintf(fp_ou,"<td class=\"data2\">%s</td>",size_str);
507460ae 628 }
9afddeae 629 fputs("</tr>\n",fp_ou);
9bd92830
FM
630 }
631
632 fputs("</table></div>\n",fp_ou);
342bd723 633 write_html_trailer(fp_ou);
507460ae 634 if (fclose(fp_ou)==EOF) {
96dadc9f 635 debuga(_("Write error in \"%s\": %s\n"),yearindex,strerror(errno));
507460ae
FM
636 exit(EXIT_FAILURE);
637 }
b25b96fe
FM
638}
639
640static void make_file_index(void)
641{
9bd92830
FM
642 #define MAX_CREATION_DATE 15
643 FILE *fp_ou;
644 DIR *dirp;
645 struct dirent *direntp;
646 char wdir[MAXLEN];
647 char data[80];
648 char ftime[128];
649 char day[6], mon[8], year[40], hour[10];
650 long long int tbytes;
651 long long int media;
652 int iyear, imonth, iday, ihour, iminute, isecond, idst;
653 int nsort;
654 int nallocated;
655 int order;
656 int i;
657 int tuser;
658 struct getwordstruct gwarea;
659 struct sortstruct
660 {
661 int year, month, day, sortnum;
662 char creationdate[MAX_CREATION_DATE];
663 char *dirname;
664 char date[60];
665 } **sortlist, *item, **tempsort;
666
a2d39aed 667 sprintf(wdir,"%s"INDEX_HTML_FILE,outdir);
9bd92830
FM
668
669 order=(strcmp(IndexSortOrder,"A") == 0) ? 1 : -1;
670
671 if ((dirp = opendir(outdir)) == NULL) {
cd012e70 672 debuga(_("Cannot open directory \"%s\": %s\n"),outdir,strerror(errno));
9bd92830
FM
673 exit(EXIT_FAILURE);
674 }
675
676 nsort=0;
677 nallocated=0;
678 sortlist=NULL;
679 while ((direntp = readdir( dirp )) != NULL) {
680 if (strchr(direntp->d_name,'-') == 0) continue;
15d3cb5c
FM
681 if (obtdate(outdir,direntp->d_name,data)<0) {
682 debuga(_("The directory \"%s%s\" looks like a report directory but doesn't contain a sarg-date file. You should delete it\n"),outdir,direntp->d_name);
683 continue;
684 }
9bd92830
FM
685 item=malloc(sizeof(*item));
686 if (!item) {
687 debuga(_("not enough memory to sort the index\n"));
688 exit(EXIT_FAILURE);
689 }
81a022d8 690 if (df=='u') {
9bd92830
FM
691 item->year=atoi(direntp->d_name);
692 item->month=conv_month(direntp->d_name+4);
693 item->day=atoi(direntp->d_name+7);
694 } else {
695 item->year=atoi(direntp->d_name+5);
696 item->month=conv_month(direntp->d_name+2);
697 item->day=atoi(direntp->d_name);
698 }
699 item->sortnum=(item->year*16+item->month)*32+item->day;
9bd92830
FM
700 if (sscanf(data,"%d-%d-%d %d:%d:%d %d",&iyear,&imonth,&iday,&ihour,&iminute,&isecond,&idst)==7) {
701 formatdate(data,sizeof(data),iyear,imonth,iday,ihour,iminute,isecond,idst);
702 snprintf(item->creationdate,sizeof(item->creationdate),"%04d%02d%02d%02d%02d%02d",iyear,imonth,iday,ihour,iminute,isecond);
703 } else {
704 /*
705 Old code to parse a date stored by sarg before 2.2.6.1 in the sarg-date file of each report directory.
706 */
707 getword_start(&gwarea,data);
708 if (getword_skip(16,&gwarea,' ')<0) {
709 debuga(_("Maybe you have a broken week day in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
710 exit(EXIT_FAILURE);
711 }
712 if (getword_multisep(mon,sizeof(mon),&gwarea,' ')<0) {
713 debuga(_("Maybe you have a broken month in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
714 exit(EXIT_FAILURE);
715 }
716 if (getword_multisep(day,sizeof(day),&gwarea,' ')<0) {
717 debuga(_("Maybe you have a broken day in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
718 exit(EXIT_FAILURE);
719 }
720 if (getword_multisep(hour,sizeof(hour),&gwarea,' ')<0) {
721 debuga(_("Maybe you have a broken time in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
722 exit(EXIT_FAILURE);
723 }
724 do {
725 if (getword_multisep(year,sizeof(year),&gwarea,' ')<0) {
726 debuga(_("Maybe you have a broken year in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
727 exit(EXIT_FAILURE);
728 }
729 } while (year[0] && !isdigit(year[0])); //skip time zone information with spaces until the year is found
730 if (sscanf(hour,"%d:%d:%d",&ihour,&iminute,&isecond)!=3) {
731 debuga(_("Maybe you have a broken time in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
732 exit(EXIT_FAILURE);
733 }
60ec7f09 734 buildymd(day,mon,year,ftime,sizeof(ftime));
9bd92830
FM
735 snprintf(item->creationdate,sizeof(item->creationdate),"%s%02d%02d%02d",ftime, ihour, iminute, isecond);
736 }
737 item->dirname=strdup(direntp->d_name);
738 if (!item->dirname) {
739 debuga(_("Not enough memory to store the directory name \"%s\" in the index\n"),direntp->d_name);
740 exit(EXIT_FAILURE);
741 }
a87d4d11 742 safe_strcpy(item->date,data,sizeof(item->date));
9bd92830
FM
743 if (nsort+1>nallocated) {
744 nallocated+=10;
745 tempsort=realloc(sortlist,nallocated*sizeof(*item));
746 if (!tempsort) {
747 debuga(_("not enough memory to sort the index\n"));
748 exit(EXIT_FAILURE);
749 }
750 sortlist=tempsort;
751 }
752 for (i=nsort ; i>0 ; i--) {
753 if (item->sortnum>sortlist[i-1]->sortnum) break;
754 if (item->sortnum==sortlist[i-1]->sortnum) {
755 if (strcmp(item->creationdate,sortlist[i-1]->creationdate)>=0) break;
756 }
757 sortlist[i]=sortlist[i-1];
758 }
759 sortlist[i]=item;
760 nsort++;
761 }
762
763 closedir( dirp );
764
765 if((fp_ou=fopen(wdir,"w"))==NULL) {
96dadc9f 766 debuga(_("Cannot open file \"%s\": %s\n"),wdir,strerror(errno));
9bd92830
FM
767 exit(EXIT_FAILURE);
768 }
769 write_html_header(fp_ou,0,ngettext("SARG report","SARG reports",nsort),HTML_JS_SORTTABLE);
770 close_html_header(fp_ou);
771 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\"",fp_ou);
772 if (SortTableJs[0]) fputs(" class=\"sortable\"",fp_ou);
773 fputs(">\n",fp_ou);
a73c9448
FM
774 fprintf(fp_ou,"<thead><tr><th class=\"header_l\">%s</th><th class=\"header_l\">%s</th><th class=\"header_l\">%s</th><th class=\"header_l\">%s</th><th class=\"header_l\">%s</th></tr></thead>\n",
775 _("FILE/PERIOD"),_("CREATION DATE"),_("USERS"),_("BYTES"),_("AVERAGE"));
9bd92830
FM
776 for (i=0 ; i<nsort ; i++) {
777 if (order>0)
778 item=sortlist[i];
779 else
780 item=sortlist[nsort-i-1];
781 tuser=obtuser(outdir,item->dirname);
782 obttotal(outdir,item->dirname,tuser,&tbytes,&media);
783 fputs("<tr><td class=\"data2\"",fp_ou);
784 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%d\"",item->sortnum);
785 fprintf(fp_ou,"><a href='%s/%s'>%s</a></td>",item->dirname,ReplaceIndex,item->dirname);
786 fputs("<td class=\"data2\"",fp_ou);
787 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%s\"",item->creationdate);
788 fprintf(fp_ou,">%s</td>",item->date);
789 fprintf(fp_ou,"<td class=\"data\">%d</td>",tuser);
790 fputs("<td class=\"data\"",fp_ou);
791 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%"PRId64"\"",(int64_t)tbytes);
792 fprintf(fp_ou,">%s</td>",fixnum(tbytes,1));
793 fputs("<td class=\"data\"",fp_ou);
794 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%"PRId64"\"",(int64_t)media);
795 fprintf(fp_ou,">%s</td></tr>\n",fixnum(media,1));
796 }
797 fputs("</table></div>\n",fp_ou);
342bd723 798 write_html_trailer(fp_ou);
9bd92830 799 if (fclose(fp_ou)==EOF)
cd012e70 800 debuga(_("Write error in file \"%s\": %s\n"),wdir,strerror(errno));
9bd92830
FM
801
802 if (sortlist) {
803 for (i=0 ; i<nsort ; i++) {
804 free(sortlist[i]->dirname);
805 free(sortlist[i]);
806 }
807 free(sortlist);
808 }
25697a35 809}
06ced858
FM
810
811static void file_index_to_date_index(const char *entry)
812{
9bd92830
FM
813 int y1, y2, m1, m2, d1, d2;
814 int i, j;
815 int ndirlen;
816 int monthlen;
817 char sm1[8], sm2[8];
818 char olddir[MAXLEN], newdir[MAXLEN];
819
820 if(strlen(entry) < 19) return;
821
822 y1=0;
823 y2=0;
824 memset(sm1,0,sizeof(sm1));
825 memset(sm2,0,sizeof(sm2));
826 d1=0;
827 d2=0;
828 i=0;
81a022d8 829 if (df=='u') {
9bd92830
FM
830 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
831 y1=y1*10+(entry[i++]-'0');
832 if (j!=4) return;
833 for (j=0 ; j<sizeof(sm1)-1 && entry[i] && isalpha(entry[i]) ; j++)
834 sm1[j]=entry[i++];
835 if (j!=3) return;
836 sm1[j]='\0';
837 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
838 d1=d1*10+(entry[i++]-'0');
839 if (j!=2) return;
840
841 if (entry[i++]!='-') return;
842
843 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
844 y2=y2*10+(entry[i++]-'0');
845 if (j!=4) return;
846 for (j=0 ; j<sizeof(sm2)-1 && entry[i] && isalpha(entry[i]) ; j++)
847 sm2[j]=entry[i++];
848 if (j!=3) return;
849 sm2[j]='\0';
850 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
851 d2=d2*10+(entry[i++]-'0');
852 if (j!=2) return;
81a022d8 853 } else if (df=='e') {
9bd92830
FM
854 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
855 d1=d1*10+(entry[i++]-'0');
856 if (j!=2) return;
857 for (j=0 ; j<sizeof(sm1)-1 && entry[i] && isalpha(entry[i]) ; j++)
858 sm1[j]=entry[i++];
859 if (j!=3) return;
860 sm1[j]='\0';
861 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
862 y1=y1*10+(entry[i++]-'0');
863 if (j!=4) return;
864
865 if (entry[i++]!='-') return;
866
867 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
868 d2=d2*10+(entry[i++]-'0');
869 if (j!=2) return;
870 for (j=0 ; j<sizeof(sm2)-1 && entry[i] && isalpha(entry[i]) ; j++)
871 sm2[j]=entry[i++];
872 if (j!=3) return;
873 sm2[j]='\0';
874 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
875 y2=y2*10+(entry[i++]-'0');
876 if (j!=4) return;
877 } else
878 return;
879
880 m1=conv_month(sm1);
881 m2=conv_month(sm2);
882 ndirlen=sprintf(newdir,"%s%04d",outdir,y1);
affa72c5 883 if (access(newdir, R_OK) != 0) {
7a9d0965 884 if (PortableMkDir(newdir,0755)) {
96dadc9f 885 debuga(_("Cannot create directory \"%s\": %s\n"),newdir,strerror(errno));
affa72c5
FM
886 exit(EXIT_FAILURE);
887 }
888 }
9bd92830
FM
889 if(m1 != m2) ndirlen+=sprintf(newdir+ndirlen,"/%02d-%02d",m1,m2);
890 else ndirlen+=sprintf(newdir+ndirlen,"/%02d",m1);
affa72c5 891 if (access(newdir, R_OK) != 0) {
7a9d0965 892 if (PortableMkDir(newdir,0755)) {
96dadc9f 893 debuga(_("Cannot create directory \"%s\": %s\n"),newdir,strerror(errno));
affa72c5
FM
894 exit(EXIT_FAILURE);
895 }
896 }
9bd92830
FM
897 monthlen=ndirlen;
898 if(d1!=d2) ndirlen+=sprintf(newdir+ndirlen,"/%02d-%02d",d1,d2);
899 else ndirlen+=sprintf(newdir+ndirlen,"/%02d",d1);
900
901 sprintf(olddir,"%s%s",outdir,entry);
902 if (rename(olddir,newdir)) {
96dadc9f 903 debuga(_("Error renaming \"%s\" to \"%s\": %s\n"),olddir,newdir,strerror(errno));
9bd92830
FM
904 exit(EXIT_FAILURE);
905 }
906
907 strcpy(newdir+monthlen,"/images");
908 if(access(newdir, R_OK) != 0) {
b5f13803 909#ifdef HAVE_SYMLINK
9bd92830 910 char linkdir[MAXLEN];
b5f13803 911
9bd92830
FM
912 sprintf(linkdir,"%simages",outdir);
913 if (symlink(linkdir,newdir)) {
96dadc9f 914 debuga(_("Failed to create link \"%s\" to \"%s\": %s\n"),linkdir,newdir,strerror(errno));
9bd92830
FM
915 exit(EXIT_FAILURE);
916 }
b5f13803 917#else
9bd92830
FM
918 char cmd[MAXLEN];
919 int cstatus;
920
921 sprintf(cmd,"ln -s \"%simages\" \"%s/images\"",outdir,newdir);
922 cstatus=system(cmd);
923 if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) {
924 debuga(_("command return status %d\n"),WEXITSTATUS(cstatus));
925 debuga(_("command: %s\n"),cmd);
926 exit(EXIT_FAILURE);
927 }
b5f13803 928#endif
9bd92830 929 }
06ced858
FM
930}
931
932static void date_index_to_file_index(const char *entry)
933{
9bd92830
FM
934 int y1, next;
935 int m1, m2;
936 int d1, d2;
937 int val1len;
938 int i, j;
939 char val1[MAXLEN];
940 const char *sm1, *sm2;
941 char *str;
942 char newdir[MAXLEN], olddir[MAXLEN];
943 DIR *dirp2, *dirp3;
944 struct dirent *direntp2;
945 struct dirent *direntp3;
946
947 if(strlen(entry) != 4) return;
948
949 next=-1;
950 if (sscanf(entry,"%d%n",&y1,&next)!=1 || next<0 || entry[next]) return;
951
952 val1len=snprintf(val1,sizeof(val1),"%s%s",outdir,entry);
953 dirp2 = opendir(val1);
954 if (!dirp2) return;
955 while ((direntp2 = readdir( dirp2 )) != NULL) {
956 if(!isdigit(direntp2->d_name[0]) || !isdigit(direntp2->d_name[1])) continue;
957 i=0;
958 str=direntp2->d_name;
959 m1=0;
960 for (j=0 ; j<2 && str[i] && isdigit(str[i]) ; j++)
961 m1=(m1*10)+(str[i++]-'0');
962 if (j>=2) continue;
963 sm1=conv_month_name(m1);
964 if (str[i]=='-') {
965 i++;
966 m2=0;
967 for (j=0 ; j<2 && str[i] && isdigit(str[i]) ; j++)
968 m2=(m2*10)+(str[i++]-'0');
969 if (j>=2) continue;
970 sm2=conv_month_name(m2);
971 } else if (!str[i]) {
972 sm2=sm1;
973 } else {
974 continue;
975 }
976
977 sprintf(val1+val1len,"/%s",direntp2->d_name);
978 dirp3 = opendir(val1);
979 if (!dirp3) continue;
980 while ((direntp3 = readdir( dirp3 )) != NULL) {
981 if(!isdigit(direntp3->d_name[0]) || !isdigit(direntp3->d_name[1])) continue;
982 i=0;
983 str=direntp3->d_name;
984 d1=0;
985 for (j=0 ; str[i] && isdigit(str[i]) ; j++)
986 d1=d1*10+(str[i++]-'0');
987 if (j!=2) continue;
988 if (str[i]=='-') {
989 i++;
990 d2=0;
991 for (j=0 ; str[i] && isdigit(str[i]) ; j++)
992 d2=d2*10+(str[i++]-'0');
993 if (j!=2) continue;
994 } else if (!str[i]) {
995 d2=d1;
996 } else {
997 continue;
998 }
999
81a022d8
FM
1000 if (df=='u') sprintf(newdir,"%s%04d%s%02d-%04d%s%02d",outdir,y1,sm1,d1,y1,sm2,d2);
1001 else if (df=='e') sprintf(newdir,"%s%02d%s%04d-%02d%s%04d",outdir,d1,sm1,y1,d2,sm2,y1);
9bd92830
FM
1002 else continue;
1003 sprintf(olddir,"%s%04d/%s/%s",outdir,y1,direntp2->d_name,direntp3->d_name);
1004 if(rename(olddir,newdir)) {
96dadc9f 1005 debuga(_("Error renaming \"%s\" to \"%s\": %s\n"),olddir,newdir,strerror(errno));
9bd92830
FM
1006 exit(EXIT_FAILURE);
1007 }
1008 }
1009 closedir( dirp3 );
1010 }
1011 closedir( dirp2 );
1012
1013 /*!
1014 \bug The links to the images in the reports are broken after moving the directories
1015 as the the HTML files are not at the right level for the images any more.
1016 */
06ced858
FM
1017}
1018