]>
git.ipfire.org Git - thirdparty/sarg.git/blob - index.c
2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
6 * please look at http://sarg.sourceforge.net/donations.php
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
9 * ---------------------------------------------------------------------
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.
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.
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.
27 #include "include/conf.h"
28 #include "include/defs.h"
31 #define MY_LSTAT lstat
37 static void make_date_index ( void );
38 static void make_file_index ( void );
39 static void file_index_to_date_index ( const char * entry
);
40 static void date_index_to_file_index ( const char * entry
);
45 struct dirent
* direntp
;
48 if ( LastLog
> 0 ) mklastlog ( outdir
);
50 if ( Index
== INDEX_NO
) {
51 sprintf ( wdir
, "%s" INDEX_HTML_FILE
, outdir
);
52 if ( access ( wdir
, R_OK
) == 0 ) {
54 debuga ( __FILE__
, __LINE__
, _ ( "Cannot delete \" %s \" : %s \n " ), wdir
, strerror ( errno
));
62 // TRANSLATORS: The %s is the name of the html index file (index.html).
63 debuga ( __FILE__
, __LINE__
, _ ( "Making %s \n " ), INDEX_HTML_FILE
);
66 // convert any old report hierarchy
67 if (( dirp
= opendir ( outdir
)) == NULL
) {
68 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open directory \" %s \" : %s \n " ), outdir
, strerror ( errno
));
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
);
76 date_index_to_file_index ( direntp
-> d_name
);
81 if ( IndexTree
== INDEX_TREE_DATE
) {
89 * Get the effective size of a regular file or directory.
91 * \param statb The structure filled by lstat(2).
93 * \return The size occupied on the disk (more or less).
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.
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).
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.
108 static long long int get_file_size ( struct stat
* statb
)
111 long long int blocks
;
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
119 return ( statb
-> st_size
);
124 * Get the size of a directory.
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
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.
135 * \return The number of bytes occupied by the directory content.
137 static long long int get_size ( char * path
, int path_size
)
141 struct dirent
* direntp
;
144 long long int total_size
= 0 ;
149 path_len
= strlen ( path
);
150 if ( path_len
+ 2 >= path_size
) {
151 debuga ( __FILE__
, __LINE__
, _ ( "Path too long: " ));
152 debuga_more ( "%s \n " , path
);
155 if (( dirp
= opendir ( path
))== NULL
) {
156 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open directory \" %s \" : %s \n " ), path
, strerror ( errno
));
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
) {
164 debuga ( __FILE__
, __LINE__
, _ ( "Path too long: " ));
165 debuga_more ( "%s%s \n " , path
, direntp
-> d_name
);
168 strcpy ( path
+ path_len
, direntp
-> d_name
);
169 if ( MY_LSTAT ( path
,& statb
) == - 1 ) {
170 debuga ( __FILE__
, __LINE__
, _ ( "Failed to get the statistics of file \" %s \" : %s \n " ), path
, strerror ( errno
));
173 if ( S_ISDIR ( statb
. st_mode
))
175 if (! dir_list
|| dir_filled
+ name_len
>= dir_allocated
)
177 int size
= 3 *( name_len
+ 1 ); //make room for three file names like this one
178 if ( size
< 256 ) size
= 256 ;
180 dir_list
= realloc ( dir_list
, dir_allocated
);
182 debuga ( __FILE__
, __LINE__
, _ ( "Not enough memory to recurse into subdirectory \" %s \"\n " ), path
);
186 strcpy ( dir_list
+ dir_filled
, direntp
-> d_name
);
187 dir_filled
+= name_len
+ 1 ;
188 total_size
+= get_file_size (& statb
);
190 else if ( S_ISREG ( statb
. st_mode
))
192 total_size
+= get_file_size (& statb
);
201 while ( start
< dir_filled
)
203 name_len
= strlen ( dir_list
+ start
);
204 strcpy ( path
+ path_len
, dir_list
+ start
);
205 total_size
+= get_size ( path
, path_size
);
211 path
[ path_len
- 1 ]= '\0' ; //restore original string
216 * Rebuild the html index file for a day when the reports are grouped in a date tree.
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.
227 * \return The approximate size occupied by the directory.
229 static long long int make_date_index_day ( char * monthdir
, int monthdir_size
, int order
, const char * yearnum
, const char * monthnum
)
234 struct dirent
* direntp
;
243 long long int total_size
= 0 ;
244 long long int sub_size
;
248 if (( dirp3
= opendir ( monthdir
)) == NULL
) {
249 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open directory \" %s \" : %s \n " ), monthdir
, strerror ( errno
));
252 monthdir_len
= strlen ( monthdir
);
253 if ( monthdir_len
+ strlen ( INDEX_HTML_FILE
)+ 2 >= monthdir_size
) {
254 debuga ( __FILE__
, __LINE__
, _ ( "Path too long: " ));
255 debuga_more ( "%s/%s \n " , monthdir
, INDEX_HTML_FILE
);
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
) {
263 debuga ( __FILE__
, __LINE__
, _ ( "Path too long: " ));
264 debuga_more ( "%s%s \n " , monthdir
, direntp
-> d_name
);
267 strcpy ( monthdir
+ monthdir_len
, direntp
-> d_name
);
268 if ( MY_LSTAT ( monthdir
,& statb
) == - 1 ) {
269 debuga ( __FILE__
, __LINE__
, _ ( "Failed to get the statistics of file \" %s \" : %s \n " ), monthdir
, strerror ( errno
));
272 if ( S_ISDIR ( statb
. st_mode
))
274 if (! isdigit ( direntp
-> d_name
[ 0 ]) && ! isdigit ( direntp
-> d_name
[ 1 ])) continue ;
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' ) {
284 if ( ndays
>= sizeof ( daysort
)/ sizeof ( daysort
[ 0 ])) {
285 debuga ( __FILE__
, __LINE__
, _ ( "Too many day directories in %s \n Supernumerary entries are ignored \n " ), monthdir
);
289 for ( i
= ndays
; i
> 0 && day
< daysort
[ i
- 1 ] ; i
--) {
290 daysort
[ i
]= daysort
[ i
- 1 ];
294 total_size
+= get_file_size (& statb
);
296 else if ( S_ISREG ( statb
. st_mode
))
298 total_size
+= get_file_size (& statb
);
303 strcpy ( monthdir
+ monthdir_len
, INDEX_HTML_FILE
);
304 if (( fp_ou
= fopen ( monthdir
, "w" ))== NULL
) {
305 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open file \" %s \" : %s \n " ), monthdir
, strerror ( errno
));
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
++) {
320 day
= daysort
[ ndays
- 1 - d
];
321 d1
=( day
>> 5 ) & 0x1F ;
322 if (( day
& 0x1F ) != 0 ) {
324 sprintf ( daynum
, "%02d-%02d" , d1
, d2
);
326 sprintf ( daynum
, "%02d" , d1
);
328 strcpy ( monthdir
+ monthdir_len
, daynum
);
329 sub_size
= get_size ( monthdir
, monthdir_size
);
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
)
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
);
340 fputs ( "</tr> \n " , fp_ou
);
341 total_size
+= sub_size
;
343 fputs ( "</table></div> \n " , fp_ou
);
344 monthdir
[ monthdir_len
- 1 ]= '\0' ;
345 write_html_trailer ( fp_ou
);
346 if ( fclose ( fp_ou
)== EOF
) {
347 strcpy ( monthdir
+ monthdir_len
, INDEX_HTML_FILE
);
348 debuga ( __FILE__
, __LINE__
, _ ( "Write error in \" %s \" : %s \n " ), monthdir
, strerror ( errno
));
355 * Get the name of a month based on its number.
357 * \param month The month number starting from one.
358 * \param month_name The buffer to store the month name.
359 * \param month_size The size of the \a month_name buffer.
361 static void name_month ( int month
, char * month_name
, int month_size
)
363 const char * m
[ 12 ]={ N_ ( "January" ), N_ ( "February" ), N_ ( "March" ), N_ ( "April" ), N_ ( "May" ), N_ ( "June" ), N_ ( "July" ),
364 N_ ( "August" ), N_ ( "September" ), N_ ( "October" ), N_ ( "November" ), N_ ( "December" )};
366 if ( month
< 1 || month
> 12 ) {
367 debuga ( __FILE__
, __LINE__
, _ ( "The internal list of month names is invalid. Please report this bug to the translator. \n " ));
370 strncpy ( month_name
, _ ( m
[ month
- 1 ]), month_size
- 1 );
371 month_name
[ month_size
- 1 ]= '\0' ;
375 * Rebuild the html index file for a month when the reports are grouped in a date tree.
377 * \param yeardir The buffer containing the path where the html index file must be rebuild.
378 * The buffer must be big enough to contain the deepest path in that directory as the buffer is
379 * used to concatenate the directory entries.
380 * \param yeardir_size The size, in byte, of the \a yeardir buffer.
381 * \param order A postive number to sort the index file in positive order. A negative value sort it
382 * in decreasing order.
383 * \param yearnum The string naming the year in the date tree.
385 * \return The approximate size occupied by the directory.
387 static long long int make_date_index_month ( char * yeardir
, int yeardir_size
, int order
, const char * yearnum
)
392 struct dirent
* direntp
;
399 char monthname1
[ 9 ], monthname2
[ 9 ];
403 long long int total_size
= 0 ;
404 long long int sub_size
;
408 if (( dirp2
= opendir ( yeardir
)) == NULL
) {
409 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open directory \" %s \" : %s \n " ), yeardir
, strerror ( errno
));
412 yeardir_len
= strlen ( yeardir
);
413 if ( yeardir_len
+ strlen ( INDEX_HTML_FILE
)+ 2 >= yeardir_size
) {
414 debuga ( __FILE__
, __LINE__
, _ ( "Path too long: " ));
415 debuga_more ( "%s/%s \n " , yeardir
, INDEX_HTML_FILE
);
418 yeardir
[ yeardir_len
++]= '/' ;
419 while (( direntp
= readdir ( dirp2
)) != NULL
) {
420 if ( direntp
-> d_name
[ 0 ]== '.' && ( direntp
-> d_name
[ 1 ]== '\0' || ( direntp
-> d_name
[ 1 ]== '.' && direntp
-> d_name
[ 2 ]== '\0' ))) continue ;
421 name_len
= strlen ( direntp
-> d_name
);
422 if ( yeardir_len
+ name_len
+ 1 >= yeardir_size
) {
423 debuga ( __FILE__
, __LINE__
, _ ( "Path too long: " ));
424 debuga_more ( "%s%s \n " , yeardir
, direntp
-> d_name
);
427 strcpy ( yeardir
+ yeardir_len
, direntp
-> d_name
);
428 if ( MY_LSTAT ( yeardir
,& statb
) == - 1 ) {
429 debuga ( __FILE__
, __LINE__
, _ ( "Failed to get the statistics of file \" %s \" : %s \n " ), yeardir
, strerror ( errno
));
432 if ( S_ISDIR ( statb
. st_mode
))
434 if (! isdigit ( direntp
-> d_name
[ 0 ]) || ! isdigit ( direntp
-> d_name
[ 1 ])) continue ;
436 if ( sscanf ( direntp
-> d_name
, "%d%n" ,& m1
,& i
)!= 1 || m1
< 1 || m1
> 12 || i
< 0 ) continue ;
437 if ( direntp
-> d_name
[ i
]== '-' ) {
438 if ( sscanf ( direntp
-> d_name
+ i
+ 1 , "%d" ,& m2
)!= 1 || m2
< 1 || m2
> 12 ) continue ;
439 } else if ( direntp
-> d_name
[ i
]!= '\0' ) {
444 if ( nmonths
>= sizeof ( monthsort
)/ sizeof ( monthsort
[ 0 ])) {
445 debuga ( __FILE__
, __LINE__
, _ ( "Too many month directories in %s \n Supernumerary entries are ignored \n " ), yeardir
);
449 for ( i
= nmonths
; i
> 0 && month
< monthsort
[ i
- 1 ] ; i
--) {
450 monthsort
[ i
]= monthsort
[ i
- 1 ];
454 total_size
+= get_file_size (& statb
);
456 else if ( S_ISREG ( statb
. st_mode
))
458 total_size
+= get_file_size (& statb
);
463 strcpy ( yeardir
+ yeardir_len
, INDEX_HTML_FILE
);
464 if (( fp_ou
= fopen ( yeardir
, "w" ))== NULL
) {
465 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open file \" %s \" : %s \n " ), yeardir
, strerror ( errno
));
468 snprintf ( title
, sizeof ( title
), ngettext ( "SARG: report for %s" , "SARG: reports for %s" , nmonths
), yearnum
);
469 write_html_header ( fp_ou
, 1 , title
, HTML_JS_NONE
);
470 close_html_header ( fp_ou
);
471 fputs ( "<div class= \" index \" ><table cellpadding= \" 1 \" cellspacing= \" 2 \" > \n <tr><td></td><td></td></tr> \n " , fp_ou
);
472 fprintf ( fp_ou
, "<tr><th class= \" header_l \" >%s/%s</th>" , _ ( "YEAR" ), _ ( "MONTH" ));
473 if ( IndexFields
& INDEXFIELDS_DIRSIZE
)
474 fprintf ( fp_ou
, "<th class= \" header_l \" >%s</th>" , _ ( "SIZE" ));
475 fputs ( "</tr> \n " , fp_ou
);
476 for ( m
= 0 ; m
< nmonths
; m
++) {
480 month
= monthsort
[ nmonths
- 1 - m
];
481 m1
=( month
>> 4 ) & 0x0F ;
482 if (( month
& 0x0F ) != 0 ) {
484 sprintf ( monthnum
, "%02d-%02d" , m1
, m2
);
485 name_month ( m1
, monthname1
, sizeof ( monthname1
));
486 name_month ( m2
, monthname2
, sizeof ( monthname2
));
487 sprintf ( nmonth
, "%s-%s" , monthname1
, monthname2
);
489 sprintf ( monthnum
, "%02d" , m1
);
490 name_month ( m1
, nmonth
, sizeof ( nmonth
));
492 if ( yeardir_len
+ strlen ( monthnum
)+ 1 >= yeardir_size
) {
493 yeardir
[ yeardir_len
]= '\0' ;
494 debuga ( __FILE__
, __LINE__
, _ ( "Path too long: " ));
495 debuga_more ( "%s%s \n " , yeardir
, monthnum
);
498 strcpy ( yeardir
+ yeardir_len
, monthnum
);
499 sub_size
= make_date_index_day ( yeardir
, yeardir_size
, order
, yearnum
, nmonth
);
501 fprintf ( fp_ou
, "<tr><td class= \" data2 \" ><a href= \" %s/%s \" >%s %s</a></td>" , monthnum
, INDEX_HTML_FILE
, yearnum
, nmonth
);
502 if ( IndexFields
& INDEXFIELDS_DIRSIZE
)
506 strncpy ( size_str
, fixnum ( sub_size
, 1 ), sizeof ( size_str
)- 1 );
507 size_str
[ sizeof ( size_str
)- 1 ]= '\0' ;
508 fprintf ( fp_ou
, "<td class= \" data2 \" >%s</td>" , size_str
);
510 fputs ( "</tr> \n " , fp_ou
);
511 total_size
+= sub_size
;
513 fputs ( "</table></div> \n " , fp_ou
);
514 yeardir
[ yeardir_len
- 1 ]= '\0' ;
515 write_html_trailer ( fp_ou
);
516 if ( fclose ( fp_ou
)== EOF
) {
517 strcpy ( yeardir
+ yeardir_len
, INDEX_HTML_FILE
);
518 debuga ( __FILE__
, __LINE__
, _ ( "Write error in \" %s \" : %s \n " ), yeardir
, strerror ( errno
));
525 * Rebuild a date index tree in the output directory.
527 static void make_date_index ( void )
531 struct dirent
* direntp
;
532 char yearindex
[ MAXLEN
];
533 char yeardir
[ MAXLEN
];
541 long long int total_size
;
544 if (( dirp
= opendir ( outdir
)) == NULL
) {
545 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open directory \" %s \" : %s \n " ), outdir
, strerror ( errno
));
548 while (( direntp
= readdir ( dirp
)) != NULL
) {
549 if (! isdigit ( direntp
-> d_name
[ 0 ]) || ! isdigit ( direntp
-> d_name
[ 1 ]) ||
550 ! isdigit ( direntp
-> d_name
[ 2 ]) || ! isdigit ( direntp
-> d_name
[ 3 ])) continue ;
551 year
= atoi ( direntp
-> d_name
) << 10 ;
552 if ( direntp
-> d_name
[ 4 ]== '-' )
554 if (! isdigit ( direntp
-> d_name
[ 5 ]) || ! isdigit ( direntp
-> d_name
[ 6 ]) ||
555 ! isdigit ( direntp
-> d_name
[ 7 ]) || ! isdigit ( direntp
-> d_name
[ 8 ])) continue ;
556 if ( direntp
-> d_name
[ 9 ]) continue ;
557 year
|= atoi ( direntp
-> d_name
+ 5 );
561 if ( direntp
-> d_name
[ 4 ]) continue ;
563 if ( nyears
>= sizeof ( yearsort
)/ sizeof ( yearsort
[ 0 ])) {
565 If too many years are listed in the directory, we ignore the earliest years. The yearsort array
566 is big enough to accomodate the most ambitious use of sarg but this safety is added to prevent
567 a crash should the directory be polluted by other entries.
569 if ( year
> yearsort
[ 0 ]) {
570 for ( i
= 1 ; i
< nyears
&& year
> yearsort
[ i
] ; i
++)
571 yearsort
[ i
- 1 ]= yearsort
[ i
];
575 for ( i
= nyears
; i
> 0 && year
< yearsort
[ i
- 1 ] ; i
--) {
576 yearsort
[ i
]= yearsort
[ i
- 1 ];
584 order
=( strcmp ( IndexSortOrder
, "A" ) == 0 ) ? 1 : - 1 ;
586 if ( snprintf ( yearindex
, sizeof ( yearindex
), "%s" INDEX_HTML_FILE
, outdir
)>= sizeof ( yearindex
)) {
587 debuga ( __FILE__
, __LINE__
, _ ( "Resulting index file name too long. File name is \" %s/%s \" " ), outdir
, INDEX_HTML_FILE
);
590 if (( fp_ou
= fopen ( yearindex
, "w" ))== NULL
) {
591 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open file \" %s \" : %s \n " ), yearindex
, strerror ( errno
));
594 write_html_header ( fp_ou
, 0 , ngettext ( "SARG report" , "SARG reports" , nyears
), HTML_JS_NONE
);
595 close_html_header ( fp_ou
);
596 fputs ( "<div class= \" index \" ><table cellpadding= \" 1 \" cellspacing= \" 2 \" > \n " , fp_ou
);
597 fprintf ( fp_ou
, "<tr><th class= \" header_l \" >%s</th>" , _ ( "YEAR" ));
598 if ( IndexFields
& INDEXFIELDS_DIRSIZE
)
599 fprintf ( fp_ou
, "<th class= \" header_l \" >%s</th>" , _ ( "SIZE" ));
600 fputs ( "</tr> \n " , fp_ou
);
602 yeardirlen
= strlen ( outdir
);
603 if ( yeardirlen
>= sizeof ( yeardir
)) {
604 debuga ( __FILE__
, __LINE__
, _ ( "Path too long: " ));
605 debuga_more ( "%s" , outdir
);
608 strcpy ( yeardir
, outdir
);
610 for ( y
= 0 ; y
< nyears
; y
++) {
614 year
= yearsort
[ nyears
- 1 - y
];
615 if (( year
& 0x3FF )== 0 )
616 sprintf ( yearnum
, "%04d" , year
>> 10 );
618 sprintf ( yearnum
, "%04d-%04d" , year
>> 10 , year
& 0x3FF );
619 strcpy ( yeardir
+ yeardirlen
, yearnum
);
620 total_size
= make_date_index_month ( yeardir
, sizeof ( yeardir
), order
, yearnum
);
622 fprintf ( fp_ou
, "<tr><td class= \" data2 \" ><a href= \" %s/%s \" >%s</a></td>" , yearnum
, INDEX_HTML_FILE
, yearnum
);
623 if ( IndexFields
& INDEXFIELDS_DIRSIZE
)
627 strncpy ( size_str
, fixnum ( total_size
, 1 ), sizeof ( size_str
)- 1 );
628 size_str
[ sizeof ( size_str
)- 1 ]= '\0' ;
629 fprintf ( fp_ou
, "<td class= \" data2 \" >%s</td>" , size_str
);
631 fputs ( "</tr> \n " , fp_ou
);
634 fputs ( "</table></div> \n " , fp_ou
);
635 write_html_trailer ( fp_ou
);
636 if ( fclose ( fp_ou
)== EOF
) {
637 debuga ( __FILE__
, __LINE__
, _ ( "Write error in \" %s \" : %s \n " ), yearindex
, strerror ( errno
));
642 static void make_file_index ( void )
644 #define MAX_CREATION_DATE 15
647 struct dirent
* direntp
;
651 char day
[ 6 ], mon
[ 8 ], year
[ 40 ], hour
[ 10 ];
652 long long int tbytes
;
654 int iyear
, imonth
, iday
, ihour
, iminute
, isecond
, idst
;
660 struct getwordstruct gwarea
;
663 int year
, month
, day
, sortnum
;
664 char creationdate
[ MAX_CREATION_DATE
];
667 } ** sortlist
, * item
, ** tempsort
;
669 sprintf ( wdir
, "%s" INDEX_HTML_FILE
, outdir
);
671 order
=( strcmp ( IndexSortOrder
, "A" ) == 0 ) ? 1 : - 1 ;
673 if (( dirp
= opendir ( outdir
)) == NULL
) {
674 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open directory \" %s \" : %s \n " ), outdir
, strerror ( errno
));
681 while (( direntp
= readdir ( dirp
)) != NULL
) {
682 if ( strchr ( direntp
-> d_name
, '-' ) == 0 ) continue ;
683 if ( obtdate ( outdir
, direntp
-> d_name
, data
)< 0 ) {
684 debuga ( __FILE__
, __LINE__
, _ ( "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
);
687 item
= malloc ( sizeof (* item
));
689 debuga ( __FILE__
, __LINE__
, _ ( "not enough memory to sort the index \n " ));
693 item
-> year
= atoi ( direntp
-> d_name
);
694 item
-> month
= conv_month ( direntp
-> d_name
+ 4 );
695 item
-> day
= atoi ( direntp
-> d_name
+ 7 );
697 item
-> year
= atoi ( direntp
-> d_name
+ 5 );
698 item
-> month
= conv_month ( direntp
-> d_name
+ 2 );
699 item
-> day
= atoi ( direntp
-> d_name
);
701 item
-> sortnum
=( item
-> year
* 16 + item
-> month
)* 32 + item
-> day
;
702 if ( sscanf ( data
, "%d-%d-%d %d:%d:%d %d" ,& iyear
,& imonth
,& iday
,& ihour
,& iminute
,& isecond
,& idst
)== 7 ) {
703 formatdate ( data
, sizeof ( data
), iyear
, imonth
, iday
, ihour
, iminute
, isecond
, idst
);
704 snprintf ( item
-> creationdate
, sizeof ( item
-> creationdate
), "%04d%02d%02d%02d%02d%02d" , iyear
, imonth
, iday
, ihour
, iminute
, isecond
);
707 Old code to parse a date stored by sarg before 2.2.6.1 in the sarg-date file of each report directory.
709 getword_start (& gwarea
, data
);
710 if ( getword_skip ( 16 ,& gwarea
, ' ' )< 0 ) {
711 debuga ( __FILE__
, __LINE__
, _ ( "Invalid date in file \" %s%s/sarg-date \"\n " ), outdir
, direntp
-> d_name
);
714 if ( getword_multisep ( mon
, sizeof ( mon
),& gwarea
, ' ' )< 0 ) {
715 debuga ( __FILE__
, __LINE__
, _ ( "Invalid date in file \" %s%s/sarg-date \"\n " ), outdir
, direntp
-> d_name
);
718 if ( getword_multisep ( day
, sizeof ( day
),& gwarea
, ' ' )< 0 ) {
719 debuga ( __FILE__
, __LINE__
, _ ( "Invalid date in file \" %s%s/sarg-date \"\n " ), outdir
, direntp
-> d_name
);
722 if ( getword_multisep ( hour
, sizeof ( hour
),& gwarea
, ' ' )< 0 ) {
723 debuga ( __FILE__
, __LINE__
, _ ( "Invalid time in file \" %s%s/sarg-date \"\n " ), outdir
, direntp
-> d_name
);
727 if ( getword_multisep ( year
, sizeof ( year
),& gwarea
, ' ' )< 0 ) {
728 debuga ( __FILE__
, __LINE__
, _ ( "Invalid date in file \" %s%s/sarg-date \"\n " ), outdir
, direntp
-> d_name
);
731 } while ( year
[ 0 ] && ! isdigit ( year
[ 0 ])); //skip time zone information with spaces until the year is found
732 if ( sscanf ( hour
, "%d:%d:%d" ,& ihour
,& iminute
,& isecond
)!= 3 ) {
733 debuga ( __FILE__
, __LINE__
, _ ( "Invalid time in file \" %s%s/sarg-date \"\n " ), outdir
, direntp
-> d_name
);
736 buildymd ( day
, mon
, year
, ftime
, sizeof ( ftime
));
737 snprintf ( item
-> creationdate
, sizeof ( item
-> creationdate
), "%s%02d%02d%02d" , ftime
, ihour
, iminute
, isecond
);
739 item
-> dirname
= strdup ( direntp
-> d_name
);
740 if (! item
-> dirname
) {
741 debuga ( __FILE__
, __LINE__
, _ ( "Not enough memory to store the directory name \" %s \" in the index \n " ), direntp
-> d_name
);
744 safe_strcpy ( item
-> date
, data
, sizeof ( item
-> date
));
745 if ( nsort
+ 1 > nallocated
) {
747 tempsort
= realloc ( sortlist
, nallocated
* sizeof (* item
));
749 debuga ( __FILE__
, __LINE__
, _ ( "not enough memory to sort the index \n " ));
754 for ( i
= nsort
; i
> 0 ; i
--) {
755 if ( item
-> sortnum
> sortlist
[ i
- 1 ]-> sortnum
) break ;
756 if ( item
-> sortnum
== sortlist
[ i
- 1 ]-> sortnum
) {
757 if ( strcmp ( item
-> creationdate
, sortlist
[ i
- 1 ]-> creationdate
)>= 0 ) break ;
759 sortlist
[ i
]= sortlist
[ i
- 1 ];
767 if (( fp_ou
= fopen ( wdir
, "w" ))== NULL
) {
768 debuga ( __FILE__
, __LINE__
, _ ( "Cannot open file \" %s \" : %s \n " ), wdir
, strerror ( errno
));
771 write_html_header ( fp_ou
, 0 , ngettext ( "SARG report" , "SARG reports" , nsort
), HTML_JS_SORTTABLE
);
772 close_html_header ( fp_ou
);
773 fputs ( "<div class= \" index \" ><table cellpadding= \" 1 \" cellspacing= \" 2 \" " , fp_ou
);
774 if ( SortTableJs
[ 0 ]) fputs ( " class= \" sortable \" " , fp_ou
);
776 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 " ,
777 _ ( "FILE/PERIOD" ), _ ( "CREATION DATE" ), _ ( "USERS" ), _ ( "BYTES" ), _ ( "AVERAGE" ));
778 for ( i
= 0 ; i
< nsort
; i
++) {
782 item
= sortlist
[ nsort
- i
- 1 ];
783 tuser
= obtuser ( outdir
, item
-> dirname
);
784 obttotal ( outdir
, item
-> dirname
, tuser
,& tbytes
,& media
);
785 fputs ( "<tr><td class= \" data2 \" " , fp_ou
);
786 if ( SortTableJs
[ 0 ]) fprintf ( fp_ou
, " sorttable_customkey= \" %d \" " , item
-> sortnum
);
787 fprintf ( fp_ou
, "><a href='%s/%s'>%s</a></td>" , item
-> dirname
, ReplaceIndex
, item
-> dirname
);
788 fputs ( "<td class= \" data2 \" " , fp_ou
);
789 if ( SortTableJs
[ 0 ]) fprintf ( fp_ou
, " sorttable_customkey= \" %s \" " , item
-> creationdate
);
790 fprintf ( fp_ou
, ">%s</td>" , item
-> date
);
791 fprintf ( fp_ou
, "<td class= \" data \" >%d</td>" , tuser
);
792 fputs ( "<td class= \" data \" " , fp_ou
);
793 if ( SortTableJs
[ 0 ]) fprintf ( fp_ou
, " sorttable_customkey= \" %" PRId64
" \" " ,( int64_t ) tbytes
);
794 fprintf ( fp_ou
, ">%s</td>" , fixnum ( tbytes
, 1 ));
795 fputs ( "<td class= \" data \" " , fp_ou
);
796 if ( SortTableJs
[ 0 ]) fprintf ( fp_ou
, " sorttable_customkey= \" %" PRId64
" \" " ,( int64_t ) media
);
797 fprintf ( fp_ou
, ">%s</td></tr> \n " , fixnum ( media
, 1 ));
799 fputs ( "</table></div> \n " , fp_ou
);
800 write_html_trailer ( fp_ou
);
801 if ( fclose ( fp_ou
)== EOF
)
802 debuga ( __FILE__
, __LINE__
, _ ( "Write error in \" %s \" : %s \n " ), wdir
, strerror ( errno
));
805 for ( i
= 0 ; i
< nsort
; i
++) {
806 free ( sortlist
[ i
]-> dirname
);
813 static void file_index_to_date_index ( const char * entry
)
815 int y1
, y2
, m1
, m2
, d1
, d2
;
820 char olddir
[ MAXLEN
], newdir
[ MAXLEN
];
822 if ( strlen ( entry
) < 19 ) return ;
826 memset ( sm1
, 0 , sizeof ( sm1
));
827 memset ( sm2
, 0 , sizeof ( sm2
));
832 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
833 y1
= y1
* 10 +( entry
[ i
++]- '0' );
835 for ( j
= 0 ; j
< sizeof ( sm1
)- 1 && entry
[ i
] && isalpha ( entry
[ i
]) ; j
++)
839 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
840 d1
= d1
* 10 +( entry
[ i
++]- '0' );
843 if ( entry
[ i
++]!= '-' ) return ;
845 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
846 y2
= y2
* 10 +( entry
[ i
++]- '0' );
848 for ( j
= 0 ; j
< sizeof ( sm2
)- 1 && entry
[ i
] && isalpha ( entry
[ i
]) ; j
++)
852 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
853 d2
= d2
* 10 +( entry
[ i
++]- '0' );
855 } else if ( df
== 'e' ) {
856 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
857 d1
= d1
* 10 +( entry
[ i
++]- '0' );
859 for ( j
= 0 ; j
< sizeof ( sm1
)- 1 && entry
[ i
] && isalpha ( entry
[ i
]) ; j
++)
863 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
864 y1
= y1
* 10 +( entry
[ i
++]- '0' );
867 if ( entry
[ i
++]!= '-' ) return ;
869 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
870 d2
= d2
* 10 +( entry
[ i
++]- '0' );
872 for ( j
= 0 ; j
< sizeof ( sm2
)- 1 && entry
[ i
] && isalpha ( entry
[ i
]) ; j
++)
876 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
877 y2
= y2
* 10 +( entry
[ i
++]- '0' );
884 ndirlen
= sprintf ( newdir
, "%s%04d" , outdir
, y1
);
885 if ( access ( newdir
, R_OK
) != 0 ) {
886 if ( PortableMkDir ( newdir
, 0755 )) {
887 debuga ( __FILE__
, __LINE__
, _ ( "Cannot create directory \" %s \" : %s \n " ), newdir
, strerror ( errno
));
891 if ( m1
!= m2
) ndirlen
+= sprintf ( newdir
+ ndirlen
, "/%02d-%02d" , m1
, m2
);
892 else ndirlen
+= sprintf ( newdir
+ ndirlen
, "/%02d" , m1
);
893 if ( access ( newdir
, R_OK
) != 0 ) {
894 if ( PortableMkDir ( newdir
, 0755 )) {
895 debuga ( __FILE__
, __LINE__
, _ ( "Cannot create directory \" %s \" : %s \n " ), newdir
, strerror ( errno
));
900 if ( d1
!= d2
) ndirlen
+= sprintf ( newdir
+ ndirlen
, "/%02d-%02d" , d1
, d2
);
901 else ndirlen
+= sprintf ( newdir
+ ndirlen
, "/%02d" , d1
);
903 sprintf ( olddir
, "%s%s" , outdir
, entry
);
904 if ( rename ( olddir
, newdir
)) {
905 debuga ( __FILE__
, __LINE__
, _ ( "Error renaming \" %s \" to \" %s \" : %s \n " ), olddir
, newdir
, strerror ( errno
));
909 strcpy ( newdir
+ monthlen
, "/images" );
910 if ( access ( newdir
, R_OK
) != 0 ) {
912 char linkdir
[ MAXLEN
];
914 sprintf ( linkdir
, "%simages" , outdir
);
915 if ( symlink ( linkdir
, newdir
)) {
916 debuga ( __FILE__
, __LINE__
, _ ( "Failed to create link \" %s \" to \" %s \" : %s \n " ), linkdir
, newdir
, strerror ( errno
));
923 sprintf ( cmd
, "ln -s \" %simages \" \" %s/images \" " , outdir
, newdir
);
925 if (! WIFEXITED ( cstatus
) || WEXITSTATUS ( cstatus
)) {
926 debuga ( __FILE__
, __LINE__
, _ ( "command return status %d \n " ), WEXITSTATUS ( cstatus
));
927 debuga ( __FILE__
, __LINE__
, _ ( "command: %s \n " ), cmd
);
934 static void date_index_to_file_index ( const char * entry
)
942 const char * sm1
, * sm2
;
944 char newdir
[ MAXLEN
], olddir
[ MAXLEN
];
946 struct dirent
* direntp2
;
947 struct dirent
* direntp3
;
949 if ( strlen ( entry
) != 4 ) return ;
952 if ( sscanf ( entry
, "%d%n" ,& y1
,& next
)!= 1 || next
< 0 || entry
[ next
]) return ;
954 val1len
= snprintf ( val1
, sizeof ( val1
), "%s%s" , outdir
, entry
);
955 dirp2
= opendir ( val1
);
957 while (( direntp2
= readdir ( dirp2
)) != NULL
) {
958 if (! isdigit ( direntp2
-> d_name
[ 0 ]) || ! isdigit ( direntp2
-> d_name
[ 1 ])) continue ;
960 str
= direntp2
-> d_name
;
962 for ( j
= 0 ; j
< 2 && str
[ i
] && isdigit ( str
[ i
]) ; j
++)
963 m1
=( m1
* 10 )+( str
[ i
++]- '0' );
965 sm1
= conv_month_name ( m1
);
969 for ( j
= 0 ; j
< 2 && str
[ i
] && isdigit ( str
[ i
]) ; j
++)
970 m2
=( m2
* 10 )+( str
[ i
++]- '0' );
972 sm2
= conv_month_name ( m2
);
973 } else if (! str
[ i
]) {
979 sprintf ( val1
+ val1len
, "/%s" , direntp2
-> d_name
);
980 dirp3
= opendir ( val1
);
981 if (! dirp3
) continue ;
982 while (( direntp3
= readdir ( dirp3
)) != NULL
) {
983 if (! isdigit ( direntp3
-> d_name
[ 0 ]) || ! isdigit ( direntp3
-> d_name
[ 1 ])) continue ;
985 str
= direntp3
-> d_name
;
987 for ( j
= 0 ; str
[ i
] && isdigit ( str
[ i
]) ; j
++)
988 d1
= d1
* 10 +( str
[ i
++]- '0' );
993 for ( j
= 0 ; str
[ i
] && isdigit ( str
[ i
]) ; j
++)
994 d2
= d2
* 10 +( str
[ i
++]- '0' );
996 } else if (! str
[ i
]) {
1002 if ( df
== 'u' ) sprintf ( newdir
, "%s%04d%s%02d-%04d%s%02d" , outdir
, y1
, sm1
, d1
, y1
, sm2
, d2
);
1003 else if ( df
== 'e' ) sprintf ( newdir
, "%s%02d%s%04d-%02d%s%04d" , outdir
, d1
, sm1
, y1
, d2
, sm2
, y1
);
1005 sprintf ( olddir
, "%s%04d/%s/%s" , outdir
, y1
, direntp2
-> d_name
, direntp3
-> d_name
);
1006 if ( rename ( olddir
, newdir
)) {
1007 debuga ( __FILE__
, __LINE__
, _ ( "Error renaming \" %s \" to \" %s \" : %s \n " ), olddir
, newdir
, strerror ( errno
));
1016 \bug The links to the images in the reports are broken after moving the directories
1017 as the the HTML files are not at the right level for the images any more.