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