-/* $Id: mdocml.c,v 1.1 2008/11/22 14:53:29 kristaps Exp $ */
+/* $Id: mdocml.c,v 1.13 2008/11/27 17:27:50 kristaps Exp $ */
/*
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
*
#include "libmdocml.h"
-struct md_file {
- int fd;
- const char *name;
-};
+#define BUFFER_IN_DEF BUFSIZ /* See begin_bufs. */
+#define BUFFER_OUT_DEF BUFSIZ /* See begin_bufs. */
-struct md_buf {
- struct md_file *file;
- char *buf;
- size_t bufsz;
- size_t line;
-};
-
-static void usage(void);
-
-static int md_begin(const char *, const char *);
-static int md_begin_io(const char *, const char *);
-static int md_begin_bufs(struct md_file *, struct md_file *);
-static int md_run(struct md_buf *, struct md_buf *);
-static int md_line(struct md_buf *, const struct md_buf *,
- const char *, size_t);
-
-static ssize_t md_buf_fill(struct md_buf *);
+static void usage(void);
+static int begin_io(const struct md_args *,
+ char *, char *);
+static int leave_io(const struct md_buf *,
+ const struct md_buf *, int);
+static int begin_bufs(const struct md_args *,
+ struct md_buf *, struct md_buf *);
+static int leave_bufs(const struct md_buf *,
+ const struct md_buf *, int);
int
main(int argc, char *argv[])
{
int c;
char *out, *in;
+ struct md_args args;
extern char *optarg;
extern int optind;
- out = NULL;
+ out = in = NULL;
+
+ (void)memset(&args, 0, sizeof(struct md_args));
- while (-1 != (c = getopt(argc, argv, "o:")))
+ while (-1 != (c = getopt(argc, argv, "o:vW")))
switch (c) {
case ('o'):
out = optarg;
break;
+ case ('v'):
+ args.verbosity++;
+ break;
+ case ('W'):
+ args.warnings |= MD_WARN_ALL;
+ break;
default:
usage();
return(1);
}
argv += optind;
- if (1 != (argc -= optind)) {
- usage();
- return(1);
- }
+ argc -= optind;
+
+ if (1 == argc)
+ in = *argv++;
- argc--;
- in = *argv++;
+ args.type = MD_DUMMY;
- return(md_begin(out, in));
+ return(begin_io(&args, out ? out : "-", in ? in : "-"));
}
+/*
+ * Close out file descriptors opened in begin_io. If the descriptor
+ * refers to stdin/stdout, then do nothing.
+ */
static int
-md_begin(const char *out, const char *in)
+leave_io(const struct md_buf *out,
+ const struct md_buf *in, int c)
{
- char buf[MAXPATHLEN];
-
+ assert(out);
assert(in);
- if (out)
- return(md_begin_io(out, in));
- if (strlcpy(buf, in, MAXPATHLEN) >= MAXPATHLEN)
- warnx("output filename too long");
- else if (strlcat(buf, ".html", MAXPATHLEN) >= MAXPATHLEN)
- warnx("output filename too long");
- else
- return(md_begin_io(buf, in));
+ if (-1 != in->fd && -1 == close(in->fd)) {
+ assert(in->name);
+ warn("%s", in->name);
+ c = 1;
+ }
+ if (-1 != out->fd && STDOUT_FILENO != out->fd &&
+ -1 == close(out->fd)) {
+ assert(out->name);
+ warn("%s", out->name);
+ c = 1;
+ }
- return(1);
+ return(c);
}
+/*
+ * Open file descriptors or assign stdin/stdout, if dictated by the "-"
+ * token instead of a filename.
+ */
static int
-md_begin_io(const char *out, const char *in)
+begin_io(const struct md_args *args, char *out, char *in)
{
- int c;
- struct md_file fin, fout;
+ struct md_buf fi;
+ struct md_buf fo;
+
+#define FI_FL O_RDONLY
+#define FO_FL O_WRONLY|O_CREAT|O_TRUNC
+ assert(args);
assert(out);
assert(in);
- /* XXX: put an output file in TMPDIR and switch it out when the
- * true file is ready to be written.
- */
+ bzero(&fi, sizeof(struct md_buf));
+ bzero(&fo, sizeof(struct md_buf));
- fin.name = in;
+ fi.fd = STDIN_FILENO;
+ fo.fd = STDOUT_FILENO;
- if (-1 == (fin.fd = open(fin.name, O_RDONLY, 0))) {
- warn("%s", fin.name);
- return(1);
- }
+ fi.name = in;
+ fo.name = out;
- fout.name = out;
+ if (0 != strncmp(fi.name, "-", 1))
+ if (-1 == (fi.fd = open(fi.name, FI_FL, 0))) {
+ warn("%s", fi.name);
+ return(leave_io(&fo, &fi, 1));
+ }
- fout.fd = open(fout.name, O_WRONLY | O_CREAT | O_EXCL, 0644);
- if (-1 == fout.fd) {
- warn("%s", fout.name);
- if (-1 == close(fin.fd))
- warn("%s", fin.name);
- return(1);
- }
+ if (0 != strncmp(fo.name, "-", 1))
+ if (-1 == (fo.fd = open(fo.name, FO_FL, 0644))) {
+ warn("%s", fo.name);
+ return(leave_io(&fo, &fi, 1));
+ }
- c = md_begin_bufs(&fout, &fin);
+ return(leave_io(&fo, &fi, begin_bufs(args, &fo, &fi)));
+}
- if (-1 == close(fin.fd)) {
- warn("%s", in);
- c = 1;
- }
- if (-1 == close(fout.fd)) {
- warn("%s", out);
- c = 1;
- }
+/*
+ * Free buffers allocated in begin_bufs.
+ */
+static int
+leave_bufs(const struct md_buf *out,
+ const struct md_buf *in, int c)
+{
+ assert(out);
+ assert(in);
+ if (out->buf)
+ free(out->buf);
+ if (in->buf)
+ free(in->buf);
return(c);
}
+/*
+ * Allocate buffers to the maximum of either the input file's blocksize
+ * or BUFFER_IN_DEF/BUFFER_OUT_DEF, which should be around BUFSIZE.
+ */
static int
-md_begin_bufs(struct md_file *out, struct md_file *in)
+begin_bufs(const struct md_args *args,
+ struct md_buf *out, struct md_buf *in)
{
struct stat stin, stout;
- struct md_buf inbuf, outbuf;
int c;
+ assert(args);
assert(in);
assert(out);
if (-1 == fstat(in->fd, &stin)) {
- warn("fstat: %s", in->name);
+ warn("%s", in->name);
+ return(1);
+ } else if (STDIN_FILENO != in->fd && 0 == stin.st_size) {
+ warnx("%s: empty file", in->name);
return(1);
} else if (-1 == fstat(out->fd, &stout)) {
- warn("fstat: %s", out->name);
+ warn("%s", out->name);
return(1);
}
- inbuf.file = in;
- inbuf.line = 1;
- inbuf.bufsz = MAX(stin.st_blksize, BUFSIZ);
+ in->bufsz = MAX(stin.st_blksize, BUFFER_IN_DEF);
+ out->bufsz = MAX(stout.st_blksize, BUFFER_OUT_DEF);
- outbuf.file = out;
- outbuf.line = 1;
- outbuf.bufsz = MAX(stout.st_blksize, BUFSIZ);
-
- if (NULL == (inbuf.buf = malloc(inbuf.bufsz))) {
+ if (NULL == (in->buf = malloc(in->bufsz))) {
warn("malloc");
- return(1);
- } else if (NULL == (outbuf.buf = malloc(outbuf.bufsz))) {
+ return(leave_bufs(out, in, 1));
+ } else if (NULL == (out->buf = malloc(out->bufsz))) {
warn("malloc");
- free(inbuf.buf);
- return(1);
- }
-
- c = md_run(&outbuf, &inbuf);
-
- free(inbuf.buf);
- free(outbuf.buf);
-
- return(c);
-}
-
-
-static ssize_t
-md_buf_fill(struct md_buf *in)
-{
- ssize_t ssz;
-
- assert(in);
- assert(in->file);
- assert(in->buf);
- assert(in->bufsz > 0);
- assert(in->file->name);
-
- if (-1 == (ssz = read(in->file->fd, in->buf, in->bufsz)))
- warn("%s", in->file->name);
-
- return(ssz);
-}
-
-
-static int
-md_run(struct md_buf *out, struct md_buf *in)
-{
- ssize_t sz, i;
- char line[BUFSIZ];
- size_t pos;
-
- assert(in);
- assert(out);
-
- /* LINTED */
- for (pos = 0; ; ) {
- if (-1 == (sz = md_buf_fill(in)))
- return(1);
- else if (0 == sz)
- break;
-
- for (i = 0; i < sz; i++) {
- if ('\n' == in->buf[i]) {
- if (md_line(out, in, line, pos))
- return(1);
- in->line++;
- pos = 0;
- continue;
- }
-
- if (pos < BUFSIZ) {
- /* LINTED */
- line[pos++] = in->buf[i];
- continue;
- }
-
- warnx("%s: line %zu too long",
- in->file->name, in->line);
- return(1);
- }
+ return(leave_bufs(out, in, 1));
}
- if (0 != pos)
- return(md_line(out, in, line, pos));
-
- return(0);
-}
-
-
-static int
-md_line(struct md_buf *out, const struct md_buf *in,
- const char *buf, size_t sz)
-{
- size_t i;
-
- assert(buf);
- assert(out);
- assert(in);
-
- for (i = 0; i < sz; i++)
- (void)putchar(buf[i]);
-
- (void)putchar('\n');
- return(0);
+ c = md_run(args, out, in);
+ return(leave_bufs(out, in, -1 == c ? 1 : 0));
}
{
extern char *__progname;
- (void)printf("usage: %s [-o outfile] infile\n", __progname);
+ (void)printf("usage: %s [-vW] [-o outfile] [infile]\n", __progname);
}