#include "cmpsc311.h"
#include "names.h"
static void read_file(const char *infile, FILE *out);
static void read_lines(FILE *in, FILE *out);
static void spacetab_expand(const char *str, FILE *out);
//------------------------------------------------------------------------------
static void usage(int status)
{
if (status == EXIT_SUCCESS)
{
printf("usage: %s [-h] [-v] [-a author] [-f file] [-i ifile] [-o ofile] [-t title]\n", prog);
printf(" -h print help\n");
printf(" -v verbose mode; can be repeated for more output\n");
printf(" -a author page author\n");
printf(" -f file filename, contains list of input file names\n");
printf(" -i ifile input file name; default is stdin, which is also represented by -i -\n");
printf(" -o ofile output file name; default is stdout\n");
printf(" -t title page title\n");
printf(" You probably want to use quotes as in -t \"long title\".\n");
printf(" Multiple authors can be given, but only the first gets meta-credit.\n");
printf(" Multiple input files can be given.\n");
printf(" Tabs are expanded in the output.\n");
}
else
{
fprintf(stderr, "%s: Try '%s -h' for usage information.\n", prog, prog);
}
exit(status);
}
//------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
// for use with getopt(3)
int ch;
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
// option flags and option-arguments set from the command line
prog = argv[0];
int status = EXIT_SUCCESS;
int a_flag = 0; // number of -a options supplied
int f_flag = 0; // number of -f options supplied
int i_flag = 0; // number of -i options supplied
int o_flag = 0; // only the last -o option has effect
int t_flag = 0; // only the last -t option has effect
char *a_val = "no name";
char *o_val = "stdout";
char *t_val = "no title";
FILE *ofile = stdout;
// first, see if the -v option is given
// we'll catch all the other cases on the next pass over argv
while ((ch = getopt(argc, argv, ":hva:f:i:o:t:")) != -1)
{
if (ch == 'v') verbose++;
}
struct list_names *filenames = list_names_allocate("filenames");
// scan the argv array again, from the beginning
optind = 1;
while ((ch = getopt(argc, argv, ":hva:f:i:o:t:")) != -1)
{
switch (ch) {
case 'h':
usage(EXIT_SUCCESS);
break;
case 'v':
// verbose++;
break;
case 'a':
a_flag++; // number of -a options supplied
if (a_flag == 1) a_val = optarg;
break;
case 'f':
f_flag++; // number of -f options supplied
list_names_append_from_file(filenames, optarg);
break;
case 'i':
i_flag++; // number of -i options supplied
list_names_append(filenames, optarg);
break;
case 'o':
if (o_flag == 1) fprintf(stderr, "%s: disregarding -o %s\n", prog, o_val);
o_flag = 1;
o_val = optarg;
break;
case 't':
if (t_flag == 1) fprintf(stderr, "%s: disregarding -t %s\n", prog, t_val);
t_flag = 1;
t_val = optarg;
break;
case '?':
fprintf(stderr, "%s: invalid option '%c'\n", prog, optopt);
usage(EXIT_FAILURE);
break;
case ':':
fprintf(stderr, "%s: invalid option '%c' (missing argument)\n", prog, optopt);
usage(EXIT_FAILURE);
break;
default:
usage(EXIT_FAILURE);
break;
}
}
// loop over all the input files, verify input file != output file
// ignore the default cases of stdin and stdout
if ((i_flag + f_flag) > 0 && o_flag > 0)
{
bool failed = false;
for (struct name *p = filenames->head; p != NULL; p = p->next)
{
if (strcmp(p->name, o_val) == 0)
{
fprintf(stderr, "%s: failed: input file %s == output file %s\n", prog, p->name, o_val);
failed = true;
}
}
if (failed) exit(EXIT_FAILURE);
}
if (o_flag > 0)
{
ofile = fopen(o_val, "w");
if (ofile == NULL)
{
fprintf(stderr, "%s: failed: could not open output file %s: %s\n", prog, o_val, strerror(errno));
exit(EXIT_FAILURE);
}
}
fprintf(ofile, "\n");
fprintf(ofile, "\n");
fprintf(ofile, "\n");
fprintf(ofile, " \n");
fprintf(ofile, " \n", a_val);
fprintf(ofile, " %s\n", t_val);
fprintf(ofile, "\n");
fprintf(ofile, "\n");
fprintf(ofile, "%s
\n", t_val);
// loop over all the author names
if (a_flag < 2) // 0 or 1
{ fprintf(ofile, "
Author: %s
\n", a_val); }
else
{
fprintf(ofile, "
Authors:
\n");
optind = 1;
while ((ch = getopt(argc, argv, ":hva:f:i:o:t:")) != -1)
{
if (ch == 'a')
{ fprintf(ofile, " %s
\n", optarg); }
}
}
// loop over all the input files, list the names
if (i_flag == 0 && f_flag == 0) // default is stdin
{ fprintf(ofile, "
File: [stdin]
\n"); }
else
{
fprintf(ofile, "
Files:
\n");
for (struct name *p = filenames->head; p != NULL; p = p->next)
{
fprintf(ofile, " name, ofile);
fprintf(ofile, "\">");
spacetab_expand(p->name, ofile);
fprintf(ofile, "
\n");
}
}
// loop over all the input files, list the file contents
if (i_flag == 0 && f_flag == 0) // default is stdin
{ read_file("-", ofile); }
else
{
for (struct name *p = filenames->head; p != NULL; p = p->next)
{ read_file(p->name, ofile); }
}
fprintf(ofile, "
\n");
time_t now = time(NULL);
char buf[26];
ctime_r(&now, buf);
buf[24] = '\0'; // clean up the newline
fprintf(ofile, "This page generated %s by %s
\n", buf, prog);
fprintf(ofile, "
\n");
fprintf(ofile, "\n");
fprintf(ofile, "\n");
if (ofile != stdout && fclose(ofile) != 0)
{
fprintf(stderr, "%s: failed: could not close output file %s: %s\n", prog, o_val, strerror(errno));
// exit(EXIT_FAILURE);
}
list_names_deallocate(filenames);
return status;
}
//------------------------------------------------------------------------------
static void read_file(const char *infile, FILE *out)
{
verify(infile != NULL, "null arg infile");
verify(out != NULL, "null arg out");
FILE *in;
int errnum = 0;
if (strcmp(infile, "-") == 0)
{ in = stdin; }
else
{
in = fopen(infile, "r");
if (in == NULL)
{ errnum = errno; }
}
fprintf(out, "
\n");
fprintf(out, "file: \n");
fprintf(out, "");
spacetab_expand(infile, out);
fprintf(out, "
\n");
fprintf(out, "\n");
if (in == NULL)
{ fprintf(out, "ERROR: could not open %s: %s\n", infile, strerror(errnum)); }
else
{ read_lines(in, out); }
fprintf(out, "\n");
if (in != NULL && in != stdin && fclose(in) != 0)
{
fprintf(stderr, "%s: failed: could not close input file %s: %s\n", prog, infile, strerror(errno));
// exit(EXIT_FAILURE);
}
}
//------------------------------------------------------------------------------
// the output of this function appears inside a section,
// so we don't need to translate ' ' to " ",
// but we do need to translate tabs to spaces, to compensate for the leading line number
static void read_lines(FILE *in, FILE *out)
{
verify(in != NULL, "null arg in");
verify(out != NULL, "null arg out");
int line_number = 0;
int c; // current character
int p = '\n'; // previous character
int tab_count = 8;
while ((c = getc(in)) != EOF)
{
if (p == '\n')
{
fprintf(out, "%3d ", ++line_number);
tab_count = 8;
}
switch (c) {
case '\t': for (int i = 0; i < tab_count; i++) { fprintf(out, " "); }
tab_count = 0; break;
case '<': fprintf(out, "<"); tab_count -= 1; break;
case '>': fprintf(out, ">"); tab_count -= 1; break;
case '&': fprintf(out, "&"); tab_count -= 1; break;
default: fprintf(out, "%c", c); tab_count -= 1; break;
}
p = c;
if (tab_count <= 0) tab_count += 8;
}
if (feof(in)) // end of file
{ fprintf(out, "EOF\n"); }
if (ferror(in)) // error when reading the file
{ fprintf(out, "EOF -- read error: %s\n", strerror(errno)); }
}
//------------------------------------------------------------------------------
static void spacetab_expand(const char *str, FILE *out)
{
int tab_count = 8;
for (char *p = (char *) str; *p != '\0'; p++)
{
switch(*p) {
case '\t': for (int i = 0; i < tab_count; i++) { fprintf(out, " "); }
tab_count = 0; break;
case ' ': fprintf(out, " "); tab_count -= 1; break;
default: fprintf(out, "%c", *p); tab_count -= 1; break;
}
if (tab_count <= 0) tab_count += 8;
}
}
//------------------------------------------------------------------------------