💾 Archived View for gmi.noulin.net › gitRepositories › md › file › md.c.gmi captured on 2023-01-29 at 13:22:45. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
md.c (27969B)
1 #! /usr/bin/env sheepy 2 // 3 4 #include "libsheepyObject.h" 5 #include "shpPackages/md4c/md4c.h" 6 #include "fort.h" 7 8 #include "entities.h" 9 10 /* enable/disable logging */ 11 #undef pLog 12 #define pLog(...) 13 14 15 typ struct { 16 MD_BLOCKTYPE type; 17 unsigned olCount; 18 } blockt; 19 20 sliceT(blockst, blockt); 21 22 typedef struct { 23 smallArrayt *out; 24 smallStringt *current; 25 smallArrayt *span; 26 smallStringt *rspan; // recursive span string, copy to current on last leave span 27 MD_BLOCKTYPE blockquote; 28 MD_BLOCKTYPE h; 29 MD_BLOCKTYPE p; 30 MD_BLOCKTYPE blockcode; 31 MD_BLOCKTYPE tbl; 32 blockst blocks; 33 blockst spans; 34 smallArrayt table; 35 smallArrayt row; 36 int bqlevel; 37 smallJsont *entities; 38 char colorCode[40]; 39 } outt; 40 41 #define BLOCKQUOTE FNT"┃"RST" " 42 43 #define ADD_BLOCKQUOTE \ 44 if(r->blockquote == MD_BLOCK_QUOTE) {\ 45 loop(r->bqlevel) {\ 46 prependG(r->current, BLOCKQUOTE);\ 47 }\ 48 } 49 50 #define ADD_BLOCKCODE \ 51 if(r->blockcode == MD_BLOCK_CODE) {\ 52 pushG(r->current, BLD BGRED WHT "`````````" RST "\n" BLD MGT);\ 53 r->blockcode = 0;\ 54 } 55 56 57 internal int leave_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata); 58 internal int leave_all_spans(MD_SPANTYPE type, void* detail, void* userdata); 59 60 char *e2text[50]; // block 61 char *t2text[50]; // text 62 char *s2text[50]; // span 63 64 internal int 65 enter_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata) 66 { 67 cast(outt *, r, userdata); 68 69 logD(e2text[type]); 70 if (not sliceIsEmpty(&r->blocks)) { 71 logD("stack %s",e2text[sliceLast(&r->blocks).type]); 72 } 73 74 75 logVarG(r->current); 76 logVarG(sliceCount(&r->blocks)); 77 78 switch(type) { 79 case MD_BLOCK_DOC: /* noop */ 80 break; 81 case MD_BLOCK_QUOTE: 82 r->blockquote = MD_BLOCK_QUOTE; 83 ADD_BLOCKQUOTE; 84 pErrorNULL(pushG(r->out, r->current)); 85 rallocG(r->current, ""); 86 inc r->bqlevel; 87 //puts(MGT "blockquote" RST); 88 break; 89 case MD_BLOCK_UL:; 90 if (not sliceIsEmpty(&r->blocks) and sliceLast(&r->blocks).type == MD_BLOCK_LI) { 91 leave_block_callback(MD_BLOCK_LI, null, userdata); 92 } 93 cast(MD_BLOCK_UL_DETAIL *, uld, detail); 94 logVarG(uld->is_tight); 95 logD("uld->mark=%c",uld->mark); 96 if (sliceIsEmpty(&r->blocks)) { 97 pushG(r->out, ""); 98 } 99 //puts(GRN "ul" RST); 100 sliceAppend(&r->blocks, (blockt){.type=MD_BLOCK_UL}); 101 break; 102 case MD_BLOCK_OL:; 103 if (not sliceIsEmpty(&r->blocks) and sliceLast(&r->blocks).type == MD_BLOCK_LI) { 104 leave_block_callback(MD_BLOCK_LI, null, userdata); 105 } 106 cast(MD_BLOCK_OL_DETAIL *, old, detail); 107 logVarG(old->start); 108 logVarG(old->is_tight); 109 logD("old->mark_delimiter=%c",old->mark_delimiter); 110 if (sliceIsEmpty(&r->blocks)) { 111 pushG(r->out, ""); 112 } 113 //puts(GRN "ol" RST); 114 blockt t = {.type=MD_BLOCK_OL, .olCount=1}; 115 sliceAppend(&r->blocks, t); 116 break; 117 case MD_BLOCK_LI:; 118 cast(MD_BLOCK_LI_DETAIL *, lid, detail); 119 logVarG(lid->is_task); 120 logD("lid->task_mark=%c",lid->task_mark); 121 logVarG(lid->task_mark_offset); 122 if(!isEmptyG(r->current)) { 123 ADD_BLOCKQUOTE 124 ADD_BLOCKCODE 125 pushG(r->out, r->current); 126 pushG(r->out,""); 127 rallocG(r->current, ""); 128 } 129 //puts(GRN "li" RST); 130 sliceAppend(&r->blocks, (blockt){.type=MD_BLOCK_LI}); 131 break; 132 case MD_BLOCK_HR: 133 //puts(BLU "hr" RST); 134 break; 135 case MD_BLOCK_H: 136 r->h = MD_BLOCK_H; 137 if(!isEmptyG(r->current)) { 138 ADD_BLOCKCODE 139 pushG(r->out, r->current); 140 rallocG(r->current, ""); 141 } 142 //puts(BLU "hN" RST); 143 break; 144 case MD_BLOCK_CODE: 145 r->blockcode = MD_BLOCK_CODE; 146 pushG(r->out, "\n" BLD BGRED WHT "`````````" RST BLD MGT); 147 //puts(MGT "BLOCK_CODE" RST); 148 break; 149 /* case MD_BLOCK_HTML: #<{(| noop |)}># break; */ 150 case MD_BLOCK_P: 151 if ((sliceLast(&r->blocks).type != MD_BLOCK_LI) && !isEmptyG(r->current)) { 152 ADD_BLOCKQUOTE 153 ADD_BLOCKCODE 154 pushG(r->out, r->current); 155 rallocG(r->current, ""); 156 } 157 //puts(GRN "p" RST); 158 break; 159 case MD_BLOCK_TABLE: 160 initiateG(&r->table); 161 r->tbl = MD_BLOCK_TABLE; 162 break; 163 /* case MD_BLOCK_THEAD: RENDER_LITERAL(r, "<thead>\n"); break; */ 164 /* case MD_BLOCK_TBODY: RENDER_LITERAL(r, "<tbody>\n"); break; */ 165 case MD_BLOCK_TR: 166 initiateG(&r->row); 167 break; 168 /* case MD_BLOCK_TH: render_open_td_block(r, "th", (MD_BLOCK_TD_DETAIL*)detail); break; */ 169 /* case MD_BLOCK_TD: render_open_td_block(r, "td", (MD_BLOCK_TD_DETAIL*)detail); break; */ 170 } 171 172 return 0; 173 } 174 175 internal int 176 leave_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata) 177 { 178 cast(outt *, r, userdata); 179 /* MD_RENDER_HTML* r = (MD_RENDER_HTML*) userdata; */ 180 181 logD(e2text[type]); 182 if (not sliceIsEmpty(&r->blocks)) { 183 logD("stack %s",e2text[sliceLast(&r->blocks).type]); 184 } 185 186 logVarG(r->current); 187 logVarG(sliceCount(&r->blocks)); 188 189 leave_all_spans(0, null, userdata); 190 191 switch(type) { 192 case MD_BLOCK_DOC: /*noop*/ 193 break; 194 case MD_BLOCK_QUOTE: 195 ADD_BLOCKQUOTE; 196 pErrorNULL(pushG(r->out, r->current)); 197 rallocG(r->current, ""); 198 dec r->bqlevel; 199 if (!r->bqlevel) 200 r->blockquote = 0; 201 //puts(MGT "/blockquote" RST); 202 break; 203 case MD_BLOCK_UL: 204 if (not sliceIsEmpty(&r->blocks) and sliceLast(&r->blocks).type == MD_BLOCK_UL) { 205 sliceDelLast(&r->blocks); 206 } 207 if (sliceIsEmpty(&r->blocks)) { 208 pushG(r->out, ""); 209 } 210 //puts(GRN "/ul" RST); 211 break; 212 case MD_BLOCK_OL: 213 if (not sliceIsEmpty(&r->blocks) and sliceLast(&r->blocks).type == MD_BLOCK_OL) { 214 sliceDelLast(&r->blocks); 215 } 216 if (sliceIsEmpty(&r->blocks)) { 217 pushG(r->out, ""); 218 } 219 //puts(GRN "/ol" RST); 220 break; 221 case MD_BLOCK_LI: 222 if (not sliceIsEmpty(&r->blocks) and sliceLast(&r->blocks).type == MD_BLOCK_LI) { 223 sliceDelLast(&r->blocks); 224 } 225 //puts(GRN "/li" RST); 226 //logD("'%m'", r->current); 227 if (not isEmptyG(r->current)) { 228 if (sliceLast(&r->blocks).type == MD_BLOCK_UL) { 229 prependG(r->current, "- "); 230 loop(sliceCount(&r->blocks)-1/*list level*/) { 231 prependG(r->current, " "); 232 } 233 } 234 if (sliceLast(&r->blocks).type == MD_BLOCK_OL) { 235 char *s = intToS(sliceLast(&r->blocks).olCount); 236 pErrorNULL(pushG(&s, ". ")); 237 prependG(r->current, s); 238 free(s); 239 loop(sliceCount(&r->blocks)-1/*list level*/) { 240 prependG(r->current, " "); 241 } 242 sliceLast(&r->blocks).olCount++; 243 } 244 ADD_BLOCKQUOTE 245 pushG(r->out, r->current); 246 rallocG(r->current, ""); 247 } 248 break; 249 case MD_BLOCK_HR: 250 pushG(r->out, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); 251 break; 252 case MD_BLOCK_H: 253 //puts(BLU "</hN>" RST); 254 r->h = 0; 255 pushG(r->out, ""); 256 prependG(r->current, " "); 257 loop(((MD_BLOCK_H_DETAIL*)detail)->level) { 258 prependG(r->current, "#"); 259 } 260 colorG(r->current, BLD YLW); 261 pushG(r->out, r->current); 262 rallocG(r->current, ""); 263 break; 264 case MD_BLOCK_CODE: 265 if (not isEmptyG(r->current)) { 266 pushG(r->current, RST BLD BGRED WHT "`````````" RST "\n"); 267 pushG(r->out, r->current); 268 rallocG(r->current, ""); 269 } 270 //puts(MGT "/BLOCK_CODE" RST); 271 break; 272 case MD_BLOCK_HTML: /* noop */ 273 break; 274 case MD_BLOCK_P: 275 //puts(GRN "/p" RST); 276 if (sliceLast(&r->blocks).type == MD_BLOCK_LI) { 277 if (sliceAt(&r->blocks, sliceCount(&r->blocks)-2).type == MD_BLOCK_UL) { 278 prependG(r->current, "- "); 279 loop(sliceCount(&r->blocks)-2/*list level*/) { 280 prependG(r->current, " "); 281 } 282 } 283 if (sliceAt(&r->blocks, sliceCount(&r->blocks)-2).type == MD_BLOCK_OL) { 284 char *s = intToS(sliceAt(&r->blocks, sliceCount(&r->blocks)-2).olCount); 285 pErrorNULL(pushG(&s, ". ")); 286 prependG(r->current, s); 287 free(s); 288 loop(sliceCount(&r->blocks)-2/*list level*/) { 289 prependG(r->current, " "); 290 } 291 sliceAt(&r->blocks, sliceCount(&r->blocks)-2).olCount++; 292 } 293 } 294 ADD_BLOCKQUOTE 295 pErrorNULL(pushG(r->out, r->current)); 296 rallocG(r->current, ""); 297 break; 298 case MD_BLOCK_TABLE:; 299 // TODO render with fort like csvFmt 300 int rows = lenG(&r->table); 301 int cols = 0; 302 iter(&r->table, Row) { 303 cast(smallArrayt*, row, Row); 304 cols = maxV(cols, lenG(row)); 305 } 306 char **t = malloc(rows * cols * sizeof(char*)); 307 int j = 0; 308 iter(&r->table, Row) { 309 cast(smallArrayt*, row, Row); 310 int i = 0; 311 iter(row, c) { 312 *(t + j * cols + i) = ssGet(c); 313 inc i; 314 } 315 if (i < cols) { 316 while (i < cols) { 317 *(t + j * cols + i) = ""; 318 inc i; 319 } 320 } 321 inc j; 322 } 323 ft_table_t *table2 = ft_create_table(); 324 325 ft_set_border_style(table2, FT_SIMPLE_STYLE); 326 //ft_set_border_style(table2, FT_DOUBLE_STYLE); 327 328 ft_table_write(table2, rows, cols, (const char **) t); 329 330 ft_set_cell_prop(table2, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); 331 range(i, cols) { 332 ft_set_cell_prop(table2, 0, i, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_BOLD); 333 } 334 range(i, rows) { 335 ft_set_cell_prop(table2, i, FT_ANY_COLUMN, FT_CPROP_CELL_BG_COLOR, FT_COLOR_DEFAULT); 336 ft_set_cell_prop(table2, i, FT_ANY_COLUMN, FT_CPROP_CELL_BG_RGBCOLOR, i & 1 ? 0x2f2f2f : 0x1f1f1f); 337 } 338 ft_set_cell_prop(table2, 0, FT_ANY_COLUMN, FT_CPROP_CELL_BG_RGBCOLOR, 0x4867ff); 339 340 // 0x4042f 341 // 0x2f2f2f 342 pErrorNULL(pushG(r->out, ft_to_string(table2))); 343 ft_destroy_table(table2); 344 free(t); 345 logG(&r->table); 346 r->tbl = 0; 347 freeG(&r->table); 348 break; 349 /* case MD_BLOCK_THEAD: RENDER_LITERAL(r, "</thead>\n"); break; */ 350 /* case MD_BLOCK_TBODY: RENDER_LITERAL(r, "</tbody>\n"); break; */ 351 case MD_BLOCK_TR: 352 pushG(&r->table, &r->row); 353 break; 354 case MD_BLOCK_TH: 355 pErrorNULL(pushG(&r->row, r->current)); 356 rallocG(r->current, ""); 357 break; 358 case MD_BLOCK_TD: 359 pErrorNULL(pushG(&r->row, r->current)); 360 rallocG(r->current, ""); 361 break; 362 } 363 364 return 0; 365 } 366 367 368 internal int 369 enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) 370 { 371 cast(outt *, r, userdata); 372 373 logD(s2text[type]); 374 375 // check if span a specifies a color 376 // type is changed to MD_SPAN_COLOR 377 if (type == MD_SPAN_A) { 378 MD_SPAN_A_DETAIL *a = detail; 379 /* logVarG(a->href.text); */ 380 /* logVarG(a->href.size); */ 381 // TODO detect font 382 i32 fg = -1; 383 i32 bg = -1; 384 if (a->href.text[0] == '#' and a->href.size < 17 /* maximum size of hexadecimal rgb colors */) { 385 if (a->href.size > 1) { 386 char s[30]; 387 char colorString[17] = init0Var; 388 if (a->href.text[1] == 'x') { 389 if (a->href.size > 2) { 390 memcpy(colorString, a->href.text, a->href.size); 391 // search for background color 392 // minimum: #x?#? 393 i32 bgoff = -1; 394 if (a->href.size > 5) { 395 rangeFrom(i, 3, a->href.size) { 396 if (a->href.text[i] == '#') { 397 bgoff = i; 398 break; 399 } 400 } 401 if (bgoff > 0) { 402 if (bgoff+2 > a->href.size) 403 bgoff = -1; 404 else { 405 if (a->href.text[bgoff+1] < '0' and a->href.text[bgoff+1] > '9' and a->href.text[bgoff+1] != 'x') { 406 bgoff = -1; 407 } 408 elif (a->href.text[bgoff+1] == 'x' and bgoff+3 > a->href.size) 409 bgoff = -1; 410 } 411 } 412 } 413 colorString[0] = '0'; 414 fg = parseHex(colorString); 415 colorString[0] = '#'; 416 if (bgoff < 0) { 417 sprintf(r->colorCode, "\x1b[38;2;%u;%u;%um", fg >> 16, (fg&0xFF00)>>8, fg&0xFF); 418 } 419 else { 420 if (a->href.text[bgoff+1] >= '0' and a->href.text[bgoff+1] <= '9') { 421 bg = parseInt(a->href.text + bgoff); 422 if (bg >= 0 and bg < 8) { 423 sprintf(r->colorCode, "\x1b[38;2;%u;%u;%um\x1B[4%dm", fg >> 16, (fg&0xFF00)>>8, fg&0xFF, bg); 424 } 425 } 426 elif (a->href.text[bgoff+1] == 'x') { 427 colorString[bgoff] = '0'; 428 bg = parseHex(colorString + bgoff); 429 colorString[bgoff] = '#'; 430 sprintf(r->colorCode, "\x1b[38;2;%u;%u;%um\x1b[48;2;%u;%u;%um", fg >> 16, (fg&0xFF00)>>8, fg&0xFF, bg >> 16, (bg&0xFF00)>>8, bg&0xFF); 431 } 432 } 433 type = MD_SPAN_COLOR; 434 } 435 } 436 elif (a->href.text[1] >= '0' and a->href.text[1] <= '9') { 437 // check background color 438 // Minimum: #?#? 439 i32 bgoff = -1; 440 if (a->href.size > 3) { 441 rangeFrom(i, 2, a->href.size) { 442 if (a->href.text[i] == '#') { 443 bgoff = i; 444 break; 445 } 446 } 447 if (bgoff > 0) { 448 if (bgoff+2 > a->href.size) { 449 bgoff = -1; 450 } 451 else { 452 if (a->href.text[bgoff+1] < '0' and a->href.text[bgoff+1] > '9' and a->href.text[bgoff+1] != 'x') { 453 bgoff = -1; 454 } 455 elif (a->href.text[bgoff+1] == 'x' and bgoff+3 > a->href.size) { 456 bgoff = -1; 457 } 458 } 459 } 460 } 461 fg = parseInt(a->href.text); 462 char *bright = ""; 463 if (fg >= 8 and fg < 16) { 464 bright = BLD; 465 fg -= 8; 466 } 467 if (fg >= 0 and fg < 8) { 468 if (bgoff < 0) { 469 sprintf(r->colorCode, "%s\x1B[3%dm", bright, fg); 470 } 471 else { 472 if (a->href.text[bgoff+1] >= '0' and a->href.text[bgoff+1] <= '9') { 473 bg = parseInt(a->href.text + bgoff); 474 if (bg >= 0 and bg < 8) { 475 sprintf(r->colorCode, "%s\x1B[3%dm\x1B[4%dm", bright, fg, bg); 476 } 477 } 478 elif (a->href.text[bgoff+1] == 'x') { 479 memcpy(colorString, a->href.text, a->href.size); 480 colorString[bgoff] = '0'; 481 bg = parseHex(colorString + bgoff); 482 colorString[bgoff] = '#'; 483 sprintf(r->colorCode, "%s\x1B[3%dm\x1b[48;2;%u;%u;%um", bright, fg, bg >> 16, (bg&0xFF00)>>8, bg&0xFF); 484 } 485 } 486 type = MD_SPAN_COLOR; 487 } 488 } 489 elif (a->href.size > 2 and a->href.text[1] == '#') { 490 // ##? 491 // there is no foreground color 492 if (a->href.text[2] == 'x' and a->href.size > 3) { 493 // ##x? 494 memcpy(colorString, a->href.text, a->href.size); 495 colorString[1] = '0'; 496 bg = parseHex(colorString + 1); 497 sprintf(r->colorCode, "\x1b[48;2;%u;%u;%um", bg >> 16, (bg&0xFF00)>>8, bg&0xFF); 498 type = MD_SPAN_COLOR; 499 } 500 elif (a->href.text[2] >= '0' and a->href.text[2] <= '9') { 501 bg = parseInt(a->href.text); 502 if (bg >= 0 and bg < 8) { 503 sprintf(r->colorCode, "\x1B[4%dm", bg); 504 type = MD_SPAN_COLOR; 505 } 506 507 } 508 } 509 } 510 } 511 } 512 513 if (not sliceIsEmpty(&r->spans)) { 514 pushG(r->span, r->rspan); 515 rallocG(r->rspan, ""); 516 } 517 518 sliceAppend(&r->spans, (blockt){.type=type}); 519 520 switch(type) { 521 case MD_SPAN_EM: 522 prependG(r->rspan, ITL); 523 break; 524 case MD_SPAN_STRONG: 525 prependG(r->rspan, BLD); 526 break; 527 case MD_SPAN_U: 528 prependG(r->rspan, UDL); 529 break; 530 case MD_SPAN_A: 531 prependG(r->rspan, BLD UDL BLU); 532 break; 533 case MD_SPAN_IMG: 534 prependG(r->rspan, UDL RED); 535 break; 536 case MD_SPAN_CODE: 537 prependG(r->rspan, BGBLU); 538 break; 539 case MD_SPAN_DEL: 540 prependG(r->rspan, CRD); 541 break; 542 case MD_SPAN_FNT: 543 prependG(r->rspan, FNT); 544 break; 545 case MD_SPAN_INV: 546 prependG(r->rspan, INV); 547 break; 548 case MD_SPAN_COC: 549 prependG(r->rspan, COC); 550 break; 551 case MD_SPAN_BLI: 552 prependG(r->rspan, BLI); 553 break; 554 case MD_SPAN_ANCHOR: 555 prependG(r->rspan, GRN); 556 break; 557 case MD_SPAN_COLOR: 558 prependG(r->rspan, r->colorCode); 559 break; 560 } 561 return 0; 562 } 563 564 internal int 565 leave_all_spans(MD_SPANTYPE type, void* detail, void* userdata) 566 { 567 cast(outt *, r, userdata); 568 569 if (not sliceIsEmpty(&r->spans)) { 570 sliceEmpty(&r->spans); 571 pushG(r->rspan, RST); 572 pushG(r->span, r->rspan); 573 rallocG(r->rspan, ""); 574 cleanFinishSmallStringP(s) = joinG(r->span, " "); 575 freeG(r->span); 576 pushG(r->current, s); 577 } 578 579 return 0; 580 } 581 internal int 582 leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) 583 { 584 cast(outt *, r, userdata); 585 586 logD(s2text[type]); 587 588 if (not sliceIsEmpty(&r->spans)) { 589 sliceDelLast(&r->spans); 590 if (sliceIsEmpty(&r->spans)) { 591 pushG(r->rspan, RST); 592 pushG(r->span, r->rspan); 593 rallocG(r->rspan, ""); 594 cleanFinishSmallStringP(s) = joinG(r->span, " "); 595 freeG(r->span); 596 pushG(r->current, s); 597 } 598 else { 599 pushG(r->rspan, RST); 600 sliceForEach(&r->spans, e) { 601 switch(e->type) { 602 case MD_SPAN_EM: 603 pushG(r->rspan, ITL); 604 break; 605 case MD_SPAN_STRONG: 606 pushG(r->rspan, BLD); 607 break; 608 case MD_SPAN_U: 609 pushG(r->rspan, UDL); 610 break; 611 case MD_SPAN_A: 612 pushG(r->rspan, BLD UDL BLU); 613 break; 614 case MD_SPAN_IMG: 615 pushG(r->rspan, UDL RED); 616 break; 617 case MD_SPAN_CODE: 618 pushG(r->rspan, BGBLU); 619 break; 620 case MD_SPAN_DEL: 621 pushG(r->rspan, CRD); 622 break; 623 case MD_SPAN_FNT: 624 pushG(r->rspan, FNT); 625 break; 626 case MD_SPAN_INV: 627 pushG(r->rspan, INV); 628 break; 629 case MD_SPAN_COC: 630 pushG(r->rspan, COC); 631 break; 632 case MD_SPAN_BLI: 633 pushG(r->rspan, BLI); 634 break; 635 case MD_SPAN_ANCHOR: 636 pushG(r->rspan, GRN); 637 break; 638 case MD_SPAN_COLOR: 639 pushG(r->rspan, r->colorCode); 640 break; 641 } 642 } 643 pushG(r->span, r->rspan); 644 rallocG(r->rspan, ""); 645 } 646 } 647 //logG(r->span); 648 649 return 0; 650 } 651 652 internal int 653 text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) 654 { 655 cast(outt *, r, userdata); 656 char *s = calloc(1, size+1); 657 smallStringt *t; 658 659 logD(t2text[type]); 660 661 switch(type) { 662 case MD_TEXT_NULLCHAR: 663 break; 664 case MD_TEXT_BR: 665 pushG(r->out, ""); 666 //puts(BLU "br" RST); 667 break; 668 case MD_TEXT_SOFTBR: 669 pushG(r->current, " "); 670 //puts(BLU "sbr" RST); 671 break; 672 case MD_TEXT_HTML: 673 strncpy(s, text, size); /*pushG(r->out, s);*/ //puts(BLU "html" RST); 674 break; 675 case MD_TEXT_ENTITY: 676 // spec https://html.spec.whatwg.org/entities.json 677 strncpy(s, text, size); 678 logVarG(s); 679 char utf8Code[10] = init0Var; 680 if (s[1] == '#') { 681 // numeric character reference 682 if (s[2] == 'x' or s[2] == 'X') { 683 // hexadecimal 684 s[1] = '0'; 685 rune c = parseHex(s+1); 686 pError0(bRune2CodeUTF8(utf8Code, c)); 687 s[1] = '#'; 688 } 689 else { 690 // decimal 691 rune c = parseIntG(s); 692 pError0(bRune2CodeUTF8(utf8Code, c)); 693 } 694 } 695 else { 696 // check entity list 697 if (!r->entities) { 698 initiateG(&r->entities); 699 parseG(r->entities, entitiesString); 700 } 701 char *u = getG(r->entities, rtChar, s); 702 if (!u) break; // not found 703 strcpy(utf8Code, u); 704 } 705 pushG(r->current, utf8Code); 706 break; 707 default: 708 strncpy(s, text, size); 709 t = allocG(s); 710 if (not sliceIsEmpty(&r->spans)) { 711 pushNFreeG(r->rspan, t); 712 break; 713 } 714 pushNFreeG(r->current, t); 715 //puts(BLU "default" RST); 716 //printf(">%s<\n", s); 717 break; 718 } 719 720 logVarG(s); 721 free(s); 722 723 return 0; 724 } 725 726 internal void 727 debug_log_callback(const char* msg, void* userdata) 728 { 729 shEPrintfS("md4c %s", msg); 730 } 731 732 smallArrayt * 733 md_highlight(const char *md_source) { 734 735 if (!md_source) { 736 return NULL; 737 } 738 739 outt result = init0Var; 740 741 result.out = allocG(rtSmallArrayt); 742 result.current = allocG(""); 743 result.span = allocG(rtSmallArrayt); 744 result.rspan = allocG(""); 745 sliceInitCount(&result.blocks, 16); 746 sliceInitCount(&result.spans, 16); 747 748 unsigned parser_flags = MD_DIALECT_GITHUB | MD_FLAG_UNDERLINE; 749 unsigned renderer_flags = 0; 750 751 MD_PARSER parser = { 752 0, 753 parser_flags, 754 enter_block_callback, 755 leave_block_callback, 756 enter_span_callback, 757 leave_span_callback, 758 text_callback, 759 debug_log_callback, 760 null 761 }; 762 763 md_parse(md_source, strlen(md_source), &parser, &result); 764 765 smashG(result.current); 766 terminateG(result.span); 767 terminateG(result.rspan); 768 sliceFree(&result.blocks); 769 sliceFree(&result.spans); 770 terminateG(result.entities); 771 return result.out; 772 } 773 774 #ifndef LIB 775 int main(int ARGC, char** ARGV) { 776 777 initLibsheepy(ARGV[0]); 778 setLogMode(LOG_FUNC); 779 780 if (ARGC < 2) { 781 puts(RED "Give a filename in parameter" RST); 782 XFAILURE 783 } 784 785 e2text[MD_BLOCK_DOC] = "MD_BLOCK_DOC"; 786 e2text[MD_BLOCK_QUOTE] = "MD_BLOCK_QUOTE"; 787 e2text[MD_BLOCK_UL] = "MD_BLOCK_UL"; 788 e2text[MD_BLOCK_OL] = "MD_BLOCK_OL"; 789 e2text[MD_BLOCK_LI] = "MD_BLOCK_LI"; 790 e2text[MD_BLOCK_HR] = "MD_BLOCK_HR"; 791 e2text[MD_BLOCK_H] = "MD_BLOCK_H"; 792 e2text[MD_BLOCK_CODE] = "MD_BLOCK_CODE"; 793 e2text[MD_BLOCK_HTML] = "MD_BLOCK_HTML"; 794 e2text[MD_BLOCK_P] = "MD_BLOCK_P"; 795 e2text[MD_BLOCK_TABLE] = "MD_BLOCK_TABLE"; 796 e2text[MD_BLOCK_THEAD] = "MD_BLOCK_THEAD"; 797 e2text[MD_BLOCK_TBODY] = "MD_BLOCK_TBODY"; 798 e2text[MD_BLOCK_TR] = "MD_BLOCK_TR"; 799 e2text[MD_BLOCK_TH] = "MD_BLOCK_TH"; 800 e2text[MD_BLOCK_TD] = "MD_BLOCK_TD"; 801 802 t2text[MD_TEXT_NORMAL] = "MD_TEXT_NORMAL"; 803 t2text[MD_TEXT_NULLCHAR] = "MD_TEXT_NULLCHAR"; 804 t2text[MD_TEXT_BR] = "MD_TEXT_BR"; 805 t2text[MD_TEXT_SOFTBR] = "MD_TEXT_SOFTBR"; 806 t2text[MD_TEXT_ENTITY] = "MD_TEXT_ENTITY"; 807 t2text[MD_TEXT_CODE] = "MD_TEXT_CODE"; 808 t2text[MD_TEXT_HTML] = "MD_TEXT_HTML"; 809 t2text[MD_TEXT_LATEXMATH] = "MD_TEXT_LATEXMATH"; 810 811 s2text[MD_SPAN_EM] = "MD_SPAN_EM"; 812 s2text[MD_SPAN_STRONG] = "MD_SPAN_STRONG"; 813 s2text[MD_SPAN_A] = "MD_SPAN_A"; 814 s2text[MD_SPAN_IMG] = "MD_SPAN_IMG"; 815 s2text[MD_SPAN_CODE] = "MD_SPAN_CODE"; 816 s2text[MD_SPAN_DEL] = "MD_SPAN_DEL"; 817 s2text[MD_SPAN_LATEXMATH] = "MD_SPAN_LATEXMATH"; 818 s2text[MD_SPAN_LATEXMATH_DISPLAY] = "MD_SPAN_LATEXMATH_DISPLAY"; 819 s2text[MD_SPAN_WIKILINK] = "MD_SPAN_WIKILINK"; 820 s2text[MD_SPAN_U] = "MD_SPAN_U"; 821 s2text[MD_SPAN_FNT] = "MD_SPAN_FNT"; 822 s2text[MD_SPAN_INV] = "MD_SPAN_INV"; 823 s2text[MD_SPAN_COC] = "MD_SPAN_COC"; 824 s2text[MD_SPAN_BLI] = "MD_SPAN_BLI"; 825 s2text[MD_SPAN_ANCHOR] = "MD_SPAN_ANCHOR"; 826 827 char *c = readFileG(c, ARGV[1]); 828 829 if (!c) { 830 puts(RED "Error reading:" RST); 831 puts(ARGV[1]); 832 XFAILURE 833 } 834 835 logG(md_highlight(c)); 836 837 //logVarG(result.out); 838 } 839 #endif