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