123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698 |
- /*
- * theme: use a template to create a webpage (markdown-style)
- *
- * usage: theme [-d root] [-p pagename] [-t template] [-o html] [source]
- *
- */
- /*
- * Copyright (C) 2007 David 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 <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #if defined(HAVE_BASENAME) && defined(HAVE_LIBGEN_H)
- # include <libgen.h>
- #endif
- #include <stdarg.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <time.h>
- #if HAVE_PWD_H
- # include <pwd.h>
- #endif
- #include <fcntl.h>
- #include <errno.h>
- #include <ctype.h>
- #include <unistd.h>
- #include "mkdio.h"
- #include "cstring.h"
- #include "amalloc.h"
- #include "gethopt.h"
- char *pgm = "theme";
- char *output_file = 0;
- char *pagename = 0;
- char *root = 0;
- int everywhere = 0; /* expand all <?theme elements everywhere */
- #if HAVE_PWD_H
- struct passwd *me = 0;
- #endif
- struct stat *infop = 0;
- #if USE_H1TITLE
- extern char* mkd_h1_title(MMIOT*);
- #endif
- extern int notspecial(char *filename);
- #define INTAG 0x01
- #define INHEAD 0x02
- #define INBODY 0x04
- #ifndef HAVE_BASENAME
- char *
- basename(char *path)
- {
- char *p;
- if ( p = strrchr(path, '/') )
- return 1+p;
- return path;
- }
- #endif
- #ifdef HAVE_FCHDIR
- typedef int HERE;
- #define NOT_HERE (-1)
- #define pushd(d) open(d, O_RDONLY)
- int
- popd(HERE pwd)
- {
- int rc = fchdir(pwd);
- close(pwd);
- return rc;
- }
- #else
- typedef char* HERE;
- #define NOT_HERE 0
- HERE
- pushd(char *d)
- {
- HERE cwd;
- int size;
-
- if ( chdir(d) == -1 )
- return NOT_HERE;
- for (cwd = malloc(size=40); cwd; cwd = realloc(cwd, size *= 2))
- if ( getcwd(cwd, size) )
- return cwd;
- return NOT_HERE;
- }
- int
- popd(HERE pwd)
- {
- if ( pwd ) {
- int rc = chdir(pwd);
- free(pwd);
- return rc;
- }
- return -1;
- }
- #endif
- typedef STRING(int) Istring;
- void
- fail(char *why, ...)
- {
- va_list ptr;
- va_start(ptr,why);
- fprintf(stderr, "%s: ", pgm);
- vfprintf(stderr, why, ptr);
- fputc('\n', stderr);
- va_end(ptr);
- exit(1);
- }
- /* open_template() -- start at the current directory and work up,
- * looking for the deepest nested template.
- * Stop looking when we reach $root or /
- */
- FILE *
- open_template(char *template)
- {
- char *cwd;
- int szcwd;
- HERE here = pushd(".");
- FILE *ret;
- if ( here == NOT_HERE )
- fail("cannot access the current directory");
- szcwd = root ? 1 + strlen(root) : 2;
- if ( (cwd = malloc(szcwd)) == 0 )
- return 0;
- while ( !(ret = fopen(template, "r")) ) {
- if ( getcwd(cwd, szcwd) == 0 ) {
- if ( errno == ERANGE )
- goto up;
- break;
- }
- if ( root && (strcmp(root, cwd) == 0) )
- break; /* ran out of paths to search */
- else if ( (strcmp(cwd, "/") == 0) || (*cwd == 0) )
- break; /* reached / */
- up: if ( chdir("..") == -1 )
- break;
- }
- free(cwd);
- popd(here);
- return ret;
- } /* open_template */
- static Istring inbuf;
- static int psp;
- static int
- prepare(FILE *input)
- {
- int c;
- CREATE(inbuf);
- psp = 0;
- while ( (c = getc(input)) != EOF )
- EXPAND(inbuf) = c;
- fclose(input);
- return 1;
- }
- static int
- pull()
- {
- return psp < S(inbuf) ? T(inbuf)[psp++] : EOF;
- }
- static int
- peek(int offset)
- {
- int pos = (psp + offset)-1;
- if ( pos >= 0 && pos < S(inbuf) )
- return T(inbuf)[pos];
- return EOF;
- }
- static int
- shift(int shiftwidth)
- {
- psp += shiftwidth;
- return psp;
- }
- static int*
- cursor()
- {
- 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;
- }
- /* finclude() includes some (unformatted) source
- */
- static void
- finclude(MMIOT *doc, FILE *out, mkd_flag_t *flags, int whence)
- {
- int c;
- Cstring include;
- FILE *f;
- CREATE(include);
- while ( (c = pull()) != '(' )
- ;
- while ( (c=pull()) != ')' && c != EOF )
- EXPAND(include) = c;
- if ( c != EOF ) {
- EXPAND(include) = 0;
- S(include)--;
- if (( f = fopen(T(include), "r") )) {
- while ( (c = getc(f)) != EOF )
- putc(c, out);
- fclose(f);
- }
- }
- DELETE(include);
- }
- /* fdirname() prints out the directory part of a path
- */
- static void
- fdirname(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence)
- {
- char *p;
- if ( pagename && (p = basename(pagename)) )
- fwrite(pagename, strlen(pagename)-strlen(p), 1, output);
- }
- /* fbasename() prints out the file name part of a path
- */
- static void
- fbasename(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence)
- {
- char *p;
- if ( pagename ) {
- p = basename(pagename);
- if ( !p )
- p = pagename;
- if ( p )
- fwrite(p, strlen(p), 1, output);
- }
- }
- /* ftitle() prints out the document title
- */
- static void
- ftitle(MMIOT *doc, FILE* output, mkd_flag_t *flags, int whence)
- {
- char *h;
- h = mkd_doc_title(doc);
- #if USE_H1TITLE
- if ( !h )
- h = mkd_h1_title(doc);
- #endif
- if ( !h )
- h = pagename;
- if ( h )
- mkd_generateline(h, strlen(h), output, flags);
- }
- /* fdate() prints out the document date
- */
- static void
- fdate(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence)
- {
- char *h;
- if ( (h = mkd_doc_date(doc)) || ( infop && (h = ctime(&infop->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
- * tabular versions of the flags.
- */
- static void
- fconfig(MMIOT *doc, FILE *output, mkd_flag_t *flags, int whence)
- {
- mkd_mmiot_flags(output, doc, (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:
- * <?theme date?> -- the document date (file or header date)
- * <?theme title?> -- the document title (header title or document name)
- * <?theme author?> -- the document author (header author or document owner)
- * <?theme version?> -- the version#
- * <?theme body?> -- the document body
- * <?theme source?> -- the filename part of the document name
- * <?theme dir?> -- the directory part of the document name
- * <?theme html?> -- the html file name
- * <?theme style?> -- document-supplied style blocks
- * <?theme include(file)?> -- 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])
- /* set up flags to pass into the grinder
- */
- static void
- setup_flags(mkd_flag_t *flagp, int where)
- {
- #ifdef THEME_DL_MODE
- switch (THEME_DL_MODE) {
- case 3: mkd_set_flag_num(flagp, MKD_DLEXTRA);
- case 1: mkd_set_flag_num(flagp, MKD_DLDISCOUNT);
- break;
- case 2: mkd_set_flag_num(flagp, MKD_DLEXTRA);
- break;
- }
- #endif
- #ifdef THEME_FENCED_CODE
- mkd_set_flag_num(flagp, MKD_FENCEDCODE);
- #endif
- mkd_set_flag_num(flagp, MKD_TOC);
- if ( where & INTAG )
- mkd_set_flag_num(flagp, MKD_TAGTEXT);
- else if ( where & INHEAD ) {
- mkd_set_flag_num(flagp, MKD_NOIMAGE);
- mkd_set_flag_num(flagp, MKD_NOLINKS);
- }
- }
- /* spin() - run through the theme template, looking for <?theme expansions
- */
- void
- spin(FILE *template, MMIOT *doc, FILE *output)
- {
- int c;
- int *p;
- mkd_flag_t *flags = mkd_flags();
- int where = 0x0;
- int i;
-
- if ( !flags )
- fail("cannot initialize mkd_flags");
- prepare(template);
- while ( (c = pull()) != EOF ) {
- if ( c == '<' ) {
- if ( peek(1) == '!' && peek(2) == '-' && peek(3) == '-' ) {
- fputs("<!--", output);
- shift(3);
- do {
- putc(c=pull(), output);
- } while ( ! (c == '-' && peek(1) == '-' && peek(2) == '>') );
- }
- 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) ) {
- setup_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);
- }
- mkd_free_flags(flags);
- } /* 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, '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(argc, argv)
- char **argv;
- {
- char *template = "page.theme";
- char *source = "stdin";
- FILE *tmplfile;
- int force = 0;
- MMIOT *doc;
- struct stat sourceinfo;
- char *q;
-
- struct h_opt *opt;
- struct h_context blob;
-
- mkd_flag_t *flags = mkd_flags();
- if ( !flags ) {
- 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 'p': pagename = hoptarg(&blob);
- break;
- case 'f': force = 1;
- break;
- case 't': template = hoptarg(&blob);
- break;
- case 'c': if ( strcmp(hoptarg(&blob), "?") == 0 ) {
- show_flags(1,0, 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': printf("theme+discount %s\n", markdown_version);
- exit(0);
- }
- }
- 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 ( (doc = mkd_in(stdin, 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,stdout);
- else
- mkd_generatehtml(doc, stdout);
- mkd_cleanup(doc);
- mkd_free_flags(flags);
- exit(0);
- }
|