/* * theme: use a template to create a webpage (markdown-style) * * usage: theme [-m file] [-d root] [-p pagename] [-t template] [-o html] [source] * */ /* * Copyright (C) 2007 Jessica L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include "config.h" #include "pgm_options.h" #include #include #include #if defined(HAVE_BASENAME) && defined(HAVE_LIBGEN_H) # include #endif #include #include #include #include #if HAVE_PWD_H # include #endif #include #include #include #include #include "mkdio.h" #include "cstring.h" #include "amalloc.h" #include "gethopt.h" char *pgm = "theme"; char *m4_file = 0; char *output_file = 0; char *pagename = 0; char *root = 0; int everywhere = 0; /* expand all = 0 && pos < S(inbuf) ) return T(inbuf)[pos]; return EOF; } static int shift(int shiftwidth) { psp += shiftwidth; return psp; } static int* cursor(void) { return T(inbuf) + psp; } static int thesame(int *p, char *pat) { int i; for ( i=0; pat[i]; i++ ) { if ( pat[i] == ' ' ) { if ( !isspace(peek(i+1)) ) { return 0; } } else if ( tolower(peek(i+1)) != pat[i] ) { return 0; } } return 1; } static int istag(int *p, char *pat) { int c; if ( thesame(p, pat) ) { c = peek(strlen(pat)+1); return (c == '>' || isspace(c)); } return 0; } /* set up flags for a specific st_mtime)) ) ) { mkd_set_flag_num(flags, MKD_TAGTEXT); mkd_generateline(h, strlen(h), output, flags); mkd_clr_flag_num(flags, MKD_TAGTEXT); } } /* fauthor() prints out the document author */ static void fauthor(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence) { char *h = mkd_doc_author(doc); #if HAVE_PWD_H if ( (h == 0) && me ) h = me->pw_gecos; #endif if ( h ) mkd_generateline(h, strlen(h), output, flags); } /* fconfig() prints out a tabular version of the flags */ static void fconfig(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence) { mkd_flags_are(output, flags, (whence & (INHEAD|INTAG)) ? 0 : 1); } /* fversion() prints out the document version */ static void fversion(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence) { fwrite(markdown_version, strlen(markdown_version), 1, output); } /* fbody() prints out the document */ static void fbody(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence) { mkd_generatehtml(doc, output); } /* ftoc() prints out the table of contents */ static void ftoc(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence) { mkd_generatetoc(doc, output); } /* fstyle() prints out the document's style section */ static void fstyle(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence) { mkd_generatecss(doc, output); } /* * theme expansions we love: * -- the document date (file or header date) * -- the document title (header title or document name) * -- the document author (header author or document owner) * -- the version# * -- the document body * -- the filename part of the document name * -- the directory part of the document name * -- the html file name * -- document-supplied style blocks * -- include a file. */ static struct _keyword { char *kw; int where; void (*what)(MMIOT*,FILE*,mkd_flag_t *,int); } keyword[] = { { "author?>", 0xffff, fauthor }, { "body?>", INBODY, fbody }, { "toc?>", INBODY, ftoc }, { "date?>", 0xffff, fdate }, { "dir?>", 0xffff, fdirname }, { "include(", 0xffff, finclude }, { "source?>", 0xffff, fbasename }, { "style?>", INHEAD, fstyle }, { "title?>", 0xffff, ftitle }, { "version?>", 0xffff, fversion }, { "config?>", 0xffff, fconfig }, }; #define NR(x) (sizeof x / sizeof x[0]) /* spin() - run through the theme template, looking for ') ); } else if ( (peek(1) == '?') && thesame(cursor(), "?theme ") ) { shift(strlen("?theme ")); while ( ((c = pull()) != EOF) && isspace(c) ) ; shift(-1); p = cursor(); for (i=0; i < NR(keyword); i++) if ( thesame(p, keyword[i].kw) ) { if ( everywhere || (keyword[i].where & where) ) { prepare_flags(flags,where); (*keyword[i].what)(doc,output,flags,where); } break; } while ( (c = pull()) != EOF && (c != '?' && peek(1) != '>') ) ; shift(1); } else putc(c, output); if ( istag(cursor(), "head") ) { where |= INHEAD; where &= ~INBODY; } else if ( istag(cursor(), "body") ) { where &= ~INHEAD; where |= INBODY; } where |= INTAG; continue; } else if ( c == '>' ) where &= ~INTAG; putc(c, output); } } /* spin */ struct h_opt opts[] = { { 0, 0, 'c', "flags", "set/show rendering options" }, { 0, 0, 'C', "bitmap", "set/show rendering options numerically" }, { 0, 0, 'd', "dir", "set the document root" }, { 0, 0, 'E', 0, "do all theme expansions everywhere" }, { 0, 0, 'f', 0, "forcibly overwrite existing html files" }, { 0, 0, 'm', "file", "preprocess the input with m4, with `file` containing preprocessing directives" }, { 0, 0, 'o', "file", "write output to `file`" }, { 0, 0, 'p', "title", "set the page title" }, { 0, 0, 't', "template", "use `template` as template file" }, { 0, 0, 'V', 0, "show version info" }, } ; #define NROPTS (sizeof opts / sizeof opts[0]) int main(int argc, char **argv) { char *template = "page.theme"; char *source = "stdin"; FILE *tmplfile; FILE *input; int force = 0; MMIOT *doc; struct stat sourceinfo; char *q; char *env; int show_version = 0; /* 0: run the program, 1: show version #, 2: show version# and initial flags */ struct h_opt *opt; struct h_context blob; mkd_flag_t *flags = mkd_flags(); if ( flags ) { #if THEME_DL_MODE > 0 switch (THEME_DL_MODE) { case 3: mkd_set_flag_num(flags, MKD_DLEXTRA); case 1: mkd_set_flag_num(flags, MKD_DLDISCOUNT); break; case 2: mkd_set_flag_num(flags, MKD_DLEXTRA); break; } #endif #ifdef THEME_FENCED_CODE mkd_set_flag_num(flags, MKD_FENCEDCODE); #endif mkd_set_flag_num(flags, MKD_TOC); } else { perror("mkd_flags"); exit(1); } hoptset(&blob, argc, argv); hopterr(&blob, 1); pgm = basename(argv[0]); while ( opt = gethopt(&blob, opts, NROPTS) ) { if ( opt == HOPTERR ) { hoptusage(pgm, opts, NROPTS, "[file]"); exit(1); } switch ( opt->optchar ) { case 'd': root = hoptarg(&blob); break; case 'E': everywhere = 1; break; case 'm': m4_file = hoptarg(&blob); break; case 'p': pagename = hoptarg(&blob); break; case 'f': force = 1; break; case 't': template = hoptarg(&blob); break; case 'c': if ( hoptarg(&blob)[0] == '?' ) { show_flags(1, 0, hoptarg(&blob)[1] == '?' ? flags : 0); exit(0); } else if ( q = mkd_set_flag_string(flags, hoptarg(&blob)) ) fprintf(stderr,"%s: unknown option <%s>", pgm, q); break; case 'o': output_file = hoptarg(&blob); break; case 'V': show_version++; break; } } if ( show_version ) { fprintf(stderr, "theme+discount %s", markdown_version); if ( show_version > 1 ) mkd_flags_are(stderr, flags, 0); putc('\n', stderr); exit(0); } if ( env = getenv("THEME_OPTIONS") ) mkd_set_flag_string(flags, env); tmplfile = open_template(template); argc -= hoptind(&blob); argv += hoptind(&blob); if ( argc > 0 ) { int added_text=0; if ( (source = malloc(strlen(argv[0]) + strlen("/index.text") + 1)) == 0 ) fail("out of memory allocating name buffer"); strcpy(source,argv[0]); if ( (stat(source, &sourceinfo) == 0) && S_ISDIR(sourceinfo.st_mode) ) strcat(source, "/index"); if ( !freopen(source, "r", stdin) ) { strcat(source, ".text"); added_text = 1; if ( !freopen(source, "r", stdin) ) fail("can't open either %s or %s", argv[0], source); } if ( !output_file ) { char *p, *q; if ( (output_file = malloc(strlen(source) + strlen(".html") + 1)) == 0 ) fail("out of memory allocating output file name buffer"); strcpy(output_file, source); if (( p = strchr(output_file, '/') )) q = strrchr(p+1, '.'); else q = strrchr(output_file, '.'); if ( q ) *q = 0; else q = output_file + strlen(output_file); strcat(q, ".html"); } } if ( output_file && strcmp(output_file, "-") ) { if ( force && notspecial(output_file) ) unlink(output_file); if ( !freopen(output_file, "w", stdout) ) { fail("can't write to %s", output_file); } } if ( !pagename ) pagename = source; if ( m4_file ) { char *m4_command_line; int len=0; len = 8; /* length of 'm4 "', '" -' & '\0' */ if ( (m4_command_line = malloc(len + strlen(m4_file))) == NULL ) fail("can't allocate temporary storage?"); sprintf(m4_command_line, "m4 \"%s\" -", m4_file); if ( (input = popen(m4_command_line, "r")) == NULL ) fail("can't run m4 preprocessor (%s)", strerror(errno)); } else input = stdin; if ( (doc = mkd_in(input, 0)) == 0 ) fail("can't read %s", source ? source : "stdin"); if ( fstat(fileno(stdin), &sourceinfo) == 0 ) infop = &sourceinfo; #if HAVE_GETPWUID me = getpwuid(infop ? infop->st_uid : getuid()); if ( (root = strdup(me->pw_dir)) == 0 ) fail("out of memory"); #endif if ( !mkd_compile(doc, flags) ) fail("couldn't compile input"); if ( tmplfile ) spin(tmplfile, doc, flags, stdout); else mkd_generatehtml(doc, stdout); mkd_cleanup(doc); mkd_free_flags(flags); exit(0); }