CMPSC 311, Project Example

Author: Don Heller

Files:
   Makefile
   cmpsc311.h
   cmpsc311.c
   names.h
   names.c
   c2html.c



file:  Makefile
  1   # CMPSC 311, Project Example
  2   
  3   SRC = c2html.c
  4   LIB = cmpsc311.c names.c
  5   INC = cmpsc311.h names.h
  6   
  7   AUTHOR = "Don Heller"
  8   
  9   C99 = c99 -v
 10   GCC = gcc -std=c99 -Wall -Wextra -pedantic
 11   
 12   POSIX_OPTIONS   = -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600
 13   
 14   SOLARIS_OPTIONS = -D_XOPEN_SOURCE=600
 15   LINUX_OPTIONS   = -D_XOPEN_SOURCE=700
 16   MACOSX_OPTIONS  = 
 17   
 18   dummy:
 19   
 20   solaris: $(SRC) $(LIB) $(INC)
 21           $(C99) $(SOLARIS_OPTIONS) -o c2html $(SRC) $(LIB)
 22           $(GCC) $(SOLARIS_OPTIONS) -o c2html $(SRC) $(LIB)
 23   
 24   # lint is only available on Solaris
 25   lint: $(SRC) $(LIB) $(INC)
 26           lint -Xc99 $(SOLARIS_OPTIONS)       $(SRC) $(LIB)
 27   
 28   linux: $(SRC) $(LIB) $(INC)
 29           $(GCC) $(LINUX_OPTIONS)   -o c2html $(SRC) $(LIB)
 30   
 31   mac: $(SRC) $(LIB) $(INC)
 32           $(GCC) $(MACOSX_OPTIONS)  -o c2html $(SRC) $(LIB)
 33   
 34   
 35   c2html.html: $(SRC) $(LIB) $(INC) c2html.display
 36           ./c2html -o c2html.html -f c2html.display -t "CMPSC 311, Project Example" -a $(AUTHOR)
 37           chmod 644 c2html.html
 38   
 39   cmpsc311.html: cmpsc311.test.c $(LIB) $(INC) cmpsc311.display
 40           $(GCC) $(POSIX_OPTIONS) -o cmpsc311.test cmpsc311.test.c $(LIB)
 41           ./cmpsc311.test > cmpsc311.test.out
 42           ./c2html -o cmpsc311.html -f cmpsc311.display -t "CMPSC 311, base library" -a $(AUTHOR)
 43   
 44   names.html: names.test.c $(LIB) $(INC) names.display
 45           $(GCC) $(POSIX_OPTIONS) -o names.test names.test.c $(LIB)
 46           ./names.test > names.test.out
 47           ./c2html -o names.html -f names.display -t "CMPSC 311, list of names" -a $(AUTHOR)
 48   
 49   
 50   trial: 
 51           ./c2html -o trial.html -f namelist -t "trial" -a $(AUTHOR)
 52   
 53   # Valgrind is available on Linux and Mac OS X.
 54   # For a basic intro, see http://www.valgrind.org/docs/manual/quick-start.html
 55   linux-trial:
 56           $(GCC) $(LINUX_OPTIONS) -g -O0 -o c2html $(SRC) $(LIB)
 57           valgrind --leak-check=full --track-origins=yes ./c2html -o trial.html -f namelist -t "trial" -a $(AUTHOR)
 58   
 59   clean:
 60           rm -f c2html *.o
 61           rm -f cmpsc311.test cmpsc311.test.out
 62           rm -f names.test names.test.out
 63   
 64   Clean: clean
 65           rm -f c2html.html cmpsc311.html names.html
 66           rm -f trial.html
 67   
EOF



file:  cmpsc311.h
  1   /* base library for CMPSC 311 projects
  2    * version of 25 Feb. 2013
  3    */
  4   
  5   #ifndef CMPSC311_H
  6   #define CMPSC311_H
  7   
  8   #include <stdio.h>
  9   #include <stdbool.h>
 10   
 11   //------------------------------------------------------------------------------
 12   
 13   // global variables set from the command line
 14   
 15   extern char *prog;      // program name
 16   extern int verbose;     // -v option, extra output
 17                           // -v can be repeated for even more output
 18   
 19     // verbosity levels
 20     // 0  off, default
 21     // 1  normal, behavioral info
 22     // 2  extra, functionality info
 23     // 3  memory allocation info, from Malloc(), Strdup()
 24     // 4  too much, information overload
 25   
 26   //------------------------------------------------------------------------------
 27   
 28   // utility functions
 29   //   safe_string(() -- guard against null pointer for character string output
 30   
 31   const char *safe_string(const char *str);
 32   
 33   //------------------------------------------------------------------------------
 34   
 35   // check function arguments
 36   //   verify() -- tf is expected to be true; if not, print msg and quit
 37   
 38   #define verify(tf, msg) cmpsc311_verify(tf, msg, __func__, __LINE__)
 39   
 40   void cmpsc311_verify(const bool tf,
 41           const char *msg, const char *func, const int line);
 42   
 43   //------------------------------------------------------------------------------
 44   
 45   // check function return values
 46   //   function   standards
 47   //   malloc     C and Posix
 48   //   strdup           Posix
 49   //   fopen      C and Posix
 50   // We follow the standard protoypes of the original functions.
 51   // Compare these to the error-checking wrappers in CS:APP, csapp.h and csapp.c.
 52   
 53   #define Malloc(size)         cmpsc311_malloc(size, __func__, __LINE__)
 54   #define Strdup(s)            cmpsc311_strdup(s, __func__, __LINE__)
 55   #define Fopen(filename,mode) cmpsc311_fopen(filename, mode, __func__, __LINE__)
 56   
 57   void *cmpsc311_malloc(size_t size,
 58           const char *func, const int line);
 59   char *cmpsc311_strdup(const char *s,
 60           const char *func, const int line);
 61   FILE *cmpsc311_fopen(const char * restrict filename, const char * restrict mode,
 62           const char *func, const int line);
 63   
 64   //------------------------------------------------------------------------------
 65   
 66   #endif
 67   
EOF



file:  cmpsc311.c
  1   /* base library for CMPSC 311 projects
  2    * version of 25 Feb. 2013
  3    */
  4   
  5   #include <stdio.h>
  6   #include <stdlib.h>
  7   #include <unistd.h>
  8   #include <stdbool.h>
  9   #include <errno.h>
 10   #include <string.h>
 11   
 12   #include "cmpsc311.h"
 13   
 14   //------------------------------------------------------------------------------
 15   
 16   // global variables set from the command line
 17   
 18   char *prog = "[no name]";       // program name
 19   int verbose = 0;                // -v option, extra output
 20                                   // -v can be repeated for even more output
 21   
 22     // verbosity levels
 23     // 0  off, default
 24     // 1  normal, behavioral info
 25     // 2  extra, functionality info
 26     // 3  memory allocation info, from Malloc(), Strdup()
 27     // 4  too much, information overload
 28   
 29   //------------------------------------------------------------------------------
 30   
 31   // utility functions
 32   //   safe_string(() -- guard against null pointer for character string output
 33   
 34   const char *safe_string(const char *str)
 35   {
 36     if (str == NULL) return "(null)";
 37     else return str;
 38   }
 39   
 40   /* The Solaris C library doesn't like printf("%s", p) when p is a null pointer.
 41    * It will generate a segmentation fault, and end the program.  This is
 42    * an acceptable implementation according to the C and Posix Standards.
 43    * The GNU C library (on Linux and Mac OS X) is more generous; it will print
 44    * the string "(null)" and keep going.
 45    * If you compile with GCC with all the warnings turned on, there are complaints
 46    * about printf("%s", NULL); but none about printf("%s", p) when p happens to be
 47    * a null pointer.
 48    * With the safe_string() function, we can act like the GNU C library version of
 49    * printf(), even on Solaris.
 50    * Of course, we could also use assert() or verify() to catch NULL pointers,
 51    * which is often a better idea, unless you really want the program to keep
 52    * going.
 53    */
 54   
 55   //------------------------------------------------------------------------------
 56   
 57   // check function arguments
 58   //   verify() -- tf is expected to be true; if not, print msg and quit
 59   
 60   // #define verify(tf, msg) cmpsc311_verify(tf, msg, __func__, __LINE__)
 61   
 62   void cmpsc311_verify(const bool tf, const char *msg,
 63           const char *func, const int line)
 64   {
 65     if (tf == false)
 66       {
 67         fprintf(stderr, "%s: %s() at line %d failed: %s\n", prog, func, line, msg);
 68         exit(EXIT_FAILURE);
 69       }
 70   }
 71   
 72   //------------------------------------------------------------------------------
 73   
 74   // check function return values
 75   //   function   standards
 76   //   malloc     C and Posix
 77   //   strdup           Posix
 78   //   fopen      C and Posix
 79   // We follow the standard protoypes of the original functions.
 80   // Compare these to the error-checking wrappers in CS:APP, csapp.h and csapp.c.
 81   
 82   // #define Malloc(size) cmpsc311_malloc(size, __func__, __LINE__)
 83   
 84   void *cmpsc311_malloc(size_t size,
 85           const char *func, const int line)
 86   {
 87     void *p = malloc(size);
 88     if (p == NULL)
 89       {
 90         fprintf(stderr, "%s: %s() at line %d failed: malloc(): %s\n",
 91           prog, func, line, strerror(errno));
 92         exit(EXIT_FAILURE);
 93       }
 94   
 95     if (verbose > 2)
 96       { // which address?
 97         fprintf(stderr, "%s: malloc(%zd) at %p from %s line %d\n",
 98           prog, size, p, func, line);
 99       }
100   
101     return p;
102   }
103   
104   // #define Strdup(s) cmpsc311_strdup(s, __func__, __LINE__)
105   
106   char *cmpsc311_strdup(const char *s,
107           const char *func, const int line)
108   {
109     char *p = strdup(s);
110     if (p == NULL)
111       {
112         fprintf(stderr, "%s: %s() at line %d failed: strdup(): %s\n",
113           prog, func, line, strerror(errno));
114         exit(EXIT_FAILURE);
115       }
116   
117     if (verbose > 2)
118       { // which address?
119         fprintf(stderr, "%s: strdup(%zd) at %p from %s line %d\n",
120           prog, strlen(s)+1, (void *) p, func, line);
121       }
122   
123     return p;
124   }
125   
126   // #define Fopen(filename,mode) cmpsc311_fopen(filename, mode, __func__, __LINE__)
127   
128   FILE *cmpsc311_fopen(const char * restrict filename, const char * restrict mode,
129           const char *func, const int line)
130   {
131     FILE *f = fopen(filename, mode);
132     if (f == NULL)
133       {
134         fprintf(stderr, "%s: %s() at line %d failed: fopen(%s): %s\n",
135           prog, func, line, filename, strerror(errno));
136         exit(EXIT_FAILURE);
137       }
138   
139     return f;
140   }
141   
142   //------------------------------------------------------------------------------
143   
EOF



file:  names.h
  1   /* base library for CMPSC 311 projects
  2    * version of 25 Feb. 2013
  3    */
  4   
  5   #ifndef CMPSC311_NAMES_H
  6   #define CMPSC311_NAMES_H
  7   
  8   //------------------------------------------------------------------------------
  9   
 10   // singly-linked list of names
 11   
 12   struct name
 13   {
 14     struct name *next;    // NULL indicates end-of-list
 15     char *name;           // from strdup()
 16   };
 17   
 18   struct list_names
 19   {
 20     struct name *head;    // NULL indicates empty list
 21     struct name *tail;
 22     int reference_count;  // for delayed deallocation of the list
 23     char *name;           // from strdup()
 24   };
 25   
 26   //------------------------------------------------------------------------------
 27   
 28   void list_names_init(struct list_names * const list, const char *listname);
 29   
 30   struct list_names *list_names_allocate(const char *listname);
 31   struct list_names *list_names_reference(struct list_names * const list);
 32   void list_names_deallocate(struct list_names * const list);
 33   
 34   void list_names_print(const struct list_names * const list);
 35   
 36   void list_names_append(struct list_names * const list, const char *name);
 37    int list_names_append_if_new(struct list_names * const list, const char *name);
 38   void list_names_append_from_file(struct list_names * const list, const char *filename);
 39   
 40   void list_names_iterate(struct list_names *list, void (*func)(void *));
 41   
 42   //------------------------------------------------------------------------------
 43   
 44   /* Notes
 45    *
 46    * The assumption is that a struct name can appear on only one list, but the
 47    * list itself could have more than one reference to it.
 48    *
 49    * list_names_allocate() allocates a new list; list_names_reference() creates
 50    * another reference to an existing list.
 51    *
 52    * Do not apply list_names_deallocate() to something that did not come from
 53    * list_names_allocate() or list_names_reference().
 54    *
 55    * list_names_append_if_new() works as follows:
 56    *     if (name is on the list already) { return 1 }
 57    *     else { put name on the list and return 0 }
 58    *
 59    * If list_names_allocate() or one of the list_names_append() functions
 60    * can't allocate enough memory, the program ends with an error message.
 61    */
 62   
 63   //------------------------------------------------------------------------------
 64   
 65   /* Examples
 66    *
 67    * Declare, initialize and print a list
 68    *   struct list_names list;
 69    *   list_names_init(&list, "something");
 70    *   list_names_print(&list);
 71    *
 72    * Declare and initialize a list (incorrect)
 73    *   struct list_names list = { "something", NULL, NULL, 0 };
 74    *
 75    * Allocate a list, initialized
 76    *   struct list_names *list = list_names_allocate("something");
 77    *   if (list == NULL) { ... allocation failed ... }
 78    *   list_names_print(list);
 79    *
 80    * Deallocate a list
 81    *   struct list_names *list = list_names_allocate("something");
 82    *   list_names_deallocate(list);
 83    *   list = NULL;
 84    *
 85    * Deallocate a list (incorrect)
 86    *   struct list_names list;
 87    *   list_names_init(&list, "something");
 88    *   list_names_deallocate(list);
 89    *   list = NULL;
 90    *
 91    * Put a name on the end of the list (assume a declared list)
 92    *   list_names_append(&list1, "one");
 93    *
 94    * Put a name on the end of the list (assume an allocated list)
 95    *   list_names_append(list2, "two");
 96    */
 97   
 98   //------------------------------------------------------------------------------
 99   
100   #endif
101   
EOF



file:  names.c
  1   /* base library for CMPSC 311 projects
  2    * version of 25 Feb. 2013
  3    */
  4   
  5   #include <stdio.h>
  6   #include <stdlib.h>
  7   #include <unistd.h>
  8   #include <stdbool.h>
  9   #include <errno.h>
 10   #include <string.h>
 11   #include <ctype.h>
 12   
 13   #include "cmpsc311.h"
 14   #include "names.h"
 15   
 16   //------------------------------------------------------------------------------
 17   
 18   void list_names_init(struct list_names * const list, const char *listname)
 19   {
 20     verify(list != NULL, "null arg list");
 21     verify(listname != NULL, "null arg listname");
 22     verify(listname[0] != '\0', "empty arg listname");
 23   
 24     list->head = list->tail = NULL;
 25     list->reference_count = 0;
 26     list->name = Strdup(listname);
 27   }
 28   
 29   //------------------------------------------------------------------------------
 30   
 31   struct list_names *list_names_allocate(const char *listname)
 32   {
 33     verify(listname != NULL, "null arg listname");
 34     verify(listname[0] != '\0', "empty arg listname");
 35   
 36     struct list_names *list = Malloc(sizeof(struct list_names));
 37   
 38     list->head = list->tail = NULL;
 39     list->reference_count = 0;
 40     list->name = Strdup(listname);
 41   
 42     return list;
 43   }
 44   
 45   //------------------------------------------------------------------------------
 46   
 47   struct list_names *list_names_reference(struct list_names * const list)
 48   {
 49     verify(list != NULL, "null arg list");
 50   
 51     list->reference_count++;
 52   
 53     return list;
 54   }
 55   
 56   //------------------------------------------------------------------------------
 57   
 58   void list_names_deallocate(struct list_names * const list)
 59   {
 60     verify(list != NULL, "null arg list");
 61   
 62     if (--list->reference_count > 0)
 63       { return; }
 64   
 65     struct name *prev = NULL;
 66     for (struct name *p = list->head; p != NULL; p = p->next)
 67       {
 68         free(prev);       // free(NULL) is harmless
 69         free(p->name);
 70         prev = p;
 71       }
 72     free(prev);           // now, prev == list->tail
 73   
 74     free(list->name);
 75     free(list);
 76   }
 77   
 78   //------------------------------------------------------------------------------
 79   
 80   void list_names_print(const struct list_names * const list)
 81   {
 82     verify(list != NULL, "null arg list");
 83   
 84     printf("list of names: %s\n", safe_string(list->name));
 85   
 86     if (list->head == NULL)
 87       { printf("  <empty>\n"); }
 88     else
 89       {
 90         for (struct name *p = list->head; p != NULL; p = p->next)
 91           { printf("  %s\n", p->name); }
 92       }
 93   }
 94   
 95   //------------------------------------------------------------------------------
 96   
 97   void list_names_append(struct list_names * const list, const char *name)
 98   {
 99     verify(list != NULL, "null arg list");
100     verify(name != NULL, "null arg name");
101     verify(name[0] != '\0', "empty arg name");
102   
103     struct name *p = Malloc(sizeof(struct name));
104   
105     p->next = NULL;
106     p->name = Strdup(name);
107   
108     if (list->head == NULL)       // empty list, list->tail is also NULL
109       {
110         list->head = list->tail = p;
111       }
112     else
113       {
114         list->tail->next = p;
115         list->tail = p;
116       }
117   }
118   
119   //------------------------------------------------------------------------------
120   
121   // if (name is on the list already) { return 1 }
122   // else { put name on the list and return 0 }
123   
124   int list_names_append_if_new(struct list_names * const list, const char *name)
125   {
126     verify(list != NULL, "null arg list");
127     verify(name != NULL, "null arg name");
128     verify(name[0] != '\0', "empty arg name");
129   
130     for (struct name *p = list->head; p != NULL; p = p->next)
131       {
132         if (strcmp(p->name, name) == 0)
133           { return 1; }   // name is on the list already
134       }
135   
136     list_names_append(list, name);
137   
138     return 0;
139   }
140   
141   //------------------------------------------------------------------------------
142   
143   void list_names_append_from_file(struct list_names * const list, const char *filename)
144   {
145     verify(list != NULL, "null arg list");
146     verify(filename != NULL, "null arg filename");
147     verify(filename[0] != '\0', "empty arg filename");
148   
149     FILE *infile = NULL;
150   
151     if (strcmp(filename, "-") == 0)
152       { infile = stdin; }
153     else
154       {
155         infile = fopen(filename, "r");
156         if (infile == NULL)
157           {
158             fprintf(stderr, "%s: failed: could not open file %s: %s\n",
159               prog, filename, strerror(errno));
160             exit(EXIT_FAILURE);
161           }
162       }
163   
164     #define MAXLINE 256
165     char buffer[MAXLINE+2];               // extra space for newline
166     buffer[MAXLINE+2-2] = '\n';           // force a newline
167     buffer[MAXLINE+2-1] = '\0';           // to end the string
168   
169     char whsp[] = " \t\n\v\f\r";          // whitespace characters
170     char comm[] = "#\n";                  // comment, newline
171   
172     while (fgets(buffer, MAXLINE, infile) != NULL)
173       {
174         /* Note that fgets() places a newline in buffer, if the input line
175          *   was short enough to fit.  Line-too-long is probably an error,
176          *   to be caught later.  We work around the rare case by forcing a
177          *   newline and not overwriting it.
178          * NULL from fgets() indicates end-of-file or error, so in that case
179          *   we just quit.
180          */
181   
182         // remove comment, if present
183         // remove trailing newline
184         int m = strcspn(buffer, comm);    // index of # or newline
185         buffer[m] = '\0';                 // remove the tail
186   
187         m = strspn(buffer, whsp);         // index of first non-whitespace character
188         char *buf = &buffer[m];
189   
190         // remove trailing whitespace, by working backward from the end of string
191         char *p = strchr(buf, '\0');      // *p is '\0', or p is NULL
192         if (p == NULL)
193           {
194             fprintf(stderr, "%s: strange string\n", prog);
195             exit(EXIT_FAILURE);
196           }
197         else
198           {
199             p--;                          // *p is '\0', so back up one position
200             while (p > buf && isspace(*p))
201               { *p = '\0'; p--; }
202           }
203   
204         if (*buf == '\0')                 // empty line
205           { continue; }
206   
207         list_names_append(list, buf);
208   
209         // get ready for the next iteration
210         buffer[MAXLINE+2-2] = '\n';       // force a newline
211         buffer[MAXLINE+2-1] = '\0';       // to end the string
212       }
213   
214     if (infile != stdin && fclose(infile) != 0)
215       {
216         fprintf(stderr, "%s: failed: could not close input file %s: %s\n",
217           prog, filename, strerror(errno));
218         exit(EXIT_FAILURE);
219       }
220   }
221   
222   //------------------------------------------------------------------------------
223   
224   // unfinished -- seemed like a good idea, but we didn't actually need it
225   
226   void list_names_iterate(struct list_names *list, void (*func)(void *))
227   {
228     verify(list != NULL, "null arg list");
229     verify(func != NULL, "null arg func");
230   
231     for (struct name *p = list->head; p != NULL; p = p->next)
232       {
233         printf("calling func on %s\n", p->name);
234       }
235   }
236   
237   //------------------------------------------------------------------------------
238   
EOF



file:  c2html.c
  1   // CMPSC 311, Project Example
  2   //   produce HTML from C
  3   //
  4   // possible extensions or additional tools
  5   //   generate list for use with -f option, from the #include "file" lines (easy)
  6   //   syntax-oriented HTML highlighting (not easy - requires a parser)
  7   //   comment-tags that control HTML highlighting, as in /*c2html prototype */ (ugly hack)
  8   //   table-structured HTML (easy? not easy?)
  9   
 10   #include <stdio.h>
 11   #include <stdlib.h>
 12   #include <unistd.h>
 13   #include <stdbool.h>
 14   #include <errno.h>
 15   #include <string.h>
 16   #include <time.h>
 17   
 18   #include "cmpsc311.h"
 19   #include "names.h"
 20   
 21   static void read_file(const char *infile, FILE *out);
 22   static void read_lines(FILE *in, FILE *out);
 23   static void spacetab_expand(const char *str, FILE *out);
 24   
 25   //------------------------------------------------------------------------------
 26   
 27   static void usage(int status)
 28   {
 29     if (status == EXIT_SUCCESS)
 30       {
 31         printf("usage: %s [-h] [-v] [-a author] [-f file] [-i ifile] [-o ofile] [-t title]\n", prog);
 32         printf("  -h           print help\n");
 33         printf("  -v           verbose mode; can be repeated for more output\n");
 34         printf("  -a author    page author\n");
 35         printf("  -f file      filename, contains list of input file names\n");
 36         printf("  -i ifile     input file name; default is stdin, which is also represented by -i -\n");
 37         printf("  -o ofile     output file name; default is stdout\n");
 38         printf("  -t title     page title\n");
 39         printf("  You probably want to use quotes as in -t \"long title\".\n");
 40         printf("  Multiple authors can be given, but only the first gets meta-credit.\n");
 41         printf("  Multiple input files can be given.\n");
 42         printf("  Tabs are expanded in the output.\n");
 43       }
 44     else
 45       {
 46         fprintf(stderr, "%s: Try '%s -h' for usage information.\n", prog, prog);
 47       }
 48   
 49     exit(status);
 50   }
 51   
 52   //------------------------------------------------------------------------------
 53   
 54   int main(int argc, char *argv[])
 55   {
 56     // for use with getopt(3)
 57     int ch;
 58     extern char *optarg;
 59     extern int optind;
 60     extern int optopt;
 61     extern int opterr;
 62   
 63     // option flags and option-arguments set from the command line
 64     prog = argv[0];
 65     int status = EXIT_SUCCESS;
 66   
 67     int a_flag = 0;       // number of -a options supplied
 68     int f_flag = 0;       // number of -f options supplied
 69     int i_flag = 0;       // number of -i options supplied
 70     int o_flag = 0;       // only the last -o option has effect
 71     int t_flag = 0;       // only the last -t option has effect
 72   
 73     char *a_val = "no name";
 74     char *o_val = "stdout";
 75     char *t_val = "no title";
 76   
 77     FILE *ofile = stdout;
 78   
 79     // first, see if the -v option is given
 80     // we'll catch all the other cases on the next pass over argv
 81     while ((ch = getopt(argc, argv, ":hva:f:i:o:t:")) != -1)
 82       {
 83         if (ch == 'v') verbose++;
 84       }
 85   
 86     struct list_names *filenames = list_names_allocate("filenames");
 87   
 88     // scan the argv array again, from the beginning
 89     optind = 1;
 90     while ((ch = getopt(argc, argv, ":hva:f:i:o:t:")) != -1)
 91       {
 92         switch (ch) {
 93           case 'h':
 94             usage(EXIT_SUCCESS);
 95             break;
 96           case 'v':
 97             // verbose++;
 98             break;
 99           case 'a':
100             a_flag++;             // number of -a options supplied
101             if (a_flag == 1) a_val = optarg;
102             break;
103           case 'f':
104             f_flag++;             // number of -f options supplied
105             list_names_append_from_file(filenames, optarg);
106             break;
107           case 'i':
108             i_flag++;             // number of -i options supplied
109             list_names_append(filenames, optarg);
110             break;
111           case 'o':
112             if (o_flag == 1) fprintf(stderr, "%s: disregarding -o %s\n", prog, o_val);
113             o_flag = 1;
114             o_val = optarg;
115             break;
116           case 't':
117             if (t_flag == 1) fprintf(stderr, "%s: disregarding -t %s\n", prog, t_val);
118             t_flag = 1;
119             t_val = optarg;
120             break;
121           case '?':
122             fprintf(stderr, "%s: invalid option '%c'\n", prog, optopt);
123             usage(EXIT_FAILURE);
124             break;
125           case ':':
126             fprintf(stderr, "%s: invalid option '%c' (missing argument)\n", prog, optopt);
127             usage(EXIT_FAILURE);
128             break;
129           default:
130             usage(EXIT_FAILURE);
131             break;
132         }
133       }
134   
135     // loop over all the input files, verify input file != output file
136     // ignore the default cases of stdin and stdout
137     if ((i_flag + f_flag) > 0 && o_flag > 0)
138       {
139         bool failed = false;
140         for (struct name *p = filenames->head; p != NULL; p = p->next)
141           {
142             if (strcmp(p->name, o_val) == 0)
143               {
144                 fprintf(stderr, "%s: failed: input file %s == output file %s\n", prog, p->name, o_val);
145                 failed = true;
146               }
147           }
148         if (failed) exit(EXIT_FAILURE);
149       }
150   
151     if (o_flag > 0)
152       {
153         ofile = fopen(o_val, "w");
154         if (ofile == NULL)
155           {
156             fprintf(stderr, "%s: failed: could not open output file %s: %s\n", prog, o_val, strerror(errno));
157             exit(EXIT_FAILURE);
158           }
159       }
160   
161     fprintf(ofile, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
162     fprintf(ofile, "<html>\n");
163     fprintf(ofile, "<head>\n");
164     fprintf(ofile, "  <meta content=\"text/html; charset=ISO-8859-1\" http-equiv=\"Content-Type\">\n");
165     fprintf(ofile, "  <meta content=\"%s\" name=\"author\">\n", a_val);
166     fprintf(ofile, "  <title>%s</title>\n", t_val);
167     fprintf(ofile, "</head>\n");
168     fprintf(ofile, "<body>\n");
169     fprintf(ofile, "%s<br>\n", t_val);
170   
171     // loop over all the author names
172     if (a_flag < 2)       // 0 or 1
173       { fprintf(ofile, "<br>Author: %s<br>\n", a_val); }
174     else
175       {
176         fprintf(ofile, "<br>Authors: <br>\n");
177         optind = 1;
178         while ((ch = getopt(argc, argv, ":hva:f:i:o:t:")) != -1)
179           {
180             if (ch == 'a')
181               { fprintf(ofile, "&nbsp;&nbsp; %s<br>\n", optarg); }
182           }
183       }
184   
185     // loop over all the input files, list the names
186     if (i_flag == 0 && f_flag == 0)       // default is stdin
187       { fprintf(ofile, "<br>File: <span style=\"font-family: monospace;\">[stdin]</span><br>\n"); }
188     else
189       {
190         fprintf(ofile, "<br>Files: <br>\n");
191         for (struct name *p = filenames->head; p != NULL; p = p->next)
192           {
193             fprintf(ofile, "&nbsp;&nbsp; <a href=\"");
194             spacetab_expand(p->name, ofile);
195             fprintf(ofile, "\"><span style=\"font-family: monospace;\">");
196             spacetab_expand(p->name, ofile);
197             fprintf(ofile, "</span></a><br>\n");
198           }
199       }
200   
201     // loop over all the input files, list the file contents
202     if (i_flag == 0 && f_flag == 0)       // default is stdin
203       { read_file("-", ofile); }
204     else
205       {
206         for (struct name *p = filenames->head; p != NULL; p = p->next)
207           { read_file(p->name, ofile); }
208       }
209   
210     fprintf(ofile, "<br><hr style=\"width: 100%%; height: 2px;\"><br>\n");
211   
212     time_t now = time(NULL);
213     char buf[26];
214     ctime_r(&now, buf);
215     buf[24] = '\0';       // clean up the newline
216     fprintf(ofile, "This page generated %s by <span style=\"font-family: monospace;\">%s</span><br>\n", buf, prog);
217   
218     fprintf(ofile, "<br>\n");
219     fprintf(ofile, "</body>\n");
220     fprintf(ofile, "</html>\n");
221   
222     if (ofile != stdout && fclose(ofile) != 0)
223       {
224         fprintf(stderr, "%s: failed: could not close output file %s: %s\n", prog, o_val, strerror(errno));
225         // exit(EXIT_FAILURE);
226       }
227   
228     list_names_deallocate(filenames);
229   
230     return status;
231   }
232   
233   //------------------------------------------------------------------------------
234   
235   static void read_file(const char *infile, FILE *out)
236   {
237     verify(infile != NULL, "null arg infile");
238     verify(out != NULL, "null arg out");
239   
240     FILE *in;
241     int errnum = 0;
242   
243     if (strcmp(infile, "-") == 0)
244       { in = stdin; }
245     else
246       {
247         in = fopen(infile, "r");
248         if (in == NULL)
249           { errnum = errno; }
250       }
251   
252     fprintf(out, "<br><hr style=\"width: 100%%; height: 2px;\"><br>\n");
253     fprintf(out, "<span style=\"font-style: italic;\">file:</span>&nbsp;\n");
254     fprintf(out, "<span style=\"font-family: monospace; font-weight: bold;\">");
255     spacetab_expand(infile, out);
256     fprintf(out, "</span><br>\n");
257   
258     fprintf(out, "<pre>\n");
259     if (in == NULL)
260       { fprintf(out, "ERROR: could not open %s: %s\n", infile, strerror(errnum)); }
261     else
262       { read_lines(in, out); }
263     fprintf(out, "</pre>\n");
264   
265     if (in != NULL && in != stdin && fclose(in) != 0)
266       {
267         fprintf(stderr, "%s: failed: could not close input file %s: %s\n", prog, infile, strerror(errno));
268         // exit(EXIT_FAILURE);
269       }
270   }
271   
272   //------------------------------------------------------------------------------
273   
274   // the output of this function appears inside a <pre> section,
275   //   so we don't need to translate ' ' to "&nbsp;",
276   //   but we do need to translate tabs to spaces, to compensate for the leading line number
277   
278   static void read_lines(FILE *in, FILE *out)
279   {
280     verify(in != NULL, "null arg in");
281     verify(out != NULL, "null arg out");
282   
283     int line_number = 0;
284     int c;                // current character
285     int p = '\n';         // previous character
286     int tab_count = 8;
287   
288     while ((c = getc(in)) != EOF)
289       {
290         if (p == '\n')
291           {
292             fprintf(out, "<span style=\"font-style: italic;\">%3d</span>   ", ++line_number);
293             tab_count = 8;
294           }
295   
296         switch (c) {
297           case '\t':      for (int i = 0; i < tab_count; i++) { fprintf(out, " "); }
298                                                           tab_count = 0;  break;
299           case '<':       fprintf(out, "&lt;");           tab_count -= 1; break;
300           case '>':       fprintf(out, "&gt;");           tab_count -= 1; break;
301           case '&':       fprintf(out, "&amp;");          tab_count -= 1; break;
302           default:        fprintf(out, "%c", c);          tab_count -= 1; break;
303         }
304   
305         p = c;
306         if (tab_count <= 0) tab_count += 8;
307       }
308   
309     if (feof(in))         // end of file
310       { fprintf(out, "EOF\n"); }
311   
312     if (ferror(in))       // error when reading the file
313       { fprintf(out, "EOF -- read error: %s\n", strerror(errno)); }
314   }
315   
316   //------------------------------------------------------------------------------
317   
318   static void spacetab_expand(const char *str, FILE *out)
319   {
320     int tab_count = 8;
321   
322     for (char *p = (char *) str; *p != '\0'; p++)
323       {
324         switch(*p) {
325           case '\t':      for (int i = 0; i < tab_count; i++) { fprintf(out, "&nbsp;"); }
326                                                           tab_count = 0;  break;
327           case ' ':       fprintf(out, "&nbsp;");         tab_count -= 1; break;
328           default:        fprintf(out, "%c", *p);         tab_count -= 1; break;
329         }
330   
331         if (tab_count <= 0) tab_count += 8;
332       }
333   }
334   
335   //------------------------------------------------------------------------------
336   
EOF



This page generated Mon Feb 25 08:48:36 2013 by ./c2html