diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2005-05-12 14:57:14 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2005-05-12 14:57:14 +0000 |
commit | ccd209a0390cdd95f75bcb0be6db5bc26e2d8a6c (patch) | |
tree | 2093c9404e97ed633140de578b08b015f7da0a70 | |
parent | da85d32d80aee17965326da78913d0b509e75f53 (diff) | |
download | paxutils-ccd209a0390cdd95f75bcb0be6db5bc26e2d8a6c.tar.gz |
Add new files
-rw-r--r-- | doc/DISTFILES | 1 | ||||
-rw-r--r-- | doc/genfile.texi | 284 | ||||
-rw-r--r-- | tests/DISTFILES | 2 | ||||
-rw-r--r-- | tests/argcv.c | 396 | ||||
-rw-r--r-- | tests/argcv.h | 50 |
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], "e); + + 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 */ |