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, " %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, " <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> \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 " ",
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, "<"); tab_count -= 1; break;
300 case '>': fprintf(out, ">"); tab_count -= 1; break;
301 case '&': fprintf(out, "&"); 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, " "); }
326 tab_count = 0; break;
327 case ' ': fprintf(out, " "); 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