44 * Copyright (C) 2003 Gabriel L. Somlo
55 */
66#include <iprange.h>
7+ #include <sys/stat.h>
8+ #include <dirent.h>
79
810char * PROG ;
911int debug ;
@@ -236,6 +238,8 @@ static void usage(const char *me) {
236238 "Other options:\n"
237239 " --has-compare\n"
238240 " --has-reduce\n"
241+ " --has-filelist-loading\n"
242+ " --has-directory-loading\n"
239243 " Exits with 0,\n"
240244 " other versions of iprange will exit with 1.\n"
241245 " Use this option in scripts to find if this\n"
@@ -262,6 +266,23 @@ static void usage(const char *me) {
262266 " to change its name in the CSV output.\n"
263267 " If no filename is given, stdin is assumed.\n"
264268 "\n"
269+ " > @filename\n"
270+ " A file list containing filenames, one per line.\n"
271+ " Each file in the list is loaded as an individual ipset.\n"
272+ " Comments starting with # or ; are ignored.\n"
273+ " Empty lines are ignored.\n"
274+ " Multiple @filename parameters can be used.\n"
275+ " @filename works with all modes and respects the positional\n"
276+ " nature of the parameters.\n"
277+ "\n"
278+ " > @directory\n"
279+ " If @filename refers to a directory, all files in that directory\n"
280+ " will be loaded, each as an individual ipset.\n"
281+ " Subdirectories are ignored.\n"
282+ " Multiple @directory parameters can be used.\n"
283+ " @directory works with all modes and respects the positional\n"
284+ " nature of the parameters.\n"
285+ "\n"
265286 " Files may contain any or all of the following:\n"
266287 " (1) comments starting with hashes (#) or semicolons (;);\n"
267288 " (2) one IP per line (without mask);\n"
@@ -537,43 +558,226 @@ int main(int argc, char **argv) {
537558 fprintf (stderr , "yes, compare and reduce is present.\n" );
538559 exit (0 );
539560 }
561+ else if (!strcmp (argv [i ], "--has-filelist-loading" )
562+ || !strcmp (argv [i ], "--has-directory-loading" )) {
563+ fprintf (stderr , "yes, @filename and @directory support is present.\n" );
564+ exit (0 );
565+ }
540566 else {
541- if (!strcmp (argv [i ], "-" ))
567+ if (!strcmp (argv [i ], "-" )) {
542568 ips = ipset_load (NULL );
543- else
544- ips = ipset_load (argv [i ]);
545-
546- if (!ips ) {
547- fprintf (stderr , "%s: Cannot load ipset: %s\n" , PROG , argv [i ]);
548- exit (1 );
569+
570+ if (!ips ) {
571+ fprintf (stderr , "%s: Cannot load ipset from stdin\n" , PROG );
572+ exit (1 );
573+ }
574+
575+ if (read_second ) {
576+ ips -> next = second ;
577+ second = ips ;
578+ if (ips -> next ) ips -> next -> prev = ips ;
579+ }
580+ else {
581+ if (!first ) first = ips ;
582+ ips -> next = root ;
583+ root = ips ;
584+ if (ips -> next ) ips -> next -> prev = ips ;
585+ }
549586 }
550-
551- if (read_second ) {
552- ips -> next = second ;
553- second = ips ;
554- if (ips -> next ) ips -> next -> prev = ips ;
587+ else if (argv [i ][0 ] == '@' ) {
588+ /* Handle @filename as a file list or directory */
589+ const char * listname = argv [i ] + 1 ; /* Skip the @ character */
590+ struct stat st ;
591+
592+ if (stat (listname , & st ) != 0 ) {
593+ fprintf (stderr , "%s: Cannot access %s: %s\n" , PROG , listname , strerror (errno ));
594+ exit (1 );
595+ }
596+
597+ /* Check if it's a directory */
598+ if (S_ISDIR (st .st_mode )) {
599+ DIR * dir ;
600+ struct dirent * entry ;
601+
602+ if (unlikely (debug ))
603+ fprintf (stderr , "%s: Loading files from directory %s\n" , PROG , listname );
604+
605+ dir = opendir (listname );
606+ if (!dir ) {
607+ fprintf (stderr , "%s: Cannot open directory: %s - %s\n" , PROG , listname , strerror (errno ));
608+ exit (1 );
609+ }
610+
611+ /* Flag to track if we loaded any files */
612+ int files_loaded = 0 ;
613+
614+ /* Read all files from the directory */
615+ while ((entry = readdir (dir ))) {
616+ /* Skip "." and ".." */
617+ if (!strcmp (entry -> d_name , "." ) || !strcmp (entry -> d_name , ".." ))
618+ continue ;
619+
620+ /* Create full path */
621+ char filepath [FILENAME_MAX + 1 ];
622+ snprintf (filepath , FILENAME_MAX , "%s/%s" , listname , entry -> d_name );
623+
624+ /* Skip subdirectories */
625+ if (stat (filepath , & st ) != 0 || S_ISDIR (st .st_mode ))
626+ continue ;
627+
628+ if (unlikely (debug ))
629+ fprintf (stderr , "%s: Loading file %s from directory %s\n" , PROG , entry -> d_name , listname );
630+
631+ /* Load the file as an independent ipset */
632+ ips = ipset_load (filepath );
633+ if (!ips ) {
634+ fprintf (stderr , "%s: Cannot load file %s from directory %s\n" ,
635+ PROG , filepath , listname );
636+ continue ;
637+ }
638+
639+ files_loaded = 1 ;
640+
641+ /* Add the ipset to the appropriate chain */
642+ if (read_second ) {
643+ ips -> next = second ;
644+ second = ips ;
645+ if (ips -> next ) ips -> next -> prev = ips ;
646+ }
647+ else {
648+ if (!first ) first = ips ;
649+ ips -> next = root ;
650+ root = ips ;
651+ if (ips -> next ) ips -> next -> prev = ips ;
652+ }
653+ }
654+
655+ closedir (dir );
656+
657+ /* Handle empty directory case */
658+ if (!files_loaded ) {
659+ if (unlikely (debug ))
660+ fprintf (stderr , "%s: Directory %s is empty or contains no valid files\n" , PROG , listname );
661+
662+ /* Report an error for empty directory */
663+ fprintf (stderr , "%s: No valid files found in directory: %s\n" , PROG , listname );
664+ }
665+ }
666+ else {
667+ /* Handle as a file list */
668+ FILE * fp ;
669+ char line [MAX_LINE + 1 ];
670+ int lineid = 0 ;
671+
672+ if (unlikely (debug ))
673+ fprintf (stderr , "%s: Loading files from list %s\n" , PROG , listname );
674+
675+ fp = fopen (listname , "r" );
676+ if (!fp ) {
677+ fprintf (stderr , "%s: Cannot open file list: %s - %s\n" , PROG , listname , strerror (errno ));
678+ exit (1 );
679+ }
680+
681+ /* Flag to track if we loaded any files */
682+ int files_loaded = 0 ;
683+
684+ /* Read each line and load the corresponding file */
685+ while (fgets (line , MAX_LINE , fp )) {
686+ lineid ++ ;
687+
688+ /* Skip empty lines and comments */
689+ char * s = line ;
690+ while (* s && (* s == ' ' || * s == '\t' )) s ++ ;
691+ if (* s == '\n' || * s == '\r' || * s == '\0' || * s == '#' || * s == ';' )
692+ continue ;
693+
694+ /* Remove trailing newlines/whitespace */
695+ char * end = s + strlen (s ) - 1 ;
696+ while (end > s && (* end == '\n' || * end == '\r' || * end == ' ' || * end == '\t' ))
697+ * end -- = '\0' ;
698+
699+ if (unlikely (debug ))
700+ fprintf (stderr , "%s: Loading file %s from list (line %d)\n" , PROG , s , lineid );
701+
702+ /* Load the file as an independent ipset */
703+ ips = ipset_load (s );
704+ if (!ips ) {
705+ fprintf (stderr , "%s: Cannot load file %s from list %s (line %d)\n" ,
706+ PROG , s , listname , lineid );
707+ continue ;
708+ }
709+
710+ files_loaded = 1 ;
711+
712+ /* Add the ipset to the appropriate chain */
713+ if (read_second ) {
714+ ips -> next = second ;
715+ second = ips ;
716+ if (ips -> next ) ips -> next -> prev = ips ;
717+ }
718+ else {
719+ if (!first ) first = ips ;
720+ ips -> next = root ;
721+ root = ips ;
722+ if (ips -> next ) ips -> next -> prev = ips ;
723+ }
724+ }
725+
726+ fclose (fp );
727+
728+ /* Handle empty file list case */
729+ if (!files_loaded ) {
730+ if (unlikely (debug ))
731+ fprintf (stderr , "%s: File list %s is empty or contains no valid entries\n" , PROG , listname );
732+
733+ /* Report an error for empty file list */
734+ fprintf (stderr , "%s: No valid files found in file list: %s\n" , PROG , listname );
735+ }
736+ }
555737 }
556738 else {
557- if (!first ) first = ips ;
558- ips -> next = root ;
559- root = ips ;
560- if (ips -> next ) ips -> next -> prev = ips ;
739+ ips = ipset_load (argv [i ]);
740+
741+ if (!ips ) {
742+ fprintf (stderr , "%s: Cannot load ipset: %s\n" , PROG , argv [i ]);
743+ continue ; /* Continue with other arguments instead of exiting */
744+ }
745+
746+ if (read_second ) {
747+ ips -> next = second ;
748+ second = ips ;
749+ if (ips -> next ) ips -> next -> prev = ips ;
750+ }
751+ else {
752+ if (!first ) first = ips ;
753+ ips -> next = root ;
754+ root = ips ;
755+ if (ips -> next ) ips -> next -> prev = ips ;
756+ }
561757 }
562758 }
563759 }
564760
565761 /*
566762 * if no ipset was given on the command line
567- * assume stdin
763+ * assume stdin, but only if no other filenames were specified
568764 */
569765
570- if (!root ) {
766+ if (!root && argc <= 1 ) {
767+ if (unlikely (debug ))
768+ fprintf (stderr , "%s: No inputs provided, reading from stdin\n" , PROG );
769+
571770 first = root = ipset_load (NULL );
572771 if (!root ) {
573772 fprintf (stderr , "%s: No ipsets to merge.\n" , PROG );
574773 exit (1 );
575774 }
576775 }
776+ else if (!root ) {
777+ /* We had parameters but still ended up with no valid ipsets */
778+ fprintf (stderr , "%s: No valid ipsets to merge from the provided inputs.\n" , PROG );
779+ exit (1 );
780+ }
577781
578782 gettimeofday (& load_dt , NULL );
579783
0 commit comments