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