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