generic/486: Get rid of the redundant error=%d printing
[xfstests-dev.git] / src / fill2.c
1 /*
2  * Copyright (c) 2000-2003 Silicon Graphics, Inc.
3  * All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it would be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write the Free Software Foundation,
16  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19
20 /*
21  * fill2:
22  *   Derived from fill.c, adds command line options, block boundary marking
23  *   and allows me to tweak to suit fill2fs without breaking other qa tests.
24  *   This version marks block boundaries (512, 1k, 4k and 16k) within the file
25  *   using characters guaranteed not to appear anywhere else, this may allow
26  *   simple checks in future which can inspect these offsets and ensure one of
27  *   of the four characters is present. Also fixes off-by-one error in fill.c
28  *   and is more careful about checking when write operations fail - this is 
29  *   needed by fill2fs to ensure that the number of bytes written is accurately
30  *   determined.
31  */
32
33 #include "global.h"
34
35 #define constpp char * const *
36
37 #define N(x) (sizeof(x)/sizeof(x[0]))
38
39 /* function prototypes */
40 static void illegal(char *, char *);
41 static void reqval(char, char * [], int);
42 static void respec(char, char * [], int);
43 static void unknown(char, char *);
44 static void usage(void);
45 char *progname;
46
47 char *dopts[] =         { "nbytes", "linelength", "seed", "file", NULL };
48 enum                    { D_NBYTES, D_LINELENGTH, D_SEED, D_FILE, D_ISSET, D_NUM };
49 int dflags[D_NUM] =     { 0 };
50 char *bopts[] =         { "512", "1k", "4k", "16k" };
51 enum                    { B_512, B_1K, B_4K, B_16K, B_ISSET, B_NUM };
52 int bflags[B_NUM] =     { 0 };
53
54
55 /*
56  * block boundaries
57  *
58  */
59
60 /* block boundaries which should be flagged in the output file */
61 /* flag is the character that should be used to indicate each type */
62 /* of block boundary */
63
64
65 struct s_block_type {
66     int mark;
67     int size;
68     char flag;
69 };
70
71 static struct s_block_type btypes[] = {
72     { 0, 512, '!' },    /* 512 */
73     { 0, 1024, '"' },   /* 1k */
74     { 0, 4096, '#' },   /* 4k */
75     { 0, 16384, '$' },  /* 16k */
76 };
77
78 /*
79  * main
80  *
81  */
82
83 int
84 main(int argc, char **argv)
85 {
86     int                 status = 0;     /* return status */
87     int                 c;              /* current option character */
88     char                *p;             /* for getsubopt calls */
89     long                nbytes;         /* total number of bytes to write */
90     int                 dlen;           /* length of normal output data line */
91     const char          *dseed = NULL;  /* input string for seeding rand */
92     unsigned int        seed;           /* seed for output data */    
93     char                *dfile = NULL;  /* where to write output */
94
95     FILE                *f;             /* output file */
96     char                *dbuf;          /* output line buffer */
97     char                bbuf[50];       /* block boundary string */
98     char                *active = NULL; /* active buffer to copy out of */
99     size_t              hlen;           /* header length (bytes+key) in output */
100                                         /* lines */
101     char                *hptr;          /* pointer to end of header */
102     char                *ptr;           /* current position to copy from */
103     int                 blktype = 0;    /* current block boundary type */
104     int                 boundary;       /* set if current output char lies on */
105                                         /* block boundary */
106     long                i;
107     int                 j;
108     int                 l = 0;
109
110
111     /* defaults */
112
113     progname = basename(argv[0]);
114     for (p = progname; *p; p++) {
115             if (*p == '/') {
116                     progname = p + 1;
117             }
118     }
119     nbytes = 1024 * 1024;
120     dlen = 73;  /* includes the trailing newline */
121
122     while ((c = getopt(argc, argv, "d:b:")) != EOF) {
123         switch (c) {
124         case 'd':
125             if (dflags[D_ISSET])
126                 respec('d', NULL, 0);
127             dflags[D_ISSET] = 1;
128
129             p = optarg;
130             while (*p != '\0') {
131                 char *value;
132                 switch (getsubopt(&p, (constpp)dopts, &value)) {
133                 case D_NBYTES:
134                     if (! value) reqval('d', dopts, D_NBYTES);
135                     if (dflags[D_NBYTES]) respec('d', dopts, D_NBYTES);
136                     dflags[D_NBYTES] = 1;
137                     nbytes = strtol(value, &ptr, 10);
138                     if (ptr == value) illegal(value, "d nbytes");
139                     break;
140
141                 case D_LINELENGTH:
142                     if (! value) reqval('d', dopts, D_LINELENGTH);
143                     if (dflags[D_LINELENGTH]) respec('d', dopts, D_LINELENGTH);
144                     dflags[D_LINELENGTH] = 1;
145                     dlen = (int) strtol(value, &ptr, 10) + 1;
146                     if (ptr == value) illegal(value, "d linelength");
147                     break;
148
149                 case D_SEED:
150                     if (! value) reqval('d', dopts, D_SEED);
151                     if (dflags[D_SEED]) respec('D', dopts, D_SEED);
152                     dflags[D_SEED] = 1;
153                     dseed = value;
154                     break;
155
156                 case D_FILE:
157                     if (! value) reqval('d', dopts, D_FILE);
158                     if (dflags[D_FILE]) respec('d', dopts, D_FILE);
159                     dflags[D_FILE] = 1;
160                     dfile = value;
161                     break;
162                     
163                 default:
164                     unknown('d', value);
165                 }
166             }
167             break;
168
169         case 'b':
170             if (bflags[B_ISSET])
171                 respec('b', NULL, 0);
172             bflags[B_ISSET] = 1;
173
174             p = optarg;
175             while (*p != '\0') {
176                 char *value;
177                 switch (getsubopt(&p, (constpp)bopts, &value)) {
178                 case B_512:
179                     if (value) illegal(value, "b 512");
180                     if (bflags[B_512]) respec('b', bopts, B_512);
181                     bflags[B_512] = 1;
182                     btypes[0].mark = 1;
183                     break;
184
185                 case B_1K:
186                     if (value) illegal(value, "b 1k");
187                     if (bflags[B_1K]) respec('b', bopts, B_1K);
188                     bflags[B_1K] = 1;
189                     btypes[1].mark = 1;
190                     break;
191
192                 case B_4K:
193                     if (value) illegal(value, "b 4k");
194                     if (bflags[B_4K]) respec('b', bopts, B_4K);
195                     bflags[B_4K] = 1;
196                     btypes[2].mark = 1;
197                     break;
198
199                 case B_16K:
200                     if (value) illegal(value, "b 16k");
201                     if (bflags[B_16K]) respec('b', bopts, B_16K);
202                     bflags[B_16K] = 1;
203                     btypes[3].mark = 1;
204                     break;
205
206                 default:
207                     unknown('b', value);
208                     break;
209                 }
210             }
211             break;
212
213         case '?':
214             usage();
215         }
216     }
217
218     if (dflags[D_FILE] && optind != argc)
219         usage();
220
221     if (! dflags[D_FILE] && argc - optind != 1)
222         usage();
223
224     if (! dflags[D_FILE])
225         dfile = argv[optind];
226
227     if ((f = fopen(dfile, "w")) == NULL) {
228         fprintf(stderr, "fill2: cannot create \"%s\": %s\n",
229                 dfile, strerror(errno));
230         status = 1;
231         goto cleanup;
232     }
233
234     if (! dflags[D_SEED]) 
235         dseed = dfile;
236
237     /* seed is an ascii string */
238     seed = 0;
239     i = 0;
240     while (dseed[i]) {
241         seed <<= 8;
242         seed |= dseed[i];
243         i++;
244     }
245     srand(seed);
246
247
248     /* normal data line format: XXXXXXXXXXXX CCCC...CCC CCCCCCCCCCCC...CCC */
249     /* block boundary format: CXXXXXXX */
250
251     dbuf = (char *) malloc(dlen + 1);
252     hlen = 12+1+strlen(dseed)+1;
253     assert(hlen < dlen);
254     hptr = dbuf + hlen;
255
256     for (i = 0; i < nbytes; i++) {
257
258         
259         /* is this a block boundary? check largest to smallest */
260         boundary = 0;
261         for (j = N(btypes) - 1; j >= 0; j--) {
262             if (btypes[j].mark) {
263                 /* first time through or just before a block boundary? */
264                 if (i == 0 || i % btypes[j].size == btypes[j].size - 1) {
265                     boundary = 1;
266                     blktype = j;
267                     break;
268                 }
269             }
270         }
271
272
273         /* are there block boundaries to check? */
274         /* is this the first time through? */
275         /* block boundaries take priority over other output */
276         if (bflags[B_ISSET] && (i == 0 || boundary)) {
277             sprintf(bbuf, "%s%c%07ld\n",
278                     i ? "\n" : "",
279                     btypes[blktype].flag,
280                     /* remember i is one less than block boundary */
281                     i ? (i+1) / btypes[blktype].size : 0);
282             active = bbuf;
283             ptr = bbuf;
284         }
285         /* are we at the start of a new line? */
286         else if (i == 0
287                  || (active == bbuf && *ptr == '\0')
288                  || (active == dbuf && l == dlen))
289         {
290             sprintf(dbuf, "%012ld %s ", i, dseed);
291             assert(*hptr == '\0');
292             for (ptr = hptr; ptr != dbuf + dlen - 1; ptr++) {
293                 /* characters upto 126 '~' are used */
294                 /* do not use !"#$ - leave free for use as block markers */
295                 *ptr = 37 + (rand() % (127 - 37)); 
296             }
297             *ptr++ = '\n';
298             *ptr = '\0';
299             assert(ptr == dbuf + dlen);
300             active = dbuf;
301             ptr = dbuf;
302             l = 0;
303         }
304         else {
305             /* continue copying from the active buffer */
306         }
307
308         /* output one new character from current buffer */
309         if (fputc(*ptr++, f) == EOF) {
310             fprintf(stderr, "fill2: could not write character to \"%s\": %s\n",
311                     dfile, strerror(errno));
312             status = 1;
313             goto cleanup;
314         }
315         if (active == dbuf) l++;
316
317     }
318
319  cleanup:
320
321     /* close file and flush buffers - check if this fails */
322     if (fclose(f) != 0) {
323         fprintf(stderr, "fill2: fclose() on \"%s\" failed: %s\n",
324                 dfile, strerror(errno));
325         status = 1;
326     }
327     return status;
328 }
329
330
331
332 /*
333  * suboption checking functions
334  *
335  */
336
337 static void
338 illegal(char *value, char *opt)
339 {
340     fprintf(stderr, "%s: Error: Illegal value \"%s\" for -%s option\n",
341             progname, value, opt);
342     usage();
343 }
344 static void
345 reqval(char opt, char *tab[], int idx)
346 {
347     fprintf(stderr, "%s: Error: -%c %s option requires a value\n",
348             progname, opt, tab[idx]);
349     usage();
350 }
351 static void
352 respec(char opt, char *tab[], int idx)
353 {
354     fprintf(stderr, "%s: Error: ", progname);
355     fprintf(stderr, "-%c ", opt);
356     if (tab) fprintf(stderr, "%s ", tab[idx]);
357     fprintf(stderr, "option respecified\n");
358     usage();
359 }
360 static void
361 unknown(char opt, char *s)
362 {
363     fprintf(stderr, "%s: Error: Unknown option -%c %s\n", progname, opt, s);
364     usage();
365 }
366 static void
367 usage(void)
368 {
369     fprintf(stderr, "Usage: %s [options] filename\n"
370 "Options:\n"
371 "  -d [nbytes=num,linelength=num,   output data properties\n"
372 "      seed=string,file=name]\n"
373 "  -b [512,1k,4k,16k]               where to mark block boundaries\n", progname);
374     exit(2);
375 }