]>
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 ( _ ( "Cannot delete \" %s \" : %s \n " ), wdir
, strerror ( errno
));
62 // TRANSLATORS: The %s is the name of the html index file (index.html).
63 debuga ( _ ( "Making %s \n " ), INDEX_HTML_FILE
);
66 // convert any old report hierarchy
67 if (( dirp
= opendir ( outdir
)) == NULL
) {
68 debuga ( _ ( "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 ( _ ( "Path too long: " ));
152 debuga_more ( "%s \n " , path
);
155 if (( dirp
= opendir ( path
))== NULL
) {
156 debuga ( _ ( "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 ( _ ( "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 ( _ ( "Failed to get the file statistics of %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 ( _ ( "Not enough memory to recurse into subdirectory \n " ));
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 ( _ ( "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 ( _ ( "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 ( _ ( "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 ( _ ( "Failed to get the file statistics of %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 ( _ ( "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 ( _ ( "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 debuga ( _ ( "Write error in %s/%s: %s \n " ), monthdir
, INDEX_HTML_FILE
, strerror ( errno
));
354 * Get the name of a month based on its number.
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.
360 static void name_month ( int month
, char * month_name
, int month_size
)
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" )};
365 if ( month
< 1 || month
> 12 ) {
366 debuga ( _ ( "The internal list of month names is invalid. Please report this bug to the translator. \n " ));
369 strncpy ( month_name
, _ ( m
[ month
- 1 ]), month_size
- 1 );
370 month_name
[ month_size
- 1 ]= '\0' ;
374 * Rebuild the html index file for a month when the reports are grouped in a date tree.
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.
384 * \return The approximate size occupied by the directory.
386 static long long int make_date_index_month ( char * yeardir
, int yeardir_size
, int order
, const char * yearnum
)
391 struct dirent
* direntp
;
398 char monthname1
[ 9 ], monthname2
[ 9 ];
402 long long int total_size
= 0 ;
403 long long int sub_size
;
407 if (( dirp2
= opendir ( yeardir
)) == NULL
) {
408 debuga ( _ ( "Cannot open directory \" %s \" : %s \n " ), yeardir
, strerror ( errno
));
411 yeardir_len
= strlen ( yeardir
);
412 if ( yeardir_len
+ strlen ( INDEX_HTML_FILE
)+ 2 >= yeardir_size
) {
413 debuga ( _ ( "Path too long: " ));
414 debuga_more ( "%s/%s \n " , yeardir
, INDEX_HTML_FILE
);
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
) {
422 debuga ( _ ( "Path too long: " ));
423 debuga_more ( "%s%s \n " , yeardir
, direntp
-> d_name
);
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
));
431 if ( S_ISDIR ( statb
. st_mode
))
433 if (! isdigit ( direntp
-> d_name
[ 0 ]) || ! isdigit ( direntp
-> d_name
[ 1 ])) continue ;
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' ) {
443 if ( nmonths
>= sizeof ( monthsort
)/ sizeof ( monthsort
[ 0 ])) {
444 debuga ( _ ( "Too many month directories in %s \n Supernumerary entries are ignored \n " ), yeardir
);
448 for ( i
= nmonths
; i
> 0 && month
< monthsort
[ i
- 1 ] ; i
--) {
449 monthsort
[ i
]= monthsort
[ i
- 1 ];
453 total_size
+= get_file_size (& statb
);
455 else if ( S_ISREG ( statb
. st_mode
))
457 total_size
+= get_file_size (& statb
);
462 strcpy ( yeardir
+ yeardir_len
, INDEX_HTML_FILE
);
463 if (( fp_ou
= fopen ( yeardir
, "w" ))== NULL
) {
464 debuga ( _ ( "Cannot open file \" %s \" : %s \n " ), yeardir
, strerror ( errno
));
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
++) {
479 month
= monthsort
[ nmonths
- 1 - m
];
480 m1
=( month
>> 4 ) & 0x0F ;
481 if (( month
& 0x0F ) != 0 ) {
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
);
488 sprintf ( monthnum
, "%02d" , m1
);
489 name_month ( m1
, nmonth
, sizeof ( nmonth
));
491 if ( yeardir_len
+ strlen ( monthnum
)+ 1 >= yeardir_size
) {
492 yeardir
[ yeardir_len
]= '\0' ;
493 debuga ( _ ( "Path too long: " ));
494 debuga_more ( "%s%s \n " , yeardir
, monthnum
);
497 strcpy ( yeardir
+ yeardir_len
, monthnum
);
498 sub_size
= make_date_index_day ( yeardir
, yeardir_size
, order
, yearnum
, nmonth
);
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
)
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
);
509 fputs ( "</tr> \n " , fp_ou
);
510 total_size
+= sub_size
;
512 fputs ( "</table></div> \n " , fp_ou
);
513 yeardir
[ yeardir_len
- 1 ]= '\0' ;
514 write_html_trailer ( fp_ou
);
515 if ( fclose ( fp_ou
)== EOF
) {
516 debuga ( _ ( "Write error in %s/%s: %s \n " ), yeardir
, INDEX_HTML_FILE
, strerror ( errno
));
523 * Rebuild a date index tree in the output directory.
525 static void make_date_index ( void )
529 struct dirent
* direntp
;
530 char yearindex
[ MAXLEN
];
531 char yeardir
[ MAXLEN
];
539 long long int total_size
;
542 if (( dirp
= opendir ( outdir
)) == NULL
) {
543 debuga ( _ ( "Cannot open directory \" %s \" : %s \n " ), outdir
, strerror ( errno
));
546 while (( direntp
= readdir ( dirp
)) != NULL
) {
547 if (! isdigit ( direntp
-> d_name
[ 0 ]) || ! isdigit ( direntp
-> d_name
[ 1 ]) ||
548 ! isdigit ( direntp
-> d_name
[ 2 ]) || ! isdigit ( direntp
-> d_name
[ 3 ])) continue ;
549 year
= atoi ( direntp
-> d_name
) << 10 ;
550 if ( direntp
-> d_name
[ 4 ]== '-' )
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 );
559 if ( direntp
-> d_name
[ 4 ]) continue ;
561 if ( nyears
>= sizeof ( yearsort
)/ sizeof ( yearsort
[ 0 ])) {
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.
567 if ( year
> yearsort
[ 0 ]) {
568 for ( i
= 1 ; i
< nyears
&& year
> yearsort
[ i
] ; i
++)
569 yearsort
[ i
- 1 ]= yearsort
[ i
];
573 for ( i
= nyears
; i
> 0 && year
< yearsort
[ i
- 1 ] ; i
--) {
574 yearsort
[ i
]= yearsort
[ i
- 1 ];
582 order
=( strcmp ( IndexSortOrder
, "A" ) == 0 ) ? 1 : - 1 ;
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
);
588 if (( fp_ou
= fopen ( yearindex
, "w" ))== NULL
) {
589 debuga ( _ ( "Cannot open file \" %s \" : %s \n " ), yearindex
, strerror ( errno
));
592 write_html_header ( fp_ou
, 0 , ngettext ( "SARG report" , "SARG reports" , nyears
), HTML_JS_NONE
);
593 close_html_header ( fp_ou
);
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
);
600 yeardirlen
= strlen ( outdir
);
601 if ( yeardirlen
>= sizeof ( yeardir
)) {
602 debuga ( _ ( "Path too long: " ));
603 debuga_more ( "%s" , outdir
);
606 strcpy ( yeardir
, outdir
);
608 for ( y
= 0 ; y
< nyears
; y
++) {
612 year
= yearsort
[ nyears
- 1 - y
];
613 if (( year
& 0x3FF )== 0 )
614 sprintf ( yearnum
, "%04d" , year
>> 10 );
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
);
620 fprintf ( fp_ou
, "<tr><td class= \" data2 \" ><a href= \" %s/%s \" >%s</a></td>" , yearnum
, INDEX_HTML_FILE
, yearnum
);
621 if ( IndexFields
& INDEXFIELDS_DIRSIZE
)
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
);
629 fputs ( "</tr> \n " , fp_ou
);
632 fputs ( "</table></div> \n " , fp_ou
);
633 write_html_trailer ( fp_ou
);
634 if ( fclose ( fp_ou
)== EOF
) {
635 debuga ( _ ( "Write error in \" %s \" : %s \n " ), yearindex
, strerror ( errno
));
640 static void make_file_index ( void )
642 #define MAX_CREATION_DATE 15
645 struct dirent
* direntp
;
649 char day
[ 6 ], mon
[ 8 ], year
[ 40 ], hour
[ 10 ];
650 long long int tbytes
;
652 int iyear
, imonth
, iday
, ihour
, iminute
, isecond
, idst
;
658 struct getwordstruct gwarea
;
661 int year
, month
, day
, sortnum
;
662 char creationdate
[ MAX_CREATION_DATE
];
665 } ** sortlist
, * item
, ** tempsort
;
667 sprintf ( wdir
, "%s" INDEX_HTML_FILE
, outdir
);
669 order
=( strcmp ( IndexSortOrder
, "A" ) == 0 ) ? 1 : - 1 ;
671 if (( dirp
= opendir ( outdir
)) == NULL
) {
672 debuga ( _ ( "Cannot open directory \" %s \" : %s \n " ), outdir
, strerror ( errno
));
679 while (( direntp
= readdir ( dirp
)) != NULL
) {
680 if ( strchr ( direntp
-> d_name
, '-' ) == 0 ) continue ;
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
);
685 item
= malloc ( sizeof (* item
));
687 debuga ( _ ( "not enough memory to sort the index \n " ));
691 item
-> year
= atoi ( direntp
-> d_name
);
692 item
-> month
= conv_month ( direntp
-> d_name
+ 4 );
693 item
-> day
= atoi ( direntp
-> d_name
+ 7 );
695 item
-> year
= atoi ( direntp
-> d_name
+ 5 );
696 item
-> month
= conv_month ( direntp
-> d_name
+ 2 );
697 item
-> day
= atoi ( direntp
-> d_name
);
699 item
-> sortnum
=( item
-> year
* 16 + item
-> month
)* 32 + item
-> day
;
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
);
705 Old code to parse a date stored by sarg before 2.2.6.1 in the sarg-date file of each report directory.
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
);
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
);
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
);
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
);
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
);
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
);
734 buildymd ( day
, mon
, year
, ftime
, sizeof ( ftime
));
735 snprintf ( item
-> creationdate
, sizeof ( item
-> creationdate
), "%s%02d%02d%02d" , ftime
, ihour
, iminute
, isecond
);
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
);
742 safe_strcpy ( item
-> date
, data
, sizeof ( item
-> date
));
743 if ( nsort
+ 1 > nallocated
) {
745 tempsort
= realloc ( sortlist
, nallocated
* sizeof (* item
));
747 debuga ( _ ( "not enough memory to sort the index \n " ));
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 ;
757 sortlist
[ i
]= sortlist
[ i
- 1 ];
765 if (( fp_ou
= fopen ( wdir
, "w" ))== NULL
) {
766 debuga ( _ ( "Cannot open file \" %s \" : %s \n " ), wdir
, strerror ( errno
));
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
);
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" ));
776 for ( i
= 0 ; i
< nsort
; i
++) {
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 ));
797 fputs ( "</table></div> \n " , fp_ou
);
798 write_html_trailer ( fp_ou
);
799 if ( fclose ( fp_ou
)== EOF
)
800 debuga ( _ ( "Write error in file \" %s \" : %s \n " ), wdir
, strerror ( errno
));
803 for ( i
= 0 ; i
< nsort
; i
++) {
804 free ( sortlist
[ i
]-> dirname
);
811 static void file_index_to_date_index ( const char * entry
)
813 int y1
, y2
, m1
, m2
, d1
, d2
;
818 char olddir
[ MAXLEN
], newdir
[ MAXLEN
];
820 if ( strlen ( entry
) < 19 ) return ;
824 memset ( sm1
, 0 , sizeof ( sm1
));
825 memset ( sm2
, 0 , sizeof ( sm2
));
830 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
831 y1
= y1
* 10 +( entry
[ i
++]- '0' );
833 for ( j
= 0 ; j
< sizeof ( sm1
)- 1 && entry
[ i
] && isalpha ( entry
[ i
]) ; j
++)
837 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
838 d1
= d1
* 10 +( entry
[ i
++]- '0' );
841 if ( entry
[ i
++]!= '-' ) return ;
843 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
844 y2
= y2
* 10 +( entry
[ i
++]- '0' );
846 for ( j
= 0 ; j
< sizeof ( sm2
)- 1 && entry
[ i
] && isalpha ( entry
[ i
]) ; j
++)
850 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
851 d2
= d2
* 10 +( entry
[ i
++]- '0' );
853 } else if ( df
== 'e' ) {
854 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
855 d1
= d1
* 10 +( entry
[ i
++]- '0' );
857 for ( j
= 0 ; j
< sizeof ( sm1
)- 1 && entry
[ i
] && isalpha ( entry
[ i
]) ; j
++)
861 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
862 y1
= y1
* 10 +( entry
[ i
++]- '0' );
865 if ( entry
[ i
++]!= '-' ) return ;
867 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
868 d2
= d2
* 10 +( entry
[ i
++]- '0' );
870 for ( j
= 0 ; j
< sizeof ( sm2
)- 1 && entry
[ i
] && isalpha ( entry
[ i
]) ; j
++)
874 for ( j
= 0 ; entry
[ i
] && isdigit ( entry
[ i
]) ; j
++)
875 y2
= y2
* 10 +( entry
[ i
++]- '0' );
882 ndirlen
= sprintf ( newdir
, "%s%04d" , outdir
, y1
);
883 if ( access ( newdir
, R_OK
) != 0 ) {
884 if ( PortableMkDir ( newdir
, 0755 )) {
885 debuga ( _ ( "Cannot create directory \" %s \" : %s \n " ), newdir
, strerror ( errno
));
889 if ( m1
!= m2
) ndirlen
+= sprintf ( newdir
+ ndirlen
, "/%02d-%02d" , m1
, m2
);
890 else ndirlen
+= sprintf ( newdir
+ ndirlen
, "/%02d" , m1
);
891 if ( access ( newdir
, R_OK
) != 0 ) {
892 if ( PortableMkDir ( newdir
, 0755 )) {
893 debuga ( _ ( "Cannot create directory \" %s \" : %s \n " ), newdir
, strerror ( errno
));
898 if ( d1
!= d2
) ndirlen
+= sprintf ( newdir
+ ndirlen
, "/%02d-%02d" , d1
, d2
);
899 else ndirlen
+= sprintf ( newdir
+ ndirlen
, "/%02d" , d1
);
901 sprintf ( olddir
, "%s%s" , outdir
, entry
);
902 if ( rename ( olddir
, newdir
)) {
903 debuga ( _ ( "Error renaming \" %s \" to \" %s \" : %s \n " ), olddir
, newdir
, strerror ( errno
));
907 strcpy ( newdir
+ monthlen
, "/images" );
908 if ( access ( newdir
, R_OK
) != 0 ) {
910 char linkdir
[ MAXLEN
];
912 sprintf ( linkdir
, "%simages" , outdir
);
913 if ( symlink ( linkdir
, newdir
)) {
914 debuga ( _ ( "Failed to create link \" %s \" to \" %s \" : %s \n " ), linkdir
, newdir
, strerror ( errno
));
921 sprintf ( cmd
, "ln -s \" %simages \" \" %s/images \" " , outdir
, newdir
);
923 if (! WIFEXITED ( cstatus
) || WEXITSTATUS ( cstatus
)) {
924 debuga ( _ ( "command return status %d \n " ), WEXITSTATUS ( cstatus
));
925 debuga ( _ ( "command: %s \n " ), cmd
);
932 static void date_index_to_file_index ( const char * entry
)
940 const char * sm1
, * sm2
;
942 char newdir
[ MAXLEN
], olddir
[ MAXLEN
];
944 struct dirent
* direntp2
;
945 struct dirent
* direntp3
;
947 if ( strlen ( entry
) != 4 ) return ;
950 if ( sscanf ( entry
, "%d%n" ,& y1
,& next
)!= 1 || next
< 0 || entry
[ next
]) return ;
952 val1len
= snprintf ( val1
, sizeof ( val1
), "%s%s" , outdir
, entry
);
953 dirp2
= opendir ( val1
);
955 while (( direntp2
= readdir ( dirp2
)) != NULL
) {
956 if (! isdigit ( direntp2
-> d_name
[ 0 ]) || ! isdigit ( direntp2
-> d_name
[ 1 ])) continue ;
958 str
= direntp2
-> d_name
;
960 for ( j
= 0 ; j
< 2 && str
[ i
] && isdigit ( str
[ i
]) ; j
++)
961 m1
=( m1
* 10 )+( str
[ i
++]- '0' );
963 sm1
= conv_month_name ( m1
);
967 for ( j
= 0 ; j
< 2 && str
[ i
] && isdigit ( str
[ i
]) ; j
++)
968 m2
=( m2
* 10 )+( str
[ i
++]- '0' );
970 sm2
= conv_month_name ( m2
);
971 } else if (! str
[ i
]) {
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 ;
983 str
= direntp3
-> d_name
;
985 for ( j
= 0 ; str
[ i
] && isdigit ( str
[ i
]) ; j
++)
986 d1
= d1
* 10 +( str
[ i
++]- '0' );
991 for ( j
= 0 ; str
[ i
] && isdigit ( str
[ i
]) ; j
++)
992 d2
= d2
* 10 +( str
[ i
++]- '0' );
994 } else if (! str
[ i
]) {
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
);
1003 sprintf ( olddir
, "%s%04d/%s/%s" , outdir
, y1
, direntp2
-> d_name
, direntp3
-> d_name
);
1004 if ( rename ( olddir
, newdir
)) {
1005 debuga ( _ ( "Error renaming \" %s \" to \" %s \" : %s \n " ), olddir
, newdir
, strerror ( errno
));
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.