1 /* copyg.c version 2.4; B D McKay, May 2020 */
2 
3 #ifdef FILTER
4 #define HELPUSECMD
5 #define USAGE "[-v] [-gszfp#:#qhx] [infile [outfile]]"
6 #else
7 #define USAGE "copyg [-gszfp#:#qhx] [infile [outfile]]"
8 #endif
9 
10 #define HELPTEXT \
11 "  Copy a file of graphs with possible format conversion.\n\
12 \n\
13      -g  Use graph6 format for output\n\
14      -s  Use sparse6 format for output\n\
15      -z  Use digraph6 format for output\n\
16      -i  Use incremental sparse6 format for output\n\
17      In the absence of -g, -s, -z or -i, the format\n\
18      depends on the header or, if none, the first input line.\n\
19      As an exception, digraphs are always written in digraph6.\n\
20 \n\
21      -p# -p#:#  \n\
22         Specify range of input lines (first is 1)\n\
23         This can fail if the input has incremental lines.\n\
24      -f  With -p, assume input lines of fixed length\n\
25         (ignored if header or first line has sparse6 format).\n\
26      -I#  Have at most this number of incremental steps\n\
27         in a row.  Implies -i. \n\
28 \n\
29      -h  Write a header.\n\
30      -x  Don't write a header.\n\
31      In the absence of -h and -x, a header is written if\n\
32      there is one in the input.\n\
33 \n\
34      -q  Suppress auxiliary output.\n"
35 
36 #ifdef FILTER
37 #define HELPTEXT1 HELPTEXT
38 #define HELPTEXT2 \
39 "\n\
40    -Q# -Q#:# a number or range to pass to the filter.\n\
41 \n\
42    Only inputs satisfying the filter, or not satisfying the\n\
43    filter if -v is given, are passed through.\n"
44 #endif
45 
46 /***********************************************************************/
47 
48 /* How to use this as a skeleton to construct a filter.
49 
50    Suppose we want to make a filter "numleaves" that filters graphs
51    according to their number of leaves.
52 
53    (1) In a separate file numleaves.c (present in the package), write
54    a procedure with prototype matching
55       boolean
56       numleaves(graph *g, boolean digraph, long Qlo, long Qhi, int m, int n);
57    It should return a nonzero value if g has between Qlo and Qhi leaves,
58    and return 0 otherwise. The values of Qlo and Qhi are obtained from
59    the -Q switch, with missing lower bound -NOLIMIT and missing upper
60    bound NOLIMIT.
61 
62    (2) Compile numleaves.c together with this program with the preprocessor
63    variable FILTER defined as "numleaves" (without the quotes). Link with
64    nauty.a. For the gcc, icc, and some other compilers, you can do it like
65    this:
66      gcc -o numleaves -O3 -DFILTER=numleaves copyg.c numleaves.c nauty.a
67 
68    (3) That's it.  Now "numleaves -Q2:7" will copy through graphs with 2-7
69    leaves, while "numleaves -v -Q7" will copy through graphs whose number
70    of leaves is not 7.
71 
72    Within the procedure numleaves(), the index number of the current graph
73    is available in the external nauty_counter variable nin.  The first
74    graph has index 1.
75 
76    If you also define SUMMARY to be a procedure with prototype
77    void name(void), it is called before the final summary (unless
78    -q is given).  You can't use SUMMARY without FILTER.
79 */
80 
81 /***********************************************************************/
82 
83 #include "gtools.h"
84 
85 #ifdef FILTER
86 extern boolean
87 FILTER(graph *g, boolean digraph, long Qlo, long Qhi, int m, int n);
88 #ifdef SUMMARY
89 extern void SUMMARY(void);
90 #endif
91 #endif
92 
93 nauty_counter nin;
94 
95 int
main(int argc,char * argv[])96 main(int argc, char *argv[])
97 {
98     graph *g,*gprev,*gbase;
99     int m,n,codetype;
100     char *infilename,*outfilename;
101     FILE *infile,*outfile;
102     int outcode;
103     nauty_counter nout;
104     int argnum,j,nprev,mprev;
105     char *arg,sw;
106     boolean sswitch,fswitch,pswitch,qswitch,gswitch;
107     boolean hswitch,xswitch,iswitch,Iswitch,zswitch;
108     boolean badargs,digraph,vswitch,Qswitch,kept;
109     long Qlo,Qhi;
110     long pval1,pval2,maxin,refresh,inclines;
111     double t0,t1;
112 
113     HELP; PUTVERSION;
114 
115     iswitch = Iswitch = sswitch = fswitch = pswitch = FALSE;
116     gswitch = qswitch = xswitch = hswitch = zswitch = FALSE;
117     vswitch = Qswitch = FALSE;
118     Qlo = Qhi = 0;  /* avoid some warnings */
119     infilename = outfilename = NULL;
120 
121     argnum = 0;
122     badargs = FALSE;
123     for (j = 1; !badargs && j < argc; ++j)
124     {
125         arg = argv[j];
126         if (arg[0] == '-' && arg[1] != '\0')
127         {
128             ++arg;
129             while (*arg != '\0')
130             {
131                 sw = *arg++;
132                      SWBOOLEAN('s',sswitch)
133                 else SWBOOLEAN('g',gswitch)
134                 else SWBOOLEAN('z',zswitch)
135                 else SWBOOLEAN('q',qswitch)
136                 else SWBOOLEAN('f',fswitch)
137                 else SWBOOLEAN('h',hswitch)
138                 else SWBOOLEAN('i',iswitch)
139                 else SWBOOLEAN('x',xswitch)
140 #ifdef FILTER
141                 else SWBOOLEAN('v',vswitch)
142 		else SWRANGE('Q',":-",Qswitch,Qlo,Qhi,"filter -Q")
143 #endif
144                 else SWLONG('I',Iswitch,refresh,"copyg -I")
145                 else SWRANGE('p',":-",pswitch,pval1,pval2,"copyg -p")
146                 else badargs = TRUE;
147             }
148         }
149         else
150         {
151             ++argnum;
152             if      (argnum == 1) infilename = arg;
153             else if (argnum == 2) outfilename = arg;
154             else                  badargs = TRUE;
155         }
156     }
157 
158     if (Iswitch) iswitch = TRUE;
159     if ((sswitch!=0) + (gswitch!=0) + (iswitch!=0) + (zswitch!=0) > 1)
160         gt_abort(">E copyg: -s, -g, -z and -i/-I are incompatible\n");
161     if (hswitch && xswitch)
162         gt_abort(">E copyg: -h and -x are incompatible\n");
163 
164     if (badargs || argnum > 2)
165     {
166         fprintf(stderr,">E Usage: %s\n",USAGE);
167         GETHELP;
168         exit(1);
169     }
170 
171     if (!qswitch)
172     {
173         fprintf(stderr,">A %s",argv[0]);
174         if (sswitch || gswitch || iswitch || Iswitch || fswitch
175             || zswitch || pswitch || xswitch || hswitch || vswitch || Qswitch)
176             fprintf(stderr," -");
177         if (sswitch) fprintf(stderr,"s");
178         if (gswitch) fprintf(stderr,"g");
179         if (zswitch) fprintf(stderr,"z");
180         if (hswitch) fprintf(stderr,"h");
181         if (Iswitch) fprintf(stderr,"I%ld",refresh);
182         else if (iswitch) fprintf(stderr,"i");
183         if (xswitch) fprintf(stderr,"x");
184         if (fswitch) fprintf(stderr,"f");
185         if (pswitch) writerange(stderr,'p',pval1,pval2);
186 	if (vswitch) fprintf(stderr,"v");
187         if (Qswitch) writerange(stderr,'Q',Qlo,Qhi);
188         if (argnum > 0) fprintf(stderr," %s",infilename);
189         if (argnum > 1) fprintf(stderr," %s",outfilename);
190         fprintf(stderr,"\n");
191         fflush(stderr);
192     }
193 
194     if (infilename && infilename[0] == '-') infilename = NULL;
195     infile = opengraphfile(infilename,&codetype,fswitch,
196                                         pswitch ? pval1 : 1);
197     if (!infile) exit(1);
198     if (!infilename) infilename = "stdin";
199 
200     if (!outfilename || outfilename[0] == '-')
201     {
202         outfilename = "stdout";
203         outfile = stdout;
204     }
205     else if ((outfile = fopen(outfilename,"w")) == NULL)
206     {
207         fprintf(stderr,"Can't open output file %s\n",outfilename);
208         gt_abort(NULL);
209     }
210 
211     if (gswitch)                  outcode = GRAPH6;
212     else if (sswitch || iswitch)  outcode = SPARSE6;
213     else if (zswitch)             outcode = DIGRAPH6;
214     else if ((codetype&GRAPH6))   outcode = GRAPH6;
215     else if ((codetype&SPARSE6))  outcode = SPARSE6;
216     else if ((codetype&DIGRAPH6)) outcode = DIGRAPH6;
217     else
218     {
219         outcode = GRAPH6;
220         fprintf(stderr,
221              ">W copyg doesn't handle this graph format, writing graph6.\n");
222     }
223 
224     if (hswitch || (!xswitch && (codetype&HAS_HEADER)))
225     {
226         if (outcode == SPARSE6)       writeline(outfile,SPARSE6_HEADER);
227         else if (outcode == DIGRAPH6) writeline(outfile,DIGRAPH6_HEADER);
228         else                          writeline(outfile,GRAPH6_HEADER);
229     }
230 
231     nin = nout = 0;
232     if (!pswitch || pval2 == NOLIMIT) maxin = NOLIMIT;
233     else if (pval1 < 1) maxin = pval2;
234     else                maxin = pval2 - pval1 + 1;
235 
236 #ifdef FILTER
237     if (!Qswitch) { Qlo = -NOLIMIT; Qhi = NOLIMIT; }
238 #endif
239 
240     gprev = NULL;
241     nprev = mprev = 1;
242     inclines = 0;
243     t0 = CPUTIME;
244     while (nin < maxin || maxin == NOLIMIT)
245     {
246         if ((g = readgg_inc(infile,NULL,0,&m,&n,gprev,mprev,nprev,&digraph))
247                                                       == NULL) break;
248         ++nin;
249 	kept = TRUE;
250 #ifdef FILTER
251 	if ((!vswitch && !FILTER(g,digraph,Qlo,Qhi,m,n)) ||
252 	                   (vswitch && FILTER(g,digraph,Qlo,Qhi,m,n)))
253 	    kept = FALSE;
254 	++nout;
255 #endif
256 
257 	if (!kept)
258 	{ }
259 	else if (iswitch)
260         {
261 	    if (digraph) gt_abort(
262                 ">Z incremental sparse6 is incompatible with digraphs\n");
263             gbase = gprev;
264             if (nprev != n || mprev != m) gbase = NULL;
265             if (Iswitch > 0 && inclines == refresh) gbase = NULL;
266             if (gbase == NULL) inclines = 0; else ++inclines;
267             writeis6(outfile,g,gbase,m,n);
268         }
269         else if (outcode == readg_code)   writelast(outfile);
270 	else if (digraph)             writed6(outfile,g,m,n);
271         else if (outcode == SPARSE6)  writes6(outfile,g,m,n);
272         else if (outcode == DIGRAPH6) writed6(outfile,g,m,n);
273         else                          writeg6(outfile,g,m,n);
274 
275         if (gprev) FREES(gprev);
276         gprev = g;
277         nprev = n;
278         mprev = m;
279     }
280     t1 = CPUTIME;
281 
282     if (!qswitch)
283 #ifdef FILTER
284 #ifdef SUMMARY
285 	SUMMARY();
286 #endif
287         fprintf(stderr,">Z  " COUNTER_FMT " graphs read from %s, "
288                               COUNTER_FMT " written to %s; %.2f sec\n",
289                        nin,infilename,nout,outfilename,t1-t0);
290 #else
291         fprintf(stderr,">Z  " COUNTER_FMT " graphs copied from %s to %s\n",
292                        nin,infilename,outfilename);
293 #endif
294 
295     exit(0);
296 }
297