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