1 /* quick but useful static ssi processor.
2 */
3 #include "ex_utils.h"
4 #include "opt.h"
5
6 #include <sys/time.h>
7 #include <time.h>
8
9 #include <pwd.h>
10
11 #include <dirent.h>
12
13 #include <getopt.h>
14
15 #define USE_POPEN 1 /* hacky ... */
16
17 #if USE_POPEN
18 #include <stdio.h>
19 #else
20 /* spawn.h doesn't exist on MacOSX ... *sigh*/
21 #include <spawn.h>
22 #endif
23
24 #define EX_SSI_FAILED(x) do { \
25 vstr_add_fmt(s1, s1->len, \
26 "<!-- \"%s\" SSI command FAILED -->\n", (x)); \
27 goto ssi_parse_failed; \
28 } \
29 while (FALSE)
30
31 #define EX_SSI_OK(x, sf, p, l, end) do { \
32 vstr_add_fmt(s1, s1->len, "<!-- \"%s ${vstr:%p%zu%zu%u}" \
33 "\" SSI command OK -->%s", \
34 (x), (sf), (p), (l), VSTR_TYPE_ADD_ALL_REF, \
35 (end) ? "\n" : ""); \
36 } \
37 while (FALSE)
38
39
40
41
42 static char *timespec = NULL;
43 static int use_size_abbrev = TRUE;
44
ex_ssi_cat_read_fd_write_stdout(Vstr_base * s1,int fd)45 static void ex_ssi_cat_read_fd_write_stdout(Vstr_base *s1, int fd)
46 {
47 while (TRUE)
48 {
49 int io_w_state = IO_OK;
50 int io_r_state = io_get(s1, fd);
51
52 if (io_r_state == IO_EOF)
53 break;
54
55 io_w_state = io_put(s1, 1);
56
57 io_limit(io_r_state, fd, io_w_state, 1, s1);
58 }
59 }
60
ex_ssi_srch_end(Vstr_base * s2,size_t pos,size_t len)61 static size_t ex_ssi_srch_end(Vstr_base *s2, size_t pos, size_t len)
62 {
63 int instr = FALSE;
64
65 while (TRUE)
66 {
67 size_t srch = vstr_cspn_cstr_chrs_fwd(s2, pos, len, "\\\"-");
68
69 pos += srch;
70 len -= srch;
71
72 if (!len)
73 break;
74
75 if (0) { }
76 else if (!instr && vstr_export_chr(s2, pos) == '-')
77 { /* see if it's the end... */
78 if (len < strlen("-->"))
79 return (0);
80
81 if (vstr_cmp_bod_cstr_eq(s2, pos, len, "-->"))
82 return (pos + strlen("->"));
83 }
84 else if (!instr && vstr_export_chr(s2, pos) == '\\')
85 { /* do nothing */ }
86 else if ( instr && vstr_export_chr(s2, pos) == '\\')
87 {
88 if (len > 1)
89 {
90 ++pos;
91 --len;
92 }
93 }
94 else if (vstr_export_chr(s2, pos) == '"')
95 { instr = !instr; }
96
97 ++pos;
98 --len;
99 }
100
101 return (0);
102 }
103
ex_ssi_skip_val(Vstr_base * s2,size_t * srch,size_t val)104 static inline void ex_ssi_skip_val(Vstr_base *s2, size_t *srch, size_t val)
105 {
106 vstr_del(s2, 1, val);
107 *srch -= val;
108 }
109
ex_ssi_skip_wsp(Vstr_base * s2,size_t * srch)110 static inline void ex_ssi_skip_wsp(Vstr_base *s2, size_t *srch)
111 {
112 size_t val = vstr_spn_cstr_chrs_fwd(s2, 1, *srch, " ");
113
114 ex_ssi_skip_val(s2, srch, val);
115 }
116
ex_ssi_skip_str(Vstr_base * s2,size_t * srch,const char * passed_val)117 static inline void ex_ssi_skip_str(Vstr_base *s2, size_t *srch,
118 const char *passed_val)
119 {
120 size_t val = strlen(passed_val);
121
122 ex_ssi_skip_val(s2, srch, val);
123 }
124
ex_ssi_attr_val(Vstr_base * s2,size_t * srch)125 static size_t ex_ssi_attr_val(Vstr_base *s2, size_t *srch)
126 {
127 size_t pos = 1;
128 size_t len = *srch;
129 size_t ret = 0;
130
131 while (TRUE)
132 {
133 size_t scan = vstr_cspn_cstr_chrs_fwd(s2, pos, len, "\\\"");
134
135 if (scan == len)
136 return (0);
137
138 ret += scan;
139
140 if (vstr_export_chr(s2, pos + scan) != '\\')
141 break;
142
143 if (scan == (len - 1))
144 return (0);
145
146 ++ret;
147
148 vstr_del(s2, pos + scan, 1);
149 pos += scan + 1;
150 len -= scan + 1;
151 --*srch;
152
153 ASSERT(len);
154 }
155
156 return (ret);
157 }
158
ex_ssi_file_attr(Vstr_base * s2,size_t * srch)159 static size_t ex_ssi_file_attr(Vstr_base *s2, size_t *srch)
160 {
161 size_t ret = 0;
162
163 ex_ssi_skip_wsp(s2, srch);
164
165 if (vstr_cmp_case_bod_cstr_eq(s2, 1, *srch, "file=\""))
166 ex_ssi_skip_str(s2, srch, "file=\"");
167 else
168 return (0);
169
170 if (!(ret = ex_ssi_attr_val(s2, srch)))
171 return (0);
172
173 return (ret);
174 }
175
176 #if !USE_POPEN
ex_ssi_spawn_r(const char * prog,pid_t * pid,char * argv[],char * env[])177 static int ex_ssi_spawn_r(const char *prog, pid_t *pid,
178 char *argv[], char *env[])
179 {
180 posix_spawn_file_actions_t acts[1];
181 int fds[2];
182 char *dummy_env[] = {NULL};
183
184 if (!env)
185 env = dummy_env;
186
187 if ((errno = posix_spawn_file_actions_init(acts)))
188 err(EXIT_FAILURE, "spawn_make");
189
190 if (pipe(fds) == -1)
191 err(EXIT_FAILURE, "pipe");
192
193 if ((errno = posix_spawn_file_actions_adddup2(attr, fds[1], FILENO_STDOUT)))
194 err(EXIT_FAILURE, "spawn_dup2");
195 if ((errno = posix_spawn_file_actions_addclose(attr, fds[0])))
196 err(EXIT_FAILURE, "spawn_close");
197 if ((errno = posix_spawn_file_actions_addclose(attr, fds[1])))
198 err(EXIT_FAILURE, "spawn_close");
199
200 if ((errno = posix_spawnp(pid, prog, acts, NULL, argv, NULL)))
201 err(EXIT_FAILURE, "spawn");
202
203 if ((errno = posix_spawn_file_actions_destroy(acts)))
204 err(EXIT_FAILURE, "spawn_free");
205
206 close(fds[1]);
207
208 return (fds[0]);
209 }
210 #endif
211
ex_ssi_exec(Vstr_base * s1,Vstr_base * s2,size_t pos,size_t len)212 static void ex_ssi_exec(Vstr_base *s1,
213 Vstr_base *s2, size_t pos, size_t len)
214 {
215 #if USE_POPEN
216 FILE *fp = NULL; /* FIXME: hack job */
217 /* struct stat64 sbuf[1];
218 *
219 * if (stat64(vstr_export_cstr_ptr(s2, pos, len), sbuf))
220 * err(EXIT_FAILURE, "stat(%s)", vstr_export_cstr_ptr(s2, pos, len));
221 */
222 if (!(fp = popen(vstr_export_cstr_ptr(s2, pos, len), "r")))
223 err(EXIT_FAILURE, "popen(%s)", vstr_export_cstr_ptr(s2, pos, len));
224
225 ex_ssi_cat_read_fd_write_stdout(s1, fileno(fp));
226
227 pclose(fp);
228 #else
229 Vstr_sects *sects = vstr_sects_make(4);
230 size_t srch = 0;
231 size_t tpos = pos;
232 size_t tlen = len;
233 pid_t pid;
234
235 while ((srch < tlen) &&
236 (tmp = vstr_cspn_cstr_chrs_fwd(s2, , tlen, " ")))
237 {
238 }
239
240 /* FIXME: doesn't handle <foo "bar baz" arg2>... */
241 vstr_split_cstr_buf(s2, pos, len, " ", sects, 0, VSTR_FLAG_SPLIT_NO_RET);
242
243 ex_ssi_cat_read_fd_write_stdout(s1,
244 ex_ssi_spawn_r(argv[0], &pid, argv, NULL));
245 waitpid(pid, NULL, 0);
246 #endif
247 }
248
ex_ssi_strftime(time_t val,int use_gmt)249 static const char *ex_ssi_strftime(time_t val, int use_gmt)
250 {
251 static char ret[4096];
252 const char *spec = timespec;
253 struct tm *tm_val = NULL;
254
255 if (!spec) spec = "%c";
256
257 if (use_gmt)
258 tm_val = gmtime(&val);
259 else
260 tm_val = localtime(&val);
261
262 if (!tm_val)
263 err(EXIT_FAILURE, "gmtime");
264
265 strftime(ret, sizeof(ret), spec, tm_val);
266
267 return (ret);
268 }
269
ex_ssi_getpwuid_name(uid_t uid)270 static const char *ex_ssi_getpwuid_name(uid_t uid)
271 {
272 struct passwd *pw = getpwuid(uid);
273
274 if (!pw)
275 return (":unknown:");
276
277 return (pw->pw_name);
278 }
279
ex_ssi_process(Vstr_base * s1,Vstr_base * s2,time_t last_modified,int last)280 static int ex_ssi_process(Vstr_base *s1, Vstr_base *s2, time_t last_modified,
281 int last)
282 {
283 size_t srch = 0;
284 int ret = FALSE;
285
286 /* we don't want to create more data, if we are over our limit */
287 if (s1->len > EX_MAX_W_DATA_INCORE)
288 return (FALSE);
289
290 while (s2->len >= strlen("<!--#"))
291 {
292 if (!(srch = vstr_srch_cstr_buf_fwd(s2, 1, s2->len, "<!--#")))
293 {
294 if (last)
295 break;
296
297 ret = TRUE;
298 vstr_mov(s1, s1->len, s2, 1, s2->len - strlen("<!--#"));
299 break;
300 }
301
302 if (srch > 1)
303 {
304 ret = TRUE;
305 vstr_add_vstr(s1, s1->len, s2, 1, srch - 1, VSTR_TYPE_ADD_BUF_REF);
306 vstr_del(s2, 1, srch - 1);
307 }
308
309 if (!(srch = ex_ssi_srch_end(s2, 1, s2->len)))
310 break;
311
312 ret = TRUE;
313
314 if (0) { }
315 else if (vstr_cmp_case_bod_cstr_eq(s2, 1, srch, "<!--#include"))
316 {
317 int fd = -1;
318 size_t tmp = 0;
319
320 ex_ssi_skip_str(s2, &srch, "<!--#include");
321
322 if (!(tmp = ex_ssi_file_attr(s2, &srch)))
323 EX_SSI_FAILED("include");
324
325 EX_SSI_OK("include", s2, 1, tmp, TRUE);
326
327 if (s1->conf->malloc_bad)
328 errno = ENOMEM, err(EXIT_FAILURE, "add data");
329
330 fd = io_open(vstr_export_cstr_ptr(s2, 1, tmp));
331
332 ex_ssi_cat_read_fd_write_stdout(s1, fd);
333
334 if (close(fd) == -1)
335 warn("close(%s)", vstr_export_cstr_ptr(s2, 1, tmp));
336 }
337 else if (vstr_cmp_case_bod_cstr_eq(s2, 1, s2->len, "<!--#exec"))
338 {
339 size_t tmp = 0;
340
341 ex_ssi_skip_str(s2, &srch, "<!--#exec");
342 ex_ssi_skip_wsp(s2, &srch);
343
344 if (vstr_cmp_case_bod_cstr_eq(s2, 1, srch, "cmd=\""))
345 ex_ssi_skip_str(s2, &srch, "cmd=\"");
346 else
347 EX_SSI_FAILED("exec");
348
349 if (!(tmp = ex_ssi_attr_val(s2, &srch)))
350 EX_SSI_FAILED("exec");
351
352 if (s1->conf->malloc_bad)
353 errno = ENOMEM, err(EXIT_FAILURE, "add data");
354
355 EX_SSI_OK("exec", s2, 1, tmp, TRUE);
356
357 ex_ssi_exec(s1, s2, 1, tmp);
358 }
359 else if (vstr_cmp_case_bod_cstr_eq(s2, 1, s2->len, "<!--#config"))
360 {
361 size_t tmp = 0;
362 enum { tERR = -1,
363 tsize, ttime } type = tERR;
364 const char *tname[2] = {"config sizefmt", "config timefmt"};
365
366 ex_ssi_skip_str(s2, &srch, "<!--#config");
367 ex_ssi_skip_wsp(s2, &srch);
368
369 if (0) { }
370 else if (vstr_cmp_case_bod_cstr_eq(s2, 1, srch, "timefmt=\""))
371 ex_ssi_skip_str(s2, &srch, "timefmt=\""), type = ttime;
372 else if (vstr_cmp_case_bod_cstr_eq(s2, 1, srch, "sizefmt=\""))
373 ex_ssi_skip_str(s2, &srch, "sizefmt=\""), type = tsize;
374 else
375 EX_SSI_FAILED("config");
376
377 ASSERT(type >= 0);
378
379 if (!(tmp = ex_ssi_attr_val(s2, &srch)))
380 EX_SSI_FAILED(tname[type]);
381
382 EX_SSI_OK(tname[type], s2, 1, tmp, FALSE);
383
384 switch (type)
385 {
386 case ttime:
387 free(timespec);
388 timespec = vstr_export_cstr_malloc(s2, 1, tmp);
389 break;
390 case tsize:
391 if (0){ }
392 else if (vstr_cmp_cstr_eq(s2, 1, tmp, "bytes"))
393 use_size_abbrev = FALSE;
394 else if (vstr_cmp_cstr_eq(s2, 1, tmp, "abbrev"))
395 use_size_abbrev = TRUE;
396 break;
397 default:
398 ASSERT(FALSE);
399 }
400
401 /* <!--#config errmsg="foo" --> ? */
402 }
403 else if (vstr_cmp_case_bod_cstr_eq(s2, 1, s2->len, "<!--#echo"))
404 {
405 size_t tmp = 0;
406
407 ex_ssi_skip_str(s2, &srch, "<!--#echo");
408 ex_ssi_skip_wsp(s2, &srch);
409
410 if (vstr_cmp_case_bod_cstr_eq(s2, 1, srch, "encoding=\""))
411 ex_ssi_skip_str(s2, &srch, "encoding=\"");
412 else
413 EX_SSI_FAILED("echo");
414
415 if (!(tmp = ex_ssi_attr_val(s2, &srch)))
416 EX_SSI_FAILED("echo");
417
418 if (!vstr_cmp_cstr_eq(s2, 1, tmp, "none"))
419 EX_SSI_FAILED("echo");
420
421 srch -= tmp + 1;
422 vstr_del(s2, 1, tmp + 1);
423 ex_ssi_skip_wsp(s2, &srch);
424
425 if (vstr_cmp_case_bod_cstr_eq(s2, 1, srch, "var=\""))
426 ex_ssi_skip_str(s2, &srch, "var=\"");
427 else
428 EX_SSI_FAILED("echo");
429
430 if (!(tmp = ex_ssi_attr_val(s2, &srch)))
431 EX_SSI_FAILED("echo");
432
433 if (0) { }
434 else if (vstr_cmp_cstr_eq(s2, 1, tmp, "LAST_MODIFIED"))
435 vstr_add_cstr_buf(s1, s1->len, ex_ssi_strftime(last_modified, FALSE));
436 else if (vstr_cmp_cstr_eq(s2, 1, tmp, "DATE_GMT"))
437 vstr_add_cstr_buf(s1, s1->len, ex_ssi_strftime(time(NULL), TRUE));
438 else if (vstr_cmp_cstr_eq(s2, 1, tmp, "DATE_LOCAL"))
439 vstr_add_cstr_buf(s1, s1->len, ex_ssi_strftime(time(NULL), FALSE));
440 else if (vstr_cmp_cstr_eq(s2, 1, tmp, "USER_NAME"))
441 vstr_add_cstr_buf(s1, s1->len, ex_ssi_getpwuid_name(getuid()));
442 else
443 EX_SSI_FAILED("echo");
444
445 EX_SSI_OK("echo", s2, 1, tmp, FALSE);
446
447
448 /* <!--#echo encoding="none" var="LAST_MODIFIED" --> */
449 /* <!--#echo encoding="url" var="LAST_MODIFIED" --> */
450 /* <!--#echo encoding="entity" var="LAST_MODIFIED" --> */
451
452 /* <!--#echo var="DOCUMENT_NAME" --> */
453 /* <!--#echo var="DOCUMENT_URI" --> */
454
455 }
456 else if (vstr_cmp_case_bod_cstr_eq(s2, 1, s2->len, "<!--#fsize"))
457 {
458 size_t tmp = 0;
459 struct stat64 sbuf[1];
460
461 ex_ssi_skip_str(s2, &srch, "<!--#fsize");
462
463 if (!(tmp = ex_ssi_file_attr(s2, &srch)))
464 EX_SSI_FAILED("fsize");
465
466 EX_SSI_OK("fsize", s2, 1, tmp, FALSE);
467
468 if (s1->conf->malloc_bad)
469 errno = ENOMEM, err(EXIT_FAILURE, "add data");
470
471 if (stat64(vstr_export_cstr_ptr(s2, 1, tmp), sbuf))
472 err(EXIT_FAILURE, "stat(%s)", vstr_export_cstr_ptr(s2, 1, tmp));
473
474 if (use_size_abbrev)
475 vstr_add_fmt(s1, s1->len, "${BKMG.ju:%ju}",
476 (VSTR_AUTOCONF_uintmax_t)sbuf->st_size);
477 else
478 vstr_add_fmt(s1, s1->len, "%ju",
479 (VSTR_AUTOCONF_uintmax_t)sbuf->st_size);
480 }
481 else if (vstr_cmp_case_bod_cstr_eq(s2, 1, s2->len, "<!--#flastmod"))
482 {
483 size_t tmp = 0;
484 struct stat64 sbuf[1];
485
486 ex_ssi_skip_str(s2, &srch, "<!--#flastmod");
487
488 if (!(tmp = ex_ssi_file_attr(s2, &srch)))
489 EX_SSI_FAILED("flastmod");
490
491 EX_SSI_OK("flastmod", s2, 1, tmp, FALSE);
492
493 if (s1->conf->malloc_bad)
494 errno = ENOMEM, err(EXIT_FAILURE, "add data");
495
496 if (stat64(vstr_export_cstr_ptr(s2, 1, tmp), sbuf))
497 err(EXIT_FAILURE, "stat(%s)", vstr_export_cstr_ptr(s2, 1, tmp));
498
499 vstr_add_cstr_buf(s1, s1->len, ex_ssi_strftime(sbuf->st_mtime, TRUE));
500 }
501 else if (0 && vstr_cmp_case_bod_cstr_eq(s2, 1, s2->len, "<!--#if"))
502 {
503 ex_ssi_skip_str(s2, &srch, "<!--#if");
504 ex_ssi_skip_wsp(s2, &srch);
505
506 /* <!--#if expr="foo" --> ? */
507 /* <!--#elif expt="foo" --> ? */
508 /* <!--#else --> ? */
509 /* <!--#endif --> ? */
510
511 }
512 else
513 vstr_add_cstr_ptr(s1, s1->len, "<!-- UNKNOWN SSI command -->\n");
514
515 ASSERT(vstr_export_chr(s2, srch) == '>');
516
517 vstr_del(s2, 1, srch);
518 }
519
520 ssi_parse_failed:
521
522 if (last && s2->len)
523 {
524 ret = TRUE;
525 vstr_mov(s1, s1->len, s2, 1, s2->len);
526 }
527
528 return (ret);
529 }
530
531
ex_ssi_process_limit(Vstr_base * s1,Vstr_base * s2,time_t last_modified,unsigned int lim)532 static void ex_ssi_process_limit(Vstr_base *s1, Vstr_base *s2,
533 time_t last_modified, unsigned int lim)
534 {
535 while (s2->len > lim)
536 {
537 int proc_data = ex_ssi_process(s1, s2, last_modified, !lim);
538 if (!proc_data && (io_put(s1, STDOUT_FILENO) == IO_BLOCK))
539 io_block(-1, STDOUT_FILENO);
540 }
541 }
542
ex_ssi_read_fd_write_stdout(Vstr_base * s1,Vstr_base * s2,int fd)543 static void ex_ssi_read_fd_write_stdout(Vstr_base *s1, Vstr_base *s2, int fd)
544 {
545 struct stat64 sbuf[1];
546 time_t last_modified = time(NULL);
547
548 if (fstat64(fd, sbuf))
549 warn("fstat");
550 else
551 last_modified = sbuf->st_mtime;
552
553 while (TRUE)
554 {
555 int io_w_state = IO_OK;
556 int io_r_state = io_get(s2, fd);
557
558 if (io_r_state == IO_EOF)
559 break;
560
561 ex_ssi_process(s1, s2, last_modified, FALSE);
562
563 io_w_state = io_put(s1, 1);
564
565 io_limit(io_r_state, fd, io_w_state, 1, s1);
566 }
567
568 ex_ssi_process_limit(s1, s2, last_modified, 0);
569 }
570
ex_ssi_fin(Vstr_base * s1,time_t timestamp,const char * fname)571 static void ex_ssi_fin(Vstr_base *s1, time_t timestamp, const char *fname)
572 {
573 free(timespec);
574 timespec = NULL;
575
576 vstr_add_fmt(s1, s1->len,
577 "<!-- SSI processing of %s -->\n"
578 "<!-- done on %s -->\n"
579 "<!-- done by jssi -->\n",
580 fname, ex_ssi_strftime(timestamp, FALSE));
581 }
582
usage(const char * program_name,int ret,const char * prefix)583 static void usage(const char *program_name, int ret, const char *prefix)
584 {
585 Vstr_base *out = vstr_make_base(NULL);
586
587 if (!out)
588 errno = ENOMEM, err(EXIT_FAILURE, "usage");
589
590 vstr_add_fmt(out, 0, "%s\n"
591 " Format: %s [-hV]\n"
592 " --help -h - Print this message.\n"
593 " --prefix-path - Prefix path with argument.\n"
594 " --suffix-path - Suffix path with argument.\n"
595 " --version -V - Print the version string.\n",
596 prefix, program_name);
597
598 if (io_put_all(out, ret ? STDERR_FILENO : STDOUT_FILENO) == IO_FAIL)
599 err(EXIT_FAILURE, "write");
600
601 exit (ret);
602 }
603
merge_path(const char * beg,const char * end,const char * name)604 static void merge_path(const char *beg, const char *end, const char *name)
605 {
606 Vstr_base *tmp = vstr_dup_cstr_ptr(NULL, beg);
607
608 if (!tmp)
609 errno = ENOMEM, err(EXIT_FAILURE, "%s", name);
610
611 vstr_add_cstr_ptr(tmp, tmp->len, ":");
612 vstr_add_cstr_ptr(tmp, tmp->len, end);
613
614 if (tmp->conf->malloc_bad || !vstr_export_cstr_ptr(tmp, 1, tmp->len))
615 errno = ENOMEM, err(EXIT_FAILURE, "%s", name);
616
617 setenv("PATH", vstr_export_cstr_ptr(tmp, 1, tmp->len), TRUE);
618 vstr_free_base(tmp);
619 }
620
cl_cmd_line(int * passed_argc,char *** passed_argv)621 static void cl_cmd_line(int *passed_argc, char ***passed_argv)
622 {
623 int argc = *passed_argc;
624 char **argv = *passed_argv;
625
626 char optchar = 0;
627 const char *program_name = NULL;
628 struct option long_options[] =
629 {
630 {"help", no_argument, NULL, 'h'},
631 {"prefix-path", required_argument, NULL, 1},
632 {"suffix-path", required_argument, NULL, 2},
633 {"version", no_argument, NULL, 'V'},
634 {NULL, 0, NULL, 0}
635 };
636 Vstr_base *out = vstr_make_base(NULL);
637
638 if (!out)
639 errno = ENOMEM, err(EXIT_FAILURE, "command line");
640
641 program_name = opt_program_name(argv[0], "jssi");
642
643 while ((optchar = getopt_long(argc, argv, "hV", long_options, NULL)) != -1)
644 {
645 switch (optchar)
646 {
647 case '?': usage(program_name, EXIT_FAILURE, "");
648 case 'h': usage(program_name, EXIT_SUCCESS, "");
649
650 case 'V':
651 vstr_add_fmt(out, 0,"\
652 %s version 1.0.0, compiled on %s.\n\
653 Written by James Antill\n\
654 \n\
655 Uses Vstr string library.\n\
656 ",
657 program_name, __DATE__);
658
659 if (io_put_all(out, STDOUT_FILENO) == IO_FAIL)
660 err(EXIT_FAILURE, "write");
661
662 exit (EXIT_SUCCESS);
663
664 case 1:
665 merge_path(optarg, getenv("PATH"), "prefix-path");
666 break;
667
668 case 2:
669 merge_path(getenv("PATH"), optarg, "suffix-path");
670 break;
671
672 default:
673 abort();
674 }
675 }
676 vstr_free_base(out); out = NULL;
677
678 argc -= optind;
679 argv += optind;
680
681 *passed_argc = argc;
682 *passed_argv = argv;
683 }
684
main(int argc,char * argv[])685 int main(int argc, char *argv[])
686 {
687 Vstr_base *s2 = NULL;
688 Vstr_base *s1 = ex_init(&s2);
689 int count = 0; /* getopt reduces it by one */
690 time_t now = time(NULL);
691 int beg_dir = -1;
692 DIR *beg_dir_obj = NULL;
693
694 cl_cmd_line(&argc, &argv);
695
696 vstr_sc_fmt_add_all(s1->conf);
697 vstr_cntl_conf(s1->conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$');
698
699 if (count >= argc)
700 {
701 io_fd_set_o_nonblock(STDIN_FILENO);
702 ex_ssi_read_fd_write_stdout(s1, s2, STDIN_FILENO);
703 ex_ssi_fin(s1, now, "stdin");
704 }
705
706 if (!(beg_dir_obj = opendir(".")))
707 err(EXIT_FAILURE, "opendir(.)");
708 beg_dir = dirfd(beg_dir_obj);
709
710 while (count < argc)
711 {
712 int fd = io_open(argv[count]);
713 size_t len = strlen(argv[count]);
714 size_t dbeg = 0;
715 size_t tdname_len = 0;
716
717 if (!vstr_add_buf(s1, s1->len, argv[count], len))
718 errno = ENOMEM, err(EXIT_FAILURE, "add data");
719
720 if (fchdir(beg_dir) == -1)
721 err(EXIT_FAILURE, "fchdir()");
722
723 dbeg = s1->len - len + 1;
724 vstr_sc_dirname(s1, dbeg, len, &tdname_len);
725 if (tdname_len)
726 {
727 const char *tmp = vstr_export_cstr_ptr(s1, dbeg, tdname_len);
728
729 if (chdir(tmp) == -1)
730 err(EXIT_FAILURE, "chdir(%s)", tmp);
731 }
732 vstr_del(s1, s1->len - len + 1, len);
733
734 ex_ssi_read_fd_write_stdout(s1, s2, fd);
735 ex_ssi_fin(s1, now, argv[count]);
736
737 if (close(fd) == -1)
738 warn("close(%s)", argv[count]);
739
740 ++count;
741 }
742 closedir(beg_dir_obj);
743
744 io_put_all(s1, STDOUT_FILENO);
745
746 exit (ex_exit(s1, s2));
747 }
748