// CMPSC 311, Project Example // produce HTML from C // // possible extensions or additional tools // generate list for use with -f option, from the #include "file" lines (easy) // syntax-oriented HTML highlighting (not easy - requires a parser) // comment-tags that control HTML highlighting, as in /*c2html prototype */ (ugly hack) // table-structured HTML (easy? not easy?) #include #include #include #include #include #include #include #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;
    }
}

//------------------------------------------------------------------------------