re-mark 1.29b-2 as not yet uploaded (merge madness!)
[debian/tar] / tests / genfile.c
index 1b338bce2f95a77635abe7f7f6926b53802d4395..219fb5e9ed90d702674c74cac6956dc4a0d11d0d 100644 (file)
@@ -2,7 +2,7 @@
    Print statistics for existing files.
 
    Copyright (C) 1995, 1996, 1997, 2001, 2003, 2004, 2005, 2006, 2007,
-   2008 Free Software Foundation, Inc.
+   2008, 2009, 2016 Free Software Foundation, Inc.
 
    François Pinard <pinard@iro.umontreal.ca>, 1995.
    Sergey Poznyakoff <gray@mirddin.farlep.net>, 2004, 2005, 2006, 2007, 2008.
 #include <argmatch.h>
 #include <argp.h>
 #include <argcv.h>
-#include <getdate.h>
-#include <utimens.h>
+#include <parse-datetime.h>
 #include <inttostr.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <c-ctype.h>
 #define obstack_chunk_alloc malloc
 #define obstack_chunk_free free
 #include <obstack.h>
@@ -99,6 +99,7 @@ char *buffer;
 /* Number of arguments and argument vector for mode == mode_exec */
 int exec_argc;
 char **exec_argv;
+char *checkpoint_option;
 
 /* Time for --touch option */
 struct timespec touch_time;
@@ -106,6 +107,9 @@ struct timespec touch_time;
 /* Verbose mode */
 int verbose;
 
+/* Quiet mode */
+int quiet;
+
 const char *argp_program_version = "genfile (" PACKAGE ") " VERSION;
 const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
 static char doc[] = N_("genfile manipulates data files for GNU paxutils test suite.\n"
@@ -119,6 +123,7 @@ static char doc[] = N_("genfile manipulates data files for GNU paxutils test sui
 #define OPT_DATE       261
 #define OPT_VERBOSE    262
 #define OPT_SEEK       263
+#define OPT_UNLINK     264
 
 static struct argp_option options[] = {
 #define GRP 0
@@ -143,7 +148,8 @@ static struct argp_option options[] = {
   {"seek", OPT_SEEK, N_("OFFSET"), 0,
    N_("Seek to the given offset before writing data"),
    GRP+1 },
-
+  {"quiet", 'q', NULL, 0,
+   N_("Suppress non-fatal diagnostic messages") },
 #undef GRP
 #define GRP 10
   {NULL, 0, NULL, 0,
@@ -159,8 +165,8 @@ static struct argp_option options[] = {
   {NULL, 0, NULL, 0,
    N_("Synchronous execution options:"), GRP},
 
-  {"run", 'r', N_("COMMAND"), 0,
-   N_("Execute given COMMAND. Useful with --checkpoint and one of --cut, --append, --touch"),
+  {"run", 'r', N_("OPTION"), OPTION_ARG_OPTIONAL,
+   N_("Execute ARGS. Useful with --checkpoint and one of --cut, --append, --touch, --unlink"),
    GRP+1 },
   {"checkpoint", OPT_CHECKPOINT, N_("NUMBER"), 0,
    N_("Perform given action (see below) upon reaching checkpoint NUMBER"),
@@ -189,6 +195,9 @@ static struct argp_option options[] = {
   {"exec", OPT_EXEC, N_("COMMAND"), 0,
    N_("Execute COMMAND"),
    GRP+1 },
+  {"unlink", OPT_UNLINK, N_("FILE"), 0,
+   N_("Unlink FILE"),
+   GRP+1 },
 #undef GRP
   { NULL, }
 };
@@ -261,11 +270,11 @@ verify_file (char *file_name)
        error (0, errno, _("stat(%s) failed"), file_name);
 
       if (st.st_size != file_length + seek_offset)
-       error (1, 0, _("requested file length %lu, actual %lu"),
+       error (EXIT_FAILURE, 0, _("requested file length %lu, actual %lu"),
               (unsigned long)st.st_size, (unsigned long)file_length);
 
-      if (mode == mode_sparse && !ST_IS_SPARSE (st))
-       error (1, 0, _("created file is not sparse"));
+      if (!quiet && mode == mode_sparse && !ST_IS_SPARSE (st))
+       error (EXIT_FAILURE, 0, _("created file is not sparse"));
     }
 }
 
@@ -321,6 +330,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
       block_size = get_size (arg, 0);
       break;
 
+    case 'q':
+      quiet = 1;
+      break;
+      
     case 's':
       mode = mode_sparse;
       break;
@@ -333,7 +346,11 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
     case 'r':
       mode = mode_exec;
-      argcv_get (arg, "", NULL, &exec_argc, &exec_argv);
+      if (arg)
+       {
+         argcv_get (arg, "", NULL, &exec_argc, &exec_argv);
+         checkpoint_option = "--checkpoint";
+       }
       break;
 
     case 'T':
@@ -355,7 +372,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
       break;
 
     case OPT_DATE:
-      if (!get_date (&touch_time, arg, NULL))
+      if (! parse_datetime (&touch_time, arg, NULL))
        argp_error (state, _("Unknown date format"));
       break;
 
@@ -363,6 +380,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
     case OPT_TRUNCATE:
     case OPT_TOUCH:
     case OPT_EXEC:
+    case OPT_UNLINK:
       reg_action (key, arg);
       break;
 
@@ -476,9 +494,11 @@ generate_files_from_list ()
 static void
 mkhole (int fd, off_t displ)
 {
-  if (lseek (fd, displ, SEEK_CUR) == -1)
+  off_t offset = lseek (fd, displ, SEEK_CUR);
+  if (offset < 0)
     error (EXIT_FAILURE, errno, "lseek");
-  ftruncate (fd, lseek (fd, 0, SEEK_CUR));
+  if (ftruncate (fd, offset) != 0)
+    error (EXIT_FAILURE, errno, "ftruncate");
 }
 
 static void
@@ -495,10 +515,56 @@ mksparse (int fd, off_t displ, char *marks)
     }
 }
 
+static int
+make_fragment (int fd, char *offstr, char *mapstr)
+{
+  int i;
+  off_t displ = get_size (offstr, 1);
+
+  file_length += displ;
+
+  if (!mapstr || !*mapstr)
+    {
+      mkhole (fd, displ);
+      return 1;
+    }
+  else if (*mapstr == '=')
+    {
+      off_t n = get_size (mapstr + 1, 1);
+
+      switch (pattern)
+       {
+       case DEFAULT_PATTERN:
+         for (i = 0; i < block_size; i++)
+           buffer[i] = i & 255;
+         break;
+         
+       case ZEROS_PATTERN:
+         memset (buffer, 0, block_size);
+         break;
+       }
+
+      if (lseek (fd, displ, SEEK_CUR) == -1)
+       error (EXIT_FAILURE, errno, "lseek");
+      
+      for (; n; n--)
+       {
+         if (write (fd, buffer, block_size) != block_size)
+           error (EXIT_FAILURE, errno, "write");
+         file_length += block_size;
+       }
+    }
+  else
+    {
+      file_length += block_size * strlen (mapstr);
+      mksparse (fd, displ, mapstr);
+    }
+  return 0;
+}
+
 static void
 generate_sparse_file (int argc, char **argv)
 {
-  int i;
   int fd;
   int flags = O_CREAT | O_RDWR | O_BINARY;
 
@@ -515,20 +581,33 @@ generate_sparse_file (int argc, char **argv)
 
   file_length = 0;
 
-  for (i = 0; i < argc; i += 2)
+  while (argc)
     {
-      off_t displ = get_size (argv[i], 1);
-      file_length += displ;
-
-      if (i == argc-1)
+      if (argv[0][0] == '-' && argv[0][1] == 0)
        {
-         mkhole (fd, displ);
-         break;
+         char buf[256];
+         while (fgets (buf, sizeof (buf), stdin))
+           {
+             size_t n = strlen (buf);
+
+             while (n > 0 && c_isspace (buf[n-1]))
+               buf[--n] = 0;
+             
+             n = strcspn (buf, " \t");
+             buf[n++] = 0;
+             while (buf[n] && c_isblank (buf[n]))
+               ++n;
+             make_fragment (fd, buf, buf + n);
+           }
+         ++argv;
+         --argc;
        }
       else
        {
-         file_length += block_size * strlen (argv[i+1]);
-         mksparse (fd, displ, argv[i+1]);
+         if (make_fragment (fd, argv[0], argv[1]))
+           break;
+         argc -= 2;
+         argv += 2;
        }
     }
 
@@ -572,13 +651,13 @@ print_stat (const char *name)
        printf ("%lu", (unsigned long) st.st_ino);
       else if (strncmp (p, "mode", 4) == 0)
        {
-         mode_t mask = ~0;
+         unsigned val = st.st_mode;
 
-         if (ispunct (p[4]))
+         if (ispunct ((unsigned char) p[4]))
            {
              char *q;
 
-             mask = strtoul (p + 5, &q, 8);
+             val &= strtoul (p + 5, &q, 8);
              if (*q)
                {
                  printf ("\n");
@@ -590,7 +669,7 @@ print_stat (const char *name)
              printf ("\n");
              error (EXIT_FAILURE, 0, _("Unknown field `%s'"), p);
            }
-         printf ("%0o", st.st_mode & mask);
+         printf ("%0o", val);
        }
       else if (strcmp (p, "nlink") == 0)
        printf ("%lu", (unsigned long) st.st_nlink);
@@ -646,7 +725,7 @@ exec_checkpoint (struct action *p)
        struct timespec ts[2];
 
        ts[0] = ts[1] = p->ts;
-       if (utimens (p->name, ts) != 0)
+       if (utimensat (AT_FDCWD, p->name, ts, 0) != 0)
          {
            error (0, errno, _("cannot set time on `%s'"), p->name);
            break;
@@ -676,13 +755,23 @@ exec_checkpoint (struct action *p)
            error (0, errno, _("cannot open `%s'"), p->name);
            break;
          }
-       ftruncate (fd, p->size);
+       if (ftruncate (fd, p->size) != 0)
+         {
+           error (0, errno, _("cannot truncate `%s'"), p->name);
+           break;
+         }
        close (fd);
       }
       break;
 
     case OPT_EXEC:
-      system (p->name);
+      if (system (p->name) != 0)
+       error (0, 0, _("command failed: %s"), p->name);
+      break;
+
+    case OPT_UNLINK:
+      if (unlink (p->name))
+       error (0, errno, _("cannot unlink `%s'"), p->name);
       break;
 
     default:
@@ -730,18 +819,25 @@ exec_command (void)
 
   /* Insert --checkpoint option.
      FIXME: This assumes that exec_argv does not use traditional tar options
-     (without dash) */
-  exec_argc++;
-  exec_argv = xrealloc (exec_argv, (exec_argc + 1) * sizeof (*exec_argv));
-  memmove (exec_argv+2, exec_argv+1, (exec_argc - 1) * sizeof (*exec_argv));
-  exec_argv[1] = "--checkpoint";
+     (without dash).
+     FIXME: There is no way to set checkpoint argument (granularity).
+  */
+  if (checkpoint_option)
+    {
+      exec_argc++;
+      exec_argv = xrealloc (exec_argv, (exec_argc + 1) * sizeof (*exec_argv));
+      memmove (exec_argv+2, exec_argv+1,
+              (exec_argc - 1) * sizeof (*exec_argv));
+      exec_argv[1] = checkpoint_option;
+    }
 
 #ifdef SIGCHLD
   /* System V fork+wait does not work if SIGCHLD is ignored.  */
   signal (SIGCHLD, SIG_DFL);
 #endif
 
-  pipe (fd);
+  if (pipe (fd) != 0)
+    error (EXIT_FAILURE, errno, "pipe");
 
   pid = fork ();
   if (pid == -1)
@@ -760,7 +856,7 @@ exec_command (void)
       setenv ("LC_ALL", "POSIX", 1);
 
       execvp (exec_argv[0], exec_argv);
-      error (EXIT_FAILURE, errno, "execvp");
+      error (EXIT_FAILURE, errno, "execvp %s", exec_argv[0]);
     }
 
   /* Master */
@@ -771,12 +867,12 @@ exec_command (void)
 
   while ((p = fgets (buf, sizeof buf, fp)))
     {
-      while (*p && !isspace (*p) && *p != ':')
+      while (*p && !isspace ((unsigned char) *p) && *p != ':')
        p++;
 
       if (*p == ':')
        {
-         for (p++; *p && isspace (*p); p++)
+         for (p++; *p && isspace ((unsigned char) *p); p++)
            ;
 
          if (*p
@@ -784,7 +880,7 @@ exec_command (void)
            {
              char *end;
              size_t n = strtoul (p + sizeof CHECKPOINT_TEXT - 1, &end, 10);
-             if (!(*end && !isspace (*end)))
+             if (!(*end && !isspace ((unsigned char) *end)))
                {
                  process_checkpoint (n);
                  continue;
@@ -834,7 +930,7 @@ main (int argc, char **argv)
   bindtextdomain (PACKAGE, LOCALEDIR);
   textdomain (PACKAGE);
 
-  get_date (&touch_time, "now", NULL);
+  parse_datetime (&touch_time, "now", NULL);
 
   /* Decode command options.  */
 
@@ -872,6 +968,13 @@ main (int argc, char **argv)
       break;
 
     case mode_exec:
+      if (!checkpoint_option)
+       {
+         exec_argc = argc;
+         exec_argv = argv;
+       }
+      else if (argc)
+       error (EXIT_FAILURE, 0, _("too many arguments"));
       exec_command ();
       break;