#include "ex_utils.h" #include #include #include #include #include #define OUTPUT_TYPE_HTML 0 #define OUTPUT_TYPE_ATOM 1 static const char *output_fname = "-"; static int output_fd = STDOUT_FILENO; static unsigned long output_num = 0; static int output_fmode = 0664; /* or 0644 as default ? -- umask */ static unsigned int output_type = OUTPUT_TYPE_HTML; static int output_perms = FALSE; static int output_mtime = TRUE; static const char *def_prefix = ""; static const char *css_fname = "dir_list.css"; static void ex__open_num(const char *xtra) { int flags = O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY; int fd = -1; if (!xtra) fd = EX_UTILS_OPEN(output_fname, flags, output_fmode); else { /* dead code, slideshow ? */ Vstr_base *fname = vstr_dup_cstr_ptr(NULL, output_fname); const char *ptr = NULL; int save_errno = 0; vstr_add_sysfmt(fname, fname->len, "-%lu%s", ++output_num, xtra); if (fname->conf->malloc_bad || !(ptr = vstr_export_cstr_ptr(fname, 1, fname->len))) errno = ENOMEM, err(EXIT_FAILURE, "open(%s)", output_fname); fd = EX_UTILS_OPEN(ptr, flags, output_fmode); save_errno = errno; vstr_free_base(fname); errno = save_errno; } if (fd == -1) err(EXIT_FAILURE, "open(%s)", output_fname); io_fd_set_o_nonblock(fd); output_fd = fd; } #define SUB_CODE(x) do { \ if (!vstr_sub_cstr_buf(s1, pos, 1, x)) \ return (FALSE); \ \ count = strlen(x); \ } while (FALSE) static int html_conv_encode_data(Vstr_base *s1, size_t pos, size_t len) { if (vstr_cmp_cstr_eq(s1, pos, len, "..")) return (vstr_sub_cstr_buf(s1, pos, len, "Parent directory")); while (len) { size_t count = vstr_cspn_cstr_chrs_fwd(s1, pos, len, "&<>"); len -= count; pos += count; if (len) { switch (vstr_export_chr(s1, pos)) { case '&': SUB_CODE("&"); break; case '<': SUB_CODE("<"); break; case '>': SUB_CODE(">"); break; default: ASSERT_NOT_REACHED(); } len -= count; pos += count; } } return (TRUE); } static int ex_dir_list2html_process(Vstr_base *s1, Vstr_base *s2, int *parsed_header, int *row_num) { size_t pos = 0; size_t len = 0; size_t ns1 = vstr_parse_netstr(s2, 1, s2->len, &pos, &len); Vstr_sect_node name[1] = {{0,0}}; Vstr_sect_node size[1] = {{0,0}}; Vstr_sect_node type[1] = {{0,0}}; Vstr_sect_node mtim[1] = {{0,0}}; Vstr_sect_node perm[1] = {{0,0}}; if (!ns1) { if ((len > EX_MAX_R_DATA_INCORE) || (s2->len > EX_MAX_R_DATA_INCORE)) errx(EXIT_FAILURE, "bad input"); return (FALSE); } if (!*parsed_header) { size_t vpos = 0; size_t vlen = 0; size_t nst = 0; if (!(nst = vstr_parse_netstr(s2, pos, len, &vpos, &vlen))) errx(EXIT_FAILURE, "bad input"); pos += nst; len -= nst; if (!vstr_cmp_cstr_eq(s2, vpos, vlen, "version")) errx(EXIT_FAILURE, "bad input"); if (!(nst = vstr_parse_netstr(s2, pos, len, &vpos, &vlen))) errx(EXIT_FAILURE, "bad input"); pos += nst; len -= nst; if (!vstr_cmp_cstr_eq(s2, vpos, vlen, "2")) errx(EXIT_FAILURE, "Unsupported version"); *parsed_header = TRUE; len = 0; } while (len) { size_t kpos = 0; size_t klen = 0; size_t vpos = 0; size_t vlen = 0; size_t nst = 0; if (!(nst = vstr_parse_netstr(s2, pos, len, &kpos, &klen))) errx(EXIT_FAILURE, "bad input"); pos += nst; len -= nst; if (!(nst = vstr_parse_netstr(s2, pos, len, &vpos, &vlen))) errx(EXIT_FAILURE, "bad input"); pos += nst; len -= nst; if (0) { } else if (vstr_cmp_cstr_eq(s2, kpos, klen, "name")) { name->pos = vpos; name->len = vlen; } else if (vstr_cmp_cstr_eq(s2, kpos, klen, "type")) { type->pos = vpos; type->len = vlen; } else if (vstr_cmp_cstr_eq(s2, kpos, klen, "perms")) { perm->pos = vpos; perm->len = vlen; } else if (vstr_cmp_cstr_eq(s2, kpos, klen, "size")) { size->pos = vpos; size->len = vlen; } else if (vstr_cmp_cstr_eq(s2, kpos, klen, "mtime")) { mtim->pos = vpos; mtim->len = vlen; } } if (name->pos) { Vstr_base *href = vstr_dup_vstr(NULL, s2, name->pos, name->len, 0); Vstr_base *text = vstr_dup_vstr(NULL, s2, name->pos, name->len, 0); VSTR_AUTOCONF_uintmax_t type_val = 0; VSTR_AUTOCONF_uintmax_t val = 0; time_t tval = 0; *row_num %= 2; ++*row_num; if (!href || !vstr_conv_encode_uri(href, 1, href->len)) errno = ENOMEM, err(EXIT_FAILURE, "html"); if (!text || !html_conv_encode_data(text, 1, text->len)) errno = ENOMEM, err(EXIT_FAILURE, "html"); if (type->pos) type_val = vstr_parse_uintmax(s2, type->pos, type->len, 8, NULL, NULL); vstr_add_fmt(s1, s1->len, " ", *row_num); if (type_val && S_ISDIR(type_val)) vstr_add_fmt(s1, s1->len, "${vstr:%p%zu%zu%u}", "dir", def_prefix, href, (size_t)1, href->len, 0, "/", text, (size_t)1, text->len, 0); else if (type_val && S_ISLNK(type_val)) vstr_add_fmt(s1, s1->len, "${vstr:%p%zu%zu%u}", "link", def_prefix, href, (size_t)1, href->len, 0, "", text, (size_t)1, text->len, 0); else vstr_add_fmt(s1, s1->len, "${vstr:%p%zu%zu%u}", "file", def_prefix, href, (size_t)1, href->len, 0, "", text, (size_t)1, text->len, 0); vstr_add_fmt(s1, s1->len, ""); vstr_free_base(href); href = NULL; vstr_free_base(text); text = NULL; if (!size->pos) vstr_add_cstr_buf(s1, s1->len, " "); else { val = vstr_parse_uintmax(s2, size->pos, size->len, 10, NULL, NULL); vstr_add_fmt(s1, s1->len, " ${BKMG.ju:%ju}", val); } if (output_mtime && !mtim->pos) vstr_add_cstr_buf(s1, s1->len, " "); else if (output_mtime) { struct tm *tm_val = NULL; tval = vstr_parse_uintmax(s2, mtim->pos, mtim->len, 10, NULL, NULL); if (!(tm_val = localtime(&tval))) vstr_add_cstr_buf(s1, s1->len, " unknown"); else { char buf[4096]; strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm_val); vstr_add_fmt(s1, s1->len, " %s", buf); } } if (output_perms && !perm->pos) vstr_add_cstr_buf(s1, s1->len, " "); else if (output_perms) { val = vstr_parse_uintmax(s2, perm->pos, perm->len, 8, NULL, NULL); switch (val & 07) { case 07: if (type_val && S_ISDIR(type_val)) vstr_add_fmt(s1, s1->len, " %s", "Read, write and search", "rwx"); else vstr_add_fmt(s1, s1->len, " %s", "Read, write and execute", "rwx"); break; case 06: vstr_add_fmt(s1, s1->len, " %s", "Read and write", "rw-"); break; case 05: if (type_val && S_ISDIR(type_val)) vstr_add_fmt(s1, s1->len, " %s", "Read and search", "r-x"); else vstr_add_fmt(s1, s1->len, " %s", "Read and execute", "r-x"); break; case 04: vstr_add_fmt(s1, s1->len, " %s", "Read only", "r--"); break; case 03: if (type_val && S_ISDIR(type_val)) vstr_add_fmt(s1, s1->len, " %s", "Write and search", "-wx"); else vstr_add_fmt(s1, s1->len, " %s", "Write and execute", "-wx"); break; case 02: vstr_add_fmt(s1, s1->len, " %s", "Write only", "-w-"); break; case 01: if (type_val && S_ISDIR(type_val)) vstr_add_fmt(s1, s1->len, " %s", "Search only", "--x"); else vstr_add_fmt(s1, s1->len, " %s", "Execute only", "--x"); break; case 0: vstr_add_fmt(s1, s1->len, " %s", "None", "---"); break; } } if (!type_val) vstr_add_cstr_buf(s1, s1->len, " "); else { vstr_add_cstr_buf(s1, s1->len, " "); if (0) { } else if (S_ISREG(type_val)) { /* do nothing */ } else if (S_ISDIR(type_val)) vstr_add_cstr_buf(s1, s1->len, "(directory)"); else if (S_ISCHR(type_val)) vstr_add_cstr_buf(s1, s1->len, "(character device)"); else if (S_ISBLK(type_val)) vstr_add_cstr_buf(s1, s1->len, "(block device)"); else if (S_ISLNK(type_val)) vstr_add_cstr_buf(s1, s1->len, "(symbolic link)"); else if (S_ISSOCK(type_val)) vstr_add_cstr_buf(s1, s1->len, "(socket)"); else vstr_add_cstr_buf(s1, s1->len, "(UNKNOWN)"); vstr_add_cstr_buf(s1, s1->len, ""); } vstr_add_cstr_buf(s1, s1->len, "\n"); } vstr_del(s2, 1, ns1); return (TRUE); } static void ex_dir_list2html_process_limit(Vstr_base *s1, Vstr_base *s2, int *parsed_header, int *row_num, int fd) { while (s2->len) { /* Finish processing read data (try writing if we need memory) */ int proc_data = ex_dir_list2html_process(s1, s2, parsed_header, row_num); if (!proc_data && (io_put(s1, fd) == IO_BLOCK)) io_block(-1, fd); } } static const char *atom_time(time_t cur, char *ret, size_t sz) { const struct tm *tm = gmtime(&cur); strftime(ret, sz, "%Y-%m-%dT%H:%M:%SZ", tm); return (ret); } static void ex_dir_list2html_beg(Vstr_base *s1, const char *fname) { char buf[1024]; switch (output_type) { case OUTPUT_TYPE_ATOM: vstr_add_fmt(s1, s1->len, "\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ Directory listing of %s\n\ \n\ %s\n\ ", fname, atom_time(time(NULL), buf, sizeof(buf))); break; case OUTPUT_TYPE_HTML: /* DTD from: http://www.w3.org/QA/2002/04/valid-dtd-list.html */ vstr_add_fmt(s1, s1->len, "\ \n\ \n\ \n\ Directory listing of %s\n\ \ \n\ \n\ \n\

Directory listing of %s

\n\ \n\ \n\ \n\ \n\ %s%s\n\ \n\ \n\ \n\ ", fname, css_fname, fname, !output_mtime ? "" : " ", !output_perms ? "" : " "); ASSERT_NO_SWITCH_DEF(); } } static void ex_dir_list2html_end(Vstr_base *s1) { switch (output_type) { case OUTPUT_TYPE_ATOM: vstr_add_cstr_buf(s1, s1->len, "\n\ \n\ "); case OUTPUT_TYPE_HTML: vstr_add_cstr_buf(s1, s1->len, "\n\ \n\
Name SizeType
Modifiedrwx
\n\ \n\ \n\ "); ASSERT_NO_SWITCH_DEF(); } } static void ex_dir_list2html_read_fd_write_fd(Vstr_base *s1, Vstr_base *s2, int rfd, int wfd) { int parsed_header[1] = {FALSE}; int row_num[1] = {0}; while (TRUE) { int io_w_state = IO_OK; int io_r_state = io_get(s2, rfd); if (io_r_state == IO_EOF) break; ex_dir_list2html_process(s1, s2, parsed_header, row_num); io_w_state = io_put(s1, wfd); io_limit(io_r_state, rfd, io_w_state, wfd, s1); } ex_dir_list2html_process_limit(s1, s2, parsed_header, row_num, wfd); } int main(int argc, char *argv[]) { Vstr_base *s1 = NULL; Vstr_base *s2 = ex_init(&s1); /* init the library etc. */ int count = 1; /* skip the program name */ const char *def_name = "<stdin>"; const char *output_tname = "html"; if (!vstr_cntl_conf(s1->conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$') || !vstr_sc_fmt_add_all(s1->conf)) errno = ENOMEM, err(EXIT_FAILURE, "init"); /* parse command line arguments... */ while (count < argc) { /* quick hack getopt_long */ if (!strcmp("--", argv[count])) { ++count; break; } EX_UTILS_GETOPT_CSTR("prefix-path", def_prefix); EX_UTILS_GETOPT_CSTR("css-file", css_fname); EX_UTILS_GETOPT_CSTR("cssfile", css_fname); EX_UTILS_GETOPT_CSTR("css-filename", css_fname); EX_UTILS_GETOPT_CSTR("cssfilename", css_fname); EX_UTILS_GETOPT_CSTR("name", def_name); EX_UTILS_GETOPT_CSTR("output", output_fname); EX_UTILS_GETOPT_TOGGLE("mtime", output_mtime); EX_UTILS_GETOPT_TOGGLE("permissions",output_perms); EX_UTILS_GETOPT_TOGGLE("perms", output_perms); /* EX_UTILS_GETOPT_CSTR("style", output_tname); */ EX_UTILS_GETOPT_NUM("filemode", output_fmode); else if (!strcmp("--version", argv[count])) { /* print version and exit */ vstr_add_fmt(s1, 0, "%s", "\ and-dir_list2html 1.0.0\n\ Written by James Antill\n\ \n\ Uses Vstr string library.\n\ "); goto out; } else if (!strcmp("--help", argv[count])) { /* print version and exit */ usage: vstr_add_fmt(s1, 0, "%s", "\ Usage: and-dir_list2html [FILENAME]...\n\ or: and-dir_list2html OPTION\n\ Output filenames.\n\ \n\ --help - Display this help and exit.\n\ --version - Output version information and exit.\n\ --css-filename - Location of css used HTML.\n\ --name - Name to be used if input from stdin.\n\ --prefix-path - Prefix for href on each name in directory listing.\n\ --output - Filename to output to, default is stdout.\n\ --filemode - Mode to create output file with.\n\ -- - Treat rest of cmd line as input filenames.\n\ \n\ Report bugs to James Antill .\n\ "); /* --style - Output a different style, instead of HTML.\n\ type = html | atom\n\ */ goto out; } else break; ++count; } if (0) { } else if (CSTREQ(output_tname, "html")) output_type = OUTPUT_TYPE_HTML; else if (CSTREQ(output_tname, "atom")) output_type = OUTPUT_TYPE_ATOM; else goto usage; if (!CSTREQ(output_fname, "-")) ex__open_num(NULL); /* if no arguments are given just do stdin to stdout */ if (count >= argc) { io_fd_set_o_nonblock(STDIN_FILENO); ex_dir_list2html_beg(s1, def_name); ex_dir_list2html_read_fd_write_fd(s1, s2, STDIN_FILENO, output_fd); ex_dir_list2html_end(s1); } /* loop through all arguments, open the dir specified * and do the read/write loop */ while (count < argc) { int fd = io_open(argv[count]); ex_dir_list2html_beg(s1, argv[count]); ex_dir_list2html_read_fd_write_fd(s1, s2, fd, output_fd); ex_dir_list2html_end(s1); if (close(fd) == -1) warn("close(%s)", argv[count]); ++count; } /* output all remaining data */ out: io_put_all(s1, output_fd); exit (ex_exit(s1, s2)); }