Warning, file /education/cantor/thirdparty/discount-2.2.6-patched/theme.c was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * theme:  use a template to create a webpage (markdown-style)
0003  *
0004  * usage:  theme [-d root] [-p pagename] [-t template] [-o html] [source]
0005  *
0006  */
0007 /*
0008  * Copyright (C) 2007 David L Parsons.
0009  * The redistribution terms are provided in the COPYRIGHT file that must
0010  * be distributed with this source code.
0011  */
0012 #include "config.h"
0013 #include "pgm_options.h"
0014 
0015 #include <stdio.h>
0016 #include <stdlib.h>
0017 #include <string.h>
0018 #if defined(HAVE_BASENAME) && defined(HAVE_LIBGEN_H)
0019 #  include <libgen.h>
0020 #endif
0021 #include <stdarg.h>
0022 #include <sys/types.h>
0023 #include <sys/stat.h>
0024 #include <time.h>
0025 #if HAVE_PWD_H
0026 #  include <pwd.h>
0027 #endif
0028 #include <fcntl.h>
0029 #include <errno.h>
0030 #include <ctype.h>
0031 #include <unistd.h>
0032 
0033 #include "mkdio.h"
0034 #include "cstring.h"
0035 #include "amalloc.h"
0036 #include "gethopt.h"
0037 
0038 char *pgm = "theme";
0039 char *output_file = 0;
0040 char *pagename = 0;
0041 char *root = 0;
0042 int   everywhere = 0;   /* expand all <?theme elements everywhere */
0043 
0044 #if HAVE_PWD_H
0045 struct passwd *me = 0;
0046 #endif
0047 struct stat *infop = 0;
0048 
0049 #if USE_H1TITLE
0050 extern char* mkd_h1_title(MMIOT*);
0051 #endif
0052 
0053 extern int notspecial(char *filename);
0054 
0055 #define INTAG 0x01
0056 #define INHEAD 0x02
0057 #define INBODY 0x04
0058 
0059 
0060 #ifndef HAVE_BASENAME
0061 char *
0062 basename(char *path)
0063 {
0064     char *p;
0065 
0066     if ( p = strrchr(path, '/') )
0067     return 1+p;
0068     return path;
0069 }
0070 #endif
0071 
0072 #ifdef HAVE_FCHDIR
0073 typedef int HERE;
0074 #define NOT_HERE (-1)
0075 
0076 #define pushd(d)    open(d, O_RDONLY)
0077 
0078 int
0079 popd(HERE pwd)
0080 {
0081     int rc = fchdir(pwd);
0082     close(pwd);
0083     return rc;
0084 }
0085 
0086 #else
0087 
0088 typedef char* HERE;
0089 #define NOT_HERE 0
0090 
0091 HERE
0092 pushd(char *d)
0093 {
0094     HERE cwd;
0095     int size;
0096     
0097     if ( chdir(d) == -1 )
0098     return NOT_HERE;
0099 
0100     for (cwd = malloc(size=40); cwd; cwd = realloc(cwd, size *= 2))
0101     if ( getcwd(cwd, size) )
0102         return cwd;
0103 
0104     return NOT_HERE;
0105 }
0106 
0107 int
0108 popd(HERE pwd)
0109 {
0110     if ( pwd ) {
0111     int rc = chdir(pwd);
0112     free(pwd);
0113 
0114     return rc;
0115     }
0116     return -1;
0117 }
0118 #endif
0119 
0120 typedef STRING(int) Istring;
0121 
0122 void
0123 fail(char *why, ...)
0124 {
0125     va_list ptr;
0126 
0127     va_start(ptr,why);
0128     fprintf(stderr, "%s: ", pgm);
0129     vfprintf(stderr, why, ptr);
0130     fputc('\n', stderr);
0131     va_end(ptr);
0132     exit(1);
0133 }
0134 
0135 
0136 /* open_template() -- start at the current directory and work up,
0137  *                    looking for the deepest nested template. 
0138  *                    Stop looking when we reach $root or /
0139  */
0140 FILE *
0141 open_template(char *template)
0142 {
0143     char *cwd;
0144     int szcwd;
0145     HERE here = pushd(".");
0146     FILE *ret;
0147 
0148     if ( here == NOT_HERE )
0149     fail("cannot access the current directory");
0150 
0151     szcwd = root ? 1 + strlen(root) : 2;
0152 
0153     if ( (cwd = malloc(szcwd)) == 0 )
0154     return 0;
0155 
0156     while ( !(ret = fopen(template, "r")) ) {
0157     if ( getcwd(cwd, szcwd) == 0 ) {
0158         if ( errno == ERANGE )
0159         goto up;
0160         break;
0161     }
0162 
0163     if ( root && (strcmp(root, cwd) == 0) )
0164         break;  /* ran out of paths to search */
0165     else if ( (strcmp(cwd, "/") == 0) || (*cwd == 0) )
0166         break;  /* reached / */
0167 
0168     up: if ( chdir("..") == -1 )
0169         break;
0170     }
0171     free(cwd);
0172     popd(here);
0173     return ret;
0174 } /* open_template */
0175 
0176 
0177 static Istring inbuf;
0178 static int psp;
0179 
0180 static int
0181 prepare(FILE *input)
0182 {
0183     int c;
0184 
0185     CREATE(inbuf);
0186     psp = 0;
0187     while ( (c = getc(input)) != EOF )
0188     EXPAND(inbuf) = c;
0189     fclose(input);
0190     return 1;
0191 }
0192 
0193 static int
0194 pull()
0195 {
0196     return psp < S(inbuf) ? T(inbuf)[psp++] : EOF;
0197 }
0198 
0199 static int
0200 peek(int offset)
0201 {
0202     int pos = (psp + offset)-1;
0203 
0204     if ( pos >= 0 && pos < S(inbuf) )
0205     return T(inbuf)[pos];
0206 
0207     return EOF;
0208 }
0209 
0210 static int
0211 shift(int shiftwidth)
0212 {
0213     psp += shiftwidth;
0214     return psp;
0215 }
0216 
0217 static int*
0218 cursor()
0219 {
0220     return T(inbuf) + psp;
0221 }
0222 
0223 
0224 static int
0225 thesame(int *p, char *pat)
0226 {
0227     int i;
0228 
0229     for ( i=0; pat[i]; i++ ) {
0230     if ( pat[i] == ' ' ) {
0231         if ( !isspace(peek(i+1)) ) {
0232         return 0;
0233         }
0234     }
0235     else if ( tolower(peek(i+1)) != pat[i] ) {
0236         return 0;
0237     }
0238     }
0239     return 1;
0240 }
0241 
0242 
0243 static int
0244 istag(int *p, char *pat)
0245 {
0246     int c;
0247 
0248     if ( thesame(p, pat) ) {
0249     c = peek(strlen(pat)+1);
0250     return (c == '>' || isspace(c));
0251     }
0252     return 0;
0253 }
0254 
0255 
0256 /* finclude() includes some (unformatted) source
0257  */
0258 static void
0259 finclude(MMIOT *doc, FILE *out, int flags, int whence)
0260 {
0261     int c;
0262     Cstring include;
0263     FILE *f;
0264 
0265     CREATE(include);
0266 
0267     while ( (c = pull()) != '(' )
0268     ;
0269 
0270     while ( (c=pull()) != ')' && c != EOF )
0271     EXPAND(include) = c;
0272 
0273     if ( c != EOF ) {
0274     EXPAND(include) = 0;
0275     S(include)--;
0276 
0277     if (( f = fopen(T(include), "r") )) {
0278         while ( (c = getc(f)) != EOF )
0279         putc(c, out);
0280         fclose(f);
0281     }
0282     }
0283     DELETE(include);
0284 }
0285 
0286 
0287 /* fdirname() prints out the directory part of a path
0288  */
0289 static void
0290 fdirname(MMIOT *doc, FILE *output, int flags, int whence)
0291 {
0292     char *p;
0293 
0294     if ( pagename && (p = basename(pagename)) )
0295     fwrite(pagename, strlen(pagename)-strlen(p), 1, output);
0296 }
0297 
0298 
0299 /* fbasename() prints out the file name part of a path
0300  */
0301 static void
0302 fbasename(MMIOT *doc, FILE *output, int flags, int whence)
0303 {
0304     char *p;
0305 
0306     if ( pagename ) {
0307     p = basename(pagename);
0308 
0309     if ( !p )
0310         p = pagename;
0311 
0312     if ( p )
0313         fwrite(p, strlen(p), 1, output);
0314     }
0315 }
0316 
0317 
0318 /* ftitle() prints out the document title
0319  */
0320 static void
0321 ftitle(MMIOT *doc, FILE* output, int flags, int whence)
0322 {
0323     char *h;
0324     h = mkd_doc_title(doc);
0325 
0326 #if USE_H1TITLE
0327     if ( !h )
0328     h = mkd_h1_title(doc);
0329 #endif
0330 
0331     if ( !h )
0332     h = pagename;
0333 
0334     if ( h )
0335     mkd_generateline(h, strlen(h), output, flags);
0336 }
0337 
0338 
0339 /* fdate() prints out the document date
0340  */
0341 static void
0342 fdate(MMIOT *doc, FILE *output, int flags, int whence)
0343 {
0344     char *h;
0345 
0346     if ( (h = mkd_doc_date(doc)) || ( infop && (h = ctime(&infop->st_mtime)) ) )
0347     mkd_generateline(h, strlen(h), output, flags|MKD_TAGTEXT);
0348 }
0349 
0350 
0351 /* fauthor() prints out the document author
0352  */
0353 static void
0354 fauthor(MMIOT *doc, FILE *output, int flags, int whence)
0355 {
0356     char *h = mkd_doc_author(doc);
0357 
0358 #if HAVE_PWD_H
0359     if ( (h == 0) && me )
0360     h = me->pw_gecos;
0361 #endif
0362 
0363     if ( h )
0364     mkd_generateline(h, strlen(h), output, flags);
0365 }
0366 
0367 
0368 /* fconfig() prints out a tabular version of
0369  * tabular versions of the flags.
0370  */
0371 static void
0372 fconfig(MMIOT *doc, FILE *output, int flags, int whence)
0373 {
0374     mkd_mmiot_flags(output, doc, (whence & (INHEAD|INTAG)) ? 0 : 1);
0375 }
0376 
0377 
0378 /* fversion() prints out the document version
0379  */
0380 static void
0381 fversion(MMIOT *doc, FILE *output, int flags, int whence)
0382 {
0383     fwrite(markdown_version, strlen(markdown_version), 1, output);
0384 }
0385 
0386 
0387 /* fbody() prints out the document
0388  */
0389 static void
0390 fbody(MMIOT *doc, FILE *output, int flags, int whence)
0391 {
0392     mkd_generatehtml(doc, output);
0393 }
0394 
0395 /* ftoc() prints out the table of contents
0396  */
0397 static void
0398 ftoc(MMIOT *doc, FILE *output, int flags, int whence)
0399 {
0400     mkd_generatetoc(doc, output);
0401 }
0402 
0403 /* fstyle() prints out the document's style section
0404  */
0405 static void
0406 fstyle(MMIOT *doc, FILE *output, int flags, int whence)
0407 {
0408     mkd_generatecss(doc, output);
0409 }
0410 
0411 
0412 /*
0413  * theme expansions we love:
0414  *   <?theme date?> -- the document date (file or header date)
0415  *   <?theme title?>    -- the document title (header title or document name)
0416  *   <?theme author?>   -- the document author (header author or document owner)
0417  *   <?theme version?>  -- the version#
0418  *   <?theme body?> -- the document body
0419  *   <?theme source?>   -- the filename part of the document name
0420  *   <?theme dir?>  -- the directory part of the document name
0421  *   <?theme html?> -- the html file name
0422  *   <?theme style?>    -- document-supplied style blocks
0423  *   <?theme include(file)?> -- include a file.
0424  */
0425 static struct _keyword {
0426     char *kw;
0427     int where;
0428     void (*what)(MMIOT*,FILE*,int,int);
0429 } keyword[] = { 
0430     { "author?>",  0xffff, fauthor },
0431     { "body?>",    INBODY, fbody },
0432     { "toc?>",     INBODY, ftoc },
0433     { "date?>",    0xffff, fdate },
0434     { "dir?>",     0xffff, fdirname },
0435     { "include(",  0xffff, finclude },
0436     { "source?>",  0xffff, fbasename },
0437     { "style?>",   INHEAD, fstyle },
0438     { "title?>",   0xffff, ftitle },
0439     { "version?>", 0xffff, fversion },
0440     { "config?>",  0xffff, fconfig },
0441 };
0442 #define NR(x)   (sizeof x / sizeof x[0])
0443 
0444 
0445 /* spin() - run through the theme template, looking for <?theme expansions
0446  */
0447 void
0448 spin(FILE *template, MMIOT *doc, FILE *output)
0449 {
0450     int c;
0451     int *p;
0452     int flags;
0453     int where = 0x0;
0454     int i;
0455 
0456     prepare(template);
0457 
0458     while ( (c = pull()) != EOF ) {
0459     if ( c == '<' ) {
0460         if ( peek(1) == '!' && peek(2) == '-' && peek(3) == '-' ) {
0461         fputs("<!--", output);
0462         shift(3);
0463         do {
0464             putc(c=pull(), output);
0465         } while ( ! (c == '-' && peek(1) == '-' && peek(2) == '>') );
0466         }
0467         else if ( (peek(1) == '?') && thesame(cursor(), "?theme ") ) {
0468         shift(strlen("?theme "));
0469 
0470         while ( ((c = pull()) != EOF) && isspace(c) )
0471             ;
0472 
0473         shift(-1);
0474         p = cursor();
0475 
0476         if ( where & INTAG ) 
0477             flags = MKD_TAGTEXT;
0478         else if ( where & INHEAD )
0479             flags = MKD_NOIMAGE|MKD_NOLINKS;
0480         else
0481             flags = 0;
0482 
0483         for (i=0; i < NR(keyword); i++)
0484             if ( thesame(p, keyword[i].kw) ) {
0485             if ( everywhere || (keyword[i].where & where) )
0486                 (*keyword[i].what)(doc,output,flags,where);
0487             break;
0488             }
0489 
0490         while ( (c = pull()) != EOF && (c != '?' && peek(1) != '>') )
0491             ;
0492         shift(1);
0493         }
0494         else
0495         putc(c, output);
0496 
0497         if ( istag(cursor(), "head") ) {
0498         where |= INHEAD;
0499         where &= ~INBODY;
0500         }
0501         else if ( istag(cursor(), "body") ) {
0502         where &= ~INHEAD;
0503         where |= INBODY;
0504         }
0505         where |= INTAG;
0506         continue;
0507     }
0508     else if ( c == '>' )
0509         where &= ~INTAG;
0510 
0511     putc(c, output);
0512     }
0513 } /* spin */
0514 
0515 
0516 struct h_opt opts[] = {
0517     { 0, 0, 'c', "flags",  "set/show rendering options" },
0518     { 0, 0, 'C', "bitmap", "set/show rendering options numerically" },
0519     { 0, 0, 'd', "dir",    "set the document root" },
0520     { 0, 0, 'E', 0,        "do all theme expansions everywhere" },
0521     { 0, 0, 'f', 0,        "forcibly overwrite existing html files" },
0522     { 0, 0, 'o', "file",   "write output to `file`" },
0523     { 0, 0, 'p', "title",  "set the page title" },
0524     { 0, 0, 't', "template",  "use `template` as template file" },
0525     { 0, 0, 'V', 0,        "show version info" },
0526 } ;
0527 #define NROPTS (sizeof opts / sizeof opts[0])
0528 
0529 int
0530 main(argc, argv)
0531 char **argv;
0532 {
0533     char *template = "page.theme";
0534     char *source = "stdin";
0535     FILE *tmplfile;
0536     mkd_flag_t flags = THEME_CF|MKD_TOC;
0537     int force = 0;
0538     MMIOT *doc;
0539     struct stat sourceinfo;
0540     char *q;
0541 
0542     struct h_opt *opt;
0543     struct h_context blob;
0544 
0545     hoptset(&blob, argc, argv);
0546     hopterr(&blob, 1);
0547     
0548     pgm = basename(argv[0]);
0549 
0550     while ( opt = gethopt(&blob, opts, NROPTS) ) {
0551     if ( opt == HOPTERR ) {
0552         hoptusage(pgm, opts, NROPTS, "[file]");
0553         exit(1);
0554     }
0555     switch ( opt->optchar ) {
0556     case 'd':   root = hoptarg(&blob);
0557             break;
0558     case 'E':   everywhere = 1;
0559             break;
0560     case 'p':   pagename = hoptarg(&blob);
0561             break;
0562     case 'f':   force = 1;
0563             break;
0564     case 't':   template = hoptarg(&blob);
0565             break;
0566     case 'C':   if ( strcmp(hoptarg(&blob), "?") == 0 ) {
0567             show_flags(0,0);
0568             exit(0);
0569             }
0570             else
0571             flags = strtol(hoptarg(&blob), 0, 0);
0572             break;
0573     case 'c':   if ( strcmp(hoptarg(&blob), "?") == 0 ) {
0574             show_flags(1,0);
0575             exit(0);
0576             }
0577             else if ( q = set_flag(&flags, hoptarg(&blob)) )
0578             fprintf(stderr,"%s: unknown option <%s>", pgm, q);
0579             break;          
0580     case 'o':   output_file = hoptarg(&blob);
0581             break;
0582     case 'V':   printf("theme+discount %s\n", markdown_version);
0583             exit(0);
0584     }
0585     }
0586 
0587     tmplfile = open_template(template);
0588 
0589     argc -= hoptind(&blob);
0590     argv += hoptind(&blob);
0591 
0592 
0593     if ( argc > 0 ) {
0594     int added_text=0;
0595 
0596     if ( (source = malloc(strlen(argv[0]) + strlen("/index.text") + 1)) == 0 )
0597         fail("out of memory allocating name buffer");
0598 
0599     strcpy(source,argv[0]);
0600     if ( (stat(source, &sourceinfo) == 0) && S_ISDIR(sourceinfo.st_mode) )
0601         strcat(source, "/index");
0602 
0603     if ( !freopen(source, "r", stdin) ) {
0604         strcat(source, ".text");
0605         added_text = 1;
0606         if ( !freopen(source, "r", stdin) )
0607         fail("can't open either %s or %s", argv[0], source);
0608     }
0609 
0610     if ( !output_file ) {
0611         char *p, *q;
0612 
0613         
0614         if ( (output_file = malloc(strlen(source) + strlen(".html") + 1)) == 0 )
0615         fail("out of memory allocating output file name buffer");
0616 
0617         strcpy(output_file, source);
0618 
0619         if (( p = strchr(output_file, '/') ))
0620         q = strrchr(p+1, '.');
0621         else
0622         q = strrchr(output_file, '.');
0623 
0624         if ( q )
0625         *q = 0;
0626         else
0627         q = output_file + strlen(output_file);
0628 
0629         strcat(q, ".html");
0630     }
0631     }
0632     if ( output_file && strcmp(output_file, "-") ) {
0633     if ( force && notspecial(output_file) )
0634         unlink(output_file);
0635     if ( !freopen(output_file, "w", stdout) ) {
0636         fail("can't write to %s", output_file);
0637     }
0638     }
0639 
0640     if ( !pagename )
0641     pagename = source;
0642 
0643     if ( (doc = mkd_in(stdin, 0)) == 0 )
0644     fail("can't read %s", source ? source : "stdin");
0645 
0646     if ( fstat(fileno(stdin), &sourceinfo) == 0 )
0647     infop = &sourceinfo;
0648 
0649 #if HAVE_GETPWUID
0650     me = getpwuid(infop ? infop->st_uid : getuid());
0651 
0652     if ( (root = strdup(me->pw_dir)) == 0 )
0653     fail("out of memory");
0654 #endif
0655 
0656     if ( !mkd_compile(doc, flags) )
0657     fail("couldn't compile input");
0658 
0659     if ( tmplfile )
0660     spin(tmplfile,doc,stdout);
0661     else
0662     mkd_generatehtml(doc, stdout);
0663 
0664     mkd_cleanup(doc);
0665     exit(0);
0666 }