#include <system.h>
#include <stdarg.h>
#include <argmatch.h>
#include <argp.h>
#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif
enum pattern
{
DEFAULT_PATTERN,
ZEROS_PATTERN
};
const char *program_name;
static char *file_name;
static int file_length = 0;
static enum pattern pattern = DEFAULT_PATTERN;
static int sparse_file;
size_t block_size = 512;
char *buffer;
const char *argp_program_version = "genfile (" PACKAGE ") " VERSION;
const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
static char doc[] = N_("genfile generates data files for GNU paxutils test suite");
static struct argp_option options[] = {
{"length", 'l', N_("SIZE"), 0,
N_("Create file of the given SIZE"), 0 },
{"file", 'f', N_("NAME"), 0,
N_("Write to file NAME, instead of standard output"), 0},
{"pattern", 'p', N_("PATTERN"), 0,
N_("Fill the file with the given PATTERN. PATTERN is 'default' or 'zeros'"),
0 },
{"block-size", 'b', N_("SIZE"), 0,
N_("Size of a block for sparse file"), 0},
{"sparse", 's', NULL, 0,
N_("Generate sparse file. The rest of the command line gives the file map"),
0 },
{ NULL, }
};
static char const * const pattern_args[] = { "default", "zeros", 0 };
static enum pattern const pattern_types[] = {DEFAULT_PATTERN, ZEROS_PATTERN};
static void
die (char const *fmt, ...)
{
va_list ap;
fprintf (stderr, "%s: ", program_name);
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fprintf (stderr, "\n");
exit (EXIT_FAILURE);
}
static int
xlat_suffix (off_t *vp, char *p)
{
if (p[1])
return 1;
switch (p[0])
{
case 'g':
case 'G':
*vp *= 1024;
case 'm':
case 'M':
*vp *= 1024;
case 'k':
case 'K':
*vp *= 1024;
break;
default:
return 1;
}
return 0;
}
static off_t
get_size (const char *str, int allow_zero)
{
char *p;
off_t n;
n = strtoul (str, &p, 0);
if (n < 0 || (!allow_zero && n == 0) || (*p && xlat_suffix (&n, p)))
die (_("Invalid size: %s"), str);
return n;
}
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
switch (key)
{
case 'f':
file_name = arg;
break;
case 'l':
file_length = get_size (arg, 1);
break;
case 'p':
pattern = XARGMATCH ("--pattern", arg, pattern_args, pattern_types);
break;
case 'b':
block_size = get_size (arg, 0);
break;
case 's':
sparse_file = 1;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = {
options,
parse_opt,
N_("[disp letters [disp letters...] [disp]...]"),
doc,
NULL,
NULL,
NULL
};
static void
generate_simple_file (int argc, char **argv)
{
int i;
FILE *fp;
if (argc)
die (_("too many arguments"));
if (file_name)
{
fp = fopen (file_name, "w");
if (!fp)
die (_("cannot open `%s'"), file_name);
}
else
fp = stdout;
switch (pattern)
{
case DEFAULT_PATTERN:
for (i = 0; i < file_length; i++)
fputc (i & 255, fp);
break;
case ZEROS_PATTERN:
for (i = 0; i < file_length; i++)
fputc (0, fp);
break;
}
fclose (fp);
}
static void
mkhole (int fd, off_t displ)
{
if (lseek (fd, displ, SEEK_CUR) == -1)
{
perror ("lseek");
exit (EXIT_FAILURE);
}
ftruncate (fd, lseek (fd, 0, SEEK_CUR));
}
static void
mksparse (int fd, off_t displ, char *marks)
{
for (; *marks; marks++)
{
memset (buffer, *marks, block_size);
if (write (fd, buffer, block_size) != block_size)
{
perror ("write");
exit (EXIT_FAILURE);
}
if (lseek (fd, displ, SEEK_CUR) == -1)
{
perror ("lseek");
exit (EXIT_FAILURE);
}
}
}
static void
generate_sparse_file (int argc, char **argv)
{
int i;
int fd;
if (!file_name)
die (_("cannot generate sparse files on standard output, use --file option"));
fd = open (file_name, O_CREAT|O_TRUNC|O_RDWR, 0644);
if (fd < 0)
die (_("cannot open `%s'"), file_name);
buffer = malloc (block_size);
if (!buffer)
die (_("Not enough memory"));
for (i = 0; i < argc; i += 2)
{
off_t displ = get_size (argv[i], 1);
if (i == argc-1)
{
mkhole (fd, displ);
break;
}
else
mksparse (fd, displ, argv[i+1]);
}
close (fd);
}
int
main (int argc, char **argv)
{
int index;
program_name = argv[0];
setlocale (LC_ALL, "");
if (argp_parse (&argp, argc, argv, 0, &index, NULL))
exit (EXIT_FAILURE);
argc -= index;
argv += index;
if (sparse_file)
generate_sparse_file (argc, argv);
else
generate_simple_file (argc, argv);
exit (EXIT_SUCCESS);
}