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