2005-10-05 15:23:51 +00:00
|
|
|
/*
|
|
|
|
* copy - test program for my getopt() re-implementation
|
|
|
|
*
|
|
|
|
* This program is in the public domain.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define COPYRIGHT \
|
|
|
|
"This program is in the public domain."
|
|
|
|
|
|
|
|
/* for isprint(), printf(), fopen(), perror(), getenv(), strcmp(), etc. */
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
/* for my getopt() re-implementation */
|
2010-09-21 09:39:04 +00:00
|
|
|
#include "compat_getopt.h"
|
2005-10-05 15:23:51 +00:00
|
|
|
|
2010-09-21 09:42:39 +00:00
|
|
|
#undef VERSION
|
|
|
|
#define VERSION "0.3"
|
|
|
|
|
2005-10-05 15:23:51 +00:00
|
|
|
/* the default verbosity level is 0 (no verbose reporting) */
|
|
|
|
static unsigned verbose = 0;
|
|
|
|
|
|
|
|
/* print version and copyright information */
|
|
|
|
static void
|
|
|
|
version(char *progname)
|
|
|
|
{
|
|
|
|
printf("%s version %s\n"
|
|
|
|
"%s\n",
|
|
|
|
progname,
|
|
|
|
VERSION,
|
|
|
|
COPYRIGHT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* print a help summary */
|
|
|
|
static void
|
|
|
|
help(char *progname)
|
|
|
|
{
|
|
|
|
printf("Usage: %s [options] [FILE]...\n"
|
|
|
|
"Options:\n"
|
|
|
|
"-h or -help show this message and exit\n"
|
|
|
|
"-append append to the output file\n"
|
|
|
|
"-o FILE or\n"
|
|
|
|
"-output FILE send output to FILE (default is stdout)\n"
|
|
|
|
"-r or --rotate rotate letters 13 positions (rot13)\n"
|
|
|
|
"-rNUM or\n"
|
|
|
|
"--rotate=NUM rotate letters NUM positions\n"
|
|
|
|
"-truncate truncate the output file "
|
|
|
|
"(this is the default)\n"
|
|
|
|
"-v or -verbose increase the level of verbosity by 1"
|
|
|
|
"(the default is 0)\n"
|
|
|
|
"-vNUM or\n"
|
|
|
|
"-verbose=NUM set the level of verbosity to NUM\n"
|
|
|
|
"-V or -version print program version and exit\n"
|
|
|
|
"\n"
|
|
|
|
"This program reads the specified FILEs "
|
|
|
|
"(or stdin if none are given)\n"
|
|
|
|
"and writes their bytes to the specified output FILE "
|
|
|
|
"(or stdout if none is\n"
|
|
|
|
"given.) It can optionally rotate letters.\n",
|
|
|
|
progname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* print usage information to stderr */
|
|
|
|
static void
|
|
|
|
usage(char *progname)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"Summary: %s [-help] [-version] [options] [FILE]...\n",
|
|
|
|
progname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* input file handler -- returns nonzero or exit()s on failure */
|
|
|
|
static int
|
|
|
|
handle(char *progname,
|
|
|
|
FILE *infile, char *infilename,
|
|
|
|
FILE *outfile, char *outfilename,
|
|
|
|
int rotate)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
unsigned long bytes_copied = 0;
|
|
|
|
|
|
|
|
if (verbose > 2)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: copying from `%s' to `%s'\n",
|
|
|
|
progname,
|
|
|
|
infilename,
|
|
|
|
outfilename);
|
|
|
|
}
|
|
|
|
while ((c = getc(infile)) != EOF)
|
|
|
|
{
|
|
|
|
if (rotate && isalpha(c))
|
|
|
|
{
|
|
|
|
const char *letters = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
char *match;
|
|
|
|
if ((match = strchr(letters, tolower(c))))
|
|
|
|
{
|
|
|
|
char rc = letters[(match - letters + rotate) % 26];
|
|
|
|
if (isupper(c))
|
|
|
|
rc = toupper(rc);
|
|
|
|
c = rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (putc(c, outfile) == EOF)
|
|
|
|
{
|
|
|
|
perror(outfilename);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
bytes_copied ++;
|
|
|
|
}
|
|
|
|
if (! feof(infile))
|
|
|
|
{
|
|
|
|
perror(infilename);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (verbose > 2)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: %lu bytes copied from `%s' to `%s'\n",
|
2010-09-21 09:45:09 +00:00
|
|
|
progname, bytes_copied, infilename, outfilename);
|
2005-10-05 15:23:51 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* argument parser and dispatcher */
|
|
|
|
int
|
|
|
|
main(int argc, char * argv[])
|
|
|
|
{
|
|
|
|
/* the program name */
|
|
|
|
char *progname = argv[0];
|
|
|
|
/* during argument parsing, opt contains the return value from getopt() */
|
|
|
|
int opt;
|
|
|
|
/* the output filename is initially 0 (a.k.a. stdout) */
|
|
|
|
char *outfilename = 0;
|
|
|
|
/* the default return value is initially 0 (success) */
|
|
|
|
int retval = 0;
|
|
|
|
/* initially we truncate */
|
|
|
|
int append = 0;
|
|
|
|
/* initially we don't rotate letters */
|
|
|
|
int rotate = 0;
|
|
|
|
|
|
|
|
/* short options string */
|
|
|
|
char *shortopts = "Vho:r::v::";
|
|
|
|
/* long options list */
|
|
|
|
struct option longopts[] =
|
|
|
|
{
|
|
|
|
/* name, has_arg, flag, val */ /* longind */
|
|
|
|
{ "append", no_argument, 0, 0 }, /* 0 */
|
|
|
|
{ "truncate", no_argument, 0, 0 }, /* 1 */
|
|
|
|
{ "version", no_argument, 0, 'V' }, /* 3 */
|
|
|
|
{ "help", no_argument, 0, 'h' }, /* 4 */
|
|
|
|
{ "output", required_argument, 0, 'o' }, /* 5 */
|
|
|
|
{ "rotate", optional_argument, 0, 'r' }, /* 6 */
|
|
|
|
{ "verbose", optional_argument, 0, 'v' }, /* 7 */
|
|
|
|
/* end-of-list marker */
|
|
|
|
{ 0, 0, 0, 0 }
|
|
|
|
};
|
|
|
|
/* long option list index */
|
|
|
|
int longind = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* print a warning when the POSIXLY_CORRECT environment variable will
|
|
|
|
* interfere with argument placement
|
|
|
|
*/
|
|
|
|
if (getenv("POSIXLY_CORRECT"))
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: "
|
|
|
|
"Warning: implicit argument reordering disallowed by "
|
|
|
|
"POSIXLY_CORRECT\n",
|
|
|
|
progname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse all options from the command line */
|
|
|
|
while ((opt =
|
|
|
|
getopt_long_only(argc, argv, shortopts, longopts, &longind)) != -1)
|
|
|
|
switch (opt)
|
|
|
|
{
|
|
|
|
case 0: /* a long option without an equivalent short option */
|
|
|
|
switch (longind)
|
|
|
|
{
|
|
|
|
case 0: /* -append */
|
|
|
|
append = 1;
|
|
|
|
break;
|
|
|
|
case 1: /* -truncate */
|
|
|
|
append = 0;
|
|
|
|
break;
|
|
|
|
default: /* something unexpected has happened */
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: "
|
|
|
|
"getopt_long_only unexpectedly returned %d for `--%s'\n",
|
|
|
|
progname,
|
|
|
|
opt,
|
|
|
|
longopts[longind].name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'V': /* -version */
|
|
|
|
version(progname);
|
|
|
|
return 0;
|
|
|
|
case 'h': /* -help */
|
|
|
|
help(progname);
|
|
|
|
return 0;
|
|
|
|
case 'r': /* -rotate[=NUM] */
|
|
|
|
if (optarg)
|
|
|
|
{
|
|
|
|
/* we use this while trying to parse a numeric argument */
|
|
|
|
char ignored;
|
|
|
|
if (sscanf(optarg,
|
|
|
|
"%d%c",
|
|
|
|
&rotate,
|
|
|
|
&ignored) != 1)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: "
|
|
|
|
"rotation `%s' is not a number\n",
|
|
|
|
progname,
|
|
|
|
optarg);
|
|
|
|
usage(progname);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
/* normalize rotation */
|
|
|
|
while (rotate < 0)
|
|
|
|
{
|
|
|
|
rotate += 26;
|
|
|
|
}
|
|
|
|
rotate %= 26;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
rotate = 13;
|
|
|
|
break;
|
|
|
|
case 'o': /* -output=FILE */
|
|
|
|
outfilename = optarg;
|
|
|
|
/* we allow "-" as a synonym for stdout here */
|
|
|
|
if (! strcmp(optarg, "-"))
|
|
|
|
{
|
|
|
|
outfilename = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'v': /* -verbose[=NUM] */
|
|
|
|
if (optarg)
|
|
|
|
{
|
|
|
|
/* we use this while trying to parse a numeric argument */
|
|
|
|
char ignored;
|
|
|
|
if (sscanf(optarg,
|
|
|
|
"%u%c",
|
|
|
|
&verbose,
|
|
|
|
&ignored) != 1)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: "
|
|
|
|
"verbosity level `%s' is not a number\n",
|
|
|
|
progname,
|
|
|
|
optarg);
|
|
|
|
usage(progname);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
verbose ++;
|
|
|
|
break;
|
|
|
|
case '?': /* getopt_long_only noticed an error */
|
|
|
|
usage(progname);
|
|
|
|
return 2;
|
|
|
|
default: /* something unexpected has happened */
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: "
|
|
|
|
"getopt_long_only returned an unexpected value (%d)\n",
|
|
|
|
progname,
|
|
|
|
opt);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* re-open stdout to outfilename, if requested */
|
|
|
|
if (outfilename)
|
|
|
|
{
|
|
|
|
if (! freopen(outfilename, (append ? "a" : "w"), stdout))
|
|
|
|
{
|
|
|
|
perror(outfilename);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* make a human-readable version of the output filename "-" */
|
|
|
|
outfilename = "stdout";
|
|
|
|
/* you can't truncate stdout */
|
|
|
|
append = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: verbosity level is %u; %s `%s'; rotation %d\n",
|
|
|
|
progname,
|
|
|
|
verbose,
|
|
|
|
(append ? "appending to" : "truncating"),
|
|
|
|
outfilename,
|
|
|
|
rotate);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose > 1)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: %d input file(s) were given\n",
|
|
|
|
progname,
|
|
|
|
((argc > optind) ? (argc - optind) : 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose > 3)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"\topterr: %d\n\toptind: %d\n\toptopt: %d (%c)\n\toptarg: %s\n",
|
|
|
|
opterr,
|
|
|
|
optind,
|
|
|
|
optopt, optopt,
|
|
|
|
optarg ? optarg : "(null)");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle each of the input files (or stdin, if no files were given) */
|
|
|
|
if (optind < argc)
|
|
|
|
{
|
|
|
|
int argindex;
|
|
|
|
|
|
|
|
for (argindex = optind; argindex < argc; argindex ++)
|
|
|
|
{
|
|
|
|
char *infilename = argv[argindex];
|
|
|
|
FILE *infile;
|
|
|
|
|
|
|
|
/* we allow "-" as a synonym for stdin here */
|
|
|
|
if (! strcmp(infilename, "-"))
|
|
|
|
{
|
|
|
|
infile = stdin;
|
|
|
|
infilename = "stdin";
|
|
|
|
}
|
|
|
|
else if (! (infile = fopen(infilename, "r")))
|
|
|
|
{
|
|
|
|
perror(infilename);
|
|
|
|
retval = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (handle(progname,
|
|
|
|
infile, argv[optind],
|
|
|
|
stdout, outfilename,
|
|
|
|
rotate))
|
|
|
|
{
|
|
|
|
retval = 1;
|
|
|
|
fclose(infile);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((infile != stdin) && fclose(infile))
|
|
|
|
{
|
|
|
|
perror(infilename);
|
|
|
|
retval = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
retval =
|
|
|
|
handle(progname,
|
|
|
|
stdin, "stdin",
|
|
|
|
stdout, outfilename,
|
|
|
|
rotate);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* close stdout */
|
|
|
|
if (fclose(stdout))
|
|
|
|
{
|
|
|
|
perror(outfilename);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose > 3)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: normal return, exit code is %d\n",
|
|
|
|
progname,
|
|
|
|
retval);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|