1/* Generate a file containing some preset patterns.
2   Print statistics for existing files.
3
4   Copyright (C) 1995, 1996, 1997, 2001, 2003, 2004, 2005, 2006
5   Free Software Foundation, Inc.
6
7   François Pinard <pinard@iro.umontreal.ca>, 1995.
8   Sergey Poznyakoff <gray@mirddin.farlep.net>, 2004, 2005, 2006.
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2, or (at your option)
13   any later version.
14
15   This program is distributed in the hope that it will be useful, but
16   WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software Foundation,
22   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23*/
24
25#include <system.h>
26#include <signal.h>
27#include <stdarg.h>
28#include <argmatch.h>
29#include <argp.h>
30#include <argcv.h>
31#include <getdate.h>
32#include <setenv.h>
33#include <utimens.h>
34#include <inttostr.h>
35#define obstack_chunk_alloc malloc
36#define obstack_chunk_free free
37#include <obstack.h>
38
39#ifndef EXIT_SUCCESS
40# define EXIT_SUCCESS 0
41#endif
42#ifndef EXIT_FAILURE
43# define EXIT_FAILURE 1
44#endif
45
46#if ! defined SIGCHLD && defined SIGCLD
47# define SIGCHLD SIGCLD
48#endif
49
50enum pattern
51{
52  DEFAULT_PATTERN,
53  ZEROS_PATTERN
54};
55
56/* The name this program was run with. */
57const char *program_name;
58
59/* Name of file to generate */
60static char *file_name;
61
62/* Name of the file-list file: */
63static char *files_from;
64static char filename_terminator = '\n';
65
66/* Length of file to generate.  */
67static off_t file_length = 0;
68static off_t seek_offset = 0;
69
70/* Pattern to generate.  */
71static enum pattern pattern = DEFAULT_PATTERN;
72
73/* Next checkpoint number */
74size_t checkpoint;
75
76enum genfile_mode
77  {
78    mode_generate,
79    mode_sparse,
80    mode_stat,
81    mode_exec
82  };
83
84enum genfile_mode mode = mode_generate;
85
86#define DEFAULT_STAT_FORMAT \
87  "name,dev,ino,mode,nlink,uid,gid,size,blksize,blocks,atime,mtime,ctime"
88
89/* Format for --stat option */
90static char *stat_format = DEFAULT_STAT_FORMAT;
91
92/* Size of a block for sparse file */
93size_t block_size = 512;
94
95/* Block buffer for sparse file */
96char *buffer;
97
98/* Number of arguments and argument vector for mode == mode_exec */
99int exec_argc;
100char **exec_argv;
101
102/* Time for --touch option */
103struct timespec touch_time;
104
105/* Verbose mode */
106int verbose;
107
108const char *argp_program_version = "genfile (" PACKAGE ") " VERSION;
109const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
110static char doc[] = N_("genfile manipulates data files for GNU paxutils test suite.\n"
111"OPTIONS are:\n");
112
113#define OPT_CHECKPOINT 256
114#define OPT_TOUCH      257
115#define OPT_APPEND     258
116#define OPT_TRUNCATE   259
117#define OPT_EXEC       260
118#define OPT_DATE       261
119#define OPT_VERBOSE    262
120#define OPT_SEEK       263
121
122static struct argp_option options[] = {
123#define GRP 0
124  {NULL, 0, NULL, 0,
125   N_("File creation options:"), GRP},
126  {"length", 'l', N_("SIZE"), 0,
127   N_("Create file of the given SIZE"), GRP+1 },
128  {"file", 'f', N_("NAME"), 0,
129   N_("Write to file NAME, instead of standard output"), GRP+1},
130  {"files-from", 'T', N_("FILE"), 0,
131   N_("Read file names from FILE"), GRP+1},
132  {"null", '0', NULL, 0,
133   N_("-T reads null-terminated names"), GRP+1},
134  {"pattern", 'p', N_("PATTERN"), 0,
135   N_("Fill the file with the given PATTERN. PATTERN is 'default' or 'zeros'"),
136   GRP+1 },
137  {"block-size", 'b', N_("SIZE"), 0,
138   N_("Size of a block for sparse file"), GRP+1},
139  {"sparse", 's', NULL, 0,
140   N_("Generate sparse file. Rest of the command line gives the file map."),
141   GRP+1 },
142  {"seek", OPT_SEEK, N_("OFFSET"), 0,
143   N_("Seek to the given offset before writing data"),
144   GRP+1 },
145
146#undef GRP
147#define GRP 10
148  {NULL, 0, NULL, 0,
149   N_("File statistics options:"), GRP},
150
151  {"stat", 'S', N_("FORMAT"), OPTION_ARG_OPTIONAL,
152   N_("Print contents of struct stat for each given file. Default FORMAT is: ")
153   DEFAULT_STAT_FORMAT,
154   GRP+1 },
155
156#undef GRP
157#define GRP 20
158  {NULL, 0, NULL, 0,
159   N_("Synchronous execution options:"), GRP},
160
161  {"run", 'r', N_("COMMAND"), 0,
162   N_("Execute given COMMAND. Useful with --checkpoint and one of --cut, --append, --touch"),
163   GRP+1 },
164  {"checkpoint", OPT_CHECKPOINT, N_("NUMBER"), 0,
165   N_("Perform given action (see below) upon reaching checkpoint NUMBER"),
166   GRP+1 },
167  {"date", OPT_DATE, N_("STRING"), 0,
168   N_("Set date for next --touch option"),
169   GRP+1 },
170  {"verbose", OPT_VERBOSE, NULL, 0,
171   N_("Display executed checkpoints and exit status of COMMAND"),
172   GRP+1 },
173#undef GRP
174#define GRP 30
175  {NULL, 0, NULL, 0,
176   N_("Synchronous execution actions. These are executed when checkpoint number given by --checkpoint option is reached."), GRP},
177
178  {"cut", OPT_TRUNCATE, N_("FILE"), 0,
179   N_("Truncate FILE to the size specified by previous --length option (or 0, if it is not given)"),
180   GRP+1 },
181  {"truncate", 0, NULL, OPTION_ALIAS, NULL, GRP+1 },
182  {"append", OPT_APPEND, N_("FILE"), 0,
183   N_("Append SIZE bytes to FILE. SIZE is given by previous --length option."),
184   GRP+1 },
185  {"touch", OPT_TOUCH, N_("FILE"), 0,
186   N_("Update the access and modification times of FILE"),
187   GRP+1 },
188  {"exec", OPT_EXEC, N_("COMMAND"), 0,
189   N_("Execute COMMAND"),
190   GRP+1 },
191#undef GRP
192  { NULL, }
193};
194
195static char const * const pattern_args[] = { "default", "zeros", 0 };
196static enum pattern const pattern_types[] = {DEFAULT_PATTERN, ZEROS_PATTERN};
197
198static int
199xlat_suffix (off_t *vp, const char *p)
200{
201  off_t val = *vp;
202
203  if (p[1])
204    return 1;
205  switch (p[0])
206    {
207    case 'g':
208    case 'G':
209      *vp *= 1024;
210
211    case 'm':
212    case 'M':
213      *vp *= 1024;
214
215    case 'k':
216    case 'K':
217      *vp *= 1024;
218      break;
219
220    default:
221      return 1;
222    }
223  return *vp <= val;
224}
225
226static off_t
227get_size (const char *str, int allow_zero)
228{
229  const char *p;
230  off_t v = 0;
231
232  for (p = str; *p; p++)
233    {
234      int digit = *p - '0';
235      off_t x = v * 10;
236      if (9 < (unsigned) digit)
237	{
238	  if (xlat_suffix (&v, p))
239	    error (EXIT_FAILURE, 0, _("Invalid size: %s"), str);
240	  else
241	    break;
242	}
243      else if (x / 10 != v)
244	error (EXIT_FAILURE, 0, _("Number out of allowed range: %s"), str);
245      v = x + digit;
246      if (v < 0)
247	error (EXIT_FAILURE, 0, _("Negative size: %s"), str);
248    }
249  return v;
250}
251
252void
253verify_file (char *file_name)
254{
255  if (file_name)
256    {
257      struct stat st;
258
259      if (stat (file_name, &st))
260	error (0, errno, _("stat(%s) failed"), file_name);
261
262      if (st.st_size != file_length + seek_offset)
263	{
264	  printf ("%lu %lu\n", (unsigned long)st.st_size , (unsigned long)file_length);
265	  exit (1);
266	}
267
268      if (mode == mode_sparse && !ST_IS_SPARSE (st))
269	exit (1);
270    }
271}
272
273struct action
274{
275  struct action *next;
276  size_t checkpoint;
277  int action;
278  char *name;
279  off_t size;
280  enum pattern pattern;
281  struct timespec ts;
282};
283
284static struct action *action_list;
285
286void
287reg_action (int action, char *arg)
288{
289  struct action *act = xmalloc (sizeof (*act));
290  act->checkpoint = checkpoint;
291  act->action = action;
292  act->pattern = pattern;
293  act->ts = touch_time;
294  act->size = file_length;
295  act->name = arg;
296  act->next = action_list;
297  action_list = act;
298}
299
300static error_t
301parse_opt (int key, char *arg, struct argp_state *state)
302{
303  switch (key)
304    {
305    case '0':
306      filename_terminator = 0;
307      break;
308
309    case 'f':
310      file_name = arg;
311      break;
312
313    case 'l':
314      file_length = get_size (arg, 1);
315      break;
316
317    case 'p':
318      pattern = XARGMATCH ("--pattern", arg, pattern_args, pattern_types);
319      break;
320
321    case 'b':
322      block_size = get_size (arg, 0);
323      break;
324
325    case 's':
326      mode = mode_sparse;
327      break;
328
329    case 'S':
330      mode = mode_stat;
331      if (arg)
332	stat_format = arg;
333      break;
334
335    case 'r':
336      mode = mode_exec;
337      argcv_get (arg, "", NULL, &exec_argc, &exec_argv);
338      break;
339
340    case 'T':
341      files_from = arg;
342      break;
343
344    case OPT_SEEK:
345      seek_offset = get_size (arg, 0);
346      break;
347
348    case OPT_CHECKPOINT:
349      {
350	char *p;
351
352	checkpoint = strtoul (arg, &p, 0);
353	if (*p)
354	  argp_error (state, _("Error parsing number near `%s'"), p);
355      }
356      break;
357
358    case OPT_DATE:
359      if (!get_date (&touch_time, arg, NULL))
360	argp_error (state, _("Unknown date format"));
361      break;
362
363    case OPT_APPEND:
364    case OPT_TRUNCATE:
365    case OPT_TOUCH:
366    case OPT_EXEC:
367      reg_action (key, arg);
368      break;
369
370    case OPT_VERBOSE:
371      verbose++;
372      break;
373
374    default:
375      return ARGP_ERR_UNKNOWN;
376    }
377  return 0;
378}
379
380static struct argp argp = {
381  options,
382  parse_opt,
383  N_("[ARGS...]"),
384  doc,
385  NULL,
386  NULL,
387  NULL
388};
389
390
391void
392fill (FILE *fp, off_t length, enum pattern pattern)
393{
394  off_t i;
395
396  switch (pattern)
397    {
398    case DEFAULT_PATTERN:
399      for (i = 0; i < length; i++)
400	fputc (i & 255, fp);
401      break;
402
403    case ZEROS_PATTERN:
404      for (i = 0; i < length; i++)
405	fputc (0, fp);
406      break;
407    }
408}
409
410/* Generate Mode: usual files */
411static void
412generate_simple_file (char *filename)
413{
414  FILE *fp;
415
416  if (filename)
417    {
418      fp = fopen (filename, seek_offset ? "r+" : "w");
419      if (!fp)
420	error (EXIT_FAILURE, 0, _("cannot open `%s'"), filename);
421    }
422  else
423    fp = stdout;
424
425  if (fseeko (fp, seek_offset, 0))
426    error (EXIT_FAILURE, 0, _("cannot seek: %s"), strerror (errno));
427
428  fill (fp, file_length, pattern);
429
430  fclose (fp);
431}
432
433/* A simplified version of the same function from tar */
434int
435read_name_from_file (FILE *fp, struct obstack *stk)
436{
437  int c;
438  size_t counter = 0;
439
440  for (c = getc (fp); c != EOF && c != filename_terminator; c = getc (fp))
441    {
442      if (c == 0)
443	error (EXIT_FAILURE, 0, _("file name contains null character"));
444      obstack_1grow (stk, c);
445      counter++;
446    }
447
448  obstack_1grow (stk, 0);
449
450  return (counter == 0 && c == EOF);
451}
452
453void
454generate_files_from_list ()
455{
456  FILE *fp = strcmp (files_from, "-") ? fopen (files_from, "r") : stdin;
457  struct obstack stk;
458
459  if (!fp)
460    error (EXIT_FAILURE, errno, _("cannot open `%s'"), files_from);
461
462  obstack_init (&stk);
463  while (!read_name_from_file (fp, &stk))
464    {
465      char *name = obstack_finish (&stk);
466      generate_simple_file (name);
467      verify_file (name);
468      obstack_free (&stk, name);
469    }
470  fclose (fp);
471  obstack_free (&stk, NULL);
472}
473
474
475/* Generate Mode: sparse files */
476
477static void
478mkhole (int fd, off_t displ)
479{
480  if (lseek (fd, displ, SEEK_CUR) == -1)
481    error (EXIT_FAILURE, errno, "lseek");
482  ftruncate (fd, lseek (fd, 0, SEEK_CUR));
483}
484
485static void
486mksparse (int fd, off_t displ, char *marks)
487{
488  if (lseek (fd, displ, SEEK_CUR) == -1)
489    error (EXIT_FAILURE, errno, "lseek");
490
491  for (; *marks; marks++)
492    {
493      memset (buffer, *marks, block_size);
494      if (write (fd, buffer, block_size) != block_size)
495	error (EXIT_FAILURE, errno, "write");
496    }
497}
498
499static void
500generate_sparse_file (int argc, char **argv)
501{
502  int i;
503  int fd;
504  int flags = O_CREAT|O_RDWR;
505
506  if (!file_name)
507    error (EXIT_FAILURE, 0,
508	   _("cannot generate sparse files on standard output, use --file option"));
509  if (!seek_offset)
510    flags |= O_TRUNC;
511  fd = open (file_name, flags, 0644);
512  if (fd < 0)
513    error (EXIT_FAILURE, 0, _("cannot open `%s'"), file_name);
514
515  buffer = xmalloc (block_size);
516
517  file_length = 0;
518
519  for (i = 0; i < argc; i += 2)
520    {
521      off_t displ = get_size (argv[i], 1);
522      file_length += displ;
523
524      if (i == argc-1)
525	{
526	  mkhole (fd, displ);
527	  break;
528	}
529      else
530	{
531	  file_length += block_size * strlen (argv[i+1]);
532	  mksparse (fd, displ, argv[i+1]);
533	}
534    }
535
536  close (fd);
537}
538
539
540/* Status Mode */
541
542void
543print_time (time_t t)
544{
545  char buf[20]; /* ccyy-mm-dd HH:MM:SS\0 */
546  strftime (buf, sizeof buf, "%Y-%m-%d %H:%M:%S", gmtime (&t));
547  printf ("%s ", buf);
548}
549
550void
551print_stat (const char *name)
552{
553  char *fmt, *p;
554  struct stat st;
555  char buf[UINTMAX_STRSIZE_BOUND];
556
557  if (stat (name, &st))
558    {
559      error (0, errno, _("stat(%s) failed"), name);
560      return;
561    }
562
563  fmt = strdup (stat_format);
564  for (p = strtok (fmt, ","); p; )
565    {
566      if (memcmp (p, "st_", 3) == 0)
567	p += 3;
568      if (strcmp (p, "name") == 0)
569	printf ("%s", name);
570      else if (strcmp (p, "dev") == 0)
571	printf ("%lu", (unsigned long) st.st_dev);
572      else if (strcmp (p, "ino") == 0)
573	printf ("%lu", (unsigned long) st.st_ino);
574      else if (strncmp (p, "mode", 4) == 0)
575	{
576	  mode_t mask = ~0;
577
578	  if (ispunct (p[4]))
579	    {
580	      char *q;
581
582	      mask = strtoul (p + 5, &q, 8);
583	      if (*q)
584		{
585		  printf ("\n");
586		  error (EXIT_FAILURE, 0, _("incorrect mask (near `%s')"), q);
587		}
588	    }
589	  else if (p[4])
590	    {
591	      printf ("\n");
592	      error (EXIT_FAILURE, 0, _("Unknown field `%s'"), p);
593	    }
594	  printf ("%0o", st.st_mode & mask);
595	}
596      else if (strcmp (p, "nlink") == 0)
597	printf ("%lu", (unsigned long) st.st_nlink);
598      else if (strcmp (p, "uid") == 0)
599	printf ("%ld", (long unsigned) st.st_uid);
600      else if (strcmp (p, "gid") == 0)
601	printf ("%lu", (unsigned long) st.st_gid);
602      else if (strcmp (p, "size") == 0)
603	printf ("%s", umaxtostr (st.st_size, buf));
604      else if (strcmp (p, "blksize") == 0)
605	printf ("%s", umaxtostr (st.st_blksize, buf));
606      else if (strcmp (p, "blocks") == 0)
607	printf ("%s", umaxtostr (st.st_blocks, buf));
608      else if (strcmp (p, "atime") == 0)
609	printf ("%lu", (unsigned long) st.st_atime);
610      else if (strcmp (p, "atimeH") == 0)
611	print_time (st.st_atime);
612      else if (strcmp (p, "mtime") == 0)
613	printf ("%lu", (unsigned long) st.st_mtime);
614      else if (strcmp (p, "mtimeH") == 0)
615	print_time (st.st_mtime);
616      else if (strcmp (p, "ctime") == 0)
617	printf ("%lu", (unsigned long) st.st_ctime);
618      else if (strcmp (p, "ctimeH") == 0)
619	print_time (st.st_ctime);
620      else if (strcmp (p, "sparse") == 0)
621	printf ("%d", ST_IS_SPARSE (st));
622      else
623	{
624	  printf ("\n");
625	  error (EXIT_FAILURE, 0, _("Unknown field `%s'"), p);
626	}
627      p = strtok (NULL, ",");
628      if (p)
629	printf (" ");
630    }
631  printf ("\n");
632  free (fmt);
633}
634
635
636/* Exec Mode */
637
638void
639exec_checkpoint (struct action *p)
640{
641  if (verbose)
642    printf ("processing checkpoint %lu\n", (unsigned long) p->checkpoint);
643  switch (p->action)
644    {
645    case OPT_TOUCH:
646      {
647	struct timespec ts[2];
648
649	ts[0] = ts[1] = p->ts;
650	if (utimens (p->name, ts) != 0)
651	  {
652	    error (0, errno, _("cannot set time on `%s'"), p->name);
653	    break;
654	  }
655      }
656      break;
657
658    case OPT_APPEND:
659      {
660	FILE *fp = fopen (p->name, "a");
661	if (!fp)
662	  {
663	    error (0, errno, _("cannot open `%s'"), p->name);
664	    break;
665	  }
666
667	fill (fp, p->size, p->pattern);
668	fclose (fp);
669      }
670      break;
671
672    case OPT_TRUNCATE:
673      {
674	int fd = open (p->name, O_RDWR);
675	if (fd == -1)
676	  {
677	    error (0, errno, _("cannot open `%s'"), p->name);
678	    break;
679	  }
680	ftruncate (fd, p->size);
681	close (fd);
682      }
683      break;
684
685    case OPT_EXEC:
686      system (p->name);
687      break;
688
689    default:
690      abort ();
691    }
692}
693
694void
695process_checkpoint (size_t n)
696{
697  struct action *p, *prev = NULL;
698
699  for (p = action_list; p; )
700    {
701      struct action *next = p->next;
702
703      if (p->checkpoint <= n)
704	{
705	  exec_checkpoint (p);
706	  /* Remove the item from the list */
707	  if (prev)
708	    prev->next = next;
709	  else
710	    action_list = next;
711	  free (p);
712	}
713      else
714	prev = p;
715
716      p = next;
717    }
718}
719
720#define CHECKPOINT_TEXT "Write checkpoint"
721
722void
723exec_command (void)
724{
725  int status;
726  pid_t pid;
727  int fd[2];
728  char *p;
729  FILE *fp;
730  char buf[128];
731
732  /* Insert --checkpoint option.
733     FIXME: This assumes that exec_argv does not use traditional tar options
734     (without dash) */
735  exec_argc++;
736  exec_argv = xrealloc (exec_argv, (exec_argc + 1) * sizeof (*exec_argv));
737  memmove (exec_argv+2, exec_argv+1, (exec_argc - 1) * sizeof (*exec_argv));
738  exec_argv[1] = "--checkpoint";
739
740#ifdef SIGCHLD
741  /* System V fork+wait does not work if SIGCHLD is ignored.  */
742  signal (SIGCHLD, SIG_DFL);
743#endif
744
745  pipe (fd);
746
747  pid = fork ();
748  if (pid == -1)
749    error (EXIT_FAILURE, errno, "fork");
750
751  if (pid == 0)
752    {
753      /* Child */
754
755      /* Pipe stderr */
756      if (fd[1] != 2)
757	dup2 (fd[1], 2);
758      close (fd[0]);
759
760      /* Make sure POSIX locale is used */
761      setenv ("LC_ALL", "POSIX", 1);
762
763      execvp (exec_argv[0], exec_argv);
764      error (EXIT_FAILURE, errno, "execvp");
765    }
766
767  /* Master */
768  close (fd[1]);
769  fp = fdopen (fd[0], "r");
770  if (fp == NULL)
771    error (EXIT_FAILURE, errno, "fdopen");
772
773  while ((p = fgets (buf, sizeof buf, fp)))
774    {
775      while (*p && !isspace (*p) && *p != ':')
776	p++;
777
778      if (*p == ':')
779	{
780	  for (p++; *p && isspace (*p); p++)
781	    ;
782
783	  if (*p
784	      && memcmp (p, CHECKPOINT_TEXT, sizeof CHECKPOINT_TEXT - 1) == 0)
785	    {
786	      char *end;
787	      size_t n = strtoul (p + sizeof CHECKPOINT_TEXT - 1, &end, 10);
788	      if (!(*end && !isspace (*end)))
789		{
790		  process_checkpoint (n);
791		  continue;
792		}
793	    }
794	}
795      fprintf (stderr, "%s", buf);
796    }
797
798  /* Collect exit status */
799  waitpid (pid, &status, 0);
800
801  if (verbose)
802    {
803      if (WIFEXITED (status))
804	{
805	  if (WEXITSTATUS (status) == 0)
806	    printf (_("Command exited successfully\n"));
807	  else
808	    printf (_("Command failed with status %d\n"),
809		    WEXITSTATUS (status));
810	}
811      else if (WIFSIGNALED (status))
812	printf (_("Command terminated on signal %d\n"), WTERMSIG (status));
813      else if (WIFSTOPPED (status))
814	printf (_("Command stopped on signal %d\n"), WSTOPSIG (status));
815#ifdef WCOREDUMP
816      else if (WCOREDUMP (status))
817	printf (_("Command dumped core\n"));
818#endif
819      else
820	printf(_("Command terminated\n"));
821    }
822
823  if (WIFEXITED (status))
824    exit (WEXITSTATUS (status));
825  exit (EXIT_FAILURE);
826}
827
828int
829main (int argc, char **argv)
830{
831  int index;
832
833  program_name = argv[0];
834  setlocale (LC_ALL, "");
835  bindtextdomain (PACKAGE, LOCALEDIR);
836  textdomain (PACKAGE);
837
838  get_date (&touch_time, "now", NULL);
839
840  /* Decode command options.  */
841
842  if (argp_parse (&argp, argc, argv, 0, &index, NULL))
843    exit (EXIT_FAILURE);
844
845  argc -= index;
846  argv += index;
847
848  switch (mode)
849    {
850    case mode_stat:
851      if (argc == 0)
852	error (EXIT_FAILURE, 0, _("--stat requires file names"));
853
854      while (argc--)
855	print_stat (*argv++);
856      break;
857
858    case mode_sparse:
859      generate_sparse_file (argc, argv);
860      verify_file (file_name);
861      break;
862
863    case mode_generate:
864      if (argc)
865	error (EXIT_FAILURE, 0, _("too many arguments"));
866      if (files_from)
867	generate_files_from_list ();
868      else
869	{
870	  generate_simple_file (file_name);
871	  verify_file (file_name);
872	}
873      break;
874
875    case mode_exec:
876      exec_command ();
877      break;
878
879    default:
880      /* Just in case */
881      abort ();
882    }
883  exit (EXIT_SUCCESS);
884}
885