File indexing completed on 2024-04-28 11:21:08

0001 /*
0002  * markdown: convert a single markdown document into html
0003  */
0004 /*
0005  * Copyright (C) 2007 David L Parsons.
0006  * The redistribution terms are provided in the COPYRIGHT file that must
0007  * be distributed with this source code.
0008  */
0009 #include <stdio.h>
0010 #include <stdlib.h>
0011 #include <limits.h>
0012 #include <mkdio.h>
0013 #include <errno.h>
0014 #include <string.h>
0015 #include <stdarg.h>
0016 #include <ctype.h>
0017 
0018 #include "config.h"
0019 #include "amalloc.h"
0020 #include "pgm_options.h"
0021 #include "tags.h"
0022 #include "gethopt.h"
0023 
0024 #if HAVE_LIBGEN_H
0025 #include <libgen.h>
0026 #endif
0027 
0028 #ifndef HAVE_BASENAME
0029 #include <string.h>
0030 
0031 char*
0032 basename(char *p)
0033 {
0034     char *ret = strrchr(p, '/');
0035 
0036     return ret ? (1+ret) : p;
0037 }
0038 #endif
0039 
0040 
0041 char *pgm = "markdown";
0042 
0043 char *
0044 e_flags(const char *text, const int size, void *context)
0045 {
0046     return (char*)context;
0047 }
0048 
0049 
0050 void
0051 complain(char *fmt, ...)
0052 {
0053     va_list ptr;
0054 
0055     fprintf(stderr, "%s: ", pgm);
0056     va_start(ptr, fmt);
0057     vfprintf(stderr, fmt, ptr);
0058     va_end(ptr);
0059     fputc('\n', stderr);
0060     fflush(stderr);
0061 }
0062 
0063 
0064 char *
0065 anchor_format(char *input, void *ctx)
0066 {
0067     int i, j, size;
0068     char* ret;
0069 
0070     if ( !input )
0071     return NULL;
0072 
0073      size = strlen(input);
0074 
0075      ret = malloc(1+size);
0076 
0077      if ( !ret )
0078      return NULL;
0079 
0080 
0081     while ( size && isspace(input[size-1]) )
0082     --size;
0083 
0084     for ( j=i=0; i < size; i++ ) {
0085     if (isalnum(input[i]) || strchr("-_+", input[i]) )
0086         ret[j++] = input[i];
0087     else if ( input[i] == ' ' )
0088         ret[j++] = '-';
0089     }
0090     ret[j++] = 0;
0091 
0092     return ret;
0093 }
0094 
0095 void
0096 free_it(char *object, void *ctx)
0097 {
0098     if ( object )
0099     free(object);
0100 }
0101 
0102 char *
0103 external_codefmt(char *src, int len, char *lang)
0104 {
0105     int extra = 0;
0106     int i, x;
0107     char *res;
0108 
0109     if ( lang == 0 )
0110     lang = "generic_code";
0111 
0112     for ( i=0; i < len; i++) {
0113     if ( src[i] == '&' )
0114         extra += 5;
0115     else if ( src[i] == '<' || src[i] == '>' )
0116         extra += 4;
0117     }
0118 
0119     /* 80 characters for the format wrappers */
0120     if ( (res = malloc(len+extra+80+strlen(lang))) ==0 )
0121     /* out of memory?  drat! */
0122     return 0;
0123 
0124     sprintf(res, "<pre><code class=\"%s\">\n", lang);
0125     x = strlen(res);
0126     for ( i=0; i < len; i++ ) {
0127     switch (src[i]) {
0128     case '&':   strcpy(&src[x], "&amp;");
0129             x += 5 /*strlen(&amp;)*/ ;
0130             break;
0131     case '<':   strcpy(&src[x], "&lt;");
0132             x += 4 /*strlen(&lt;)*/ ;
0133             break;
0134     case '>':   strcpy(&src[x], "&gt;");
0135             x += 4 /*strlen(&gt;)*/ ;
0136             break;
0137     default:    res[x++] = src[i];
0138             break;
0139     }
0140     }
0141     strcpy(&res[x], "</code></pre>\n");
0142     return res;
0143 }
0144 
0145 
0146 struct h_opt opts[] = {
0147     { 0, "html5",  '5', 0,           "recognise html5 block elements" },
0148     { 0, "base",   'b', "url-base",  "URL prefix" },
0149     { 0, "debug",  'd', 0,           "debugging" },
0150     { 0, "version",'V', 0,           "show version info" },
0151     { 0, 0,        'E', "flags",     "url flags" },
0152     { 0, 0,        'F', "bitmap",    "set/show hex flags" },
0153     { 0, 0,        'f', "{+-}flags", "set/show named flags" },
0154     { 0, 0,        'G', 0,           "github flavoured markdown" },
0155     { 0, 0,        'n', 0,           "don't write generated html" },
0156     { 0, 0,        's', "text",      "format `text`" },
0157     { 0, "style",  'S', 0,           "output <style> blocks" },
0158     { 0, 0,        't', "text",      "format `text` with mkd_line()" },
0159     { 0, "toc",    'T', 0,           "output a TOC" },
0160     { 0, 0,        'C', "prefix",    "prefix for markdown extra footnotes" },
0161     { 0, 0,        'o', "file",      "write output to file" },
0162     { 0, "squash", 'x', 0,           "squash toc labels to be more like github" },
0163     { 0, "codefmt",'X', 0,           "use an external code formatter" },
0164 };
0165 #define NROPTS (sizeof opts/sizeof opts[0])
0166 
0167 int
0168 main(int argc, char **argv)
0169 {
0170     int rc;
0171     mkd_flag_t flags = 0;
0172     int debug = 0;
0173     int toc = 0;
0174     int content = 1;
0175     int version = 0;
0176     int with_html5 = 0;
0177     int styles = 0;
0178     int use_mkd_line = 0;
0179     int use_e_codefmt = 0;
0180     int github_flavoured = 0;
0181     int squash = 0;
0182     char *extra_footnote_prefix = 0;
0183     char *urlflags = 0;
0184     char *text = 0;
0185     char *ofile = 0;
0186     char *urlbase = 0;
0187     char *q;
0188     MMIOT *doc;
0189     struct h_context blob;
0190     struct h_opt *opt;
0191 
0192     hoptset(&blob, argc, argv);
0193     hopterr(&blob, 1);
0194 
0195     if ( q = getenv("MARKDOWN_FLAGS") )
0196     flags = strtol(q, 0, 0);
0197 
0198     pgm = basename(argv[0]);
0199 
0200     while ( opt=gethopt(&blob, opts, NROPTS) ) {
0201     if ( opt == HOPTERR ) {
0202         hoptusage(pgm, opts, NROPTS, "[file]");
0203         exit(1);
0204     }
0205     switch (opt->optchar) {
0206     case '5':   with_html5 = 1;
0207             break;
0208     case 'b':   urlbase = hoptarg(&blob);
0209             break;
0210     case 'd':   debug = 1;
0211             break;
0212     case 'V':   version++;
0213             break;
0214     case 'E':   urlflags = hoptarg(&blob);
0215             break;
0216     case 'F':   if ( strcmp(hoptarg(&blob), "?") == 0 ) {
0217             show_flags(0, 0);
0218             exit(0);
0219             }
0220             else
0221             flags = strtol(hoptarg(&blob), 0, 0);
0222             break;
0223     case 'f':   if ( strcmp(hoptarg(&blob), "?") == 0 ) {
0224             show_flags(1, version);
0225             exit(0);
0226             }
0227             else if ( q=set_flag(&flags, hoptarg(&blob)) )
0228             complain("unknown option <%s>", q);
0229             break;
0230     case 'G':   github_flavoured = 1;
0231             break;
0232     case 'n':   content = 0;
0233             break;
0234     case 's':   text = hoptarg(&blob);
0235             break;
0236     case 'S':   styles = 1;
0237             break;
0238     case 't':   text = hoptarg(&blob);
0239             use_mkd_line = 1;
0240             break;
0241     case 'T':   flags |= MKD_TOC;
0242             toc = 1;
0243             break;
0244     case 'C':   extra_footnote_prefix = hoptarg(&blob);
0245             break;
0246     case 'o':   if ( ofile ) {
0247             complain("Too many -o options");
0248             exit(1);
0249             }
0250             if ( !freopen(ofile = hoptarg(&blob), "w", stdout) ) {
0251             perror(ofile);
0252             exit(1);
0253             }
0254             break;
0255     case 'x':   squash = 1;
0256             break;
0257     case 'X':   use_e_codefmt = 1;
0258             set_flag(&flags, "fencedcode");
0259             break;
0260     }
0261     }
0262 
0263     if ( version ) {
0264     printf("%s: discount %s%s", pgm, markdown_version,
0265                   with_html5 ? " +html5":"");
0266     if ( version > 1 )
0267         mkd_flags_are(stdout, flags, 0);
0268     putchar('\n');
0269     exit(0);
0270     }
0271 
0272     argc -= hoptind(&blob);
0273     argv += hoptind(&blob);
0274 
0275     if ( with_html5 )
0276     mkd_with_html5_tags();
0277 
0278     if ( use_mkd_line )
0279     rc = mkd_generateline( text, strlen(text), stdout, flags);
0280     else {
0281     if ( text ) {
0282         doc = github_flavoured ? gfm_string(text, strlen(text), flags)
0283                    : mkd_string(text, strlen(text), flags) ;
0284 
0285         if ( !doc ) {
0286         perror(text);
0287         exit(1);
0288         }
0289     }
0290     else {
0291         if ( argc && !freopen(argv[0], "r", stdin) ) {
0292         perror(argv[0]);
0293         exit(1);
0294         }
0295 
0296         doc = github_flavoured ? gfm_in(stdin,flags) : mkd_in(stdin,flags);
0297         if ( !doc ) {
0298         perror(argc ? argv[0] : "stdin");
0299         exit(1);
0300         }
0301     }
0302     if ( urlbase )
0303         mkd_basename(doc, urlbase);
0304     if ( urlflags ) {
0305         mkd_e_data(doc, urlflags);
0306         mkd_e_flags(doc, e_flags);
0307     }
0308     if ( squash )
0309         mkd_e_anchor(doc, (mkd_callback_t) anchor_format);
0310     if ( use_e_codefmt )
0311         mkd_e_code_format(doc, external_codefmt);
0312 
0313     if ( use_e_codefmt || squash )
0314         mkd_e_free(doc, free_it);
0315 
0316     if ( extra_footnote_prefix )
0317         mkd_ref_prefix(doc, extra_footnote_prefix);
0318 
0319     if ( debug )
0320         rc = mkd_dump(doc, stdout, 0, argc ? basename(argv[0]) : "stdin");
0321     else {
0322         rc = 1;
0323         if ( mkd_compile(doc, flags) ) {
0324         rc = 0;
0325         if ( styles )
0326             mkd_generatecss(doc, stdout);
0327         if ( toc )
0328             mkd_generatetoc(doc, stdout);
0329         if ( content )
0330             mkd_generatehtml(doc, stdout);
0331         }
0332     }
0333     mkd_cleanup(doc);
0334     }
0335     mkd_deallocate_tags();
0336     adump();
0337     exit( (rc == 0) ? 0 : errno );
0338 }