summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2005-05-12 14:57:14 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2005-05-12 14:57:14 +0000
commitccd209a0390cdd95f75bcb0be6db5bc26e2d8a6c (patch)
tree2093c9404e97ed633140de578b08b015f7da0a70
parentda85d32d80aee17965326da78913d0b509e75f53 (diff)
downloadpaxutils-ccd209a0390cdd95f75bcb0be6db5bc26e2d8a6c.tar.gz
Add new files
-rw-r--r--doc/DISTFILES1
-rw-r--r--doc/genfile.texi284
-rw-r--r--tests/DISTFILES2
-rw-r--r--tests/argcv.c396
-rw-r--r--tests/argcv.h50
5 files changed, 733 insertions, 0 deletions
diff --git a/doc/DISTFILES b/doc/DISTFILES
new file mode 100644
index 0000000..954ea44
--- /dev/null
+++ b/doc/DISTFILES
@@ -0,0 +1 @@
+genfile.texi
diff --git a/doc/genfile.texi b/doc/genfile.texi
new file mode 100644
index 0000000..dd63031
--- /dev/null
+++ b/doc/genfile.texi
@@ -0,0 +1,284 @@
+@c This is part of the paxutils manual.
+@c Copyright (C) 2005 Free Software Foundation, Inc.
+@c Written by Sergey Poznyakoff
+@c This file is distributed under GFDL 1.1 or any later version
+@c published by the Free Software Foundation.
+
+@cindex genfile
+ This appendix describes @command{genfile}, an auxiliary program
+used in the GNU tar testsuite. If you are not interested in developing
+GNU tar, skip this appendix.
+
+ Initially, @command{genfile} was used to generate data files for
+the testsuite, hence its name. However, new operation modes were being
+implemented as the testsuite grew more sophisticated, and now
+@command{genfile} is a multi-purpose instrument.
+
+ There are three basic operation modes:
+
+@table @asis
+@item File Generation
+ This is the default mode. In this mode, @command{genfile}
+generates data files.
+
+@item File Status
+ In this mode @command{genfile} displays status of specified files.
+
+@item Synchronous Execution.
+ In this mode @command{genfile} executes the given program with
+@option{--checkpoint} option and executes a set of actions when
+specified checkpoints are reached.
+@end table
+
+@menu
+* Generate Mode:: File Generation Mode.
+* Status Mode:: File Status Mode.
+* Exec Mode:: Synchronous Execution mode.
+@end menu
+
+@node Generate Mode
+@appendixsec Generate Mode
+
+@cindex Generate Mode, @command{genfile}
+@cindex @command{genfile}, generate mode
+@cindex @command{genfile}, create file
+ In this mode @command{genfile} creates a data file for the test
+suite. The size of the file is given with the @option{--length}
+(@option{-l}) option. By default the file contents is written to the
+standard output, this can be changed using @option{--file}
+(@option{-f}) command line option. Thus, the following two commands
+are equivalent:
+
+@smallexample
+genfile --length 100 > outfile
+genfile --length 100 --file outfile
+@end smallexample
+
+ If @option{--length} is not given, @command{genfile} will
+generate an empty (zero-length) file.
+
+@cindex pattern, @command{genfile}
+ The default data pattern for filling the generated file consists
+of first 256 letters of ASCII code, repeated enough times to fill the
+entire file. This behavior can be changed with @option{--pattern}
+option. This option takes a mandatory argument, specifying pattern
+name to use. Currently two patterns are implemented:
+
+@table @option
+@item --pattern=default
+ The default pattern as described above.
+
+@item --pattern=zero
+ Fills the file with zeroes.
+@end table
+
+@cindex Sparse files, creating using @command{genfile}
+@cindex @command{genfile}, creating sparse files
+ Special option @option{--sparse} (@option{-s}) instructs
+@command{genfile} to create a sparse file. Sparse files consist of
+@dfn{data fragments}, separated by @dfn{holes} or blocks of zeros. On
+many operating systems, actual disk storage is not allocated for
+holes, but they are counted in the length of the file. To create a
+sparse file @command{genfile} should know where to put data fragments,
+and what data to use to fill them. So, when @option{--sparse} is given
+the rest of the command line specifies a so-called @dfn{file map}.
+
+ The file map consists of any number of @dfn{fragment
+descriptors}. Each descriptor is composed of two values: a number,
+specifying fragment offset in the file, and @dfn{contents string},
+i.e. a string of characters, specifying the pattern to fill the
+fragment with. File offset can be suffixed with the following
+quantifiers:
+
+@table @samp
+@item k
+@itemx K
+The number is expressed in kilobytes.
+@item m
+@itemx M
+The number is expressed in megabytes.
+@item g
+@itemx G
+The number is expressed in gigabytes.
+@end table
+
+ For each letter in contents string @command{genfile} will generate
+a @dfn{block} of data, filled with this letter and will write it to
+the fragment. The size of block is given by @option{--block-size}
+option. It defaults to 512. Thus, if the string consists of @var{n}
+characters, the resulting file fragment will contain
+@code{@var{n}*@var{block-size}} of data.
+
+ Last fragment descriptor can have only file offset part. In this
+case @command{genfile} will create a hole at the end of the file up to
+the given offset.
+
+ For example, consider the following invocation:
+
+@smallexample
+genfile --sparse --file sparsefile 0 ABCD 1M EFGHI 2000K
+@end smallexample
+
+@noindent
+It will create a 2048000-bytes long file of the following structure:
+
+@multitable @columnfractions .35 .20 .45
+@item Offset @tab Length @tab Contents
+@item 0 @tab 4*512=2048 @tab Four 512-byte blocks, filled with
+letters @samp{A}, @samp{B}, @samp{C} and @samp{D}.
+@item 2048 @tab 1046528 @tab Zero bytes
+@item 1048576 @tab 5*512=2560 @tab Five blocks, filled with letters
+@samp{E}, @samp{F}, @samp{G}, @samp{H}, @samp{I}.
+@item 1051136 @tab 996864 @tab Zero bytes
+@end multitable
+
+@node Status Mode
+@appendixsec Status Mode
+
+ In status mode, @command{genfile} prints file system status for
+each file specified in the command line. This mode is toggled by
+@option{--stat} (@option{-S}) command line option. An optional argument to this
+option specifies output @dfn{format}: a comma-separated list of
+@code{struct stat} fields to be displayed. This list can contain
+following identifiers @FIXME{should we also support @samp{%} notations
+as in stat(1)??}:
+
+@table @asis
+@item name
+ The file name.
+
+@item dev
+@itemx st_dev
+ Device number in decimal.
+
+@item ino
+@itemx st_ino
+ Inode number.
+
+@item mode
+@itemx st_mode
+ File mode in octal.
+
+@item nlink
+@itemx st_nlink
+ Number of hard links.
+
+@item uid
+@itemx st_uid
+ User ID of owner.
+
+@item gid
+@itemx st_gid
+ Group ID of owner.
+
+@item size
+@itemx st_size
+ File size in decimal.
+
+@item blksize
+@itemx st_blksize
+ The size in bytes of each file block.
+
+@item blocks
+@itemx st_blocks
+ Number of blocks allocated.
+
+@item atime
+@itemx st_atime
+ Time of last access.
+
+@item mtime
+@itemx st_mtime
+ Time of last modification
+
+@item ctime
+@itemx st_ctime
+ Time of last status change
+@end table
+
+ Modification times are displayed in @acronym{UTC} as
+@acronym{UNIX} timestamps, unless suffixed with @samp{H} (for
+``human-readable''), as in @samp{ctimeH}, in which case usual
+@code{tar tv} output format is used.
+
+ The default output format is: @samp{name,dev,ino,mode,
+nlink,uid,gid,size,blksize,blocks,atime,mtime,ctime}.
+
+ For example, the following command will display file names and
+corresponding times of last access for each file in the current working
+directory:
+
+@smallexample
+genfile --stat=name,atime *
+@end smallexample
+
+@node Exec Mode
+@appendixsec Exec Mode
+
+@cindex Exec Mode, @command{genfile}
+ This mode is designed for testing the behavior of @code{paxutils}
+commands when some of the files change during archiving. It is an
+experimental mode.
+
+ The @samp{Exec Mode} is toggled by @option{--run} command line
+option (or its alias @option{-r}). The argument to this option gives
+the command line to be executed. The actual command line is
+constructed by insertin @option{--checkpoint} option between the
+command name and its first argument (if any). Due to this, the
+argument to @option{--run} may not use traditional @command{tar}
+option syntax, i.e. the following is wrong:
+
+@smallexample
+# Wrong!
+genfile --run 'tar cf foo bar'
+@end smallexample
+
+@noindent
+
+Use the following syntax instead:
+
+@smallexample
+genfile --run 'tar -cf foo bar'
+@end smallexample
+
+ The rest of command line after @option{--run} or its equivalent
+specifies checkpoint values and actions to be executed upon reaching
+them. Checkpoint values are introduced with @option{--checkpoint}
+command line option. Argument to this option is the number of
+checkpoint in decimal.
+
+ Any number of @dfn{actions} may be specified after a
+checkpoint. Available actions are
+
+@table @option
+@item --cut @var{file}
+@itemx --truncate @var{file}
+ Truncate @var{file} to the size specified by previous
+@option{--length} option (or 0, if it is not given).
+
+@item --append @var{file}
+ Append data to @var{file}. The size of data and its pattern are
+given by previous @option{--length} and @option{pattern} options.
+
+@item --touch @var{file}
+ Update the access and modification times of @var{file}. These
+timestamps are changed to the current time, unless @option{--date}
+option was given, in which case they are changed to the specified
+time. Argument to @option{--date} option is a date specification in
+almost arbitrary format (@pxref{Date input formats}).
+
+@item --exec @var{command}
+ Execute given shell command.
+
+@end table
+
+ Option @option{--verbose} instructs @command{genfile} to print on
+standard output notifications about checkpoints being executed and to
+verbosely describe exit status of the command.
+
+ While the command is being execute its standard output remains
+connected to descriptor 1. All messages it prints to file descriptor
+2, except checkpoint notifications, are forwarded to standard
+error.
+
+ @command{Genfile} exits with the exit status of the executed command.
diff --git a/tests/DISTFILES b/tests/DISTFILES
index b8d3d25..5e2e987 100644
--- a/tests/DISTFILES
+++ b/tests/DISTFILES
@@ -1 +1,3 @@
genfile.c
+argcv.c
+argcv.h
diff --git a/tests/argcv.c b/tests/argcv.c
new file mode 100644
index 0000000..be04f83
--- /dev/null
+++ b/tests/argcv.c
@@ -0,0 +1,396 @@
+/* argcv.c - simple functions for parsing input based on whitespace
+ Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <ctype.h>
+
+#include <mailutils/argcv.h>
+
+/*
+ * takes a string and splits it into several strings, breaking at ' '
+ * command is the string to split
+ * the number of strings is placed into argc
+ * the split strings are put into argv
+ * returns 0 on success, nonzero on failure
+ */
+
+#define isws(c) ((c)==' '||(c)=='\t'||(c)=='\n')
+#define isdelim(c,delim) ((c)=='"'||strchr(delim,(c))!=NULL)
+
+static int
+argcv_scan (int len, const char *command, const char *delim, const char* cmnt,
+ int *start, int *end, int *save)
+{
+ int i = 0;
+
+ for (;;)
+ {
+ i = *save;
+
+ if (i >= len)
+ return i + 1;
+
+ /* Skip initial whitespace */
+ while (i < len && isws (command[i]))
+ i++;
+ *start = i;
+
+ switch (command[i])
+ {
+ case '"':
+ case '\'':
+ while (++i < len
+ && (command[i] != command[*start]
+ || command[i-1] == '\\'))
+ ;
+ if (i < len) /* found matching quote */
+ break;
+ /*FALLTHRU*/ default:
+ if (isdelim (command[i], delim))
+ break;
+ /* Skip until next whitespace character or end of line. Honor
+ escaped whitespace. */
+ while (++i < len &&
+ !((isws (command[i]) && command[i-1] != '\\')
+ || isdelim (command[i], delim)));
+ i--;
+ break;
+ }
+
+ *end = i;
+ *save = i + 1;
+
+ /* If we have a token, and it starts with a comment character, skip
+ to the newline and restart the token search. */
+ if (*save <= len)
+ {
+ if (cmnt && strchr (cmnt, command[*start]) != NULL)
+ {
+ i = *save;
+ while (i < len && command[i] != '\n')
+ i++;
+
+ *save = i;
+ continue;
+ }
+ }
+ break;
+ }
+ return *save;
+}
+
+static char escape_transtab[] = "\\\\a\ab\bf\fn\nr\rt\t";
+
+int
+argcv_unescape_char (int c)
+{
+ char *p;
+
+ for (p = escape_transtab; *p; p += 2)
+ {
+ if (*p == c)
+ return p[1];
+ }
+ return c;
+}
+
+int
+argcv_escape_char (int c)
+{
+ char *p;
+
+ for (p = escape_transtab + sizeof(escape_transtab) - 2;
+ p > escape_transtab; p -= 2)
+ {
+ if (*p == c)
+ return p[-1];
+ }
+ return -1;
+}
+
+
+static int
+xtonum (const char *src, int base, size_t cnt)
+{
+ int val;
+ char *p;
+ char tmp[4]; /* At most three characters + zero */
+
+ /* Notice: No use to check `cnt'. It should be either 2 or 3 */
+ memcpy (tmp, src, cnt);
+ tmp[cnt] = 0;
+ val = strtoul (tmp, &p, base);
+ return (*p == 0) ? val : -1;
+}
+
+static size_t
+escaped_length (const char *str, int *quote)
+{
+ size_t len = 0;
+
+ for (; *str; str++)
+ {
+ if (*str == ' ')
+ {
+ len++;
+ *quote = 1;
+ }
+ else if (*str == '"')
+ {
+ len += 2;
+ *quote = 1;
+ }
+ else if (isprint (*str))
+ len++;
+ else if (argcv_escape_char (*str) != -1)
+ len += 2;
+ else
+ len += 4;
+ }
+ return len;
+}
+
+static void
+unescape_copy (char *dst, const char *src, size_t n)
+{
+ int c;
+
+ while (n > 0)
+ {
+ n--;
+ if (*src == '\\')
+ {
+ switch (*++src)
+ {
+ case 'x':
+ case 'X':
+ ++src;
+ --n;
+ if (n == 0)
+ {
+ *dst++ = '\\';
+ *dst++ = src[-1];
+ }
+ else
+ {
+ c = xtonum(src, 16, 2);
+ if (c == -1)
+ {
+ *dst++ = '\\';
+ *dst++ = src[-1];
+ }
+ else
+ {
+ *dst++ = c;
+ src += 2;
+ n -= 2;
+ }
+ }
+ break;
+
+ case '0':
+ ++src;
+ --n;
+ if (n == 0)
+ {
+ *dst++ = '\\';
+ *dst++ = src[-1];
+ }
+ else
+ {
+ c = xtonum(src, 8, 3);
+ if (c == -1)
+ {
+ *dst++ = '\\';
+ *dst++ = src[-1];
+ }
+ else
+ {
+ *dst++ = c;
+ src += 3;
+ n -= 3;
+ }
+ }
+ break;
+
+ default:
+ *dst++ = argcv_unescape_char (*src++);
+ n--;
+ }
+ }
+ else
+ {
+ *dst++ = *src++;
+ }
+ }
+ *dst = 0;
+}
+
+static void
+escape_copy (char *dst, const char *src)
+{
+ for (; *src; src++)
+ {
+ if (*src == '"')
+ {
+ *dst++ = '\\';
+ *dst++ = '"';
+ }
+ else if (*src != '\t' && isprint(*src))
+ *dst++ = *src;
+ else
+ {
+ int c = argcv_escape_char (*src);
+ *dst++ = '\\';
+ if (c != -1)
+ *dst++ = c;
+ else
+ {
+ char tmp[4];
+ snprintf (tmp, sizeof tmp, "%03o", *(unsigned char*)src);
+ memcpy (dst, tmp, 3);
+ dst += 3;
+ }
+ }
+ }
+}
+
+int
+argcv_get (const char *command, const char *delim, const char* cmnt,
+ int *argc, char ***argv)
+{
+ int len = strlen (command);
+ int i = 0;
+ int start, end, save;
+
+ *argv = NULL;
+
+ /* Count number of arguments */
+ *argc = 0;
+ save = 0;
+
+ while (argcv_scan (len, command, delim, cmnt, &start, &end, &save) <= len)
+ (*argc)++;
+
+ *argv = calloc ((*argc + 1), sizeof (char *));
+
+ i = 0;
+ save = 0;
+ for (i = 0; i < *argc; i++)
+ {
+ int n;
+ argcv_scan (len, command, delim, cmnt, &start, &end, &save);
+
+ if ((command[start] == '"' || command[end] == '\'')
+ && command[end] == command[start])
+ {
+ start++;
+ end--;
+ }
+ n = end - start + 1;
+ (*argv)[i] = calloc (n+1, sizeof (char));
+ if ((*argv)[i] == NULL)
+ return 1;
+ unescape_copy ((*argv)[i], &command[start], n);
+ (*argv)[i][n] = 0;
+ }
+ (*argv)[i] = NULL;
+ return 0;
+}
+
+/*
+ * frees all elements of an argv array
+ * argc is the number of elements
+ * argv is the array
+ */
+int
+argcv_free (int argc, char **argv)
+{
+ while (--argc >= 0)
+ if (argv[argc])
+ free (argv[argc]);
+ free (argv);
+ return 1;
+}
+
+/* Take a argv an make string separated by ' '. */
+
+int
+argcv_string (int argc, char **argv, char **pstring)
+{
+ size_t i, j, len;
+ char *buffer;
+
+ /* No need. */
+ if (pstring == NULL)
+ return 1;
+
+ buffer = malloc (1);
+ if (buffer == NULL)
+ return 1;
+ *buffer = '\0';
+
+ for (len = i = j = 0; i < argc; i++)
+ {
+ int quote = 0;
+ int toklen;
+
+ toklen = escaped_length (argv[i], &quote);
+
+ len += toklen + 2;
+ if (quote)
+ len += 2;
+
+ buffer = realloc (buffer, len);
+ if (buffer == NULL)
+ return 1;
+
+ if (i != 0)
+ buffer[j++] = ' ';
+ if (quote)
+ buffer[j++] = '"';
+ escape_copy (buffer + j, argv[i]);
+ j += toklen;
+ if (quote)
+ buffer[j++] = '"';
+ }
+
+ for (; j > 0 && isspace (buffer[j-1]); j--)
+ ;
+ buffer[j] = 0;
+ if (pstring)
+ *pstring = buffer;
+ return 0;
+}
+
+#if 0
+char *command = "set prompt=\"& \a\\\"\" \\x25\\0145\\098\\ta";
+
+main(int xargc, char **xargv)
+{
+ int i, argc;
+ char **argv;
+ char *s;
+
+ argcv_get (xargv[1] ? xargv[1]:command, "=", "#", &argc, &argv);
+ printf ("%d args:\n", argc);
+ for (i = 0; i < argc; i++)
+ printf ("%s\n", argv[i]);
+ printf ("===\n");
+ argcv_string (argc, argv, &s);
+ printf ("%s\n", s);
+}
+#endif
diff --git a/tests/argcv.h b/tests/argcv.h
new file mode 100644
index 0000000..911aa45
--- /dev/null
+++ b/tests/argcv.h
@@ -0,0 +1,50 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _ARGCV_H
+#define _ARGCV_H 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __P
+# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
+# define __P(args) args
+# else
+# define __P(args) ()
+# endif
+#endif /*__P */
+
+extern int argcv_get __P ((const char *command, const char *delim,
+ const char* cmnt,
+ int *argc, char ***argv));
+extern int argcv_string __P ((int argc, char **argv, char **string));
+extern int argcv_free __P ((int argc, char **argv));
+extern int argcv_unescape_char __P((int c));
+extern int argcv_escape_char __P((int c));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ARGCV_H */