gendoc.c 106 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413
  1. /*
  2. * gendoc.c
  3. *
  4. * Copyright (C) 2022 bzt (bztsrc@gitlab)
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19. *
  20. * @brief Small script to generate documentation into a single, self-contained static HTML file
  21. *
  22. */
  23. #define GENDOC_VERSION "1.0.0"
  24. #include <stdint.h>
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include <dirent.h>
  29. #include <limits.h>
  30. /**************************************** gendoc variables ****************************************/
  31. #ifndef PATH_MAX
  32. #define PATH_MAX 1024
  33. #endif
  34. enum { T_ol, T_ul, T_li, T_grid, T_gr, T_gd, T_table, T_tr, T_th, T_td, T_p, T_b, T_i, T_u, T_s, T_sup, T_sub, T_quote, T_dl,
  35. T_dt, T_dd, T_a, T_ui, T_alert, T_NUM };
  36. static char *tags[T_NUM] = { "ol", "ul", "li", "grid", "gr", "gd", "table", "tr", "th", "td", "p", "b", "i", "u", "s", "sup",
  37. "sub", "quote", "dl", "dt", "dd", "a", "ui", "alert box" };
  38. char gendoc_path[PATH_MAX];
  39. char ***gendoc_rules = NULL, **gendoc_toc = NULL, **gendoc_fwd = NULL, *gendoc_buf = NULL, *gendoc_out = NULL;
  40. char gendoc_titleimg[PATH_MAX], gendoc_theme[PATH_MAX], gendoc_lang[19][256], *gendoc_cap = NULL;
  41. int gendoc_last = -1, gendoc_first = -1, gendoc_prev = -1;
  42. int gendoc_numrules = 0, gendoc_numtoc = 0, gendoc_numfwd = 0, gendoc_alfwd = 0, gendoc_fn = 0, gendoc_l, gendoc_h = 0, gendoc_H = 0;
  43. int gendoc_buflen = 0, gendoc_err = 0, gendoc_chk_l[T_NUM];
  44. void gendoc_report_error(const char *msg, ...);
  45. void gendoc_include(char *fn);
  46. char *gendoc_gen0[] = { "\\/\\/.*?$", "\\/\\*.*?\\*\\/", "#.*?$", NULL };
  47. char *gendoc_gen2[] = { "[:=\\<\\>\\+\\-\\*\\/%&\\^\\|!][:=]?", NULL };
  48. char *gendoc_gen3[] = { "[0-9][0-9bx]?[0-9\\.a-fp]*", NULL };
  49. char *gendoc_gen4[] = { "\"", "\'", "`", NULL };
  50. char *gendoc_gen5[] = { "[", "]", "{", "}", ",", ";", ":", NULL };
  51. char *gendoc_gen6[] = { "char", "int", "float", "true", "false", "nil", "null", "nullptr", "none", "public", "static", "struct",
  52. "enum", "typedef", "from", "with", "new", "delete", "void", NULL };
  53. char *gendoc_gen7[] = { "import", "def", "if", "then", "elseif", "else", "endif", "elif", "switch", "case", "loop", "until", "for",
  54. "foreach", "as", "is", "in", "or", "and", "while", "do", "break", "continue", "function", "return", "try", "catch",
  55. "volatile", "class", "sizeof", NULL };
  56. char **gendoc_generic[] = { gendoc_gen0, NULL, gendoc_gen2, gendoc_gen3, gendoc_gen4, gendoc_gen5, gendoc_gen6, gendoc_gen7 };
  57. /**************************************** helper functions ****************************************/
  58. /**
  59. * Allocate memory with error handling.
  60. */
  61. void *myrealloc(void *buf, int size)
  62. {
  63. void *ret = realloc(buf, size);
  64. if(!ret) {
  65. fprintf(stderr, "gendoc error: unable to allocate memory\n");
  66. exit(1);
  67. }
  68. if(!buf) memset(ret, 0, size);
  69. return ret;
  70. }
  71. /**
  72. * Duplicate a string with error handling. strdup() not available in ANSI C
  73. */
  74. char *strdupl(char *s)
  75. {
  76. char *ret;
  77. int i;
  78. if(!s || !*s) return NULL;
  79. i = strlen(s);
  80. ret = (char*)myrealloc(NULL, i + 1);
  81. strcpy(ret, s);
  82. return ret;
  83. }
  84. /**
  85. * Get file contents into memory.
  86. */
  87. char *file_get_contents(char *fn, unsigned int *size)
  88. {
  89. FILE *f;
  90. char *ret = NULL;
  91. unsigned int s = 0;
  92. f = fopen(fn, "rb");
  93. if(f) {
  94. fseek(f, 0L, SEEK_END);
  95. s = (unsigned int)ftell(f);
  96. fseek(f, 0L, SEEK_SET);
  97. ret = (char*)malloc(s + 16);
  98. if(ret) {
  99. s = fread(ret, 1, s, f);
  100. memset(ret + s, 0, 16);
  101. } else
  102. s = 0;
  103. fclose(f);
  104. }
  105. if(size) *size = s;
  106. return ret;
  107. }
  108. /**
  109. * Trim a string and remove newlines.
  110. */
  111. char *trimnl(char *s)
  112. {
  113. char *ret, *d;
  114. if(!s || !*s) return NULL;
  115. ret = d = (char*)myrealloc(NULL, strlen(s) + 1);
  116. while(*s == ' ') s++;
  117. while(*s) {
  118. if(*s != '\r' && *s != '\n')
  119. *d++ = *s;
  120. s++;
  121. }
  122. while(d > ret && d[-1] == ' ') d--;
  123. *d = 0;
  124. if(d == ret) { free(ret); return NULL; }
  125. ret = (char*)myrealloc(ret, d - ret + 1);
  126. return ret;
  127. }
  128. /**
  129. * Same as in PHP, escape unsafe characters.
  130. */
  131. char *htmlspecialchars(char *s)
  132. {
  133. char *ret, *d;
  134. int t;
  135. if(!s || !*s) return NULL;
  136. t = strlen(s) * 6;
  137. ret = d = (char*)myrealloc(NULL, t + 16);
  138. while(*s == ' ') s++;
  139. while(*s && d < ret + t) {
  140. if(*s == '&') { strcpy(d, "&amp;"); d += 5; } else
  141. if(*s == '<') { strcpy(d, "&lt;"); d += 4; } else
  142. if(*s == '>') { strcpy(d, "&gt;"); d += 4; } else
  143. if(*s == '\"') { strcpy(d, "&quot;"); d += 6; } else
  144. *d++ = *s;
  145. s++;
  146. }
  147. while(d > ret && d[-1] == ' ') d--;
  148. *d = 0;
  149. if(d == ret) { free(ret); return NULL; }
  150. ret = (char*)myrealloc(ret, d - ret + 1);
  151. return ret;
  152. }
  153. /**
  154. * Same as htmlspecialchars, except it handles <hl> and <hm> tags.
  155. */
  156. char *preformat(char *s)
  157. {
  158. char *ret, *d;
  159. int t;
  160. if(!s || !*s) return NULL;
  161. t = strlen(s) * 8;
  162. ret = d = (char*)myrealloc(NULL, t + 16);
  163. while(*s == ' ') s++;
  164. while(*s && d < ret + t) {
  165. if(!memcmp(s, "<hl>", 4)) { strcpy(d, "<span class=\"hl_h\">"); d += 19; s += 3; } else
  166. if(!memcmp(s, "<hm>", 4)) { strcpy(d, "<span class=\"hl_h hl_b\">"); d += 24; s += 3; } else
  167. if(!memcmp(s, "</hl>", 5) || !memcmp(s, "</hm>", 5)) { strcpy(d, "</span>"); d += 7; s += 4;
  168. if(s[-1] == 'm') { if(s[1] == '\r') { s++; } if(s[1] == '\n') s++; }
  169. } else
  170. if(*s == '&') { strcpy(d, "&amp;"); d += 5; } else
  171. if(*s == '<') { strcpy(d, "&lt;"); d += 4; } else
  172. if(*s == '>') { strcpy(d, "&gt;"); d += 4; } else
  173. if(*s == '\"') { strcpy(d, "&quot;"); d += 6; } else
  174. *d++ = *s;
  175. s++;
  176. }
  177. while(d > ret && d[-1] == ' ') d--;
  178. *d = 0;
  179. if(d == ret) { free(ret); return NULL; }
  180. ret = (char*)myrealloc(ret, d - ret + 1);
  181. return ret;
  182. }
  183. /**
  184. * RFC-compliant base64 encoder.
  185. */
  186. char *base64_encode(unsigned char *s, int l)
  187. {
  188. unsigned char b64e[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  189. char *ret, *out;
  190. int b = 0, c = 0;
  191. if(l <= 0) return NULL;
  192. ret = out = (char*)myrealloc(NULL, (((l+2)/3)*4) + 1);
  193. while(l) {
  194. b += *s++; c++; l--;
  195. if(c == 3) {
  196. *out++ = b64e[b>>18];
  197. *out++ = b64e[(b>>12)&0x3f];
  198. *out++ = b64e[(b>>6)&0x3f];
  199. *out++ = b64e[b&0x3f];
  200. b = c = 0;
  201. } else
  202. b <<= 8;
  203. }
  204. if(c != 0) {
  205. b <<= 16 - (8 * c);
  206. *out++ = b64e[b>>18];
  207. *out++ = b64e[(b>>12) & 0x3f];
  208. if(c == 1) { *out++ = '='; *out++ = '='; }
  209. else { *out++ = b64e[(b>>6)&0x3f]; *out++ = '='; }
  210. }
  211. *out = 0;
  212. return ret;
  213. }
  214. /**
  215. * A very minimalistic, non-UTF-8 aware regexp matcher. Enough to match language keywords.
  216. * Returns how many bytes matched, 0 if pattern doesn't match, -1 if pattern is bad.
  217. * Supports:
  218. * $ - matches end of line
  219. * .*? - skip bytes until the following pattern matches
  220. *
  221. * [abc] - one of the listed chars
  222. * [^abc] - all but the listed chars
  223. * [a-z] - intervals, characters from 'a' to 'z'
  224. * [0-9] - numbers (\d not supported)
  225. * a - an exact char
  226. * . - any char
  227. *
  228. * ? - one or zero match
  229. * + - at least one match
  230. * * - any number of matches
  231. * {n} - exactly n matches
  232. * {n,} - at least n matches
  233. * {n,m} - at least n, but no more than m matches
  234. */
  235. int match(char *regexp, char *str)
  236. {
  237. unsigned char valid[256], *c=(unsigned char*)regexp, *s=(unsigned char *)str;
  238. int d, r, rmin, rmax, neg;
  239. if(!regexp || !regexp[0] || !str || !str[0]) return -1;
  240. while(*c) {
  241. if(*c == '(' || *c == ')') { c++; continue; }
  242. rmin = rmax = r = 1; neg = 0;
  243. memset(valid, 0, sizeof(valid));
  244. /* special case, non-greedy match */
  245. if(c[0] == '.' && c[1] == '*' && c[2] == '?') {
  246. c += 3; if(!*c) return -1;
  247. if(*c == '$') { c++; while(*s && *s != '\n') s++; }
  248. else { while(*s && !match((char*)c, (char*)s)) s++; }
  249. } else {
  250. /* get valid characters list */
  251. if(*c == '\\') { c++; valid[(unsigned int)*c] = 1; } else {
  252. if(*c == '[') {
  253. c++; if(*c == '^') { c++; neg = 1; }
  254. while(*c && *c != ']') {
  255. if(*c == '\\') { c++; valid[(unsigned int)*c] = 1; }
  256. if(c[1] == '-') { for(d = *c, c += 2; d <= *c; d++) valid[d] = 1; }
  257. else valid[(unsigned int)(*c == '$' ? 10 : *c)] = 1;
  258. c++;
  259. }
  260. if(!*c) return -1;
  261. if(neg) { for(d = 0; d < 256; d++) valid[d] ^= 1; }
  262. }
  263. else if(*c == '.') { for(d = 0; d < 256; d++) valid[d] = 1; }
  264. else valid[(unsigned int)(*c == '$' ? 10 : *c)] = 1;
  265. }
  266. c++;
  267. /* make it case-insensitive */
  268. for(d = 0; d < 26; d++) {
  269. if(valid[d + 'a']) valid[d + 'A'] = 1; else
  270. if(valid[d + 'A']) valid[d + 'a'] = 1;
  271. }
  272. /* get repeat count */
  273. if(*c == '{') {
  274. c++; rmin = atoi((char*)c); rmax = 0; while(*c && *c != ',' && *c != '}') c++;
  275. if(*c == ',') { c++; if(*c != '}') { rmax = atoi((char*)c); while(*c && *c != '}') c++; } }
  276. if(*c != '}') return -1;
  277. c++;
  278. }
  279. else if(*c == '?') { c++; rmin = 0; rmax = 1; }
  280. else if(*c == '+') { c++; rmin = 1; rmax = 0; }
  281. else if(*c == '*') { c++; rmin = 0; rmax = 0; }
  282. /* do the match */
  283. for(r = 0; *s && valid[(unsigned int)*s] && (!rmax || r < rmax); s++, r++);
  284. /* allow exactly one + or - inside floating point numbers if they come right after the exponent marker */
  285. if(r && ((str[0] >= '0' && str[0] <= '9') || (str[0] == '-' && str[1] >= '0' && str[1] <= '9')) &&
  286. (*s == '+' || *s == '-') && (s[-1] == 'e' || s[-1] == 'E' || s[-1] == 'p' || s[-1] == 'P'))
  287. for(s++; *s && valid[(unsigned int)*s] && (!rmax || r < rmax); s++, r++);
  288. }
  289. if((!*s && *c) || r < rmin) return 0;
  290. }
  291. return (int)((intptr_t)s - (intptr_t)str);
  292. }
  293. /**
  294. * Load image into memory, detect its mime type and dimensions.
  295. */
  296. unsigned char *detect_image(char *fn, char *mime, int *w, int *h, int *l)
  297. {
  298. unsigned char *buf, *b;
  299. unsigned int size;
  300. mime[0] = 0; *w = *h = *l = 0;
  301. buf = (unsigned char*)file_get_contents(fn, &size);
  302. if(!buf) {
  303. gendoc_report_error("unable to read image '%s'", fn);
  304. return NULL;
  305. }
  306. *l = size;
  307. if(!memcmp(buf, "GIF", 3)) {
  308. strcpy(mime, "gif");
  309. *w = (buf[7] << 8) | buf[6];
  310. *h = (buf[9] << 8) | buf[8];
  311. } else
  312. if(!memcmp(buf, "\x89PNG", 4) && !memcmp(buf + 12, "IHDR", 4)) {
  313. strcpy(mime, "png");
  314. *w = (buf[18] << 8) | buf[19];
  315. *h = (buf[22] << 8) | buf[23];
  316. } else
  317. if(buf[0] == 0xff && buf[1] == 0xd8 && buf[2] == 0xff && buf[3] == 0xe0 && !memcmp(buf + 6, "JFIF", 4)) {
  318. strcpy(mime, "jpeg");
  319. for(b = buf + 20; b < buf + size - 8; b++)
  320. if(b[0] == 0xff && b[1] == 0xc0) {
  321. *w = (b[7] << 8) | b[8];
  322. *h = (b[5] << 8) | b[6];
  323. break;
  324. }
  325. } else
  326. if((!memcmp(buf, "RIFF", 4) && !memcmp(buf + 8, "WEBP", 4)) || !memcmp(buf, "WEBP", 4)) {
  327. strcpy(mime, "webp");
  328. if(!memcmp(buf, "RIFF", 4)) buf += 8;
  329. if(!memcmp(buf + 4, "VP8 ", 4)) {
  330. for(b = buf + 8; b < buf + size - 8; b++)
  331. if(b[0] == 0x9d && b[1] == 0x01 && b[2] == 0x2a) {
  332. *w = ((b[4] << 8) | b[3]) & 0x3fff;
  333. *h = ((b[6] << 8) | b[5]) & 0x3fff;
  334. break;
  335. }
  336. } else
  337. if(!memcmp(buf + 4, "VP8L", 4) && buf[12] == 0x2f) {
  338. *w = ((buf[14] << 8) | buf[13]) & 0x3fff;
  339. *h = ((buf[15] << 2) | buf[14] >> 6) & 0x3fff;
  340. } else
  341. if(!memcmp(buf + 4, "VP8X", 4)) {
  342. *w = ((buf[17] << 8) | buf[16]) + 1;
  343. *h = ((buf[20] << 8) | buf[19]) + 1;
  344. }
  345. }
  346. if(!mime[0] || *w < 1 || *h < 1 || *w > 2048 || *h > 2048) {
  347. free(buf);
  348. gendoc_report_error("unknown file format or oversized image '%s'", fn);
  349. return NULL;
  350. }
  351. return buf;
  352. }
  353. /**
  354. * Check if a string is listed in an array
  355. */
  356. int in_array(char *s, char **arr)
  357. {
  358. int i;
  359. if(!s || !*s || !arr) return 0;
  360. for(i = 0; arr[i]; i++)
  361. if(!strcmp(s, arr[i])) return 1;
  362. return 0;
  363. }
  364. /**************************************** MarkDown ****************************************/
  365. char *md_buf = NULL, *md_out = NULL;
  366. int md_buflen = 0;
  367. void md_text(const char *str, ...)
  368. {
  369. int len;
  370. __builtin_va_list args;
  371. __builtin_va_start(args, str);
  372. if(!str) return;
  373. len = strlen(str) + 4096;
  374. if(!md_buf || (int)(md_out - md_buf) + len > md_buflen) {
  375. md_out -= (intptr_t)md_buf;
  376. md_buf = (char*)myrealloc(md_buf, md_buflen + len + 65536);
  377. memset(md_buf + md_buflen, 0, len + 65536);
  378. md_buflen += len + 65536;
  379. md_out += (intptr_t)md_buf;
  380. }
  381. md_out += vsprintf(md_out, str, args);
  382. }
  383. void md_write(const char *str, int len)
  384. {
  385. if(!str) return;
  386. if(!md_buf || (int)(md_out - md_buf) + len > md_buflen) {
  387. md_out -= (intptr_t)md_buf;
  388. md_buf = (char*)myrealloc(md_buf, md_buflen + len + 65536);
  389. memset(md_buf + md_buflen, 0, len + 65536);
  390. md_buflen += len + 65536;
  391. md_out += (intptr_t)md_buf;
  392. }
  393. memcpy(md_out, str, len);
  394. md_out += len;
  395. }
  396. /*
  397. * Originally from https://github.com/Gottox/smu (MIT licensed), but modified heavily:
  398. * 1. changed to output to a string instead of stdout
  399. * 2. removed and rewrote some functions to integrate better
  400. * 3. output gendoc tags instead of HTML tags
  401. * 4. added strike-through, superscript, subscript, table
  402. * 5. eager to keep newlines intact (important for error reporting), this is 99% perfect
  403. *
  404. * smu - simple markup
  405. * Copyright (C) <2007, 2008> Enno Boland <g s01 de>
  406. */
  407. #define LENGTH(x) sizeof(x)/sizeof(x[0])
  408. #define ADDC(b,i) if(i % BUFSIZ == 0) { b = (char*)myrealloc(b, (i + BUFSIZ) * sizeof(char)); } b[i]
  409. typedef int (*Parser)(const char *, const char *, int);
  410. typedef struct {
  411. char *search;
  412. int process;
  413. char *before, *after;
  414. } Tag;
  415. static int docomment(const char *begin, const char *end, int newblock); /* Parser for html-comments */
  416. static int dogtlt(const char *begin, const char *end, int newblock); /* Parser for < and > */
  417. static int dohtml(const char *begin, const char *end, int newblock); /* Parser for html */
  418. static int dolineprefix(const char *begin, const char *end, int newblock);/* Parser for line prefix tags */
  419. static int dolink(const char *begin, const char *end, int newblock); /* Parser for links and images */
  420. static int dolist(const char *begin, const char *end, int newblock); /* Parser for lists */
  421. static int doparagraph(const char *begin, const char *end, int newblock); /* Parser for paragraphs */
  422. static int doreplace(const char *begin, const char *end, int newblock); /* Parser for simple replaces */
  423. static int doshortlink(const char *begin, const char *end, int newblock); /* Parser for links and images */
  424. static int dosurround(const char *begin, const char *end, int newblock); /* Parser for surrounding tags */
  425. static int dounderline(const char *begin, const char *end, int newblock); /* Parser for underline tags */
  426. static int dotable(const char *begin, const char *end, int newblock); /* Parser for tables */
  427. static void md_parse(const char *begin, const char *end, int isblock); /* Processes range between begin and end. */
  428. /* list of parsers */
  429. static Parser parsers[] = { dounderline, docomment, dolineprefix, dotable,
  430. dolist, dosurround, doparagraph, dogtlt, dolink,
  431. doshortlink, dohtml, doreplace };
  432. static Tag lineprefix[] = {
  433. { ">", 2, "<quote>", "</quote>" },
  434. { "###### ",1, "<h6>", "</h6>" },
  435. { "##### ", 1, "<h5>", "</h5>" },
  436. { "#### ", 1, "<h4>", "</h4>" },
  437. { "### ", 1, "<h3>", "</h3>" },
  438. { "## ", 1, "<h2>", "</h2>" },
  439. { "# ", 1, "<h1>", "</h1>" }
  440. };
  441. static Tag underline[] = {
  442. { "=", 1, "<h1>", "</h1>" },
  443. { "-", 1, "<h2>", "</h2>" },
  444. };
  445. static Tag surround[] = {
  446. { "```", 0, "<pre>", "</pre>" }, /* must be the first. If language given, will become <code> */
  447. { "``", 0, "<tt>", "</tt>" },
  448. { "`", 0, "<tt>", "</tt>" },
  449. { "^^", 1, "<sup>", "</sup>" },
  450. { ",,", 1, "<sub>", "</sub>" },
  451. { "___", 1, "<u><i><b>","</b></i></u>" },
  452. { "***", 1, "<i><b>", "</b></i>" },
  453. { "~~", 1, "<s>", "</s>" },
  454. { "__", 1, "<u>", "</u>" },
  455. { "**", 1, "<b>", "</b>" },
  456. { "~", 1, "<s>", "</s>" },
  457. { "_", 1, "<u>", "</u>" },
  458. { "*", 1, "<i>", "</i>" },
  459. };
  460. static const char *replace[][2] = {
  461. { "\\\\", "\\" },
  462. { "\\`", "`" },
  463. { "\\*", "*" },
  464. { "\\_", "_" },
  465. { "\\~", "~" },
  466. { "\\^", "^" },
  467. { "\\,", "," },
  468. { "\\{", "{" },
  469. { "\\}", "}" },
  470. { "\\[", "[" },
  471. { "\\]", "]" },
  472. { "\\(", "(" },
  473. { "\\)", ")" },
  474. { "\\#", "#" },
  475. { "\\+", "+" },
  476. { "\\-", "-" },
  477. { "\\.", "." },
  478. { "\\!", "!" },
  479. };
  480. static const char *insert[][2] = {
  481. { " \n", "<br>\n" },
  482. };
  483. int
  484. dogtlt(const char *begin, const char *end, int newblock) {
  485. int brpos;
  486. char c;
  487. (void)newblock;
  488. if(begin + 1 >= end)
  489. return 0;
  490. brpos = begin[1] == '>';
  491. if(!brpos && *begin != '<')
  492. return 0;
  493. c = begin[brpos ? 0 : 1];
  494. if(!brpos && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '1' || c > '6')) {
  495. md_text("&lt;");
  496. return 1;
  497. }
  498. else if(brpos && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '1' || c > '6') && !strchr("/\"'",c)) {
  499. md_text("%c&gt;",c);
  500. return 2;
  501. }
  502. return 0;
  503. }
  504. int
  505. docomment(const char *begin, const char *end, int newblock) {
  506. char *p;
  507. (void)newblock;
  508. if(strncmp("<!--", begin, 4))
  509. return 0;
  510. p = strstr(begin, "-->");
  511. if(!p || p + 3 >= end)
  512. return 0;
  513. return (p + 3 - begin) * (newblock ? -1 : 1);
  514. }
  515. int
  516. dohtml(const char *begin, const char *end, int newblock) {
  517. const char *p, *tag, *tagend;
  518. (void)newblock;
  519. if(begin + 2 >= end)
  520. return 0;
  521. p = begin;
  522. if(p[0] != '<' || !((p[1] >= 'a' && p[1] <= 'z') || (p[1] >= 'A' && p[1] <= 'Z')))
  523. return 0;
  524. p++;
  525. tag = p;
  526. for(; *p != ' ' && *p != '>' && p < end; p++);
  527. tagend = p;
  528. if(p > end || tag == tagend/* || (strncmp(tag, "pre", 3) && strncmp(tag, "code", 4) && strncmp(tag, "tt", 2))*/)
  529. return 0;
  530. while((p = strstr(p, "</")) && p < end) {
  531. p += 2;
  532. if(strncmp(p, tag, tagend - tag) == 0 && p[tagend - tag] == '>') {
  533. p++;
  534. md_parse(begin, p - 3, 0);
  535. md_write(p - 3, tagend - tag + 3);
  536. return p - begin + tagend - tag;
  537. }
  538. }
  539. p = strchr(tagend, '>');
  540. if(p) {
  541. md_write(begin, p - begin + 1);
  542. return p - begin + 1;
  543. }
  544. else
  545. return 0;
  546. }
  547. int
  548. dolineprefix(const char *begin, const char *end, int newblock) {
  549. unsigned int i, j, l;
  550. char *buffer;
  551. const char *p;
  552. if(newblock)
  553. p = begin;
  554. else if(*begin == '\n')
  555. p = begin + 1;
  556. else
  557. return 0;
  558. for(i = 0; i < LENGTH(lineprefix); i++) {
  559. l = strlen(lineprefix[i].search);
  560. if(end - p < l)
  561. continue;
  562. if(strncmp(lineprefix[i].search, p, l))
  563. continue;
  564. if(*begin == '\n')
  565. md_text("\n");
  566. md_text("%s", lineprefix[i].before);
  567. if(lineprefix[i].search[l-1] == '\n') {
  568. md_text("\n");
  569. return l - 1;
  570. }
  571. buffer = (char*)myrealloc(NULL, BUFSIZ);
  572. buffer[0] = '\0';
  573. /* Collect lines into buffer while they start with the prefix */
  574. j = 0;
  575. while((strncmp(lineprefix[i].search, p, l) == 0) && p + l < end) {
  576. p += l;
  577. /* Special case for blockquotes: optional space after > */
  578. if(lineprefix[i].search[0] == '>' && *p == ' ') {
  579. p++;
  580. }
  581. while(p < end) {
  582. ADDC(buffer, j) = *p;
  583. j++;
  584. if(*(p++) == '\n')
  585. break;
  586. }
  587. }
  588. /* Skip empty lines in block */
  589. /*
  590. while(*(buffer + j - 1) == '\n') {
  591. j--;
  592. }
  593. */
  594. ADDC(buffer, j) = '\0';
  595. if(lineprefix[i].process)
  596. md_parse(buffer, buffer + strlen(buffer), lineprefix[i].process >= 2);
  597. else
  598. md_text("%s", buffer);
  599. md_text("%s\n", lineprefix[i].after);
  600. free(buffer);
  601. return -(p - begin);
  602. }
  603. return 0;
  604. }
  605. int
  606. dolink(const char *begin, const char *end, int newblock) {
  607. int img, len, parens_depth = 1;
  608. const char *desc, *link, *p, *q, *descend, *linkend;
  609. (void)newblock;
  610. if(*begin == '[')
  611. img = 0;
  612. else if(strncmp(begin, "![", 2) == 0)
  613. img = 1;
  614. else
  615. return 0;
  616. for(p = desc = begin + 1 + img; p < end && *p != '\n' && *p != ']'; p++);
  617. if(p >= end || p[1] != '(')
  618. return 0;
  619. for(q = strstr(desc, "!["); q && q < end && q < p; q = strstr(q + 1, "!["))
  620. if(!(p = strstr(p + 1, "](")) || p > end)
  621. return 0;
  622. descend = p;
  623. link = p + 2;
  624. /* find end of link while handling nested parens */
  625. q = link;
  626. while(parens_depth) {
  627. if(!(q = strpbrk(q, "()")) || q > end)
  628. return 0;
  629. if(*q == '(')
  630. parens_depth++;
  631. else
  632. parens_depth--;
  633. if(parens_depth && q < end)
  634. q++;
  635. }
  636. linkend = q;
  637. /* Links can be given in angular brackets */
  638. if(*link == '<' && *(linkend - 1) == '>') {
  639. link++;
  640. linkend--;
  641. }
  642. len = q + 1 - begin;
  643. if(img) {
  644. md_text("<img%c ", begin[-1] == '\n' ? 'w' : 't');
  645. md_write(link, linkend - link);
  646. md_text(">");
  647. if(descend > desc) {
  648. md_text("<fig>");
  649. md_write(desc, descend - desc);
  650. md_text("</fig>");
  651. }
  652. }
  653. else {
  654. md_text("<a ");
  655. md_write(link, linkend - link);
  656. md_text(">");
  657. md_parse(desc, descend, 0);
  658. md_text("</a>");
  659. }
  660. return len;
  661. }
  662. int
  663. dolist(const char *begin, const char *end, int newblock) {
  664. unsigned int i, j, indent, run, ul, isblock;
  665. const char *p, *q;
  666. char *buffer = NULL;
  667. char marker;
  668. isblock = 0;
  669. if(newblock)
  670. p = begin;
  671. else if(*begin == '\n')
  672. p = begin + 1;
  673. else
  674. return 0;
  675. q = p;
  676. if(*p == '-' || *p == '*' || *p == '+') {
  677. ul = 1;
  678. marker = *p;
  679. } else {
  680. ul = 0;
  681. for(; p < end && *p >= '0' && *p <= '9'; p++);
  682. if(p >= end || *p != '.')
  683. return 0;
  684. }
  685. p++;
  686. if(p >= end || !(*p == ' ' || *p == '\t'))
  687. return 0;
  688. for(p++; p != end && (*p == ' ' || *p == '\t'); p++);
  689. indent = p - q;
  690. buffer = (char*)myrealloc(buffer, BUFSIZ);
  691. if(!newblock || *begin == '\n')
  692. md_text("\n");
  693. md_text(ul ? "<ul>" : "<ol>");
  694. run = 1;
  695. for(; p < end && run; p++) {
  696. for(i = 0; p < end && run; p++, i++) {
  697. if(*p == '\n') {
  698. if(p + 1 == end)
  699. break;
  700. else {
  701. /* Handle empty lines */
  702. for(q = p + 1; (*q == ' ' || *q == '\t') && q < end; q++);
  703. if(*q == '\n') {
  704. ADDC(buffer, i) = '\n';
  705. i++;
  706. run = 0;
  707. isblock++;
  708. p = q;
  709. }
  710. }
  711. q = p + 1;
  712. j = 0;
  713. if(ul && *q == marker)
  714. j = 1;
  715. else if(!ul) {
  716. for(; q + j != end && q[j] >= '0' && q[j] <= '9' && j < indent; j++);
  717. if(q + j == end)
  718. break;
  719. if(j > 0 && q[j] == '.')
  720. j++;
  721. else
  722. j = 0;
  723. }
  724. if(q + indent < end)
  725. for(; (q[j] == ' ' || q[j] == '\t') && j < indent; j++);
  726. if(j == indent) {
  727. ADDC(buffer, i) = '\n';
  728. i++;
  729. p += indent;
  730. run = 1;
  731. if(*q == ' ' || *q == '\t')
  732. p++;
  733. else
  734. break;
  735. }
  736. else if (j < indent)
  737. run = 0;
  738. }
  739. ADDC(buffer, i) = *p;
  740. }
  741. ADDC(buffer, i) = '\0';
  742. md_text("<li>");
  743. md_parse(buffer, buffer + i, isblock > 1 || (isblock == 1 && run));
  744. md_text("</li>\n");
  745. }
  746. if(md_out > md_buf && md_out[-1] == '\n') md_out--;
  747. md_text(ul ? "</ul>" : "</ol>");
  748. free(buffer);
  749. p--;
  750. while(*(--p) == '\n');
  751. return -(p - begin + 1);
  752. }
  753. int
  754. doparagraph(const char *begin, const char *end, int newblock) {
  755. const char *p, *s, *tag = "p";
  756. if(!newblock)
  757. return 0;
  758. p = strstr(begin, "\n\n");
  759. if(!p || p > end)
  760. p = end;
  761. for(s = begin; s < p && *s != '\n'; s++);
  762. if(p - begin <= 1 || (s == p && *begin == '<' && p[-1] == '>'))
  763. return 0;
  764. s = begin;
  765. if(!memcmp(begin, "INFO:", 5)) { s += 5; tag = "info"; } else
  766. if(!memcmp(begin, "HINT:", 5)) { s += 5; tag = "hint"; } else
  767. if(!memcmp(begin, "NOTE:", 5)) { s += 5; tag = "note"; } else
  768. if(!memcmp(begin, "SEE ALSO:", 9)) { s += 9; tag = "also"; } else
  769. if(!memcmp(begin, "ALSO:", 5)) { s += 5; tag = "also"; } else
  770. if(!memcmp(begin, "TODO:", 5)) { s += 5; tag = "todo"; } else
  771. if(!memcmp(begin, "WARNING:", 8)) { s += 8; tag = "warn"; } else
  772. if(!memcmp(begin, "WARN:", 5)) { s += 5; tag = "warn"; }
  773. md_text("<%s>", tag);
  774. md_parse(s, p, 0);
  775. md_text("</%s>", tag);
  776. return -(p - begin);
  777. }
  778. int
  779. doreplace(const char *begin, const char *end, int newblock) {
  780. unsigned int i, l;
  781. (void)newblock;
  782. for(i = 0; i < LENGTH(insert); i++)
  783. if(strncmp(insert[i][0], begin, strlen(insert[i][0])) == 0)
  784. md_text("%s", insert[i][1]);
  785. for(i = 0; i < LENGTH(replace); i++) {
  786. l = strlen(replace[i][0]);
  787. if(end - begin < l)
  788. continue;
  789. if(strncmp(replace[i][0], begin, l) == 0) {
  790. md_text("%s", replace[i][1]);
  791. return l;
  792. }
  793. }
  794. return 0;
  795. }
  796. int
  797. doshortlink(const char *begin, const char *end, int newblock) {
  798. const char *p;
  799. (void)newblock;
  800. if(*begin != '[')
  801. return 0;
  802. for(p = begin + 1; p != end && *p != '\\' && *p != '\n' && *p != ']'; p++);
  803. if(*p == ']') {
  804. md_text("<a>");
  805. md_write(begin + 1, p - (begin + 1));
  806. md_text("</a>");
  807. return p - begin + 1;
  808. }
  809. return 0;
  810. }
  811. int
  812. dosurround(const char *begin, const char *end, int newblock) {
  813. unsigned int i, l;
  814. const char *p, *start, *stop, *lang = NULL;
  815. (void)newblock;
  816. for(i = 0; i < LENGTH(surround); i++) {
  817. l = strlen(surround[i].search);
  818. if(end - begin < 2*l || strncmp(begin, surround[i].search, l) != 0)
  819. continue;
  820. start = begin + l;
  821. p = start - 1;
  822. do {
  823. stop = p;
  824. p = strstr(p + 1, surround[i].search);
  825. } while(p && p[-1] == '\\');
  826. if (p && p[-1] != '\\')
  827. stop = p;
  828. if(!stop || stop < start || stop >= end)
  829. continue;
  830. if(!i && *start != ' ' && *start != '\n')
  831. for(lang = start; start < stop && *start != '\n'; start++);
  832. if(!i && lang) {
  833. md_text("<code ");
  834. md_write(lang, start - lang);
  835. md_text(">");
  836. } else
  837. md_text("%s", surround[i].before);
  838. /* Single space at start and end are ignored */
  839. if (*start == ' ' && *(stop - 1) == ' ') {
  840. start++;
  841. stop--;
  842. l++;
  843. }
  844. if(surround[i].process)
  845. md_parse(start, stop, 0);
  846. else
  847. md_write(start, stop - start);
  848. if(!i && lang)
  849. md_text("</code>");
  850. else
  851. md_text("%s", surround[i].after);
  852. return stop - begin + l;
  853. }
  854. return 0;
  855. }
  856. int
  857. dounderline(const char *begin, const char *end, int newblock) {
  858. unsigned int i, j, l, k;
  859. const char *p;
  860. if(!newblock)
  861. return 0;
  862. p = begin;
  863. for(l = k = 0; p + l != end && p[l] != '\n'; l++)
  864. if(p[l] > 0 || ((uint8_t)p[l] & 0xC0) == 0xC0) k++;
  865. p += l + 1;
  866. if(l == 0)
  867. return 0;
  868. for(i = 0; i < LENGTH(underline); i++) {
  869. for(j = 0; p + j != end && p[j] != '\n' && p[j] == underline[i].search[0]; j++);
  870. if(j >= k) {
  871. md_text("%s", underline[i].before);
  872. if(underline[i].process)
  873. md_parse(begin, begin + l, 0);
  874. else
  875. md_write(begin, l);
  876. md_text("%s\n", underline[i].after);
  877. return -(j + p - begin);
  878. }
  879. }
  880. return 0;
  881. }
  882. static char intable = 0, inrow, incell;
  883. static long int calign;
  884. int
  885. dotable(const char *begin, const char *end, int newblock) {
  886. const char *p, cells[] = "dnDN";
  887. int i, l = (int)sizeof(calign) * 4;
  888. (void)newblock;
  889. if(*begin != '|')
  890. return 0;
  891. if(inrow && (begin + 1 >= end || begin[1] == '\n')) {
  892. md_text("</t%c></tr>", inrow == -1 ? 'h' : 'd');
  893. inrow = 0;
  894. if(begin + 2 >= end || begin[2] == '\n') {
  895. intable = 0;
  896. md_text("</table>");
  897. }
  898. return 1;
  899. }
  900. if(begin < end && (begin[1] == '-' || begin[1] == ':' || begin[1] == '*')) {
  901. for(p = begin; p < end && *p != '\n'; p++);
  902. return p - begin;
  903. }
  904. if(!intable) {
  905. intable = 1; inrow = -1; incell = 0; calign = 0;
  906. /* look ahead and get cell alignments */
  907. for(p = begin + 1; p < end && *p != '\n'; p++);
  908. for(; p < end && *p == '\n'; p++);
  909. if(p < end && (p[1] == '-' || p[1] == ':' || p[1] == '*'))
  910. for(i = -1; p < end && *p != '\n'; p++)
  911. if(i < l)
  912. switch(*p) {
  913. case '|': i++; break;
  914. case ':': calign |= 1 << (i * 2); break;
  915. case '*': calign |= (p[1] == '|' ? 3 : 2) << (i * 2); break;
  916. }
  917. md_text("<table><tr>");
  918. }
  919. if(!inrow) {
  920. inrow = 1; incell = 0;
  921. md_text("<tr>");
  922. }
  923. if(incell)
  924. md_text("</t%c>", inrow == -1 ? 'h' : 'd');
  925. l = incell < l ? (calign >> (incell * 2)) & 3 : 0;
  926. md_text("<t%c>", inrow == -1 ? (l > 1 ? 'H' : 'h') : cells[l]);
  927. incell++;
  928. for(p = begin + 1; p < end && *p == ' '; p++);
  929. return p - begin;
  930. }
  931. /* originally was "process", renamed to match gendoc conventions */
  932. void
  933. md_parse(const char *begin, const char *end, int newblock) {
  934. const char *p, *q;
  935. int affected;
  936. unsigned int i;
  937. for(p = begin; p < end;) {
  938. if(newblock)
  939. while(*p == '\n') {
  940. md_text("\n");
  941. if(++p == end)
  942. return;
  943. }
  944. affected = 0;
  945. for(i = 0; i < LENGTH(parsers) && !affected; i++)
  946. affected = parsers[i](p, end, newblock);
  947. p += abs(affected);
  948. if(!affected) {
  949. md_text("%c", *p);
  950. p++;
  951. }
  952. for(q = p; q != end && *q == '\n'; q++);
  953. if(q == end)
  954. return;
  955. else if(p[0] == '\n' && p + 1 != end && p[1] == '\n')
  956. newblock = 1;
  957. else
  958. newblock = affected < 0;
  959. }
  960. }
  961. /**************************************** gendoc interface ****************************************/
  962. /**
  963. * Convert a string into an URL and id attribute-safe string.
  964. * Sometimes it sucks that internet was designed by English only speakers.
  965. * @param string input
  966. * @return string the converted string in an alloc'd buffer
  967. */
  968. char *gendoc_safeid(char *str)
  969. {
  970. int i, l, t;
  971. static char safeid_buf[280];
  972. char *ret, *s = str, *d, *unicodes[] = {
  973. /* first 4 bytes: zero terminated UTF-8 character, replace from; rest of the string: replace to lowercase ASCII */
  974. "À\0\0a","à\0\0a","Á\0\0a","á\0\0a","Â\0\0a","â\0\0a","Ã\0\0a","ã\0\0a","Ä\0\0a","ä\0\0a","Å\0\0a","å\0\0a","Æ\0\0ae",
  975. "æ\0\0ae","Ç\0\0c","ç\0\0c","È\0\0e","è\0\0e","É\0\0e","é\0\0e","Ê\0\0e","ê\0\0e","Ë\0\0e","ë\0\0e","Ì\0\0i","ì\0\0i",
  976. "Í\0\0i","í\0\0i","Î\0\0i","î\0\0i","Ï\0\0i","ï\0\0i","Ð\0\0d","ð\0\0d","Ñ\0\0n","ñ\0\0n","Ò\0\0o","ò\0\0o","Ó\0\0o",
  977. "ó\0\0o","Ô\0\0o","ô\0\0o","Õ\0\0o","õ\0\0o","Ö\0\0o","ö\0\0o","Ø\0\0o","ø\0\0o","Ù\0\0u","ù\0\0u","Ú\0\0u","ú\0\0u",
  978. "Û\0\0u","û\0\0u","Ü\0\0u","ü\0\0u","Ý\0\0y","ý\0\0y","Þ\0\0p","þ\0\0p","Ā\0\0a","ā\0\0a","Ă\0\0a","ă\0\0a","Ą\0\0a",
  979. "ą\0\0a","Ć\0\0c","ć\0\0c","Ĉ\0\0c","ĉ\0\0c","Ċ\0\0c","ċ\0\0c","Č\0\0c","č\0\0c","Ď\0\0d","ď\0\0d","Đ\0\0d","đ\0\0d",
  980. "Ē\0\0e","ē\0\0e","Ĕ\0\0e","ĕ\0\0e","Ė\0\0e","ė\0\0e","Ę\0\0e","ę\0\0e","Ě\0\0e","ě\0\0e","Ĝ\0\0g","ĝ\0\0g","Ğ\0\0g",
  981. "ğ\0\0g","Ġ\0\0g","ġ\0\0g","Ģ\0\0g","ģ\0\0g","Ĥ\0\0h","ĥ\0\0h","Ħ\0\0h","ħ\0\0h","Ĩ\0\0i","ĩ\0\0i","Ī\0\0i","ī\0\0i",
  982. "Ĭ\0\0i","ĭ\0\0i","Į\0\0i","į\0\0i","İ\0\0i","i\0\0\0i","IJ\0\0ij","ij\0\0ij","Ĵ\0\0j","ĵ\0\0j","Ķ\0\0k","ķ\0\0k","Ĺ\0\0l",
  983. "ĺ\0\0l","Ļ\0\0l","ļ\0\0l","Ľ\0\0l","ľ\0\0l","Ŀ\0\0l","ŀ\0\0l","Ł\0\0l","ł\0\0l","Ń\0\0n","ń\0\0n","Ņ\0\0n","ņ\0\0n",
  984. "Ň\0\0n","ň\0\0n","Ŋ\0\0n","ŋ\0\0n","Ō\0\0o","ō\0\0o","Ŏ\0\0o","ŏ\0\0o","Ő\0\0o","ő\0\0o","Œ\0\0ce","œ\0\0ce","Ŕ\0\0r",
  985. "ŕ\0\0r","Ŗ\0\0r","ŗ\0\0r","Ř\0\0r","ř\0\0r","Ś\0\0s","ś\0\0s","Ŝ\0\0s","ŝ\0\0s","Ş\0\0s","ş\0\0s","Š\0\0s","š\0\0s",
  986. "Ţ\0\0t","ţ\0\0t","Ť\0\0t","ť\0\0t","Ŧ\0\0t","ŧ\0\0t","Ũ\0\0u","ũ\0\0u","Ū\0\0u","ū\0\0u","Ŭ\0\0u","ŭ\0\0u","Ů\0\0u",
  987. "ů\0\0u","Ű\0\0u","ű\0\0u","Ų\0\0u","ų\0\0u","Ŵ\0\0w","ŵ\0\0w","Ŷ\0\0y","ŷ\0\0y","Ÿ\0\0y","ÿ\0\0y","Ź\0\0z","ź\0\0z",
  988. "Ż\0\0z","ż\0\0z","Ž\0\0z","ž\0\0z","Ɓ\0\0b","ɓ\0\0b","Ƃ\0\0b","ƃ\0\0b","Ƅ\0\0b","ƅ\0\0b","Ɔ\0\0c","ɔ\0\0c","Ƈ\0\0c",
  989. "ƈ\0\0c","Ɖ\0\0ɖ","ɖ\0\0d","Ɗ\0\0d","ɗ\0\0d","Ƌ\0\0d","ƌ\0\0d","Ǝ\0\0e","ǝ\0\0e","Ə\0\0e","ə\0\0e","Ɛ\0\0e","ɛ\0\0e",
  990. "Ƒ\0\0f","ƒ\0\0f","Ɠ\0\0g","ɠ\0\0g","Ɣ\0\0y","ɣ\0\0y","Ɩ\0\0l","ɩ\0\0l","Ɨ\0\0i","ɨ\0\0i","Ƙ\0\0k","ƙ\0\0k","Ɯ\0\0w",
  991. "ɯ\0\0w","Ɲ\0\0n","ɲ\0\0n","Ɵ\0\0o","ɵ\0\0o","Ơ\0\0o","ơ\0\0o","Ƣ\0\0oj","ƣ\0\0oj","Ƥ\0\0p","ƥ\0\0p","Ʀ\0\0r","ʀ\0\0r",
  992. "Ƨ\0\0s","ƨ\0\0s","Ʃ\0\0s","ʃ\0\0s","Ƭ\0\0t","ƭ\0\0t","Ʈ\0\0t","ʈ\0\0t","Ư\0\0u","ư\0\0u","Ʊ\0\0u","ʊ\0\0u","Ʋ\0\0u",
  993. "ʋ\0\0u","Ƴ\0\0y","ƴ\0\0y","Ƶ\0\0z","ƶ\0\0z","Ʒ\0\0z","ʒ\0\0z","Ƹ\0\0z","ƹ\0\0z","Ƽ\0\0z","ƽ\0\0z","DŽ\0\0dz","dž\0\0dz",
  994. "Dž\0\0dz","LJ\0\0lj","lj\0\0lj","Lj\0\0lj","NJ\0\0nj","nj\0\0nj","Nj\0\0nj","Ǎ\0\0a","ǎ\0\0a","Ǐ\0\0i","ǐ\0\0i","Ǒ\0\0o","ǒ\0\0o",
  995. "Ǔ\0\0u","ǔ\0\0u","Ǖ\0\0u","ǖ\0\0u","Ǘ\0\0u","ǘ\0\0u","Ǚ\0\0u","ǚ\0\0u","Ǜ\0\0u","ǜ\0\0u","Ǟ\0\0a","ǟ\0\0a","Ǡ\0\0a",
  996. "ǡ\0\0a","Ǣ\0\0ae","ǣ\0\0ae","Ǥ\0\0g","ǥ\0\0g","Ǧ\0\0g","ǧ\0\0g","Ǩ\0\0k","ǩ\0\0k","Ǫ\0\0o","ǫ\0\0o","Ǭ\0\0o","ǭ\0\0o",
  997. "Ǯ\0\0z","ǯ\0\0z","DZ\0\0dz","dz\0\0dz","Dz\0\0dz","Ǵ\0\0g","ǵ\0\0g","Ƕ\0\0hj","ƕ\0\0hj","Ƿ\0\0p","ƿ\0\0p","Ǹ\0\0n","ǹ\0\0n",
  998. "Ǻ\0\0a","ǻ\0\0a","Ǽ\0\0ae","ǽ\0\0ae","Ǿ\0\0o","ǿ\0\0ǿ","Ȁ\0\0a","ȁ\0\0a","Ȃ\0\0a","ȃ\0\0a","Ȅ\0\0e","ȅ\0\0e","Ȇ\0\0e",
  999. "ȇ\0\0e","Ȉ\0\0i","ȉ\0\0i","Ȋ\0\0i","ȋ\0\0i","Ȍ\0\0o","ȍ\0\0o","Ȏ\0\0o","ȏ\0\0o","Ȑ\0\0r","ȑ\0\0r","Ȓ\0\0r","ȓ\0\0r",
  1000. "Ȕ\0\0u","ȕ\0\0u","Ȗ\0\0u","ȗ\0\0u","Ș\0\0s","ș\0\0s","Ț\0\0t","ț\0\0t","Ȝ\0\0z","ȝ\0\0z","Ȟ\0\0h","ȟ\0\0h","Ƞ\0\0n",
  1001. "ƞ\0\0n","Ȣ\0\0o","ȣ\0\0o","Ȥ\0\0z","ȥ\0\0z","Ȧ\0\0a","ȧ\0\0a","Ȩ\0\0e","ȩ\0\0e","Ȫ\0\0o","ȫ\0\0o","Ȭ\0\0o","ȭ\0\0o",
  1002. "Ȯ\0\0o","ȯ\0\0o","Ȱ\0\0o","ȱ\0\0o","Ȳ\0\0y","ȳ\0\0y","Ⱥ\0\0a","ⱥ\0a","Ȼ\0\0c","ȼ\0\0c","Ƚ\0\0l","ƚ\0\0l","Ⱦ\0\0t",
  1003. "ⱦ\0t","Ƀ\0\0b","ƀ\0\0b","Ʉ\0\0u","ʉ\0\0u","Ɇ\0\0e","ɇ\0\0e","Ɉ\0\0j","ɉ\0\0j","Ɋ\0\0q","ɋ\0\0q","Ɍ\0\0r","ɍ\0\0r",
  1004. "Ɏ\0\0y","ɏ\0\0y"
  1005. };
  1006. if(!str || !*str) return NULL;
  1007. t = 2 * strlen(str); if(t > 252) t = 252; /* <- 255 - zero - 2 bytes for the longest UTF-8 replacement */
  1008. ret = d = (char*)safeid_buf;
  1009. while(*s == ' ') s++;
  1010. while(*s && d < ret + t) {
  1011. if(*s == '\r' || *s == '\"' || *s == '\'' || *s == '#' || *s == '?' || *s == '/' || *s == '&' || *s == ';') {
  1012. s++; continue;
  1013. }
  1014. if(*s == '&')
  1015. while(*s && *s != ';') s++;
  1016. if((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9')) {
  1017. if(*s >= 'A' && *s <= 'Z')
  1018. *d++ = (*s++) + 'a' - 'A';
  1019. else
  1020. *d++ = *s++;
  1021. continue;
  1022. } else {
  1023. for(i = 0; i < (int)(sizeof(unicodes)/sizeof(unicodes[0])); i++) {
  1024. l = strlen(unicodes[i]);
  1025. if(!memcmp(s, unicodes[i], l)) {
  1026. s += l; l = strlen(unicodes[i] + 4);
  1027. memcpy(d, unicodes[i] + 4, l); d += l;
  1028. break;
  1029. }
  1030. }
  1031. if(i < (int)(sizeof(unicodes)/sizeof(unicodes[0]))) continue;
  1032. }
  1033. if(d > ret && d[-1] != '_') *d++ = '_';
  1034. s++;
  1035. }
  1036. while(d > ret && d[-1] == '_') d--;
  1037. *d = 0;
  1038. if(d == ret) { return NULL; }
  1039. return ret;
  1040. }
  1041. /**
  1042. * Check if a tag is open
  1043. */
  1044. void gendoc_chk_o(int i)
  1045. {
  1046. if(i == T_li && !gendoc_chk_l[T_ol] && !gendoc_chk_l[T_ul])
  1047. { fprintf(stderr, "gendoc error: %s:%u: tag %s not in an ol/ul\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1048. if((i == T_dt || i == T_dd) && !gendoc_chk_l[T_dl])
  1049. { fprintf(stderr, "gendoc error: %s:%u: tag %s not in a dl\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1050. if(i == T_gr && !gendoc_chk_l[T_grid])
  1051. { fprintf(stderr, "gendoc error: %s:%u: tag %s not in a grid\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1052. if(i == T_gd && !gendoc_chk_l[T_gr])
  1053. { fprintf(stderr, "gendoc error: %s:%u: tag %s not in a gr\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1054. if(i == T_tr && !gendoc_chk_l[T_table])
  1055. { fprintf(stderr, "gendoc error: %s:%u: tag %s not in a table\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1056. if((i == T_th || i == T_td) && !gendoc_chk_l[T_tr])
  1057. { fprintf(stderr, "gendoc error: %s:%u: tag %s not in a tr\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1058. if(i < T_b)
  1059. gendoc_chk_l[i]++;
  1060. else {
  1061. if(gendoc_chk_l[i]) {
  1062. fprintf(stderr, "gendoc error: %s:%u: tag %s already open (opened in line %u)\n", gendoc_path, gendoc_l, tags[i],
  1063. gendoc_chk_l[i]);
  1064. gendoc_err++;
  1065. } else
  1066. gendoc_chk_l[i] = gendoc_l;
  1067. }
  1068. }
  1069. /**
  1070. * Check if a tag is closed
  1071. */
  1072. void gendoc_chk_c(int i)
  1073. {
  1074. if((i == T_ol || i == T_ul) && gendoc_chk_l[T_li] > gendoc_chk_l[i])
  1075. { fprintf(stderr, "gendoc error: %s:%u: tag %s close but tag li still open\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1076. if(i == T_dl && (gendoc_chk_l[T_dt] > gendoc_chk_l[i] || gendoc_chk_l[T_dd] > gendoc_chk_l[i]))
  1077. { fprintf(stderr, "gendoc error: %s:%u: tag %s close but tags dt/dd still open\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1078. if(i == T_gr && gendoc_chk_l[T_gd] > gendoc_chk_l[i])
  1079. { fprintf(stderr, "gendoc error: %s:%u: tag %s close but tag gd still open\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1080. if(i == T_gd && gendoc_chk_l[T_gr] > gendoc_chk_l[i])
  1081. { fprintf(stderr, "gendoc error: %s:%u: tag %s close but tag gr still open\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1082. if(i == T_grid && gendoc_chk_l[T_gr] > gendoc_chk_l[i])
  1083. { fprintf(stderr, "gendoc error: %s:%u: tag %s close but tag gr still open\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1084. if(i == T_gr && (gendoc_chk_l[T_th] > gendoc_chk_l[i] || gendoc_chk_l[T_td] > gendoc_chk_l[i]))
  1085. { fprintf(stderr, "gendoc error: %s:%u: tag %s close but tag th/td/tn still open\n", gendoc_path, gendoc_l, tags[i]); gendoc_err++; }
  1086. if(!gendoc_chk_l[i]) {
  1087. fprintf(stderr, "gendoc error: %s:%u: no opening tag %s\n", gendoc_path, gendoc_l, tags[i]);
  1088. gendoc_err++;
  1089. } else {
  1090. if(i < T_b)
  1091. gendoc_chk_l[i]--;
  1092. else
  1093. gendoc_chk_l[i] = 0;
  1094. }
  1095. }
  1096. /**
  1097. * Check if all tags are closed
  1098. */
  1099. void gendoc_chk_all(void)
  1100. {
  1101. int i;
  1102. for(i = 0; i < T_NUM; i++)
  1103. if(gendoc_chk_l[i]) {
  1104. if(gendoc_chk_l[i] < T_b)
  1105. fprintf(stderr, "gendoc error: %s:%u: tag %s not closed\n", gendoc_path, gendoc_l, tags[i]);
  1106. else
  1107. fprintf(stderr, "gendoc error: %s:%u: tag %s not closed (opened in line %u)\n", gendoc_path, gendoc_l, tags[i],
  1108. gendoc_chk_l[i]);
  1109. gendoc_err++;
  1110. gendoc_chk_l[i] = 0;
  1111. }
  1112. }
  1113. /**
  1114. * Report an error.
  1115. * @param string error message
  1116. * @param arguments
  1117. */
  1118. void gendoc_report_error(const char *msg, ...)
  1119. {
  1120. __builtin_va_list args;
  1121. __builtin_va_start(args, msg);
  1122. fprintf(stderr, "gendoc error: %s:%u: ", gendoc_path, gendoc_l);
  1123. vfprintf(stderr, msg, args);
  1124. fprintf(stderr, "\n");
  1125. gendoc_err++;
  1126. }
  1127. /**
  1128. * Add any arbitrary text to output. If a tag is open, then to that.
  1129. * @param string the text
  1130. * @param arguments
  1131. */
  1132. void gendoc_text(const char *str, ...)
  1133. {
  1134. int len;
  1135. __builtin_va_list args;
  1136. __builtin_va_start(args, str);
  1137. if(!str) return;
  1138. len = strlen(str) + 4096;
  1139. if(!gendoc_buf || (int)(gendoc_out - gendoc_buf) + len > gendoc_buflen) {
  1140. gendoc_out -= (intptr_t)gendoc_buf;
  1141. gendoc_buf = (char*)myrealloc(gendoc_buf, gendoc_buflen + len + 262144);
  1142. memset(gendoc_buf + gendoc_buflen, 0, len + 262144);
  1143. gendoc_buflen += len + 262144;
  1144. gendoc_out += (intptr_t)gendoc_buf;
  1145. }
  1146. gendoc_out += vsprintf(gendoc_out, str, args);
  1147. }
  1148. /**
  1149. * Parse <doc> tag.
  1150. * @param string with <doc> sub-tags
  1151. * @param end of string
  1152. */
  1153. void gendoc_doc(char *s, char *e)
  1154. {
  1155. char *d, *f;
  1156. while(s < e) {
  1157. while(*s != '<' && s < e) s++;
  1158. if(*s != '<' || s >= e) break;
  1159. s++; for(d = s; *d != '<' && d < e; d++);
  1160. if(!memcmp(s, "lang>", 5)) { s += 5; memcpy(gendoc_lang[0], s, d - s); gendoc_lang[0][d - s] = 0; } else
  1161. if(!memcmp(s, "titleimg>", 9)) {
  1162. s += 9; memcpy(gendoc_titleimg, gendoc_path, gendoc_fn); f = gendoc_titleimg + gendoc_fn;
  1163. while(s < d && *s != ' ' && *s != '<') *f++ = *s++;
  1164. *f = 0; if(*s == ' ') { s++; memcpy(gendoc_lang[1], s, d - s); gendoc_lang[1][d - s] = 0; }
  1165. } else
  1166. if(!memcmp(s, "title>", 6)) { s += 6; memcpy(gendoc_lang[2], s, d - s); gendoc_lang[2][d - s] = 0; } else
  1167. if(!memcmp(s, "url>", 4)) { s += 4; memcpy(gendoc_lang[3], s, d - s); gendoc_lang[3][d - s] = 0; } else
  1168. if(!memcmp(s, "version>", 8)) { s += 8; memcpy(gendoc_lang[4], s, d - s); gendoc_lang[4][d - s] = 0; } else
  1169. if(!memcmp(s, "theme>", 6)) {
  1170. s += 6;
  1171. memcpy(gendoc_theme, gendoc_path, gendoc_fn); memcpy(gendoc_theme + gendoc_fn, s, d - s);
  1172. gendoc_theme[gendoc_fn + (intptr_t)(d - s)] = 0;
  1173. } else
  1174. if(!memcmp(s, "rslt>", 5)) { s += 5; memcpy(gendoc_lang[5], s, d - s); gendoc_lang[5][d - s] = 0; } else
  1175. if(!memcmp(s, "home>", 5)) { s += 5; memcpy(gendoc_lang[6], s, d - s); gendoc_lang[6][d - s] = 0; } else
  1176. if(!memcmp(s, "link>", 5)) { s += 5; memcpy(gendoc_lang[7], s, d - s); gendoc_lang[7][d - s] = 0; } else
  1177. if(!memcmp(s, "info>", 5)) { s += 5; memcpy(gendoc_lang[8], s, d - s); gendoc_lang[8][d - s] = 0; } else
  1178. if(!memcmp(s, "hint>", 5)) { s += 5; memcpy(gendoc_lang[9], s, d - s); gendoc_lang[9][d - s] = 0; } else
  1179. if(!memcmp(s, "note>", 5)) { s += 5; memcpy(gendoc_lang[10], s, d - s); gendoc_lang[10][d - s] = 0; } else
  1180. if(!memcmp(s, "also>", 5)) { s += 5; memcpy(gendoc_lang[11], s, d - s); gendoc_lang[11][d - s] = 0; } else
  1181. if(!memcmp(s, "todo>", 5)) { s += 5; memcpy(gendoc_lang[12], s, d - s); gendoc_lang[12][d - s] = 0; } else
  1182. if(!memcmp(s, "warn>", 5)) { s += 5; memcpy(gendoc_lang[13], s, d - s); gendoc_lang[13][d - s] = 0; } else
  1183. if(!memcmp(s, "args>", 5)) { s += 5; memcpy(gendoc_lang[14], s, d - s); gendoc_lang[14][d - s] = 0; } else
  1184. if(!memcmp(s, "rval>", 5)) { s += 5; memcpy(gendoc_lang[15], s, d - s); gendoc_lang[15][d - s] = 0; } else
  1185. if(!memcmp(s, "prev>", 5)) { s += 5; memcpy(gendoc_lang[16], s, d - s); gendoc_lang[16][d - s] = 0; } else
  1186. if(!memcmp(s, "next>", 5)) { s += 5; memcpy(gendoc_lang[17], s, d - s); gendoc_lang[17][d - s] = 0; } else
  1187. if(!memcmp(s, "copy>", 5)) { s += 5; memcpy(gendoc_lang[18], s, d - s); gendoc_lang[18][d - s] = 0; }
  1188. s = d + 1;
  1189. }
  1190. }
  1191. /**
  1192. * Add link to previous page.
  1193. */
  1194. void gendoc_prev_link()
  1195. {
  1196. gendoc_chk_all();
  1197. if(gendoc_prev != -1) {
  1198. gendoc_text("<br style=\"clear:both;\"><label class=\"btn prev\" accesskey=\"p\" for=\"_");
  1199. if(gendoc_prev != -2) {
  1200. gendoc_text("%s\" title=\"%s", gendoc_toc[gendoc_prev * 4 + 0], gendoc_toc[gendoc_prev * 4 + 3]);
  1201. }
  1202. gendoc_text("\">%s</label>", gendoc_lang[16]);
  1203. }
  1204. }
  1205. /**
  1206. * Add link to next page.
  1207. * @param string toc id
  1208. */
  1209. void gendoc_next_link(int toc)
  1210. {
  1211. gendoc_chk_all();
  1212. if(toc >= 0 && gendoc_toc[toc * 4 + 0] && gendoc_toc[toc * 4 + 0][0]) {
  1213. if(gendoc_prev == -1)
  1214. gendoc_text("<br style=\"clear:both;\">");
  1215. gendoc_text("<label class=\"btn next\" accesskey=\"n\" for=\"_%s\" title=\"%s\">%s</label>",
  1216. gendoc_toc[toc * 4 + 0], gendoc_toc[toc * 4 + 3], gendoc_lang[17]);
  1217. }
  1218. gendoc_text("</div>\n");
  1219. }
  1220. /**
  1221. * Add an internal link to any heading.
  1222. * @param string heading user-readable name
  1223. */
  1224. void gendoc_internal_link(char *name)
  1225. {
  1226. int i;
  1227. char *lnk = gendoc_safeid(name);
  1228. if(!name || !*name || !lnk) return;
  1229. for(i = 0; i < gendoc_numtoc; i++)
  1230. if(gendoc_toc[i * 4 + 0] && !strcmp(gendoc_toc[i * 4 + 0], lnk)) break;
  1231. if(i >= gendoc_numtoc) {
  1232. for(i = 0; i < gendoc_numfwd && strcmp(gendoc_fwd[i], lnk); i++);
  1233. if(i == gendoc_numfwd) {
  1234. gendoc_numfwd++;
  1235. if(gendoc_numfwd + 256 > gendoc_alfwd) {
  1236. gendoc_alfwd = gendoc_numfwd + 256;
  1237. gendoc_fwd = (char**)myrealloc(gendoc_fwd, gendoc_alfwd * sizeof(char*));
  1238. }
  1239. gendoc_fwd[i] = (char*)myrealloc(NULL, strlen(lnk) + 1 + strlen(gendoc_path) + strlen(name) + 32);
  1240. sprintf(gendoc_fwd[i], "%s%c%s:%u unresolved link: %s", lnk, 0, gendoc_path, gendoc_l, name);
  1241. }
  1242. i = strlen(lnk);
  1243. memmove(lnk + 8, lnk, i);
  1244. memcpy(lnk, "@GENDOC:", 8);
  1245. memcpy(lnk + 8 + i, "@", 2);
  1246. }
  1247. gendoc_text("<a href=\"#%s\" onclick=\"c('%s')\">%s</a>", lnk, lnk, name);
  1248. }
  1249. /**
  1250. * Resolve a forward internal link.
  1251. * @param string heading user-readable name
  1252. * @param string the real id
  1253. */
  1254. void gendoc_resolve_link(char *subst, char *id)
  1255. {
  1256. char *old, *lnk, *s, *d;
  1257. int i, j, k;
  1258. if(!subst || !id || !*id || !gendoc_buf || gendoc_out < gendoc_buf + 10) return;
  1259. lnk = gendoc_safeid(subst);
  1260. if(!lnk) return;
  1261. for(i = j = 0; i < gendoc_numfwd; i++)
  1262. if(!strcmp(gendoc_fwd[i], lnk)) {
  1263. free(gendoc_fwd[i]);
  1264. memcpy(&gendoc_fwd[i], &gendoc_fwd[i + 1], (gendoc_numfwd - i - 1) * sizeof(char*));
  1265. gendoc_numfwd--; i--; j++;
  1266. }
  1267. if(!j) { return; }
  1268. i = strlen(lnk);
  1269. memmove(lnk + 8, lnk, i);
  1270. memcpy(lnk, "@GENDOC:", 8);
  1271. memcpy(lnk + 8 + i, "@", 2);
  1272. i += 9; j = strlen(id);
  1273. for(k = 0, s = gendoc_buf; s < gendoc_out - i; s++)
  1274. if(*s == '@' && !memcmp(s, lnk, i)) { s += i - 1; k++; }
  1275. old = gendoc_buf;
  1276. gendoc_buflen += k * (j - i) + 262144;
  1277. gendoc_buf = (char*)myrealloc(NULL, gendoc_buflen);
  1278. for(s = old, d = gendoc_buf; s < gendoc_out; s++)
  1279. if(s + i < gendoc_out && *s == '@' && !memcmp(s, lnk, i)) { memcpy(d, id, j); d += j; s += i - 1; }
  1280. else *d++ = *s;
  1281. *d = 0;
  1282. gendoc_out = d;
  1283. free(old);
  1284. }
  1285. /**
  1286. * Add a heading.
  1287. * @param string from "h1>..."
  1288. * @param string position of the ending tag
  1289. */
  1290. void gendoc_heading(char *s, char *e)
  1291. {
  1292. char *id = NULL, *alias = NULL, level = s[1];
  1293. int i;
  1294. gendoc_chk_all();
  1295. s += 2;
  1296. if(*s == ' ') {
  1297. s++; id = s; while(s < e && *s != ' ' && *s != '>') s++;
  1298. if(*s == ' ') {
  1299. alias = id; *s++ = 0; id = s;
  1300. while(s < e && *s != '>') s++;
  1301. }
  1302. if(*s == '>')
  1303. *s++ = 0;
  1304. } else
  1305. s++;
  1306. if(s >= e && gendoc_first != -2) {
  1307. gendoc_report_error("empty heading name");
  1308. return;
  1309. }
  1310. *e = 0;
  1311. if(gendoc_h) {
  1312. if(level == '1')
  1313. gendoc_text("<div class=\"page\" rel=\"_\">");
  1314. gendoc_text("\n<h%c>%s</h%c>", level, s, level);
  1315. } else {
  1316. gendoc_toc = (char**)myrealloc(gendoc_toc, (gendoc_numtoc + 1) * 4 * sizeof(char*));
  1317. gendoc_toc[gendoc_numtoc * 4 + 0] = strdupl(gendoc_safeid(id ? id : s));
  1318. if(!gendoc_toc[gendoc_numtoc * 4 + 0] || !gendoc_toc[gendoc_numtoc * 4 + 0][0]) {
  1319. gendoc_report_error("no id for heading (%s)", s);
  1320. return;
  1321. }
  1322. for(i = 0; i < gendoc_numtoc; i++)
  1323. if(gendoc_toc[i * 4 + 0] && !strcmp(gendoc_toc[i * 4 + 0], gendoc_toc[gendoc_numtoc * 4 + 0])) {
  1324. gendoc_report_error("id for heading isn't unique (%s)", gendoc_toc[gendoc_numtoc * 4 + 0]);
  1325. gendoc_toc[gendoc_numtoc * 4 + 0][0] = 0;
  1326. break;
  1327. }
  1328. gendoc_toc[gendoc_numtoc * 4 + 1] = (char*)((intptr_t)level);
  1329. gendoc_toc[gendoc_numtoc * 4 + 2] = strdupl(s);
  1330. gendoc_toc[gendoc_numtoc * 4 + 3] = htmlspecialchars(s);
  1331. gendoc_resolve_link(s, gendoc_toc[gendoc_numtoc * 4 + 0]);
  1332. if(gendoc_out)
  1333. while(gendoc_out > gendoc_buf && (gendoc_out[-1] == ' ' || gendoc_out[-1] == '\r' || gendoc_out[-1] == '\n'))
  1334. gendoc_out--;
  1335. if(level == '1') {
  1336. if(gendoc_numtoc) {
  1337. gendoc_prev_link();
  1338. gendoc_next_link(gendoc_numtoc);
  1339. gendoc_prev = gendoc_last;
  1340. gendoc_last = gendoc_numtoc;
  1341. }
  1342. if(gendoc_first == -1) gendoc_first = gendoc_numtoc;
  1343. gendoc_text("<div class=\"page\"");
  1344. if(gendoc_toc[gendoc_numtoc * 4 + 0] && gendoc_toc[gendoc_numtoc * 4 + 0][0])
  1345. gendoc_text(" rel=\"%s\"", gendoc_toc[gendoc_numtoc * 4 + 0]);
  1346. gendoc_text("><div><ul class=\"breadcrumbs\"><li><label class=\"home\" for=\"_");
  1347. if(gendoc_first >= 0) gendoc_text("%s", gendoc_toc[gendoc_first * 4 + 0]);
  1348. gendoc_text("\" title=\"%s\"></label>&nbsp;»</li>", gendoc_lang[6]);
  1349. if(gendoc_cap && *gendoc_cap) gendoc_text("<li>&nbsp;%s&nbsp;»</li>", gendoc_cap);
  1350. gendoc_text("<li>&nbsp;%s</li></ul><hr></div>", s);
  1351. }
  1352. gendoc_text("\n");
  1353. if((alias = gendoc_safeid(alias))) gendoc_text("<span id=\"%s\"></span>", alias);
  1354. gendoc_text("<h%c", level);
  1355. if(gendoc_toc[gendoc_numtoc * 4 + 0] && gendoc_toc[gendoc_numtoc * 4 + 0][0])
  1356. gendoc_text(" id=\"%s\"", gendoc_toc[gendoc_numtoc * 4 + 0]);
  1357. gendoc_text(">%s", s);
  1358. if(gendoc_toc[gendoc_numtoc * 4 + 0] && gendoc_toc[gendoc_numtoc * 4 + 0][0]) {
  1359. gendoc_text("<a href=\"#%s\"></a>", gendoc_toc[gendoc_numtoc * 4 + 0]);
  1360. }
  1361. gendoc_text("</h%c>", level);
  1362. gendoc_numtoc++;
  1363. }
  1364. }
  1365. /**
  1366. * Add source code.
  1367. * @param string the source code
  1368. * @param string the language type (or empty string)
  1369. */
  1370. void gendoc_source_code(char *str, char *lang)
  1371. {
  1372. char *s, *d, ***r = NULL, *hl = "cpons.tkfv";
  1373. int i, j, k, l, m, nt = 0, at = 0, *t = NULL;
  1374. /* load highlight rules */
  1375. if(gendoc_rules && lang && *lang) {
  1376. for(i = 0; i < gendoc_numrules; i++)
  1377. if(!strcmp((char*)gendoc_rules[i * 9], lang)) { r = &gendoc_rules[i * 9 + 1]; break; }
  1378. }
  1379. /* use a generic scheme */
  1380. if(!r) {
  1381. if(lang && *lang)
  1382. fprintf(stderr, "gendoc warning: %s:%u: no highlight rules for '%s' using generics\n", gendoc_path, gendoc_l, lang);
  1383. r = gendoc_generic;
  1384. }
  1385. /* dump current ruleset. Note: quotes (") are deliberately unescaped. */
  1386. /*
  1387. for(j = 0; j < 8; j++) {
  1388. printf("r[%d] (hl_%c): ", j, hl[j]);
  1389. if(r[j])
  1390. for(i = 0; r[j][i]; i++)
  1391. printf(" \"%s\"", r[j][i]);
  1392. printf("\n");
  1393. }
  1394. printf("\n\n");
  1395. */
  1396. gendoc_text("<div class=\"pre\"><pre class=\"lineno\">");
  1397. if(str && *str) {
  1398. for(; *str && (*str == '\r' || *str == '\n'); str++);
  1399. for(s = str; *s; s++);
  1400. for(; s > str && (s[-1] == ' ' || s[-1] == '\r' || s[-1] == '\n'); s--);
  1401. *s = 0;
  1402. for(i = 1, s = str; *s; s++)
  1403. if(*s == '\n') gendoc_text("%u<br>", i++);
  1404. gendoc_text("%u<br></pre><code>", i);
  1405. /* tokenize string */
  1406. for(k = 0; str[k]; ) {
  1407. if(nt + 2 >= at) {
  1408. t = (int*)myrealloc(t, (at + 256) * sizeof(int));
  1409. memset(t + at, 0, 256 * sizeof(int));
  1410. at += 256;
  1411. }
  1412. if(!k && str[0] == '#' && str[1] == '!') {
  1413. t[nt++] = 0; while(str[k] && str[k] != '\r' && str[k] != '\n') k++;
  1414. continue;
  1415. }
  1416. if(!memcmp(str + k, "<hl>", 4) || !memcmp(str + k, "<hm>", 4) ||
  1417. !memcmp(str + k, "</hl>", 5) || !memcmp(str + k, "</hm>", 5)) {
  1418. if(!nt || (t[nt - 1] & 0xf) != 5) t[nt++] = (k << 4) | 5;
  1419. k += str[k + 1] == '/' ? (str[k + 3] == 'm' ? (str[k + 5] == '\n' ? 6 : (str[k + 5] == '\r' && str[k + 6] == '\n' ? 7 : 5)) : 5) : 4;
  1420. continue;
  1421. }
  1422. if(str[k] == '(') { t[nt++] = (k << 4) | 5; k++; continue; }
  1423. j = 0; if(r[5]) for(i = 0; r[5][i]; i++) if(r[5][i][0] == str[k]) { j = 1; break; }
  1424. if(str[k] == ')' || str[k] == ' ' || str[k] == '\t' || str[k] == '\r' || str[k] == '\n' || j) {
  1425. if(!nt || (t[nt - 1] & 0xf) != 5) t[nt++] = (k << 4) | 5;
  1426. k++; continue;
  1427. }
  1428. for(m = 0; m < 4; m++)
  1429. if(r[m] && !(m == 3 && nt && (t[nt - 1] & 0xf) == 9))
  1430. for(i = 0; r[m][i]; i++) {
  1431. l = match(r[m][i], str + k);
  1432. /* special case, only match alphabetic operators if not inside an alphabetic string */
  1433. if(l > 0 && (m != 2 || !((str[k] >= 'a' && str[k] <= 'z') || (str[k] >= 'A' && str[k] <= 'Z')) || (
  1434. ((!k || str[k - 1] == ' ' || str[k - 1] == '\t' || str[k - 1] == '\r' || str[k - 1] == '\n' || str[k - 1] == ')' ||
  1435. str[k - 1] == ']' || (str[k - 1] >= '0' && str[k - 1] <= '9')) &&
  1436. (!str[k + l] || str[k + l] == ' ' || str[k + l] == '\t' || str[k + l] == '\r' || str[k + l] == '\n' || str[k + l] == '(' ||
  1437. str[k + l] == '[' || (str[k + l] >= '0' && str[k + l] <= '9')))))) {
  1438. if(!nt || (t[nt - 1] & 0xf) != m) t[nt++] = (k << 4) | m;
  1439. k += l - 1; goto nextchar;
  1440. }
  1441. }
  1442. if(r[4])
  1443. for(i = 0; r[4][i]; i++) {
  1444. l = strlen(r[4][i]);
  1445. if(!memcmp(str + k, r[4][i], l)) {
  1446. if(!nt || (t[nt - 1] & 0xf) != 4) t[nt++] = (k << 4) | 4;
  1447. for(k += l; str[k]; k++) {
  1448. if(str[k] == '\\') k++; else
  1449. if(str[k] == r[4][i][l - 1]) { if(str[k + 1] != r[4][i][l - 1]) break; else k++; }
  1450. }
  1451. goto nextchar;
  1452. }
  1453. }
  1454. if(!nt || (t[nt - 1] & 0xf) != 9) t[nt++] = (k << 4) | 9;
  1455. nextchar: if(str[k]) k++;
  1456. }
  1457. if(t) {
  1458. for(i = 0; i < nt; i++) {
  1459. if((t[i] & 0xf) == 9) {
  1460. j = i + 1 < nt ? t[i + 1] >> 4 : k;
  1461. l = t[i] >> 4;
  1462. s = d = (char*)myrealloc(NULL, j - l + 1);
  1463. for(; l < j; l++)
  1464. *d++ = str[l] >= 'A' && str[l] <= 'Z' ? str[l] + 'a' - 'A' : str[l];
  1465. *d = 0;
  1466. if(in_array(s, r[6])) t[i] = (t[i] & ~0xf) | 6; else
  1467. if(in_array(s, r[7])) t[i] = (t[i] & ~0xf) | 7; else
  1468. if(str[l] == '(' || (i + 2 < nt && (t[i + 1] & 0xf) == 5 && (t[i + 2] & 0xf) == 5 &&
  1469. str[t[i + 1] >> 4] == ' ' && str[t[i + 2] >> 4] == '('))
  1470. t[i] = (t[i] & ~0xf) | 8;
  1471. free(s);
  1472. }
  1473. if(i && (t[i] & 0xf) == 3 && (t[i - 1] & 0xf) == 2 && (str[t[i - 1] >> 4] == '-' || str[t[i - 1] >> 4] == '.'))
  1474. t[i] -= 16;
  1475. }
  1476. /* concatenate tokens into a string with highlight spans */
  1477. for(i = l = 0; i < nt; i++) {
  1478. j = i + 1 < nt ? t[i + 1] >> 4 : k;
  1479. if(j == l) continue;
  1480. if((t[i] & 0xf) != 5) gendoc_text("<span class=\"hl_%c\">", hl[(t[i] & 0xf)]);
  1481. for(; l < j; l++) {
  1482. if(!memcmp(str + l, "<hl>", 4)) { gendoc_text("<span class=\"hl_h\">"); l += 3; } else
  1483. if(!memcmp(str + l, "<hm>", 4)) { gendoc_text("<span class=\"hl_h hl_b\">"); l += 3; } else
  1484. if(!memcmp(str + l, "</hl>", 5) || !memcmp(str + l, "</hm>", 5)) { gendoc_text("</span>"); l += 4;
  1485. if(str[l - 1] == 'm') { if(str[l + 1] == '\r') { l++; } if(str[l + 1] == '\n') l++; }
  1486. } else
  1487. if(str[l] == '&') gendoc_text("&amp;"); else
  1488. if(str[l] == '<') gendoc_text("&lt;"); else
  1489. if(str[l] == '>') gendoc_text("&gt;"); else
  1490. if(str[l] == '\"') gendoc_text("&quot;"); else
  1491. gendoc_text("%c", str[l]);
  1492. }
  1493. if((t[i] & 0xf) != 5) gendoc_text("</span>");
  1494. }
  1495. free(t);
  1496. }
  1497. } else
  1498. gendoc_text("</pre><code>");
  1499. gendoc_text("</code></div>");
  1500. }
  1501. /**
  1502. * Add an image.
  1503. * @param string either "t" (inlined as text), "l" (left), "r" (right), "c" (centered) or "w" (wide, centered)
  1504. * @param string image's file name
  1505. */
  1506. void gendoc_image(char align, char *fn)
  1507. {
  1508. char path[PATH_MAX], mime[8] = { 0 }, *s;
  1509. unsigned char *buf;
  1510. int l, w = 0, h = 0;
  1511. l = strlen(gendoc_lang[3]);
  1512. memcpy(path, gendoc_path, gendoc_fn);
  1513. strcpy(path + gendoc_fn, !memcmp(fn, gendoc_lang[3], l) ? fn + l : fn);
  1514. buf = detect_image(path, mime, &w, &h, &l);
  1515. if(!buf) return;
  1516. if(align != 'l' && align != 'r' && align != 'c' && align != 'w') align= 't';
  1517. if(align == 'c') gendoc_text("<div class=\"imgc\">");
  1518. gendoc_text("<img class=\"img%c\"", align);
  1519. if(align == 't' && h > 22) { w = 22 * w / h; h = 22; }
  1520. if(align != 'w')
  1521. gendoc_text(" width=\"%u\" height=\"%u\"", w, h);
  1522. s = strrchr(fn, '/'); if(!s) s = fn; else s++;
  1523. s = htmlspecialchars(s);
  1524. gendoc_text(" alt=\"%s\"", s);
  1525. free(s);
  1526. s = base64_encode(buf, l);
  1527. l = strlen(s);
  1528. free(buf);
  1529. gendoc_text(" src=\"data:image/%s;base64,", mime);
  1530. /* gendoc_text() assumes that arguments are no longer than 4k, that might not be true with base64 encoded images
  1531. * luckily base64 does not use % so it is safe to use it as a formatting string without arguments */
  1532. gendoc_text(s);
  1533. gendoc_text("\">");
  1534. if(align == 'c') gendoc_text("</div>");
  1535. free(s);
  1536. }
  1537. /**
  1538. * Generate API documentation.
  1539. * @param string language
  1540. * @param string file name
  1541. */
  1542. void gendoc_api(char *lang, char *fn)
  1543. {
  1544. char path[PATH_MAX], *s, *p, *e, *l, *h, *o, *q, t, *buf;
  1545. if(!lang || !*lang || !fn || !*fn) return;
  1546. memcpy(path, gendoc_path, gendoc_fn);
  1547. strcpy(path + gendoc_fn, fn);
  1548. buf = s = (char*)file_get_contents(path, NULL);
  1549. if(!buf) {
  1550. gendoc_report_error("unable to read source '%s'", path);
  1551. return;
  1552. }
  1553. while(*s) {
  1554. if(s[0] == '/' && s[1] == '*' && s[2] == '*' && s[3] != '*') {
  1555. s += 3; for(e = s; *e && (e[-2] != '*' || e[-1] != '/'); e++);
  1556. if(!*e) break;
  1557. for(q = e - 2; *e && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'); e++);
  1558. if(!*e) break;
  1559. for(p = e; *e && *e != '\r' && *e != '\n'; e++);
  1560. path[0] = *e; *e = 0;
  1561. gendoc_text("<dl><dt>");
  1562. gendoc_source_code(p, lang);
  1563. gendoc_text("</dt><dd>");
  1564. t = 0;
  1565. while(*s && s < q) {
  1566. for(; s < q && *s && (*s == '\r' || *s == '\n' || *s == ' ' || *s == '*'); s++);
  1567. if(!*s) break;
  1568. for(l = s; l < q && *l && *l != '\r' && *l != '\n'; l++);
  1569. *l = 0; h = o = htmlspecialchars(s);
  1570. if(h) {
  1571. if(!memcmp(h, "@param ", 7)) {
  1572. for(h += 7; *h == ' '; h++);
  1573. if(!t) { t = 1; gendoc_text("<div class=\"table\"><table><tr><th>%s</th></tr>", gendoc_lang[14]); }
  1574. gendoc_text("<tr><td>%s</td></tr>", h);
  1575. } else
  1576. if(!memcmp(h, "@return ", 8)) {
  1577. for(h += 8; *h == ' '; h++);
  1578. if(!t) { t = 1; gendoc_text("<div class=\"table\"><table>"); }
  1579. gendoc_text("<tr><th>%s</th></tr><tr><td>%s</td></tr>", gendoc_lang[15], h);
  1580. } else
  1581. if(!t)
  1582. gendoc_text("%s ", h);
  1583. free(o);
  1584. }
  1585. s = l + 1;
  1586. }
  1587. if(t) gendoc_text("</table></div>");
  1588. gendoc_text("</dd></dl><br>");
  1589. *e = path[0];
  1590. if(!*s) break;
  1591. }
  1592. s++;
  1593. }
  1594. free(buf);
  1595. }
  1596. /**
  1597. * Parse a source document.
  1598. * @param string source document's content
  1599. * @param gendoc_path name of the source file
  1600. * @return gendoc_l current line number, must be incremented by the reader
  1601. */
  1602. void gendoc_parse(char *str)
  1603. {
  1604. char *s = str, *d, *e, tmp[8] = { 0 };
  1605. while(*s) {
  1606. /* skip comments */
  1607. if(!memcmp(s, "<!--", 4)) {
  1608. for(s += 4; *s && memcmp(s, "-->", 3); s++)
  1609. if(*s == '\n') gendoc_l++;
  1610. if(*s) s += 3;
  1611. continue;
  1612. }
  1613. /* copy everything between tags */
  1614. if(*s == '\n') gendoc_l++;
  1615. if(*s != '<') {
  1616. if(*s == ' ' || *s == '\t') {
  1617. if(s == str || (s[-1] != ' ' && s[-1] != '\t' && s[-1] != '\n')) gendoc_text(" ");
  1618. s++; continue;
  1619. }
  1620. if(*s == '\r' || (*s == '\n' && gendoc_out > gendoc_buf && gendoc_out[-1] == '\n')) { s++; continue; }
  1621. gendoc_text("%c", *s++); continue;
  1622. }
  1623. /* our <doc> tag, defining some variables. Skip that from output */
  1624. if(!memcmp(s, "<doc>", 5)) {
  1625. s += 5;
  1626. for(e = s; *e && memcmp(e, "</doc>", 6); e++)
  1627. if(*e == '\n') gendoc_l++;
  1628. gendoc_doc(s, e);
  1629. s = e + 6;
  1630. } else
  1631. /* hello page markers */
  1632. if(!memcmp(s, "<hello>", 7)) { s += 7; gendoc_h = gendoc_H = 1; gendoc_last = gendoc_first = -2; } else
  1633. if(!memcmp(s, "</hello>", 8)) { s += 8; gendoc_h = 0; } else
  1634. /* parse headings and collect Table of Contents info */
  1635. if(s[0] == '<' && s[1] == 'h' && s[2] >= '1' && s[2] <= '6') {
  1636. s++;
  1637. for(e = s; *e && (e[0] != '<' || e[1] != '/' || e[2] != 'h'); e++)
  1638. if(*e == '\n') gendoc_l++;
  1639. gendoc_heading(s, e);
  1640. s = e + 5;
  1641. } else
  1642. /* Table of Contents caption, just adds to the toc, but does not generate output */
  1643. if(!memcmp(s, "<cap>", 5)) {
  1644. s += 5;
  1645. for(e = s; *e && memcmp(e, "</cap>", 6); e++)
  1646. if(*e == '\n') gendoc_l++;
  1647. *e = 0;
  1648. gendoc_toc = (char**)myrealloc(gendoc_toc, (gendoc_numtoc + 1) * 4 * sizeof(char*));
  1649. gendoc_toc[gendoc_numtoc * 4 + 0] = NULL;
  1650. gendoc_toc[gendoc_numtoc * 4 + 1] = NULL;
  1651. gendoc_toc[gendoc_numtoc * 4 + 2] = gendoc_cap = strdupl(s);
  1652. gendoc_toc[gendoc_numtoc * 4 + 3] = NULL;
  1653. gendoc_numtoc++;
  1654. s = e + 6;
  1655. } else
  1656. /* styling text */
  1657. if(!memcmp(s, "<b>", 3)) { s += 3; gendoc_text("<b>"); gendoc_chk_o(T_b); } else
  1658. if(!memcmp(s, "</b>", 4)) { s += 4; gendoc_text("</b>"); gendoc_chk_c(T_b); } else
  1659. if(!memcmp(s, "<i>", 3)) { s += 3; gendoc_text("<i>"); gendoc_chk_o(T_i); } else
  1660. if(!memcmp(s, "</i>", 4)) { s += 4; gendoc_text("</i>"); gendoc_chk_c(T_i); } else
  1661. if(!memcmp(s, "<u>", 3)) { s += 3; gendoc_text("<u>"); gendoc_chk_o(T_u); } else
  1662. if(!memcmp(s, "</u>", 4)) { s += 4; gendoc_text("</u>"); gendoc_chk_c(T_u); } else
  1663. if(!memcmp(s, "<s>", 3)) { s += 3; gendoc_text("<s>"); gendoc_chk_o(T_s); } else
  1664. if(!memcmp(s, "</s>", 4)) { s += 4; gendoc_text("</s>"); gendoc_chk_c(T_s); } else
  1665. if(!memcmp(s, "<sup>", 5)) { s += 5; gendoc_text("<sup>"); gendoc_chk_o(T_sup); } else
  1666. if(!memcmp(s, "</sup>", 6)) { s += 6; gendoc_text("</sup>"); gendoc_chk_c(T_sup); } else
  1667. if(!memcmp(s, "<sub>", 5)) { s += 5; gendoc_text("<sub>"); gendoc_chk_o(T_sub); } else
  1668. if(!memcmp(s, "</sub>", 6)) { s += 6; gendoc_text("</sub>"); gendoc_chk_c(T_sub); } else
  1669. if(!memcmp(s, "<quote>", 7)) { s += 7; gendoc_text("<blockquote class=\"pre\"><span></span>"); gendoc_chk_o(T_quote); } else
  1670. if(!memcmp(s, "</quote>", 8)) { s += 8; gendoc_text("</blockquote>"); gendoc_chk_c(T_quote); } else
  1671. /* structuring text */
  1672. if(!memcmp(s, "<p>", 3)) { s += 3; gendoc_text("<p>"); gendoc_chk_o(T_p); } else
  1673. if(!memcmp(s, "</p>", 4)) { s += 4; gendoc_text("</p>"); gendoc_chk_c(T_p); } else
  1674. /* line breaks */
  1675. if(!memcmp(s, "<br>", 4)) { s += 4; gendoc_text("<br>"); } else
  1676. if(!memcmp(s, "<hr>", 4)) { s += 4; gendoc_text("<hr>"); } else
  1677. /* lists */
  1678. if(!memcmp(s, "<ol>", 4)) { s += 4; gendoc_text("<ol>"); gendoc_chk_o(T_ol); } else
  1679. if(!memcmp(s, "</ol>", 5)) { s += 5; gendoc_text("</ol>"); gendoc_chk_c(T_ol); } else
  1680. if(!memcmp(s, "<ul>", 4)) { s += 4; gendoc_text("<ul>"); gendoc_chk_o(T_ul); } else
  1681. if(!memcmp(s, "</ul>", 5)) { s += 5; gendoc_text("</ul>"); gendoc_chk_c(T_ul); } else
  1682. if(!memcmp(s, "<li>", 4)) { s += 4; gendoc_text("<li>"); gendoc_chk_o(T_li); } else
  1683. if(!memcmp(s, "</li>", 5)) { s += 5; gendoc_text("</li>"); gendoc_chk_c(T_li); } else
  1684. /* data fields */
  1685. if(!memcmp(s, "<dl>", 4)) { s += 4; gendoc_text("<dl>"); gendoc_chk_o(T_dl); } else
  1686. if(!memcmp(s, "</dl>", 5)) { s += 5; gendoc_text("</dl>"); gendoc_chk_c(T_dl); } else
  1687. if(!memcmp(s, "<dt>", 4)) { s += 4; gendoc_text("<dt>"); gendoc_chk_o(T_dt); } else
  1688. if(!memcmp(s, "</dt>", 5)) { s += 5; gendoc_text("</dt>"); gendoc_chk_c(T_dt); } else
  1689. if(!memcmp(s, "<dd>", 4)) { s += 4; gendoc_text("<dd>"); gendoc_chk_o(T_dd); } else
  1690. if(!memcmp(s, "</dd>", 5)) { s += 5; gendoc_text("</dd>"); gendoc_chk_c(T_dd); } else
  1691. /* grid, invisible table */
  1692. if(!memcmp(s, "<grid>", 6)) { s += 6; gendoc_text("<table class=\"grid\">"); gendoc_chk_o(T_grid); } else
  1693. if(!memcmp(s, "</grid>", 7)) { s += 7; gendoc_text("</table>"); gendoc_chk_c(T_grid); } else
  1694. if(!memcmp(s, "<gr>", 4)) { s += 4; gendoc_text("<tr>"); gendoc_chk_o(T_gr); } else
  1695. if(!memcmp(s, "</gr>", 5)) { s += 5; gendoc_text("</tr>"); gendoc_chk_c(T_gr); } else
  1696. if(!memcmp(s, "<gd>", 4)) { s += 4; gendoc_text("<td>"); gendoc_chk_o(T_gd); } else
  1697. if(!memcmp(s, "<gD>", 4)) { s += 4; gendoc_text("<td class=\"wide\">"); gendoc_chk_o(T_gd); } else
  1698. if(!memcmp(s, "</gd>", 5)) { s += 5; gendoc_text("</td>"); gendoc_chk_c(T_gd); } else
  1699. /* table, needs a surrounding div for scrollbars */
  1700. if(!memcmp(s, "<table>", 7)) { s += 7; gendoc_text("<div class=\"table\"><table>"); gendoc_chk_o(T_table); } else
  1701. if(!memcmp(s, "</table>", 8)) { s += 8; gendoc_text("</table></div>"); gendoc_chk_c(T_table); } else
  1702. if(!memcmp(s, "<tr>", 4)) { s += 4; gendoc_text("<tr>"); gendoc_chk_o(T_tr); } else
  1703. if(!memcmp(s, "</tr>", 5)) { s += 5; gendoc_text("</tr>"); gendoc_chk_c(T_tr); } else
  1704. /* table cell header */
  1705. if(!memcmp(s, "<th>", 4)) { s += 4; gendoc_text("<th>"); gendoc_chk_o(T_th); } else
  1706. if(!memcmp(s, "<tH>", 4)) { s += 4; gendoc_text("<th class=\"wide\">"); gendoc_chk_o(T_th); } else
  1707. if(!memcmp(s, "</th>", 5)) { s += 5; gendoc_text("</th>"); gendoc_chk_c(T_th); } else
  1708. /* table cell data */
  1709. if(!memcmp(s, "<td>", 4)) { s += 4; gendoc_text("<td>"); gendoc_chk_o(T_td); } else
  1710. if(!memcmp(s, "<tD>", 4)) { s += 4; gendoc_text("<td class=\"wide\">"); gendoc_chk_o(T_td); } else
  1711. if(!memcmp(s, "</td>", 5)) { s += 5; gendoc_text("</td>"); gendoc_chk_c(T_td); } else
  1712. /* table cell aligned right (number) */
  1713. if(!memcmp(s, "<tn>", 4)) { s += 4; gendoc_text("<td class=\"right\">"); gendoc_chk_o(T_td); } else
  1714. if(!memcmp(s, "<tN>", 4)) { s += 4; gendoc_text("<td class=\"right wide\">"); gendoc_chk_o(T_td); } else
  1715. if(!memcmp(s, "</tn>", 5)) { s += 5; gendoc_text("</td>"); gendoc_chk_c(T_td); } else
  1716. /* internal link */
  1717. if(!memcmp(s, "<a>", 3)) {
  1718. s += 3;
  1719. for(e = s; *e && memcmp(e, "</a>", 4); e++)
  1720. if(*e == '\n') gendoc_l++;
  1721. *e = 0;
  1722. gendoc_internal_link(s);
  1723. s = e + 4;
  1724. } else
  1725. /* external link, open in a new tab */
  1726. if(!memcmp(s, "<a ", 3)) {
  1727. gendoc_chk_o(T_a);
  1728. s += 3; while(*s == ' ') s++;
  1729. for(e = s; *e && *e != '>'; e++)
  1730. if(*e == '\n') gendoc_l++;
  1731. *e = 0;
  1732. /* if url starts with '#', then insert it as an internal link, but without checks */
  1733. if(s[0] == '#')
  1734. gendoc_text("<a href=\"%s\" onclick=\"c('%s')\">", s, s + 1);
  1735. else
  1736. gendoc_text("<a href=\"%s\" target=\"new\">", s);
  1737. s = e + 1;
  1738. } else
  1739. if(!memcmp(s, "</a>", 4)) { s += 4; gendoc_text("</a>"); gendoc_chk_c(T_a); } else
  1740. /* add teletype tags to output without parsing */
  1741. if(!memcmp(s, "<tt>", 4)) {
  1742. s += 4;
  1743. for(e = s; *e && memcmp(e, "</tt>", 5); e++)
  1744. if(*e == '\n') gendoc_l++;
  1745. *e = 0;
  1746. if(e > s) {
  1747. s = htmlspecialchars(s);
  1748. if(s) { gendoc_text("<samp>%s</samp>", s); free(s); }
  1749. }
  1750. s = e + 5;
  1751. } else
  1752. /* pre is pretty much the same, but adds an optional div around for scrollbars */
  1753. if(!memcmp(s, "<pre>", 5)) {
  1754. s += 5;
  1755. for(e = s; *e && memcmp(e, "</pre>", 6); e++)
  1756. if(*e == '\n') gendoc_l++;
  1757. *e = 0;
  1758. gendoc_text("<div class=\"pre\"><pre>");
  1759. if(e > s) {
  1760. s = preformat(s);
  1761. if(s) { gendoc_text("%s", s); free(s); }
  1762. }
  1763. gendoc_text("</pre></div>");
  1764. s = e + 6;
  1765. } else
  1766. /* code is similar to pre, but needs line numbers and a syntax highlighter */
  1767. if(!memcmp(s, "<code", 5)) {
  1768. s += 5;
  1769. if(*s == ' ') {
  1770. s++; d = s;
  1771. while(*s && *s != '>') s++;
  1772. if(*s == '>') *s++ = 0;
  1773. } else {
  1774. d = NULL; s++;
  1775. }
  1776. for(e = s; *e && memcmp(e, "</code>", 7); e++)
  1777. if(*e == '\n') gendoc_l++;
  1778. *e = 0;
  1779. gendoc_source_code(s, d);
  1780. s = e + 7;
  1781. } else
  1782. /* user interface elements */
  1783. if(!memcmp(s, "<ui", 3) && s[3] >= '1' && s[3] <= '6' && s[4] == '>') {
  1784. gendoc_text("<span class=\"ui%c\">", s[3]); s += 5;
  1785. gendoc_chk_o(T_ui);
  1786. } else
  1787. if(!memcmp(s, "</ui", 4) && s[4] >= '1' && s[4] <= '6' && s[5] == '>') {
  1788. gendoc_text("</span>"); s += 6;
  1789. gendoc_chk_c(T_ui);
  1790. } else
  1791. /* keyboard keys */
  1792. if(!memcmp(s, "<kbd>", 5)) {
  1793. s += 5;
  1794. for(e = s; *e && memcmp(e, "</kbd>", 6); e++)
  1795. if(*e == '\n') gendoc_l++;
  1796. *e = 0;
  1797. if(e > s) {
  1798. s = htmlspecialchars(s);
  1799. if(s) { gendoc_text("<kbd>%s</kbd>", s); free(s); }
  1800. }
  1801. s = e + 6;
  1802. } else
  1803. /* mouse button left, right and wheel "icons" */
  1804. if(!memcmp(s, "<mbl>", 5)) { s += 5; gendoc_text("<span class=\"mouseleft\"></span>"); } else
  1805. if(!memcmp(s, "<mbr>", 5)) { s += 5; gendoc_text("<span class=\"mouseright\"></span>"); } else
  1806. if(!memcmp(s, "<mbw>", 5)) { s += 5; gendoc_text("<span class=\"mousewheel\"></span>"); } else
  1807. /* parse our image tags (<imgt>, <imgl>, <imgr>, <imgc>, <imgw>), but not the original HTML <img> tags */
  1808. if(!memcmp(s, "<img", 4) && s[4] != ' ' && s[4] != '/' && s[4] != '>') {
  1809. d = s + 4; s += 6;
  1810. for(e = s; *e && *e != '>'; e++)
  1811. if(*e == '\n') gendoc_l++;
  1812. *e = 0;
  1813. gendoc_image(*d, s);
  1814. s = e + 1;
  1815. } else
  1816. /* image caption */
  1817. if(!memcmp(s, "<fig>", 5)) {
  1818. s += 5;
  1819. for(e = s; *e && memcmp(e, "</fig>", 6); e++)
  1820. if(*e == '\n') gendoc_l++;
  1821. *e = 0;
  1822. if(e > s)
  1823. gendoc_text("<span class=\"fig\">%s</span>", s);
  1824. s = e + 6;
  1825. } else
  1826. /* alert boxes */
  1827. if(!memcmp(s, "<info>", 6) || !memcmp(s, "<hint>", 6) || !memcmp(s, "<note>", 6) || !memcmp(s, "<also>", 6) ||
  1828. !memcmp(s, "<todo>", 6) || !memcmp(s, "<warn>", 6)) {
  1829. gendoc_text("<div class=\"%s\"><p><span>%s</span></p><p>",
  1830. s[1] == 'h' ? "hint" : (s[1] == 't' || s[1] == 'w' ? "warn" : "info"),
  1831. gendoc_lang[s[1] == 'i' ? 8 : (s[1] == 'h' ? 9 : (s[1] == 'n' ? 10 : (s[1] == 'a' ? 11 : (s[1] == 't' ? 12 : 13))))]);
  1832. s += 6;
  1833. gendoc_chk_o(T_alert);
  1834. } else
  1835. if(!memcmp(s, "</info>", 7) || !memcmp(s, "</hint>", 7) || !memcmp(s, "</note>", 7) || !memcmp(s, "</also>", 7) ||
  1836. !memcmp(s, "</todo>", 7) || !memcmp(s, "</warn>", 7)) {
  1837. gendoc_text("</p></div>");
  1838. s += 7;
  1839. gendoc_chk_c(T_alert);
  1840. } else
  1841. /* include another input file */
  1842. if(!memcmp(s, "<include ", 9)) {
  1843. s += 9;
  1844. for(e = s; *e && *e != '>'; e++)
  1845. if(*e == '\n') gendoc_l++;
  1846. *e = 0;
  1847. gendoc_include(s);
  1848. s = e + 1;
  1849. } else
  1850. /* generate api documentation */
  1851. if(!memcmp(s, "<api ", 5)) {
  1852. s += 5;
  1853. for(d = s; *s && *s != ' ' && *s != '>'; s++)
  1854. if(*s == '\n') gendoc_l++;
  1855. if(*s == '>') d = NULL; else *s++ = 0;
  1856. for(e = s; *e && *e != '>'; e++)
  1857. if(*e == '\n') gendoc_l++;
  1858. *e = 0;
  1859. gendoc_api(d, s);
  1860. s = e + 1;
  1861. } else {
  1862. /* copy all the other tags to the output untouched */
  1863. e = s;
  1864. for(s++; *s && *s != '<' && s[-1] != '>'; s++)
  1865. if(*s == '\n') gendoc_l++;
  1866. tmp[0] = *s; *s = 0;
  1867. gendoc_text("%s", e);
  1868. for(d = e; d < s; d++) if(*d == '\n') { *d = 0; break; }
  1869. fprintf(stderr, "gendoc warning: %s:%u: not gendoc compatible tag '%s'\n", gendoc_path, gendoc_l, e);
  1870. *s = tmp[0];
  1871. }
  1872. }
  1873. }
  1874. /**
  1875. * Include another source document.
  1876. * @param string file name
  1877. */
  1878. void gendoc_include(char *fn)
  1879. {
  1880. char p[PATH_MAX], *s, *buf;
  1881. unsigned int size;
  1882. int n, l;
  1883. strcpy(p, gendoc_path);
  1884. n = gendoc_fn;
  1885. l = gendoc_l;
  1886. strcpy(gendoc_path + gendoc_fn, fn);
  1887. s = strrchr(gendoc_path, '/');
  1888. if(!s) s = gendoc_path; else s++;
  1889. gendoc_fn = s - gendoc_path;
  1890. buf = file_get_contents(gendoc_path, &size);
  1891. s = strrchr(fn, '.');
  1892. if(buf && s && !strcmp(s, ".md")) {
  1893. for(s = buf; s < buf + size && *s; s++)
  1894. if(*s == '\r')
  1895. memcpy(s, s + 1, --size - (intptr_t)(s - buf));
  1896. md_parse(buf, buf + size, 1);
  1897. free(buf); buf = md_buf;
  1898. md_buf = md_out = NULL; md_buflen = 0;
  1899. }
  1900. if(buf) {
  1901. gendoc_l = 1;
  1902. gendoc_parse(buf);
  1903. free(buf);
  1904. } else
  1905. fprintf(stderr, "gendoc error: %s:0 unable to read\n", gendoc_path);
  1906. gendoc_l = l;
  1907. gendoc_fn = n;
  1908. strcpy(gendoc_path, p);
  1909. }
  1910. /**
  1911. * Output documentation.
  1912. * @param stream to write to
  1913. */
  1914. void gendoc_output(FILE *f)
  1915. {
  1916. char *theme, title[256], *titimg = NULL, mime[8] = { 0 }, *s;
  1917. unsigned char *buf = NULL;
  1918. int i, w, h, H;
  1919. (void)f;
  1920. if(!gendoc_toc || gendoc_numtoc < 1) {
  1921. fprintf(stderr, "gendoc error: no table of contents detected\n");
  1922. gendoc_err++;
  1923. }
  1924. if(gendoc_fwd) {
  1925. for(i = 0; i < gendoc_numfwd; i++) {
  1926. fprintf(stderr, "gendoc error: %s\n", &gendoc_fwd[i][strlen(gendoc_fwd[i]) + 1]);
  1927. gendoc_err++;
  1928. free(gendoc_fwd[i]);
  1929. }
  1930. free(gendoc_fwd);
  1931. }
  1932. if(gendoc_buf) {
  1933. if(gendoc_out)
  1934. while(gendoc_out > gendoc_buf && (gendoc_out[-1] == ' ' || gendoc_out[-1] == '\r' || gendoc_out[-1] == '\n'))
  1935. gendoc_out--;
  1936. gendoc_prev_link();
  1937. gendoc_text("</div>");
  1938. }
  1939. /* load user specified theme */
  1940. theme = "hr,table,th,td{border-color:#e1e4e5;}\n"
  1941. "th{background:#d6d6d6;}\n"
  1942. "tr:nth-child(odd){background:#f3f6f6;}\n"
  1943. "a{text-decoration:none;color:#2980B9;}\n"
  1944. ".content{background:#fcfcfc;color:#404040;font-family:Lato,Helvetica,Neue,Arial,Deja Vu,sans-serif;}\n"
  1945. ".title,.home,h1>a,h2>a,h3>a,h4>a,h5>a,h6>a{background:#2980B9;color:#fcfcfc;}\n"
  1946. ".version{color:rgba(255,255,255,0.3);}\n"
  1947. ".search{border:1px solid #2472a4;background:#fcfcfc;}\n"
  1948. ".nav{background:#343131;color:#d9d9d9;}\n"
  1949. ".nav p{color:#55a5d9;}\n"
  1950. ".nav label:hover,.nav a:hover{background:#4e4a4a;}\n"
  1951. ".nav .current{background:#fcfcfc;color:#404040;}\n"
  1952. ".nav li>ul>li{background:#e3e3e3;}\n"
  1953. ".nav li>ul>li>a{color:#404040;}\n"
  1954. ".nav li>ul>li>a:hover{background:#d6d6d6;}\n"
  1955. ".pre {border:1px solid #e1e4e5;background:#f8f8f8;}\n"
  1956. ".info{background:#e7f2fa;}\n"
  1957. ".info>p:first-child{background:#6ab0de;color:#fff;}\n"
  1958. ".hint{background:#dbfaf4;}\n"
  1959. ".hint>p:first-child{background:#1abc9c;color:#fff;}\n"
  1960. ".warn{background:#ffedcc;}\n"
  1961. ".warn>p:first-child{background:#f0b37e;color:#fff;}\n"
  1962. ".btn{background:#f3f6f6;}\n"
  1963. ".btn:hover{background:#e5ebeb;}\n"
  1964. ".hl_h{background-color:#ccffcc;}\n"
  1965. ".hl_c{color:#808080;font-style:italic;}\n"
  1966. ".hl_p{color:#1f7199;}\n"
  1967. ".hl_o{color:#404040;}\n"
  1968. ".hl_n{color:#0164eb;}\n"
  1969. ".hl_s{color:#986801;}\n"
  1970. ".hl_t{color:#60A050;}\n"
  1971. ".hl_k{color:#a626a4;}\n"
  1972. ".hl_f{color:#2a9292;}\n"
  1973. ".hl_v{color:#e95649;}\n";
  1974. if(gendoc_theme[0]) {
  1975. theme = file_get_contents(gendoc_theme, NULL);
  1976. if(!theme) { fprintf(stderr, "gendoc error: %s:0: unable to read theme css\n", gendoc_theme); gendoc_err++; }
  1977. }
  1978. /* load title image */
  1979. if(gendoc_lang[1][0]) strcpy(title, gendoc_lang[1]);
  1980. if(gendoc_lang[1][0] && gendoc_lang[2][0]) { strcat(title, " "); strcat(title, gendoc_lang[2]); } else
  1981. if(gendoc_lang[2][0]) strcpy(title, gendoc_lang[2]); else strcpy(title, "No name");
  1982. if(gendoc_titleimg[0]) {
  1983. buf = detect_image(gendoc_titleimg, mime, &w, &h, &i);
  1984. if(buf) { titimg = base64_encode(buf, i); free(buf); }
  1985. }
  1986. /*** output html ***/
  1987. fprintf(f, "<!DOCTYPE html>\n<html lang=\"%s\">\n<head>\n <meta charset=\"utf-8\">\n"
  1988. " <meta name=\"generator\" content=\"gendoc " GENDOC_VERSION ": https://gitlab.com/bztsrc/gendoc\">\n"
  1989. " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
  1990. " <title>%s</title>\n", gendoc_lang[0], title);
  1991. /* embedded stylesheet licensed under CC-BY */
  1992. fprintf(f, " <style rel=\"logic\">*{box-sizing:border-box;font-family:inherit;}"
  1993. "body {background:rgba(0,0,0,0.05);font-weight:400;font-size:16px;}"
  1994. "hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:26px 0;padding:0;border-top:1px solid;}"
  1995. "br:after,br:before{display:table;content:\"\"}br{clear:both;}"
  1996. "h1,h2,h3,h4,h5,h6{clear:both;margin:0px 0px 20px 0px;padding-top:4px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}"
  1997. "p{margin:0 0 24px}a{cursor:pointer;}"
  1998. "h1{font-size:175%%}h2{font-size:150%%}h3{font-size:125%%}h4{font-size:115%%}h5{font-size:110%%}h6{font-size:100%%}"
  1999. "pre,samp,code,var,kbd{font-family:Monaco,Consolas,Liberation Mono,Courier,monospace;font-variant-ligatures:none;}"
  2000. "pre,code{display:block;overflow:auto;white-space:pre;font-size:14px;line-height:16px!important;}pre{padding:12px;margin:0px;}"
  2001. "code{padding:0 0 12px 0;margin:12px 12px 0px 2px;background:url(data:type/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAgCAYAAADT5RIaAAAAFklEQVQI12NgYGDgZWJgYGCgDkFtAAAWnAAsyj4TxgAAAABJRU5ErkJggg==) 0 0 repeat;}"
  2002. ".lineno{display:block;padding:0px 4px 0px 4px;margin:12px 0px 0px 0px;opacity:.4;text-align:right;float:left;white-space:pre;font-size:12px;line-height:16px!important;}"
  2003. "pre .hl_b,samp .hl_b,code .hl_b{display:block;}"
  2004. "blockquote{margin:0px;padding:12px;}blockquote>span:first-child::before{content:url(data:type/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAgCAYAAABU1PscAAABRElEQVRYw+3WTytEURjH8c/4l1JTpKxsyEbYWFkpG+UVKOWFeAts7eytbJSysLLVxIKNFAslwhSlUQabUdM0xm0e967Ot+7inPN8z+13z73nXBKJRCIRoJSxbggrmMMInlHBIWoF+KEAQ9jAaJuxe2zhJUe/Iz0ZahZ/uTmMYT1nPxxg/I/xWQzn6IcD1IIha//wkEIBKhlq+nP0wwHOsYuvDjWvOfod6c1Yd9O4ZjDQZvwAbzn6oRVofpKbqLf0P+GxAD8cAO7w0NJ3WqAfDlBCuan9gaMC/XCA+cbJ+sMRqgX6oQCTWGtqX2O/QL8tfRlqyljGUlPgW2y3+SDz8Lv6mRvEQmPbm25ZqQvs/LHtRf3wCkxgtaWvij2cZJg36ocD/Pw91nGJY5zhM+O8UT/8Ck01TswrvHcxb9RPJBKJRDF8AyNbWk4WFTIzAAAAAElFTkSuQmCC);float:left;vertical-align:top;}"
  2005. ".ui1,.ui2,.ui3,.ui4,.ui5,.ui6{display:inline-block;height:24px!important;line-height:24px!important;padding:0px 4px;margin:-2px 0px -2px;}"
  2006. "kbd{display:inline-block;font-weight:700;border:1px solid #888;height:24px!important;padding:0px 4px;margin:-2px 0px -2px;border-radius:4px;background-image:linear-gradient(#ddd 0%%,#eee 10%%,#bbb 10%%,#ccc 30%%,#fff 85%%,#eee 85%%,#888 100%%);}"
  2007. ".mouseleft,.mouseright,.mousewheel{display:inline-block;min-width:16px;height:24px!important;padding:0px;margin:-2px 0px 0px 0px;vertical-align:middle;}"
  2008. ".mouseleft::before{content:url(data:type/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAYCAMAAADEfo0+AAAAe1BMVEUAAACoqKj9/f2zs7O1tbWRkZGlpaWsrKybm5u2traNjY2Wlpanp6empqaYmJiPj4+3t7f5+fmjo6P19fXu7u6ZmZnx8fHi4uLm5ube3t7q6uqwsLC0tLS7u7u4uLi/v7/W1tbDw8PLy8vPz8/T09Pa2trHx8eIiIhERkShhqFGAAAAAXRSTlMAQObYZgAAAI5JREFUGNNV0EcCwjAMRFEB6R2DKSGQUBLp/idEjsG23m7+cgAMIsJWwR8Z235pumDTnkUbv+lg7CIfTqvSbTquxsGFfjWXLlwsdOFs+XC1EHAWOEwCh4/A4S1weAkcFoHDU0CI8zGQx1Cpe0BVAO0j0PIfiR4cnZjLdHP7abQ9VRVZnaZ1VvjfO42o7edfH3EoHZS6XE4AAAAASUVORK5CYII=);}"
  2009. ".mouseright::before{content:url(data:type/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAYCAMAAADEfo0+AAAAe1BMVEUAAACoqKj9/f2zs7O1tbWRkZGlpaWsrKybm5u2traNjY2Wlpanp6empqaYmJiPj4+3t7f5+fmjo6P19fXu7u6ZmZnx8fHi4uLm5ube3t7q6uqwsLC0tLS7u7u4uLi/v7/W1tbDw8PLy8vPz8/T09Pa2trHx8eIiIhERkShhqFGAAAAAXRSTlMAQObYZgAAAI9JREFUGNNV0NkWgjAMRdGozFOxWgdEcYLk/7/Q0EpL9tNd5/ECzLRCIoJF20zdlsinTbRn5Eu0O8zIl/Jk+dAPR4uWUo6d5QNenBDOTghXJ4RRQMCnwOErcPgIHN4Ch0ng8BIQ4nxYyWOo9H1FVwDqsaL4j8T0nknmy0xz+2uMO1UXWZ2mdVbo8LtBNK2dP/2+KB2shyfVAAAAAElFTkSuQmCC);}"
  2010. ".mousewheel::before{content:url(data:type/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAYCAMAAADEfo0+AAAAe1BMVEUAAACzs7OoqKisrKyRkZGlpaX9/f22trabm5uNjY2mpqanp6ePj4+1tbWYmJi3t7eWlpajo6OZmZmwsLD5+fnu7u7x8fHi4uLW1tbm5ube3t7T09Pq6ur19fW4uLi7u7u0tLTa2trPz8+/v7/Dw8PLy8vHx8eIiIhERkS4354xAAAAAXRSTlMAQObYZgAAAKdJREFUGNNV0NkagiAUhdHjPAECzWWlWdL7P2Fno/nJumHzXx4iMMI5YeivVVOX592k2vkfy/1CxvjL6L6KJAd9ZF+GVxP144Eh4B170kPHEPAOmtwFEPxw5E6A4AeHKyD4wWEABD84nAHBDw43QPCDwyvA4RPgMAU4vAOO0mLcKFJqzHPDNETisSH4HpntVzbDyazaLZSdj2qqsk6Suqw2d7fO2fnmP7kAJW9a/HbiAAAAAElFTkSuQmCC);}"
  2011. "footer{width:100%%;padding:0 3.236em;}footer p{opacity:0.6;}footer small{opacity:0.5;}footer a{text-decoration:none;color:inherit;}footer a:hover{text-decoration:underline;}"
  2012. "dl{margin:0 0 24px 0;padding:0px;}dt{font-weight:700;margin-bottom:12px;}dd{margin:0 0 12px 24px;}"
  2013. ".table table{margin:0px;border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid;width:100%%;}"
  2014. "th{font-weight:700;padding:8px 16px;overflow:visible;vertical-align:middle;white-space:nowrap;border:1px solid;}th.wide{width:100%%;}"
  2015. "td{padding:8px 16px;overflow:visible;vertical-align:middle;font-size:90%%;border:1px solid;}td.right{text-align:right;}"
  2016. "table.grid{margin:0px;padding:0px;border:none!important;background:none!important;border-spacing:0;border:0px!important;empty-cells:show;width:100%%;}"
  2017. "table.grid tr, table.grid td{margin:0px;padding:0px;overflow:hidden;vertical-align:top;background:none!important;border:0px!important;font-size:90%%;}"
  2018. "div.frame{position:absolute;width:100%%;min-height:100%%;margin:0px;padding:0px;max-width:1100px;top:0px;left:0px;}"
  2019. "#_m{margin-left:300px;min-height:100%%;}"
  2020. "div.title{display:block;width:300px;padding-top:.809em;padding-bottom:.809em;margin-bottom:.809em;text-align:center;font-weight:700;}"
  2021. "div.title>a{padding:4px 6px;margin-bottom:.809em;font-size:150%%;}div.title>a:hover{background:transparent;}"
  2022. "div.title>a>img{max-width:280px;border:0px;padding:0px;margin:0px;}"
  2023. "div.title input{display:none;width:270px;border-radius:50px;padding:6px 12px;font-size:80%%;box-shadow:inset 0 1px 3px #ddd;transition:border .3s linear;}"
  2024. "div.title input:required:invalid{background:#fcfcfc url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAASFBMVEUAAAC6urq7u7y7vLy8vL3Dw8TLzM67urq8vL29vr69vr/AwMK5ubm5ubm4uLi5t7fAwMC/vr/FxMXBwsPExcW+u7vDw8S4uLiryZHFAAAAF3RSTlMA9vGttk4lfbWnpJQG0MpzYEIglFRCGzMa+EsAAAB0SURBVBjTbY5bCsMwDAQl2U6cR5s0fcz9b1oLpWBC52dhENqVRsmG5SIn60wwryEm0LQkbUacAveh5XGDh4uKfsQZlOqpJAkS5gHPUyzgYdeLjB6/H7lvGbzlssP2WDoRGGzxrVRD82sHRukZ/xhv7tns/QXrhgcaFdOKBwAAAABJRU5ErkJggg==) no-repeat 10px 50%%;}"
  2025. "div.title input:focus{background:#fcfcfc!important;}"
  2026. "div.version{margin-top:.4045em;margin-bottom:.809em;font-size:90%%;}"
  2027. "nav.side {display:block;position:fixed;top:0;bottom:0;left:0;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%%;font-weight:400;z-index:999;}"
  2028. "nav.mobile {display:none;font-weight:bold;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%%;*zoom:1;}"
  2029. "nav a{color:inherit;text-decoration:none;display:block;}"
  2030. "nav.side>div{position:relative;overflow-x:hidden;overflow-y:scroll;width:320px;height:100%%;padding-bottom:64px;}"
  2031. "div.nav p{height:32px;line-height:32px;padding:0 1.618em;margin:12px 0px 0px 0px;font-weight:700;text-transform:uppercase;font-size:85%%;white-space:nowrap;-webkit-font-smoothing:antialiased}"
  2032. "div.nav li>.current,div.nav li>ul{display:none;}"
  2033. "div.nav li>a,div.nav li>label{display:block;}"
  2034. "div.nav a,div.nav ul>li>label,div.nav ul>li>.current{width:300px;line-height:18px;padding:0.4045em 1.618em;}"
  2035. "div.nav a,div.nav ul>li>label{cursor:pointer;}"
  2036. "div.nav .current{font-weight:700;border-top:1px solid;border-bottom:1px solid #c9c9c9;}"
  2037. "div.nav ul>li>ul>li>a{border-right:solid 1px #c9c9c9;font-size:90%%;}"
  2038. "div.nav ul>li>ul>li.h2>a{padding:0.4045em 2.427em;}"
  2039. "div.nav ul>li>ul>li.h3>a{padding:.4045em 1.618em .4045em 4.045em;}"
  2040. "div.nav ul>li>ul>li.h4>a{padding:.4045em 1.618em .4045em 5.663em;}"
  2041. "div.nav ul>li>ul>li.h5>a{padding:.4045em 1.618em .4045em 7.281em;}"
  2042. "div.nav ul>li>ul>li.h6>a{padding:.4045em 1.618em .4045em 8.899em;}"
  2043. "div.nav ul,div.nav li,.breadcrumbs{margin:0px!important;padding:0px;list-style:none;}"
  2044. "ul.breadcrumbs,.breadcrumbs li{display:inline-block;}"
  2045. ".menu{display:inline-block;position:absolute;top:12px;right:20px;cursor:pointer;width:1.5em;height:1.5em;vertical-align:middle;padding:16px 24px 16px 24px;border:solid 1px rgba(255, 255, 255, 0.5);border-radius:5px;background:no-repeat center center url(\"data:image/svg+xml;charset=utf8,%%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%%3E%%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%%3E%%3C/svg%%3E\");}"
  2046. ".home{display:inline-block;max-width:16px;max-height:16px;line-height:16px;margin:0 5px 0 0;cursor:pointer;}"
  2047. ".home::before{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAS1BMVEUAAAD8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/PyxWsjVAAAAGHRSTlMAx6oLLRnYiXFEvPbey518NQbsj11QKhQ+J0ktAAAAbElEQVQY053ISQ7DMAwEwRYpavHurPz/SwMoMpD46L4MpvBTB0zTP+R9z55Ebh0yQL5DaDA+0WVR3h3Gikp9iPKF9MIkQhSDwUlGHCLQxhLGJkpLZcNYS/tdyorP/DQ7HgBqKRUgHBDcw2X4AFsJCSXB/5UVAAAAAElFTkSuQmCC);}"
  2048. "h1>a,h2>a,h3>a,h4>a,h5>a,h6>a{display:none;max-width:16px;max-height:24px;margin:-8px 0 0 5px;vertical-align:middle;}"
  2049. "h1:hover>a,h2:hover>a,h3:hover>a,h4:hover>a,h5:hover>a,h6:hover>a{display:inline-block;text-decoration:none!important;}"
  2050. "h1>a::before,h2>a::before,h3>a::before,h4>a::before,h5>a::before,h6>a::before{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAiBAMAAACkb0T0AAAAMFBMVEUAAAD8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/PzS+N+gAAAAD3RSTlMABab5cVY73o4k2cJTPb1Q83MyAAAAh0lEQVQY02P4DwWkM7YxsNiDGJ8EGBgmgxiKDDkCLAwgAQn7hQxARiGj8v8/IIYDi2a9GZChHiAnMLmBjeGTaIBUAMcFaYaNvA2sC1kD9RkSOCwZF7IW/WdgSP4+yYHlP5Bx/r+RgDiIIfW5kVEfyHBgPMYgBLLdkoEBKABkfHZgyIa7h04MAOVty/RC/PhoAAAAAElFTkSuQmCC);}"
  2051. "h1>a:hover::after,h2>a:hover::after,h3>a:hover::after,h4>a:hover::after,h5>a:hover::after,h6>a:hover::after{"
  2052. "content:\"%s\";display:block;padding:12px;position:absolute;margin:-8px 8px;font-weight:400;font-size:14px;background:rgba(0,0,0,.8);color:#fff;border-radius:4px;}"
  2053. "input[type=radio]{display:none;}"
  2054. "input[type=radio]:checked ~ ul{display:block;}"
  2055. ".fig{margin-top:-12px;padding-bottom:12px;display:block;text-align:center;font-style:italic;}"
  2056. "div.page{width:100%%;padding:1.618em 3.236em;margin:auto;line-height:24px;}"
  2057. "div.page ol{margin:0 0 24px 12px;padding-left:0px;}div.page ul{margin:0 0 24px 24px;list-style:disc outside;padding-left:0px;}"
  2058. "div.page ol{list-style-type:none;counter-reset:list;}div.page ol li:before{counter-increment:list;content:counters(list,\".\") \". \";}"
  2059. "div.pre{overflow-x:auto;margin:1px 0px 24px;}div.table{overflow-x:auto;margin:0px 0px 24px;}"
  2060. "div.info,div.hint,div.warn{padding:12px;line-height:24px;margin-bottom:24px;}"
  2061. "div.info>p,div.hint>p,div.warn>p{margin:0px;}"
  2062. "div.info>p:first-child,div.hint>p:first-child,div.warn>p:first-child{display:block;font-weight:700;padding:2px 8px 2px;margin:-12px -12px 8px -12px;vertical-align:middle;}"
  2063. "div.info>p:first-child>span,div.hint>p:first-child>span,div.warn>p:first-child>span{display:block;max-height:20px;margin:0px;vertical-align:middle;}"
  2064. "div.info>p:first-child>span::before,div.hint>p:first-child>span::before,div.warn>p:first-child>span::before{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAApUlEQVQ4y82TwQnDMAxFpVJyLPiSbbJETr13Fo+RZZxLRghkgnSEEni9ONS0sg1tDv3wwfjbX7Iki/wVAAd4IPBCiHuudrkHVvJYgb50eaOO7cMkpv0eeQGukYuRiUsNvBFpSvTJ0P2un0Sk+6Le3WEG58yBFrjt61r7Qqbij0gLIX3CaPgOqtqoaiMig6GPtTbOwCVyLraxMEj3yPIgHTLKv3ymJySzt16bW/sWAAAAAElFTkSuQmCC);}"
  2065. "p>div:last-child,dd>*:last-child,td>*:last-child,li>ol,li>ul{margin-bottom:0px!important;}"
  2066. "img{border:0px;}img.imgt{display:inline-block;max-height:22px!important;padding:0px;margin:-4px 0px 0px 0px;vertical-align:middle;}img.imgl{float:left;margin:0px 12px 12px 0px;}img.imgr{float:right;margin:0px 0px 12px 12px;}div.imgc{text-align:center;padding:0px;margin:0 0 12px 0;clear:both;}img.imgc{max-width:100%%;}img.imgw{width:100%%;margin-bottom:12px;clear:both;}"
  2067. ".btn{border-radius:2px;line-height:normal;white-space:nowrap;color:inherit;text-align:center;cursor:pointer;font-size:100%%;padding:4px 12px 8px;border:1px solid rgba(0,0,0,.1);text-decoration:none;box-shadow:inset 0 1px 2px -1px hsla(0,0%%,100%%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);vertical-align:middle;*zoom:1;user-select:none;transition:all .1s linear}"
  2068. ".prev{float:left;}.prev::before{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAANlBMVEUAAABAQEBAQEBAQEBAQEBAQEBAQEBBQUFAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAWW5SEAAAAEnRSTlMA/fC9r2kXAjMN34F3ZlUu6B40Y5wGAAAAVElEQVQY08XPSw6AIAwEUIbS8lFE739Zq6luGtbM8iXTTMOCZGECiCX/MhIQTyCNz+SRrUc1MWKVvR5KYCN6pUFDRtqq4Sql9IYJ+aI/70f4qdOHblOhAuUcC5KnAAAAAElFTkSuQmCC);}"
  2069. ".next{float:right;}.next::after{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASBAMAAACk4JNkAAAAJFBMVEUAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEC4lvDfAAAAC3RSTlMAx711ZjlFPh3zLASjkrYAAABDSURBVAjXY6AUsGhvcgAzOKR3797YAGIx7t5mvVsAxPLevZ159xYQS3v3zgLrTSDW7tTQBcy7ESyELEIHwhSEyQjbAAH1HsMY8tCHAAAAAElFTkSuQmCC);}"
  2070. "@media screen and (max-width:991.98px){nav.mobile{display:block;}nav.side{display:none;}#menuchk:checked ~ nav.side{display:block;}#_m{margin-left:0px;}}",
  2071. gendoc_lang[7]);
  2072. for(i = 0; i < gendoc_numtoc; i++)
  2073. if(gendoc_toc[i * 4 + 0] && gendoc_toc[i * 4 + 0][0] && (intptr_t)gendoc_toc[i * 4 + 1] == '1')
  2074. fprintf(f, "#_%s:checked ~ nav div ul li[rel=%s]>.toc,", gendoc_toc[i * 4 + 0], gendoc_toc[i * 4 + 0]);
  2075. fprintf(f, "div.page{display:none;}");
  2076. for(i = 0; i < gendoc_numtoc; i++)
  2077. if(gendoc_toc[i * 4 + 0] && gendoc_toc[i * 4 + 0][0] && (intptr_t)gendoc_toc[i * 4 + 1] == '1')
  2078. fprintf(f, "#_%s:checked ~ nav div ul li[rel=%s]>ul,#_%s:checked ~ nav div ul li[rel=%s]>.current,#_%s:checked ~ div div[rel=%s],",
  2079. gendoc_toc[i * 4 + 0], gendoc_toc[i * 4 + 0], gendoc_toc[i * 4 + 0],
  2080. gendoc_toc[i * 4 + 0], gendoc_toc[i * 4 + 0], gendoc_toc[i * 4 + 0]);
  2081. s = trimnl(theme);
  2082. fprintf(f, "#_:checked ~ div div[rel=_]{display:block;}</style>\n <style rel=\"theme\">%s</style>\n</head>\n<body>\n <div class=\"frame content\">\n ", s ? s : "");
  2083. if(s) free(s);
  2084. if(gendoc_theme[0] && theme) free(theme);
  2085. if(gendoc_H) fprintf(f, "<input type=\"radio\" name=\"page\" id=\"_\" checked>");
  2086. for(i = 0; i < gendoc_numtoc; i++)
  2087. if(gendoc_toc[i * 4 + 0] && gendoc_toc[i * 4 + 0][0] && (intptr_t)gendoc_toc[i * 4 + 1] == '1') {
  2088. fprintf(f, "<input type=\"radio\" name=\"page\" id=\"_%s\"%s>", gendoc_toc[i * 4 + 0], !gendoc_H ? " checked" : "");
  2089. gendoc_H = 1;
  2090. }
  2091. if(gendoc_H) fprintf(f, "\n");
  2092. fprintf(f, " <input type=\"checkbox\" id=\"menuchk\" style=\"display:none;\"><nav class=\"side nav\"><div>\n <div class=\"title\"><a href=\"%s\">", gendoc_lang[3]);
  2093. if(titimg) {
  2094. s = htmlspecialchars(gendoc_lang[1]);
  2095. fprintf(f, "<img alt=\"%s\" ", s ? s : "");
  2096. if(s) free(s);
  2097. fprintf(f, " src=\"data:image/%s;base64,%s\">", mime, titimg);
  2098. if(gendoc_lang[2][0]) fprintf(f, " %s", gendoc_lang[2]);
  2099. free(titimg);
  2100. } else
  2101. fprintf(f, "%s", title);
  2102. fprintf(f, "</a><div class=\"version\">%s</div>"
  2103. "<input id=\"_q\" class=\"search\" type=\"text\" required=\"required\" onkeyup=\"s(this.value);\"></div>"
  2104. " <div id=\"_s\" class=\"nav\"></div>\n <div id=\"_t\" class=\"nav\">\n", gendoc_lang[4]);
  2105. H = 0;
  2106. for(i = 0; i < gendoc_numtoc; i++)
  2107. if(gendoc_toc[i * 4 + 2] && gendoc_toc[i * 4 + 2][0]) {
  2108. if(!gendoc_toc[i * 4 + 1]) {
  2109. if(H > 1) fprintf(f, " </ul></li>\n");
  2110. if(H) fprintf(f, " </ul>\n");
  2111. fprintf(f, " <p>%s</p>\n", gendoc_toc[i * 4 + 2]);
  2112. H = 0;
  2113. } else {
  2114. if((intptr_t)gendoc_toc[i * 4 + 1] == '1') {
  2115. if(!H) fprintf(f, " <ul>\n");
  2116. else fprintf(f, " </ul></li>\n");
  2117. fprintf(f, " <li rel=\"%s\"><label class=\"toc\" for=\"_%s\">%s</label><div class=\"current\">%s</div><ul>\n",
  2118. gendoc_toc[i * 4 + 0] ? gendoc_toc[i * 4 + 0] : "",
  2119. gendoc_toc[i * 4 + 0] ? gendoc_toc[i * 4 + 0] : "",
  2120. gendoc_toc[i * 4 + 2], gendoc_toc[i * 4 + 2]);
  2121. } else
  2122. fprintf(f, " <li class=\"h%c\"><a href=\"#%s\" onclick=\"m()\">%s</a></li>\n",
  2123. (char)((intptr_t)gendoc_toc[i * 4 + 1]),
  2124. gendoc_toc[i * 4 + 0] ? gendoc_toc[i * 4 + 0] : "",
  2125. gendoc_toc[i * 4 + 2]);
  2126. H = (intptr_t)gendoc_toc[i * 4 + 1] - '0';
  2127. }
  2128. }
  2129. if(H > 1) fprintf(f, " </ul></li>\n");
  2130. if(H) fprintf(f, " </ul>\n");
  2131. fprintf(f, " </div>\n"
  2132. " </div></nav>\n"
  2133. " <div id=\"_m\">\n"
  2134. " <nav class=\"mobile title\">%s<label for=\"menuchk\" class=\"menu\"></label></nav>\n", title);
  2135. if(gendoc_buf) {
  2136. for(s = gendoc_buf; s < gendoc_out && (*s == ' ' || *s == '\r' || *s == '\n'); s++);
  2137. fwrite(s, 1, (intptr_t)gendoc_out - (intptr_t)s, f);
  2138. free(gendoc_buf);
  2139. }
  2140. fprintf(f, "\n <footer><hr><p>© Copyright %s<br><small>Generated by <a href=\"https://gitlab.com/bztsrc/gendoc\">gendoc</a> v" GENDOC_VERSION "</small></p></footer>\n"
  2141. " </div>\n"
  2142. " </div>\n", gendoc_lang[18]);
  2143. /* embedded JavaScript, optional, minimal, vanilla (jQuery-less) code. Licensed under CC-BY */
  2144. fprintf(f, "<script>"
  2145. "function m(){document.getElementById(\"menuchk\").checked=false;}"
  2146. /* onclick handler that changes the url *and* switches pages too */
  2147. "function c(s){"
  2148. "var r=document.getElementById(s);"
  2149. "if(r!=undefined){"
  2150. "if(r.tagName==\"INPUT\")r.checked=true;"
  2151. "else document.getElementById(\"_\"+r.parentNode.getAttribute(\"rel\")).checked=true;"
  2152. "}m();}");
  2153. /* search function */
  2154. fprintf(f, "function s(s){"
  2155. "var r=document.getElementById(\"_s\"),p=document.getElementById(\"_m\").getElementsByClassName(\"page\"),n,i,j,a,b,c,d;"
  2156. "if(s){"
  2157. "s=s.toLowerCase();document.getElementById(\"_t\").style.display=\"none\";r.style.display=\"block\";"
  2158. "while(r.firstChild)r.removeChild(r.firstChild);n=document.createElement(\"p\");n.appendChild(document.createTextNode(\"%s\"));r.appendChild(n);"
  2159. "for(i=1;i<p.length;i++){"
  2160. "a=p[i].getAttribute(\"rel\");b=\"\";c=p[i].childNodes;d=p[i].getElementsByTagName(\"H1\")[0].innerText;", gendoc_lang[5]);
  2161. fprintf(f, "for(j=1;j<c.length && c[j].className!=\"btn prev\";j++){"
  2162. "if(c[j].id!=undefined&&c[j].id!=\"\"){"
  2163. "a=c[j].id;d=c[j].innerText;"
  2164. "}else if(a!=b&&c[j].innerText!=undefined&&c[j].innerText.toLowerCase().indexOf(s)!=-1){"
  2165. "b=a;n=document.createElement(\"a\");n.appendChild(document.createTextNode(d));n.setAttribute(\"href\",\"#\"+a);n.setAttribute(\"onclick\",\"c('\"+a+\"');\");r.appendChild(n);"
  2166. "}}}"
  2167. "}else{"
  2168. "document.getElementById(\"_t\").style.display=\"block\";r.style.display=\"none\";}}");
  2169. fprintf(f, "document.addEventListener(\"DOMContentLoaded\",function(e){var i,r,n;document.getElementById(\"_q\").style.display=\"inline-block\";"
  2170. /* the query URL hack */
  2171. "if(document.location.href.indexOf(\"?\")!=-1)document.location.href=document.location.href.replace(\"?\",\"#\");else{"
  2172. /* replace labels with "a" tags that change the url too */
  2173. "r=document.querySelectorAll(\"LABEL:not(.menu)\");"
  2174. "while(r.length){"
  2175. "l=r[0].getAttribute(\"for\").substr(1);");
  2176. fprintf(f, "n=document.createElement(\"a\");n.appendChild(document.createTextNode(r[0].innerText));"
  2177. "n.setAttribute(\"href\",\"#\"+l);n.setAttribute(\"onclick\",\"c('\"+(l!=\"\"?l:\"_\")+\"');\");"
  2178. "if(r[0].getAttribute(\"class\")!=undefined)n.setAttribute(\"class\",r[0].getAttribute(\"class\"));"
  2179. "if(r[0].getAttribute(\"title\")!=undefined&&l!=\"\")n.setAttribute(\"title\",r[0].getAttribute(\"title\"));"
  2180. "if(r[0].getAttribute(\"accesskey\")!=undefined)n.setAttribute(\"accesskey\",r[0].getAttribute(\"accesskey\"));"
  2181. "r[0].parentNode.replaceChild(n,r[0]);"
  2182. "r=document.querySelectorAll(\"LABEL:not(.menu)\");"
  2183. /* switch to a page which is detected from the url */
  2184. "}try{c(document.location.href.split(\"#\")[1]);}catch(e){}}});"
  2185. "</script>\n</body>\n</html>\n");
  2186. if(gendoc_toc) {
  2187. for(i = 0; i < gendoc_numtoc; i++) {
  2188. if(gendoc_toc[i * 4 + 0]) free(gendoc_toc[i * 4 + 0]);
  2189. if(gendoc_toc[i * 4 + 2]) free(gendoc_toc[i * 4 + 2]);
  2190. if(gendoc_toc[i * 4 + 3]) free(gendoc_toc[i * 4 + 3]);
  2191. }
  2192. free(gendoc_toc);
  2193. }
  2194. if(gendoc_rules) {
  2195. for(i = 0; i < gendoc_numrules; i++) {
  2196. free(gendoc_rules[i * 9]);
  2197. for(h = 1; h < 9; h++) {
  2198. if(gendoc_rules[i * 9 + h]) {
  2199. for(w = 0; gendoc_rules[i * 9 + h][w]; w++)
  2200. free(gendoc_rules[i * 9 + h][w]);
  2201. free(gendoc_rules[i * 9 + h]);
  2202. }
  2203. }
  2204. }
  2205. free(gendoc_rules);
  2206. }
  2207. }
  2208. /**
  2209. * The main function
  2210. */
  2211. int main(int argc, char **argv)
  2212. {
  2213. FILE *f;
  2214. DIR *dir;
  2215. struct dirent *ent;
  2216. char *fn, *buf, *s, *d, *e;
  2217. int i, j, k;
  2218. /*** collect info ***/
  2219. if(argc < 3) {
  2220. printf("./gendoc <output.html> <input file> [input file...]\n");
  2221. return 0;
  2222. }
  2223. f = fopen(argv[1], "wb");
  2224. if(!f) {
  2225. fprintf(stderr, "gendoc error: %s:0 unable to write file\n", argv[1]);
  2226. return 1;
  2227. }
  2228. /* load all highlight rules */
  2229. strcpy(gendoc_path, argv[0]);
  2230. fn = strrchr(gendoc_path, '/');
  2231. if(!fn) { strcpy(gendoc_path, "./"); fn = gendoc_path + 2; } else fn++;
  2232. strcpy(fn, "plugins");
  2233. if(!(dir = opendir(gendoc_path))) {
  2234. strcpy(gendoc_path, "/usr/share/gendoc/plugins");
  2235. fn = gendoc_path + 18;
  2236. dir = opendir(gendoc_path);
  2237. }
  2238. if(dir) {
  2239. fn[7] = '/';
  2240. while ((ent = readdir(dir)) != NULL) {
  2241. i = strlen(ent->d_name); if(i < 9 || memcmp(ent->d_name, "hl_", 3) || strcmp(ent->d_name + i - 5, ".json")) continue;
  2242. strcpy(fn + 8, ent->d_name);
  2243. buf = file_get_contents(gendoc_path, NULL);
  2244. if(buf) {
  2245. gendoc_rules = (char***)myrealloc(gendoc_rules, (gendoc_numrules + 1) * 9 * sizeof(char**));
  2246. memset(&gendoc_rules[gendoc_numrules * 9], 0, 9 * sizeof(char**));
  2247. gendoc_rules[gendoc_numrules * 9] = (char**)myrealloc(NULL, i - 7);
  2248. memcpy((char*)gendoc_rules[gendoc_numrules * 9], ent->d_name + 3, i - 8);
  2249. for(s = buf, i = k = 0, j = 1; *s && j < 9; s++) {
  2250. if(s[0] == '/' && s[1] == '*') { s += 2; while(*s && s[-2] != '*' && s[-1] != '/') s++; }
  2251. if(*s == '[') k++; else if(*s == ']') { k--; if(k == 1) { i = 0; j++; } } else
  2252. if(*s == '\"') {
  2253. s++; for(e = s; *e && *e != '\"'; e++) if(*e == '\\') e++;
  2254. gendoc_rules[gendoc_numrules * 9 + j] = (char**)myrealloc(gendoc_rules[gendoc_numrules * 9 + j],
  2255. (i + 2) * sizeof(char*));
  2256. gendoc_rules[gendoc_numrules * 9 + j][i] = d = (char*)myrealloc(NULL, e - s + 1);
  2257. gendoc_rules[gendoc_numrules * 9 + j][i + 1] = NULL;
  2258. for(; s < e; s++)
  2259. if(s[0] != '\\' || s[1] != '\"') *d++ = *s;
  2260. i++;
  2261. }
  2262. }
  2263. free(buf);
  2264. gendoc_numrules++;
  2265. }
  2266. }
  2267. closedir(dir);
  2268. }
  2269. /* initialize variables */
  2270. memset(gendoc_chk_l, 0, sizeof(gendoc_chk_l));
  2271. memset(gendoc_titleimg, 0, sizeof(gendoc_titleimg));
  2272. memset(gendoc_theme, 0, sizeof(gendoc_theme));
  2273. memset(gendoc_lang, 0, sizeof(gendoc_lang));
  2274. strcpy(gendoc_lang[0], "en");
  2275. strcpy(gendoc_lang[3], "#");
  2276. strcpy(gendoc_lang[4], "stable");
  2277. strcpy(gendoc_lang[5], "Search Results");
  2278. strcpy(gendoc_lang[6], "Home");
  2279. strcpy(gendoc_lang[7], "Permalink to this headline");
  2280. strcpy(gendoc_lang[8], "Important");
  2281. strcpy(gendoc_lang[9], "Hint");
  2282. strcpy(gendoc_lang[10], "Note");
  2283. strcpy(gendoc_lang[11], "See Also");
  2284. strcpy(gendoc_lang[12], "To Do");
  2285. strcpy(gendoc_lang[13], "Warning");
  2286. strcpy(gendoc_lang[14], "Arguments");
  2287. strcpy(gendoc_lang[15], "Return Value");
  2288. strcpy(gendoc_lang[16], "Previous");
  2289. strcpy(gendoc_lang[17], "Next");
  2290. strcpy(gendoc_lang[18], "unknown");
  2291. /* iterate on input files */
  2292. for(i = 2; i < argc; i++) {
  2293. memset(gendoc_path, 0, sizeof(gendoc_path));
  2294. gendoc_fn = 0;
  2295. gendoc_include(argv[i]);
  2296. }
  2297. /* generate output */
  2298. gendoc_output(f);
  2299. fclose(f);
  2300. if(gendoc_err)
  2301. fprintf(stderr, "gendoc: there were errors during generation, your documentation is although saved, but probably is incomplete!\n");
  2302. return 0;
  2303. }