1 /* quick and dirty copy of the
2  * highlight (http://freshmeat.net/projects/highlight/) command */
3 #include "ex_utils.h"
4 
5 #include <sys/time.h>
6 #include <time.h>
7 
8 static struct
9 {
10  const char *name;
11  const char *seq;
12  size_t off;
13  size_t eoff;
14  size_t len;
15 } ex_hl_seqs[] =
16 /* This assumes a certain style...
17 
18    for instance neither of...
19 
20    if(foo)
21    foo;if (bar)
22 
23    ...will be seen as an if statement, as there is no space before and after
24    the if. And using TABs instead of spaces will make it not work.
25    These are all features, as the style should be the same.
26 */
27 #define EX_HL_SEQ_T(x, y) \
28    x, " "  y " ", 1, 1, 0 }, \
29  { x, "("  y " ", 1, 1, 0 }, \
30  { x, " "  y ")", 1, 1, 0 }, \
31  { x, "("  y ")", 1, 1, 0 }, \
32  { x, "*"  y " ", 1, 1, 0 }, \
33  { x, "\n" y " ", 1, 1, 0
34 #define EX_HL_SEQ_SP(x, y) \
35    x, " " y " ",  1, 1, 0
36 #define EX_HL_SEQ_WS(x, y) \
37    x, " " y " ",  1, 1, 0 }, \
38  { x, " " y "\n", 1, 1, 0
39 /* custom for default and break */
40 #define EX_HL_SEQ_DEF(x, y) \
41    x, " " y ": ", 1, 2, 0 }, \
42  { x, " " y ":\n", 1, 2, 0
43 #define EX_HL_SEQ_BRK(x, y) \
44    x, " " y ";\n", 1, 2, 0
45 #define EX_HL_SEQ_SB(x, y) \
46    x, " " y " (", 1, 2, 0
47 #define EX_HL_SEQ_RET(x, y) \
48    x, " " y " (",  1, 2, 0 }, \
49  { x, " " y ";\n", 1, 2, 0
50 #define EX_HL_SEQ_B(x, y)  \
51    x, " " y "(", 1, 1, 0
52 #define EX_HL_SEQ_VAL(x, y) \
53    x, " "  y " ",  1, 1, 0 }, \
54  { x, " "  y "\n", 1, 1, 0 }, \
55  { x, " "  y ";",  1, 1, 0 }, \
56  { x, " "  y ",",  1, 1, 0 }, \
57  { x, " "  y ")",  1, 1, 0 }, \
58  { x, "("  y " ",  1, 1, 0 }, \
59  { x, "("  y ")",  1, 1, 0 }, \
60  { x, "("  y ",",  1, 1, 0
61 #define EX_HL_SEQ_CPP(x, y) \
62    x, "#"     y " ",  1, 1, 0 }, \
63  { x, "#"     y "\n", 1, 1, 0 }, \
64  { x, "# "    y " ",  2, 1, 0 }, \
65  { x, "# "    y "\n", 2, 1, 0 }, \
66  { x, "#  "   y " ",  3, 1, 0 }, \
67  { x, "#  "   y "\n", 3, 1, 0 }, \
68  { x, "#   "  y " ",  4, 1, 0 }, \
69  { x, "#   "  y "\n", 4, 1, 0 }, \
70  { x, "#    " y " ",  5, 1, 0 }, \
71  { x, "#    " y "\n", 5, 1, 0
72 {
73  /* order matters */
74  { EX_HL_SEQ_T("vstrbase", "Vstr_base") },
75  { EX_HL_SEQ_T("vstrsects", "Vstr_sects") },
76  { EX_HL_SEQ_T("vstrfmt", "Vstr_fmt_spec") },
77  { EX_HL_SEQ_T("pollfd", "struct pollfd") },
78  { EX_HL_SEQ_T("mpzt", "mpz_t") },
79 
80  { EX_HL_SEQ_CPP("cppif", "if") },
81  { EX_HL_SEQ_CPP("cppifndef", "ifndef") },
82  { EX_HL_SEQ_CPP("cppifdef", "ifdef") },
83  { EX_HL_SEQ_CPP("cppelse", "else") },
84  { EX_HL_SEQ_CPP("cppendif", "endif") },
85  { EX_HL_SEQ_CPP("cppdefine", "define") },
86  { EX_HL_SEQ_CPP("cppinclude", "include") },
87  { EX_HL_SEQ_CPP("cppelif", "elif") },
88  { EX_HL_SEQ_SB("cppdefined", "defined") },
89  { EX_HL_SEQ_B("cppdefined", "defined") },
90 
91  { EX_HL_SEQ_SB("if", "if") },
92  { EX_HL_SEQ_WS("else", "else") },
93  { EX_HL_SEQ_WS("do", "do") },
94  { EX_HL_SEQ_SB("while", "while") },
95  { EX_HL_SEQ_SB("for", "for") },
96  { EX_HL_SEQ_RET("return", "return") },
97  { EX_HL_SEQ_SB("switch", "switch") },
98  { EX_HL_SEQ_SP("case", "case") },
99  { EX_HL_SEQ_DEF("default", "default") },
100  { EX_HL_SEQ_BRK("break", "break") },
101  { EX_HL_SEQ_SP("goto", "goto") },
102  { EX_HL_SEQ_T("extern", "extern") },
103  { EX_HL_SEQ_T("static", "static") },
104  { EX_HL_SEQ_T("const", "const") },
105  { EX_HL_SEQ_T("restrict", "restrict") },
106  { EX_HL_SEQ_T("inline", "inline") },
107  { EX_HL_SEQ_T("void", "void") },
108  { EX_HL_SEQ_T("unsigned", "unsigned") },
109  { EX_HL_SEQ_T("char", "char") },
110  { EX_HL_SEQ_T("short", "short") },
111  { EX_HL_SEQ_T("int", "int") },
112  { EX_HL_SEQ_T("long", "long") },
113  { EX_HL_SEQ_T("float", "float") },
114  { EX_HL_SEQ_T("double", "double") },
115  { EX_HL_SEQ_T("ssizet", "ssize_t") },
116  { EX_HL_SEQ_T("sizet", "size_t") },
117  { EX_HL_SEQ_T("offt", "off_t") },
118  { EX_HL_SEQ_T("off64t", "off64_t") },
119  { EX_HL_SEQ_T("intmaxt", "intmax_t") },
120  { EX_HL_SEQ_T("uintmaxt", "uintmax_t") },
121 
122  { EX_HL_SEQ_SB("exit", "exit") },
123  { EX_HL_SEQ_SB("abort", "abort") },
124  { EX_HL_SEQ_B("err", "err") },
125  { EX_HL_SEQ_B("err", "errx") },
126  { EX_HL_SEQ_B("warn", "warn") },
127  { EX_HL_SEQ_B("warn", "warnx") },
128  { EX_HL_SEQ_B("assert", "assert") },
129  { EX_HL_SEQ_B("assert", "ASSERT") },
130  { EX_HL_SEQ_B("assert", "assert_ret") },
131  { EX_HL_SEQ_B("assert", "ASSERT_RET") },
132  { EX_HL_SEQ_B("assert", "assert_ret_void") },
133  { EX_HL_SEQ_B("assert", "ASSERT_RET_VOID") },
134  { EX_HL_SEQ_B("assert", "assert_no_switch_def") },
135  { EX_HL_SEQ_B("assert", "ASSERT_NO_SWITCH_DEF") },
136 
137  { EX_HL_SEQ_VAL("compdate", "__DATE__") },
138  { EX_HL_SEQ_VAL("compversion", "__VERSION__") },
139  { EX_HL_SEQ_VAL("compfile", "__FILE__") },
140  { EX_HL_SEQ_VAL("compline", "__LINE__") },
141  { EX_HL_SEQ_VAL("charbit", "CHAR_BIT") },
142  { EX_HL_SEQ_VAL("intmax", "INT_MAX") },
143  { EX_HL_SEQ_VAL("intmin", "INT_MIN") },
144  { EX_HL_SEQ_VAL("intjmax", "INTMAX_MAX") },
145  { EX_HL_SEQ_VAL("intjmin", "INTMAX_MIN") },
146  { EX_HL_SEQ_VAL("stdin", "stdin") },
147  { EX_HL_SEQ_VAL("stdout", "stdout") },
148  { EX_HL_SEQ_VAL("stderr", "stderr") },
149  { EX_HL_SEQ_VAL("stdin", "STDIN_FILENO") },
150  { EX_HL_SEQ_VAL("stdout", "STDOUT_FILENO") },
151  { EX_HL_SEQ_VAL("stderr", "STDERR_FILENO") },
152  { EX_HL_SEQ_VAL("errno", "errno") },
153  { EX_HL_SEQ_VAL("exitsucs", "EXIT_SUCCESS") },
154  { EX_HL_SEQ_VAL("exitfail", "EXIT_FAILURE") },
155  { EX_HL_SEQ_VAL("true", "TRUE") },
156  { EX_HL_SEQ_VAL("false", "FALSE") },
157  { EX_HL_SEQ_VAL("null", "NULL") },
158  { EX_HL_SEQ_VAL("num0",   "0") },
159  { EX_HL_SEQ_VAL("numm1", "-1") },
160 
161  {NULL, NULL, 0, 0, 0},
162 };
163 static size_t ex_hl_max_seq_len = 0;
164 
165 #define C_DEF 0
166 #define C_SEQ 1
167 #define C_STR 2
168 #define C_CHR 3
169 #define C_CMO 4
170 #define C_CMN 5
171 
ex_hl_mov_clean(Vstr_base * s1,Vstr_base * s2,size_t len)172 static void ex_hl_mov_clean(Vstr_base *s1, Vstr_base *s2, size_t len)
173 { /* html the elements... */
174   while (len > 0)
175   {
176     size_t count = vstr_cspn_cstr_chrs_fwd(s2, 1, len, "&<>");
177 
178     vstr_add_vstr(s1, s1->len, s2, 1, count, VSTR_TYPE_ADD_BUF_REF);
179     vstr_del(s2, 1, count);
180     len -= count;
181 
182     if (count)
183       continue;
184 
185     --len;
186 
187     switch (vstr_export_chr(s2, 1))
188     {
189       case '<': /* html stuff ... */
190         vstr_add_cstr_ptr(s1, s1->len, "&lt;");
191         vstr_del(s2, 1, 1);
192         continue;
193 
194       case '>':
195         vstr_add_cstr_ptr(s1, s1->len, "&gt;");
196         vstr_del(s2, 1, 1);
197         continue;
198 
199       case '&':
200         vstr_add_cstr_ptr(s1, s1->len, "&amp;");
201         vstr_del(s2, 1, 1);
202         continue;
203 
204       default:
205         ASSERT(FALSE);
206         break;
207     }
208   }
209 }
210 
211 static int first_time = FALSE;
212 
ex_hl_process(Vstr_base * s1,Vstr_base * s2,int last)213 static int ex_hl_process(Vstr_base *s1, Vstr_base *s2, int last)
214 {
215   static unsigned int state = C_DEF;
216 
217   /* we don't want to create more data, if we are over our limit */
218   if (s1->len > EX_MAX_W_DATA_INCORE)
219     return (FALSE);
220 
221   if (s2->len < ex_hl_max_seq_len)
222   {
223     if (!s2->len || !last)
224       return (FALSE);
225   }
226 
227   while (((s2->len >= ex_hl_max_seq_len) || (s2->len && last)) &&
228          (s1->len <= EX_MAX_W_DATA_INCORE))
229   {
230     size_t len = 0;
231 
232     switch (state)
233     {
234       case C_DEF:
235         /* this is pretty intimately tied with the array above */
236         len = vstr_cspn_cstr_chrs_fwd(s2, 1, s2->len, "# \n(*\"'/&<>");
237         vstr_add_vstr(s1, s1->len, s2, 1, len, VSTR_TYPE_ADD_BUF_REF);
238         vstr_del(s2, 1, len);
239         state = C_SEQ;
240         break;
241 
242       case C_SEQ:
243       {
244         unsigned int scan = 0;
245         const char *seq = NULL;
246 
247         switch (vstr_export_chr(s2, 1))
248         {
249           case '\'':
250             state = C_CHR;
251             vstr_add_cstr_ptr(s1, s1->len, "<span class=\"chr\">'");
252             vstr_del(s2, 1, 1);
253             continue;
254 
255           case '/':
256             if (s2->len < 2)
257               break;
258 
259             if (vstr_cmp_cstr_eq(s2, 1, 2, "/*"))
260             {
261               state = C_CMO;
262               vstr_add_cstr_ptr(s1, s1->len, "<span class=\"comment\">/*");
263               vstr_del(s2, 1, 2);
264               continue;
265             }
266             if (vstr_cmp_cstr_eq(s2, 1, 2, "//"))
267             {
268               state = C_CMN;
269               vstr_add_cstr_ptr(s1, s1->len, "<span class=\"comment\">//");
270               vstr_del(s2, 1, 2);
271               continue;
272             }
273             break;
274 
275           case '"':
276             state = C_STR;
277             vstr_add_cstr_ptr(s1, s1->len, "<span class=\"str\">\"");
278             vstr_del(s2, 1, 1);
279             continue;
280 
281           case '<': /* html stuff ... */
282             vstr_add_cstr_ptr(s1, s1->len, "&lt;");
283             vstr_del(s2, 1, 1);
284             continue;
285 
286           case '>':
287             vstr_add_cstr_ptr(s1, s1->len, "&gt;");
288             vstr_del(s2, 1, 1);
289             continue;
290 
291           case '&':
292             vstr_add_cstr_ptr(s1, s1->len, "&amp;");
293             vstr_del(s2, 1, 1);
294             continue;
295 
296           default:
297             break;
298         }
299 
300         while (ex_hl_seqs[scan].name)
301         {
302           seq = ex_hl_seqs[scan].seq;
303           len = ex_hl_seqs[scan].len;
304 
305           if ((len <= s2->len) && vstr_cmp_buf_eq(s2, 1, len, seq, len))
306           {
307             size_t off  = ex_hl_seqs[scan].off;
308             size_t eoff = ex_hl_seqs[scan].eoff;
309             unsigned int mid = len - (off + eoff);
310 
311             if (first_time)
312             {
313               ASSERT(off && (vstr_export_chr(s2, 1) == '\n'));
314               vstr_del(s2, 1, 1);
315               --off;
316               first_time = FALSE;
317             }
318 
319             vstr_add_vstr(s1, s1->len, s2, 1, off, VSTR_TYPE_ADD_BUF_REF);
320             vstr_del(s2, 1, off);
321 
322             vstr_add_cstr_ptr(s1, s1->len, "<span class=\"");
323             vstr_add_cstr_ptr(s1, s1->len, ex_hl_seqs[scan].name);
324             vstr_add_cstr_ptr(s1, s1->len, "\">");
325 
326             vstr_add_vstr(s1, s1->len, s2, 1, mid, VSTR_TYPE_ADD_BUF_REF);
327             vstr_del(s2, 1, mid);
328 
329             vstr_add_cstr_ptr(s1, s1->len, "</span>");
330             /* don't output end marker */
331             break;
332           }
333 
334           ++scan;
335         }
336 
337         if (!ex_hl_seqs[scan].name)
338         {
339           if (first_time)
340           {
341             ASSERT(vstr_export_chr(s2, 1) == '\n');
342             first_time = FALSE;
343           }
344           else
345             vstr_add_vstr(s1, s1->len, s2, 1, 1, VSTR_TYPE_ADD_BUF_REF);
346           vstr_del(s2, 1, 1);
347         }
348 
349         state = C_DEF;
350       }
351       break;
352 
353       case C_CMO:
354         len = vstr_srch_cstr_buf_fwd(s2, 1, s2->len, "*/");
355         if (!len)
356         {
357           ex_hl_mov_clean(s1, s2, s2->len);
358           return (TRUE);
359         }
360 
361         ++len; /* move to last character */
362 
363         state = C_DEF;
364         ex_hl_mov_clean(s1, s2, len);
365         vstr_add_cstr_ptr(s1, s1->len, "</span>");
366         break;
367 
368       case C_CMN:
369         len = vstr_srch_chr_fwd(s2, 1, s2->len, '\n');
370         if (!len)
371         {
372           ex_hl_mov_clean(s1, s2, s2->len);
373           return (TRUE);
374         }
375 
376         state = C_DEF;
377         ex_hl_mov_clean(s1, s2, len);
378         vstr_add_cstr_ptr(s1, s1->len, "</span>");
379         break;
380 
381       case C_CHR:
382         len = vstr_srch_cstr_buf_fwd(s2, 1, s2->len, "'");
383         if (!len)
384         {
385           ex_hl_mov_clean(s1, s2, s2->len);
386           return (TRUE);
387         }
388 
389         if ((len == 1) || /* even number of \'s going backwards */
390             (!(vstr_spn_cstr_chrs_rev(s2, 1, len - 1, "\\") & 1)))
391           state = C_DEF;
392         ex_hl_mov_clean(s1, s2, len);
393         if (state == C_DEF)
394           vstr_add_cstr_ptr(s1, s1->len, "</span>");
395         break;
396 
397       case C_STR:
398         len = vstr_srch_cstr_buf_fwd(s2, 1, s2->len, "\"");
399         if (!len)
400         {
401           ex_hl_mov_clean(s1, s2, s2->len);
402           return (TRUE);
403         }
404 
405         if ((len == 1) || /* even number of \'s going backwards */
406             (!(vstr_spn_cstr_chrs_rev(s2, 1, len - 1, "\\") & 1)))
407           state = C_DEF;
408         ex_hl_mov_clean(s1, s2, len);
409         if (state == C_DEF)
410           vstr_add_cstr_ptr(s1, s1->len, "</span>");
411         break;
412 
413 
414       default:
415         ASSERT(FALSE);
416     }
417   }
418 
419   return (TRUE);
420 }
421 
ex_hl_process_limit(Vstr_base * s1,Vstr_base * s2,unsigned int lim)422 static void ex_hl_process_limit(Vstr_base *s1, Vstr_base *s2, unsigned int lim)
423 {
424   while (s2->len > lim)
425   {
426     int proc_data = ex_hl_process(s1, s2, !lim);
427     if (!proc_data && (io_put(s1, STDOUT_FILENO) == IO_BLOCK))
428       io_block(-1, STDOUT_FILENO);
429   }
430 }
431 
ex_hl_read_fd_write_stdout(Vstr_base * s1,Vstr_base * s2,int fd)432 static void ex_hl_read_fd_write_stdout(Vstr_base *s1, Vstr_base *s2, int fd)
433 {
434   while (TRUE)
435   {
436     int io_w_state = IO_OK;
437     int io_r_state = io_get(s2, fd);
438 
439     if (io_r_state == IO_EOF)
440       break;
441 
442     ex_hl_process(s1, s2, FALSE);
443 
444     io_w_state = io_put(s1, 1);
445 
446     io_limit(io_r_state, fd, io_w_state, 1, s1);
447   }
448 
449   ex_hl_process_limit(s1, s2, 0);
450 }
451 
ex_hl_ctime(time_t val)452 static const char *ex_hl_ctime(time_t val)
453 {
454   static char ret[4096];
455 
456   strftime(ret, sizeof(ret), "%c", localtime(&val));
457 
458   return (ret);
459 }
460 
base_fname(const char * s1)461 static const char *base_fname(const char *s1)
462 {
463   const char *sname = strrchr(s1, '/');
464 
465   if (sname)
466     ++sname;
467   else
468     sname = s1;
469 
470   return (sname);
471 }
472 
ex_hl_block_beg(Vstr_base * s1,const char * block_type,const char * block_beg,const char * attr_id,const char * attr_class)473 static void ex_hl_block_beg(Vstr_base *s1, const char *block_type,
474                             const char *block_beg,
475                             const char *attr_id, const char *attr_class)
476 {
477   vstr_add_fmt(s1, s1->len, "<%s", block_type);
478   if (attr_id)
479     vstr_add_fmt(s1, s1->len, " id=\"%s\"", attr_id);
480   if (attr_class)
481     vstr_add_fmt(s1, s1->len, " class=\"%s\"", attr_class);
482   vstr_add_fmt(s1, s1->len, ">%s", block_beg);
483 }
ex_hl_block_end(Vstr_base * s1,const char * block_end,const char * block_type,unsigned int comments,time_t timestamp,const char * fname)484 static void ex_hl_block_end(Vstr_base *s1, const char *block_end,
485                             const char *block_type,
486                             unsigned int comments,
487                             time_t timestamp, const char *fname)
488 {
489   vstr_add_fmt(s1, s1->len, "%s</%s>\n", block_end, block_type);
490   if (comments)
491     vstr_add_fmt(s1, s1->len,
492                  "<!-- C to html convertion of %s -->\n"
493                  "<!--   done on %s -->\n"
494                  "<!--   done by jhighlight -->\n",
495                  fname, ex_hl_ctime(timestamp));
496 }
497 
main(int argc,char * argv[])498 int main(int argc, char *argv[])
499 {
500   Vstr_base *s2 = NULL;
501   Vstr_base *s1 = ex_init(&s2);
502   int count = 1;
503   time_t now = time(NULL);
504   unsigned int use_mmap = FALSE;
505   unsigned int comments = TRUE;
506   const char *cssfile = NULL;
507   const char *block_type = NULL;
508   const char *block_beg = "\n";
509   const char *block_end = NULL;
510   const char *attr_id = NULL;
511   const char *attr_class = "c2html";
512 
513   {
514     size_t scan = 0;
515 
516     while (ex_hl_seqs[scan].name)
517     {
518       size_t len = strlen(ex_hl_seqs[scan].seq);
519 
520       ex_hl_seqs[scan].len = len;
521 
522       if (ex_hl_max_seq_len < len)
523         ex_hl_max_seq_len = len;
524 
525       ++scan;
526     }
527   }
528 
529   /* parse command line arguments... */
530   while (count < argc)
531   { /* quick hack getopt_long */
532     if (!strcmp("--", argv[count]))
533     {
534       ++count;
535       break;
536     }
537     else if (!strcmp("--comments", argv[count])) /* toggle use of mmap */
538       comments = !comments;
539     else if (!strcmp("--mmap", argv[count])) /* toggle use of mmap */
540       use_mmap = !use_mmap;
541     EX_UTILS_GETOPT_CSTR("beg",     block_beg);
542     EX_UTILS_GETOPT_CSTR("cssfile",      cssfile);
543     EX_UTILS_GETOPT_CSTR("css-file",     cssfile);
544     EX_UTILS_GETOPT_CSTR("cssfilename",  cssfile);
545     EX_UTILS_GETOPT_CSTR("css-filename", cssfile);
546     EX_UTILS_GETOPT_CSTR("class",   attr_class);
547     EX_UTILS_GETOPT_CSTR("end",     block_end);
548     EX_UTILS_GETOPT_CSTR("id",      attr_id);
549     EX_UTILS_GETOPT_CSTR("type",    block_type);
550     else if (!strcmp("--version", argv[count]))
551     { /* print version and exit */
552       vstr_add_fmt(s1, 0, "%s", "\
553 jhighlight 1.0.0\n\
554 Written by James Antill\n\
555 \n\
556 Uses Vstr string library.\n\
557 ");
558       goto out;
559     }
560     else if (!strcmp("--help", argv[count]))
561     { /* print version and exit */
562       vstr_add_fmt(s1, 0, "%s", "\
563 Usage: jhighlight [STRING]...\n\
564    or: jhighlight OPTION\n\
565 Output filenames in html converteed from C.\n\
566 \n\
567       --help         - Display this help and exit\n\
568       --version      - Output version information and exit\n\
569       --mmap         - Toggle use of mmap() to load input files\n\
570       --comments     - Toggle output of attribution comments\n\
571       --beg          - Extra text to output at the begining\n\
572       --css-filename - Location of css used in HTML.\n\
573       --class        - Class name for block\n\
574       --end          - Extra text to output at the ending\n\
575       --id           - Id name for block\n\
576       --type         - Name for block (Eg. \"pre\" or \"code\" etc.)\n\
577       --             - Treat rest of cmd line as input filenames\n\
578 \n\
579 Report bugs to James Antill <james@and.org>.\n\
580 ");
581       goto out;
582     }
583     else
584       break;
585     ++count;
586   }
587 
588   if (cssfile    && !*cssfile)    cssfile    = NULL;
589   if (block_type && !*block_type) block_type = NULL;
590   if (attr_id    && !*attr_id)    attr_id    = NULL;
591   if (attr_class && !*attr_class) attr_class = NULL;
592 
593   if (!block_type) block_type = "pre";
594   if (!block_end)  block_end  = "";
595 
596   if (cssfile)
597   {
598     int scan = count;
599 
600     vstr_add_cstr_ptr(s1, s1->len, "\
601 <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n\
602 <html>\n\
603   <head>\n\
604     <title>");
605 
606     if (scan >= argc)
607       vstr_add_cstr_ptr(s1, s1->len, "c2html for: STDIN");
608     else
609       vstr_add_fmt(s1, s1->len, "c2html for: %s", base_fname(argv[scan++]));
610 
611     while (scan < argc)
612       vstr_add_fmt(s1, s1->len, ", %s", base_fname(argv[scan++]));
613 
614     vstr_add_cstr_ptr(s1, s1->len, "</title>\n\
615     <link rel=\"stylesheet\" type=\"text/css\" href=\"");
616     vstr_add_cstr_ptr(s1, s1->len, cssfile);
617     vstr_add_cstr_ptr(s1, s1->len, "\">\n\
618   </head>\n\
619   <body>\n");
620   }
621 
622   /* if no arguments are given just do stdin to stdout */
623   if (count >= argc)
624   {
625     io_fd_set_o_nonblock(STDIN_FILENO);
626 
627     if (cssfile)
628       vstr_add_cstr_ptr(s1, s1->len, "     <h1>STDIN</h1>\n");
629 
630     ex_hl_block_beg(s1, block_type, block_beg, attr_id, attr_class);
631     first_time = TRUE;
632     vstr_add_rep_chr(s2, s2->len, '\n', 1);
633     ex_hl_read_fd_write_stdout(s1, s2, STDIN_FILENO);
634     ex_hl_block_end(s1, block_end, block_type, comments, now, "stdin");
635   }
636 
637   /* loop through all arguments, open the file specified
638    * and do the read/write loop */
639   while (count < argc)
640   {
641     unsigned int ern = 0;
642 
643     ASSERT(!s2->len);
644     first_time = TRUE;
645     vstr_add_rep_chr(s2, s2->len, '\n', 1); /* add to begining */
646 
647     if (use_mmap && (s2->len <= EX_MAX_R_DATA_INCORE))
648       vstr_sc_mmap_file(s2, s2->len, argv[count], 0, 0, &ern);
649 
650     if (cssfile)
651       vstr_add_fmt(s1, s1->len, "     <h1>%s</h1>\n", base_fname(argv[count]));
652 
653     ex_hl_block_beg(s1, block_type, block_beg, attr_id, attr_class);
654 
655     if (!use_mmap ||
656         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_FSTAT_ERRNO) ||
657         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_MMAP_ERRNO) ||
658         (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_TOO_LARGE))
659     {
660       int fd = io_open(argv[count]);
661 
662       ex_hl_read_fd_write_stdout(s1, s2, fd);
663 
664       if (close(fd) == -1)
665         warn("close(%s)", argv[count]);
666     }
667     else if (ern && (ern != VSTR_TYPE_SC_MMAP_FILE_ERR_CLOSE_ERRNO))
668       err(EXIT_FAILURE, "mmap");
669     else
670       ex_hl_process_limit(s1, s2, 0);
671 
672     ex_hl_block_end(s1, block_end, block_type, comments, now, argv[count]);
673 
674     ++count;
675   }
676 
677   if (cssfile)
678   {
679     vstr_add_cstr_ptr(s1, s1->len, "\n\
680   </body>\n\
681 </html>\n");
682   }
683 
684  out:
685   io_put_all(s1, STDOUT_FILENO);
686 
687   exit (ex_exit(s1, s2));
688 }
689