slw2gmi.c (122510B)
1 /* This program is licensed under the terms of GNU GPL v3 or (at your option) 2 * any later version. Copyright (C) 2021-2024 Страхиња Радић. 3 * See the file LICENSE for exact copyright and license details. */ 4 5 #include "defs.h" 6 7 static size_t lineno = 0; 8 static size_t colno = 1; 9 static int output_firstcol = 1; 10 static int skip_para_end = 0; 11 static const char* input_filename = NULL; 12 static char* input_dirname = NULL; 13 static char* basedir = NULL; 14 static size_t basedir_size = 0; 15 static char* incdir = NULL; 16 static char* global_link_prefix = NULL; 17 static size_t global_link_prefix_size = 0; 18 static KeyValue* vars = NULL; 19 static KeyValue* pvars = NULL; 20 static size_t vars_count = 0; 21 static KeyValue* macros = NULL; 22 static KeyValue* pmacros = NULL; 23 static size_t macros_count = 0; 24 static KeyValue* links = NULL; 25 static KeyValue* plinks = NULL; 26 static size_t links_count = 0; 27 static KeyValue* paralinks = NULL; 28 static KeyValue* pparalinks = NULL; 29 static size_t paralinks_count = 0; 30 static KeyValue* footnotes = NULL; 31 static KeyValue* pfootnotes = NULL; 32 static size_t footnote_count = 0; 33 static size_t current_footnote = 0; 34 static u8** inline_footnotes = NULL; 35 static size_t inline_footnote_count = 0; 36 static size_t current_inline_footnote = 0; 37 static u8* csv_template = NULL; 38 static size_t csv_template_size = 0; 39 static char* csv_filename = NULL; 40 static long csv_iter = 0; 41 static u8* tsv_template = NULL; 42 static size_t tsv_template_size = 0; 43 static char* tsv_filename = NULL; 44 static long tsv_iter = 0; 45 static ULLONG state = ST_NONE; 46 static int incdir_only_summary = 0; 47 48 #define COPYRIGHT \ 49 (" This program is licensed under the terms of GNU GPL v3" \ 50 " or (at your option)\n" \ 51 " any later version. Copyright (C) 2021-2024 Strahinya Radich.\n" \ 52 " See the file LICENSE for exact copyright and license " \ 53 "details.") 54 55 #define CHECKEXITNOMEM(ptr, func) \ 56 do \ 57 { \ 58 if (!ptr) \ 59 exit(error(ENOMEM, \ 60 (u8*)"" #func \ 61 ":%d: Memory allocation failed (out of " \ 62 "memory?)", \ 63 __LINE__)); \ 64 } while (0) 65 66 #define CALLOC(ptr, ptrtype, nmemb) \ 67 do \ 68 { \ 69 ptr = calloc(nmemb, sizeof(ptrtype)); \ 70 CHECKEXITNOMEM(ptr, calloc); \ 71 } while (0) 72 73 #define REALLOC(ptr, ptrtype, newsize) \ 74 do \ 75 { \ 76 ptrtype* newptr = realloc(ptr, newsize); \ 77 CHECKEXITNOMEM(newptr, realloc); \ 78 ptr = newptr; \ 79 } while (0) 80 81 #define REALLOCARRAY(ptr, membtype, newcount) \ 82 REALLOC(ptr, membtype, sizeof(membtype) * newcount); 83 84 #define CHECKCOPY(token, ptoken, token_size, pline) \ 85 do \ 86 { \ 87 if (ptoken + 2 > token + token_size) \ 88 { \ 89 size_t old_size = token_size; \ 90 token_size += BUFSIZE; \ 91 REALLOC(token, u8, token_size); \ 92 ptoken = token + old_size - 1; \ 93 } \ 94 *ptoken++ = *pline++; \ 95 } while (0) 96 97 #define RESET_TOKEN(token, ptoken, token_size) \ 98 do \ 99 { \ 100 token_size = BUFSIZE; \ 101 REALLOC(token, u8, token_size); \ 102 *token = 0; \ 103 ptoken = token; \ 104 } while (0) 105 106 int slweb_cleanup(void); 107 int usage(void); 108 int version(const int full); 109 110 int 111 version(const int full) 112 { 113 printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE); 114 if (full) 115 puts(COPYRIGHT); 116 return 0; 117 } 118 119 int 120 usage(void) 121 { 122 printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n" 123 "\t%s [-d directory | --basedir directory]" 124 " [-p URL | --global-link-prefix URL] [filename]\n", 125 PROGRAMNAME, PROGRAMNAME); 126 return 0; 127 } 128 129 int 130 error(const int code, const u8* fmt, ...) 131 { 132 char buf[BUFSIZE]; 133 va_list args; 134 va_start(args, fmt); 135 vsnprintf(buf, sizeof(buf), (const char*)fmt, args); 136 va_end(args); 137 fprintf(stderr, "%s:%s:%lu:%lu: %s\n", PROGRAMNAME, 138 input_filename ? input_filename : "(stdin)", lineno, colno, 139 buf); 140 return code; 141 } 142 143 int 144 warning(const int code, const u8* fmt, ...) 145 { 146 char buf[BUFSIZE]; 147 va_list args; 148 va_start(args, fmt); 149 vsnprintf(buf, sizeof(buf), (const char*)fmt, args); 150 va_end(args); 151 fprintf(stderr, "Warning: %s\n", buf); 152 return code; 153 } 154 155 int 156 free_keyvalue(KeyValue** list, const size_t list_count) 157 { 158 if (!list || !*list) 159 return 1; 160 161 for (size_t index = 0; index < list_count; index++) 162 { 163 KeyValue* current = *list + index; 164 free(current->value); 165 free(current->key); 166 } 167 return 0; 168 } 169 170 int 171 slweb_cleanup(void) 172 { 173 free(basedir); 174 free(input_dirname); 175 while (inline_footnote_count--) 176 free(inline_footnotes[inline_footnote_count]); 177 free(inline_footnotes); 178 free_keyvalue(&footnotes, footnote_count); 179 free_keyvalue(¶links, paralinks_count); 180 free_keyvalue(&links, links_count); 181 free_keyvalue(¯os, macros_count); 182 free_keyvalue(&vars, vars_count); 183 free(footnotes); 184 free(paralinks); 185 free(links); 186 free(macros); 187 free(vars); 188 189 return 0; 190 } 191 192 u8* format_date(const u8* date_arg, const char* timestamp_format); 193 int simple_parse_yaml_line(const u8* line, KeyValue** vars, size_t* vars_count, 194 KeyValue** pvars); 195 int slweb_parse(FILE* output, const char* source_filename, const u8* buffer, 196 const int read_yaml_macros_and_links); 197 198 int 199 startswith(const char* s, const char* what) 200 { 201 if (!s || !what) 202 return 0; 203 204 do 205 { 206 if (*s++ != *what++) 207 return 0; 208 } while (*s && *what); 209 210 return !*what; 211 } 212 213 int 214 endswith(const char* s, const char* what) 215 { 216 const char* ps = s; 217 const char* pwhat = what; 218 219 if (!s || !what) 220 return 0; 221 222 if (!*what) 223 return 1; 224 225 while (*ps) 226 ps++; 227 while (*pwhat) 228 pwhat++; 229 230 while (ps != s && pwhat != what) 231 if (*ps-- != *pwhat--) 232 return 0; 233 234 return *ps == *pwhat && pwhat == what; 235 } 236 237 int 238 all_numeric(const u8* start, const u8* end) 239 { 240 const u8* ps = start; 241 while (ps != end) 242 { 243 if (*ps && !isdigit((const int)*ps)) 244 return 0; 245 ps++; 246 } 247 return 1; 248 } 249 250 u8* 251 get_value(KeyValue* list, const size_t list_count, const u8* key, 252 KeyValue** cursor) 253 { 254 if (!list) 255 return NULL; 256 KeyValue* plist = list; 257 while (plist < list + list_count) 258 { 259 if (!strcmp((char*)plist->key, (char*)key)) 260 { 261 if (cursor) 262 { 263 if (plist + 1 < list + list_count) 264 *cursor = plist + 1; 265 else 266 *cursor = NULL; 267 } 268 return plist->value; 269 } 270 plist++; 271 } 272 if (cursor) 273 *cursor = NULL; 274 return NULL; 275 } 276 277 int 278 set_basedir(char** basedir, size_t* basedir_size, const char* arg) 279 { 280 size_t basedir_len = 0; 281 size_t arg_len = strlen(arg); 282 283 if (arg_len < 1) 284 exit(error(1, (u8*)"set_basedir: Argument required")); 285 286 if (arg_len + 1 > *basedir_size) 287 { 288 *basedir_size = arg_len + 1; 289 REALLOC(*basedir, char, *basedir_size); 290 } 291 292 if (strlcpy(*basedir, arg, *basedir_size) >= *basedir_size) 293 warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__); 294 basedir_len = strlen(*basedir); 295 if (*(*basedir + basedir_len - 1) == '/') 296 *(*basedir + basedir_len - 1) = 0; 297 298 return 0; 299 } 300 301 int 302 set_global_link_prefix(char** global_link_prefix, 303 size_t* global_link_prefix_size, const char* arg) 304 { 305 size_t arg_len = strlen(arg); 306 307 if (arg_len < 1) 308 exit(error(1, (u8*)"set_global_link_prefix: Argument required")); 309 310 if (arg_len + 1 > *global_link_prefix_size) 311 { 312 *global_link_prefix_size = arg_len + 1; 313 REALLOC(*global_link_prefix, char, *global_link_prefix_size); 314 } 315 if (strlcpy(*global_link_prefix, arg, *global_link_prefix_size) 316 >= *global_link_prefix_size) 317 warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__); 318 *(*global_link_prefix + arg_len) = 0; 319 320 return 0; 321 } 322 323 char* 324 strip_ext(const char* fn) 325 { 326 char* newname = NULL; 327 char* pnewname = NULL; 328 const char* pfn = NULL; 329 char* dot = NULL; 330 331 dot = strrchr(fn, '.'); 332 333 if (!dot) 334 return NULL; 335 336 CALLOC(newname, char, strlen(fn) + 1); 337 pnewname = newname; 338 pfn = fn; 339 340 while (pfn != dot && *pfn) 341 *pnewname++ = *pfn++; 342 343 return newname; 344 } 345 346 int 347 print_output(FILE* output, const char* fmt, ...) 348 { 349 char buf[BUFSIZE]; 350 va_list args; 351 u8* var_incdir_only_summary = NULL; 352 // int incdir_only_summary = 0; 353 354 if (!output || !fmt) 355 exit(error(EINVAL, (u8*)"print_output: Invalid argument")); 356 357 va_start(args, fmt); 358 359 var_incdir_only_summary 360 = get_value(vars, vars_count, (u8*)"incdir-only-summary", NULL); 361 incdir_only_summary 362 = var_incdir_only_summary && *var_incdir_only_summary == '1'; 363 364 /*if (IN(state, ST_INCDIR) && !IN(state, ST_SUMMARY) 365 && incdir_only_summary) 366 return 0; 367 else*/ 368 if (IN(state, ST_CSV_BODY)) 369 { 370 size_t buf_len = 0; 371 vsnprintf((char*)buf, BUFSIZE, fmt, args); 372 buf_len = strlen(buf); 373 if (!csv_template) 374 { 375 csv_template_size = BUFSIZE; 376 CALLOC(csv_template, u8, csv_template_size); 377 if (strlcpy((char*)csv_template, buf, csv_template_size) 378 >= csv_template_size) 379 warning(1, (u8*)"strlcpy:%d: Overflow", 380 __LINE__); 381 } 382 else 383 { 384 if (strlen((char*)csv_template) + buf_len 385 > csv_template_size) 386 { 387 csv_template_size += BUFSIZE; 388 REALLOC(csv_template, u8, csv_template_size); 389 } 390 if (strlcat((char*)csv_template, buf, csv_template_size) 391 > csv_template_size) 392 warning(1, (u8*)"strlcat:%d: Overflow", 393 __LINE__); 394 } 395 } 396 else if (IN(state, ST_TSV_BODY)) 397 { 398 size_t buf_len = 0; 399 vsnprintf((char*)buf, BUFSIZE, fmt, args); 400 buf_len = strlen(buf); 401 if (!tsv_template) 402 { 403 tsv_template_size = BUFSIZE; 404 CALLOC(tsv_template, u8, tsv_template_size); 405 if (strlcpy((char*)tsv_template, buf, tsv_template_size) 406 >= tsv_template_size) 407 warning(1, (u8*)"strlcpy:%d: Overflow", 408 __LINE__); 409 } 410 else 411 { 412 if (strlen((char*)tsv_template) + buf_len 413 > tsv_template_size) 414 { 415 tsv_template_size += BUFSIZE; 416 REALLOC(tsv_template, u8, tsv_template_size); 417 } 418 if (strlcat((char*)tsv_template, buf, tsv_template_size) 419 > tsv_template_size) 420 warning(1, (u8*)"strlcat:%d: Overflow", 421 __LINE__); 422 } 423 } 424 else 425 vfprintf(output, fmt, args); 426 va_end(args); 427 return 0; 428 } 429 430 #define PIPE_READ_INDEX 0 431 #define PIPE_WRITE_INDEX 1 432 433 int 434 print_command(FILE* output, const char* command, const u8* pass_arguments[], 435 const u8* pipe_arguments[], const int strip_newlines) 436 { 437 if (!command || !pass_arguments) 438 exit(error(EINVAL, (u8*)"print_command: Invalid argument")); 439 440 pid_t pid = 0; 441 int pstatus = 0; 442 int arg_pipe_fds[2]; 443 int output_pipe_fds[2]; 444 445 fflush(output); 446 pipe(arg_pipe_fds); 447 pipe(output_pipe_fds); 448 pid = fork(); 449 if (pid == 0) 450 { 451 close(arg_pipe_fds[PIPE_WRITE_INDEX]); 452 close(output_pipe_fds[PIPE_READ_INDEX]); 453 454 dup2(arg_pipe_fds[PIPE_READ_INDEX], STDIN_FILENO); 455 dup2(output_pipe_fds[PIPE_WRITE_INDEX], STDOUT_FILENO); 456 457 execvp(command, (char* const*)pass_arguments); 458 exit(1); 459 } 460 else if (pid < 0) 461 exit(error(errno, (u8*)"print_command: fork failed")); 462 463 /* Parent */ 464 close(arg_pipe_fds[PIPE_READ_INDEX]); 465 close(output_pipe_fds[PIPE_WRITE_INDEX]); 466 FILE* cmd_input = fdopen(arg_pipe_fds[PIPE_WRITE_INDEX], "w"); 467 FILE* cmd_output = fdopen(output_pipe_fds[PIPE_READ_INDEX], "r"); 468 469 if (!cmd_input || !cmd_output) 470 { 471 perror("fdopen"); 472 exit(error(1, (u8*)"print_command: fdopen failed")); 473 } 474 475 if (pipe_arguments) 476 { 477 const u8** ppipe_argument = pipe_arguments; 478 while (ppipe_argument && *ppipe_argument) 479 { 480 fprintf(cmd_input, "%s\n", *ppipe_argument); 481 ppipe_argument++; 482 } 483 } 484 fclose(cmd_input); 485 486 u8* cmd_output_line = NULL; 487 CALLOC(cmd_output_line, u8, BUFSIZE); 488 while (!feof(cmd_output)) 489 { 490 char* eol = NULL; 491 492 if (!fgets((char*)cmd_output_line, BUFSIZE, cmd_output)) 493 continue; 494 495 eol = strchr((char*)cmd_output_line, '\n'); 496 if (eol) 497 *eol = 0; 498 499 print_output(output, "%s%s", cmd_output_line, 500 strip_newlines ? "" : "\n"); 501 } 502 if (!strip_newlines) 503 output_firstcol = 1; 504 free(cmd_output_line); 505 506 fclose(cmd_output); 507 508 kill(pid, SIGKILL); 509 pid_t wpid = waitpid(pid, &pstatus, 0); 510 if (wpid < 0) 511 warning(pstatus, 512 (u8*)"Child returned nonzero status, errno = %d", 513 errno); 514 if (WIFEXITED(pstatus)) 515 return WEXITSTATUS(pstatus); 516 517 return wpid; 518 } 519 520 int 521 read_file_into_buffer(FILE** input, u8** buffer, size_t* buffer_size, 522 const char* input_filename, char** input_dirname) 523 { 524 struct stat fs; 525 char* slash = NULL; 526 527 *input = fopen(input_filename, "r"); 528 if (!*input) 529 { 530 perror("fopen"); 531 return error(ENOENT, 532 (u8*)"read_file_into_buffer: fopen failed: %s", 533 input_filename); 534 } 535 536 fstat(fileno(*input), &fs); 537 if (S_ISDIR(fs.st_mode)) 538 return error(EISDIR, 539 (u8*)"read_file_into_buffer: Is a directory: %s", 540 input_filename); 541 542 free(*buffer); 543 *buffer_size = fs.st_size + 1; 544 CALLOC(*buffer, u8, *buffer_size); 545 fread((void*)*buffer, 1, *buffer_size, *input); 546 547 free(*input_dirname); 548 549 slash = strrchr(input_filename, '/'); 550 if (slash) 551 { 552 CALLOC(*input_dirname, char, strlen(input_filename) + 1); 553 char* pinput_dirname = *input_dirname; 554 const char* pinput_filename = input_filename; 555 while (pinput_filename && *pinput_filename 556 && pinput_filename != slash) 557 *pinput_dirname++ = *pinput_filename++; 558 } 559 else 560 { 561 CALLOC(*input_dirname, char, 2); 562 **input_dirname = '.'; 563 } 564 565 fclose(*input); 566 567 return 0; 568 } 569 570 int read_csv(FILE* output, const char* filename, csv_callback_t callback); 571 int read_tsv(FILE* output, const char* filename, tsv_callback_t callback); 572 573 int 574 process_heading_start(FILE* output, const UBYTE heading_level) 575 { 576 if (!output_firstcol) 577 print_output(output, "\n"); 578 for (size_t i = 0; i < heading_level; i++) 579 print_output(output, "#"); 580 print_output(output, " "); 581 output_firstcol = 0; 582 return 0; 583 } 584 585 int 586 process_heading(FILE* output, const u8* token) 587 { 588 if (!token || strlen((char*)token) < 1) 589 warning(1, (u8*)"Empty heading"); 590 591 print_output(output, "%s\n", token ? (char*)token : ""); 592 output_firstcol = 1; 593 594 return 0; 595 } 596 597 int 598 process_git_log(FILE* output) 599 { 600 if (!input_filename) 601 return warning(1, (u8*)"Cannot use 'git-log' in stdin"); 602 603 char* basename = NULL; 604 size_t basename_size = BUFSIZE; 605 CALLOC(basename, char, basename_size); 606 char* slash = strrchr(input_filename, '/'); 607 pid_t result = 0; 608 609 if (slash) 610 { 611 if (strlcpy(basename, slash + 1, basename_size) >= basename_size) 612 warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__); 613 } 614 else 615 { 616 if (strlcpy(basename, input_filename, basename_size) 617 >= basename_size) 618 warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__); 619 } 620 621 u8* pipe_args[] = {(u8*)basename, NULL}; 622 623 print_output(output, "Previous commit: "); 624 result = print_command(output, CMD_GIT_LOG, 625 (const u8**)CMD_GIT_LOG_ARGS, (const u8**)pipe_args, 0); 626 print_output(output, "\n"); 627 output_firstcol = 1; 628 629 free(basename); 630 631 return result ? warning(result, (u8*)"git-log: Cannot run git") : 0; 632 } 633 634 #define PRINTTSVIF(format, arg) \ 635 do \ 636 { \ 637 if ((ALL(tsv_state, ST_CS_COND_NONEMPTY) \ 638 && *tsv_register[conditional_index - 1]) \ 639 || (ALL(tsv_state, ST_CS_COND_EMPTY) \ 640 && !*tsv_register[conditional_index - 1]) \ 641 || !ANY(tsv_state, \ 642 ST_CS_COND_EMPTY | ST_CS_COND_NONEMPTY)) \ 643 { \ 644 fprintf(output, format, arg); \ 645 } \ 646 } while (0) 647 648 int 649 print_tsv_row(FILE* output, u8** tsv_header, u8** tsv_register) 650 { 651 u8* ptsv_template = tsv_template; 652 UBYTE tsv_state = ST_CS_NONE; 653 UBYTE num = 0; 654 UBYTE conditional_index = 0; 655 656 tsv_template_start: 657 if (!*ptsv_template) 658 goto tsv_template_done; 659 if (IN(tsv_state, ST_CS_ESCAPE)) 660 { 661 PRINTTSVIF("%c", *ptsv_template); 662 tsv_state &= ~ST_CS_ESCAPE; 663 ptsv_template++; 664 goto tsv_template_start; 665 } 666 667 switch (*ptsv_template) 668 { 669 case '\\': 670 tsv_state |= ST_CS_ESCAPE; 671 ptsv_template++; 672 break; 673 case '$': 674 if (IN(tsv_state, ST_CS_REGISTER)) 675 { 676 PRINTTSVIF("%c", '$'); 677 tsv_state &= ~ST_CS_REGISTER; 678 } 679 else 680 tsv_state |= ST_CS_REGISTER; 681 ptsv_template++; 682 break; 683 case '#': 684 if (IN(tsv_state, ST_CS_HEADER)) 685 { 686 error(1, 687 (u8*)"print_tsv_row: Invalid header register" 688 " mark"); 689 tsv_state &= ~(ST_CS_REGISTER | ST_CS_HEADER); 690 } 691 else if (IN(tsv_state, ST_CS_REGISTER)) 692 { 693 tsv_state &= ~ST_CS_REGISTER; 694 tsv_state |= ST_CS_HEADER; 695 } 696 else 697 PRINTTSVIF("%c", '#'); 698 ptsv_template++; 699 break; 700 case '?': 701 if (IN(tsv_state, ST_CS_COND)) 702 { 703 error(1, 704 (u8*)"print_tsv_row: Invalid conditional" 705 " mark"); 706 tsv_state &= ~ST_CS_COND; 707 } 708 else if (IN(tsv_state, ST_CS_REGISTER)) 709 { 710 tsv_state &= ~ST_CS_REGISTER; 711 tsv_state |= ST_CS_COND; 712 } 713 else 714 PRINTTSVIF("%c", '?'); 715 ptsv_template++; 716 break; 717 case '!': 718 if (ALL(tsv_state, ST_CS_COND | ST_CS_COND_NONEMPTY)) 719 { 720 tsv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY); 721 tsv_state |= ST_CS_COND_EMPTY; 722 } 723 else if (IN(tsv_state, ST_CS_COND)) 724 { 725 error(1, 726 (u8*)"print_tsv_row: Empty conditional" 727 " before/without nonempty conditional"); 728 tsv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY); 729 } 730 else 731 PRINTTSVIF("%c", '!'); 732 ptsv_template++; 733 break; 734 case '/': 735 if (ALL(tsv_state, ST_CS_COND | ST_CS_COND_EMPTY)) 736 tsv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY); 737 else if (ALL(tsv_state, ST_CS_COND)) 738 tsv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY); 739 else 740 PRINTTSVIF("%c", '/'); 741 ptsv_template++; 742 break; 743 case '1': 744 case '2': 745 case '3': 746 case '4': 747 case '5': 748 case '6': 749 case '7': 750 case '8': 751 case '9': 752 if (IN(tsv_state, ST_CS_COND)) 753 { 754 conditional_index = *ptsv_template - '0'; 755 tsv_state &= ~ST_CS_COND; 756 tsv_state |= ST_CS_COND_NONEMPTY; 757 } 758 else if (IN(tsv_state, ST_CS_HEADER)) 759 { 760 num = *ptsv_template - '0'; 761 PRINTTSVIF("%s", tsv_header[num - 1]); 762 tsv_state &= ~ST_CS_HEADER; 763 } 764 else if (IN(tsv_state, ST_CS_REGISTER)) 765 { 766 num = *ptsv_template - '0'; 767 PRINTTSVIF("%s", tsv_register[num - 1]); 768 tsv_state &= ~ST_CS_REGISTER; 769 } 770 else 771 PRINTTSVIF("%c", *ptsv_template); 772 ptsv_template++; 773 break; 774 default: 775 if (IN(tsv_state, ST_CS_HEADER)) 776 { 777 error(1, 778 (u8*)"print_tsv_row: Invalid header register" 779 " mark"); 780 tsv_state &= ~ST_CS_HEADER; 781 } 782 else if (IN(tsv_state, ST_CS_REGISTER)) 783 { 784 error(1, (u8*)"print_tsv_row: Invalid register mark"); 785 tsv_state &= ~ST_CS_REGISTER; 786 } 787 else 788 PRINTTSVIF("%c", *ptsv_template); 789 ptsv_template++; 790 } 791 goto tsv_template_start; 792 tsv_template_done: 793 return 0; 794 } 795 796 int 797 read_tsv(FILE* output, const char* filename, tsv_callback_t callback) 798 { 799 if (!callback) 800 exit(error(EINVAL, (u8*)"read_tsv: Invalid callback argument")); 801 802 FILE* tsv = NULL; 803 size_t tsv_lineno = 0; 804 u8* bufline = NULL; 805 u8* pbufline = NULL; 806 u8* token = NULL; 807 u8* ptoken = NULL; 808 size_t token_size = 0; 809 u8* tsv_header[MAX_TSV_REGISTERS]; 810 UBYTE current_header = 0; 811 u8* tsv_register[MAX_TSV_REGISTERS]; 812 UBYTE current_register = 0; 813 814 if (!(tsv = fopen(filename, "rt"))) 815 { 816 perror("fopen"); 817 exit(error(ENOENT, (u8*)"read_tsv: fopen failed: %s", filename)); 818 } 819 820 CALLOC(bufline, u8, BUFSIZE); 821 token_size = BUFSIZE; 822 CALLOC(token, u8, token_size); 823 for (UBYTE i = 0; i < MAX_TSV_REGISTERS; i++) 824 CALLOC(tsv_header[i], u8, BUFSIZE); 825 for (UBYTE i = 0; i < MAX_TSV_REGISTERS; i++) 826 CALLOC(tsv_register[i], u8, BUFSIZE); 827 828 while (!feof(tsv) && (!tsv_iter || tsv_lineno <= tsv_iter)) 829 { 830 u8* eol = NULL; 831 if (!fgets((char*)bufline, BUFSIZE, tsv)) 832 break; 833 eol = (u8*)strchr((char*)bufline, '\n'); 834 if (eol) 835 *eol = 0; 836 837 pbufline = bufline; 838 RESET_TOKEN(token, ptoken, token_size); 839 current_register = 0; 840 tsv_bufline_start: 841 if (!*pbufline) 842 goto tsv_bufline_done; 843 switch (*pbufline) 844 { 845 case '\t': 846 *ptoken = 0; 847 if (tsv_lineno > 0 848 && current_register < MAX_TSV_REGISTERS) 849 { 850 if (strlcpy((char*)tsv_register[current_register], 851 (char*)token, BUFSIZE) 852 >= BUFSIZE) 853 warning(1, (u8*)"strlcpy:%d: Overflow", 854 __LINE__); 855 current_register++; 856 } 857 else if (tsv_lineno <= 0 858 && current_header < MAX_TSV_REGISTERS) 859 { 860 if (strlcpy((char*)tsv_header[current_header], 861 (char*)token, BUFSIZE) 862 >= BUFSIZE) 863 warning(1, (u8*)"strlcpy:%d: Overflow", 864 __LINE__); 865 current_header++; 866 } 867 RESET_TOKEN(token, ptoken, token_size); 868 pbufline++; 869 break; 870 default: 871 CHECKCOPY(token, ptoken, token_size, pbufline); 872 } 873 goto tsv_bufline_start; 874 tsv_bufline_done: 875 *ptoken = 0; 876 if (tsv_lineno > 0) 877 { 878 if (current_register < MAX_TSV_REGISTERS) 879 { 880 if (strlcpy((char*)tsv_register[current_register], 881 (char*)token, BUFSIZE) 882 >= BUFSIZE) 883 warning(1, (u8*)"strlcpy:%d: Overflow", 884 __LINE__); 885 current_register++; 886 } 887 } 888 else 889 { 890 if (current_header < MAX_TSV_REGISTERS) 891 { 892 if (strlcpy((char*)tsv_header[current_header], 893 (char*)token, BUFSIZE) 894 >= BUFSIZE) 895 warning(1, (u8*)"strlcpy:%d: Overflow", 896 __LINE__); 897 current_header++; 898 } 899 } 900 RESET_TOKEN(token, ptoken, token_size); 901 902 if (tsv_lineno > 0 && pbufline != bufline) 903 (*callback)(output, tsv_header, tsv_register); 904 905 for (UBYTE i = 0; i < MAX_TSV_REGISTERS; i++) 906 *tsv_register[i] = 0; 907 tsv_lineno++; 908 } 909 fclose(tsv); 910 for (UBYTE i = MAX_TSV_REGISTERS; i > 0; i--) 911 free(tsv_register[i - 1]); 912 for (UBYTE i = MAX_TSV_REGISTERS; i > 0; i--) 913 free(tsv_header[i - 1]); 914 free(token); 915 free(bufline); 916 917 return 0; 918 } 919 920 int 921 process_tsv(FILE* output, const u8* arg_token, 922 const int read_yaml_macros_and_links, const int end_tag) 923 { 924 if (end_tag) 925 { 926 state &= ~ST_TSV_BODY; 927 928 if (read_yaml_macros_and_links) 929 return 0; 930 931 read_tsv(output, tsv_filename, &print_tsv_row); 932 933 free(tsv_filename); 934 tsv_filename = NULL; 935 free(tsv_template); 936 tsv_template = NULL; 937 tsv_template_size = 0; 938 } 939 else 940 { 941 if (ANY(state, ST_CSV_BODY | ST_TSV_BODY)) 942 exit(error(1, 943 (u8*)"process_tsv: Can't nest csv/tsv " 944 "directives")); 945 946 state |= ST_TSV_BODY; 947 948 if (read_yaml_macros_and_links) 949 return 0; 950 951 tsv_iter = 0; 952 u8* saveptr = NULL; 953 u8* args = NULL; 954 955 (void)strtok_r((char*)arg_token, " ", (char**)&saveptr); 956 args = (u8*)strtok_r(NULL, " ", (char**)&saveptr); 957 if (!args) 958 exit(error(EINVAL, 959 (u8*)"process_tsv: Arguments required")); 960 size_t args_len = strlen((char*)args); 961 if (*args != '"' || *(args + args_len - 1) != '"') 962 exit(error(EINVAL, 963 (u8*)"process_tsv: First argument must be a " 964 "string")); 965 if (!tsv_filename) 966 CALLOC(tsv_filename, char, BUFSIZE); 967 u8* args_base = (u8*)strdup((char*)args + 1); 968 *(args_base + strlen((char*)args_base) - 1) = 0; 969 snprintf(tsv_filename, BUFSIZE, "%s/%s.tsv", input_dirname, 970 (char*)args_base); 971 free(args_base); 972 args = (u8*)strtok_r(NULL, " ", (char**)&saveptr); 973 if (args) 974 { 975 errno = 0; 976 tsv_iter = strtol((char*)args, NULL, 10); 977 if (errno) 978 exit(error(errno, 979 (u8*)"process_tsv: Invalid argument " 980 "'%s'", 981 args)); 982 } 983 } 984 985 return 0; 986 } 987 988 #define PRINTCSVIF(format, arg) \ 989 do \ 990 { \ 991 if ((ALL(csv_state, ST_CS_COND_NONEMPTY) \ 992 && *csv_register[conditional_index - 1]) \ 993 || (ALL(csv_state, ST_CS_COND_EMPTY) \ 994 && !*csv_register[conditional_index - 1]) \ 995 || !ANY(csv_state, \ 996 ST_CS_COND_EMPTY | ST_CS_COND_NONEMPTY)) \ 997 { \ 998 fprintf(output, format, arg); \ 999 } \ 1000 } while (0) 1001 1002 int 1003 print_csv_row(FILE* output, u8** csv_header, u8** csv_register) 1004 { 1005 u8* pcsv_template = csv_template; 1006 UBYTE csv_state = ST_CS_NONE; 1007 UBYTE num = 0; 1008 UBYTE conditional_index = 0; 1009 1010 csv_template_start: 1011 if (!*pcsv_template) 1012 goto csv_template_done; 1013 if (IN(csv_state, ST_CS_ESCAPE)) 1014 { 1015 PRINTCSVIF("%c", *pcsv_template); 1016 csv_state &= ~ST_CS_ESCAPE; 1017 pcsv_template++; 1018 goto csv_template_start; 1019 } 1020 1021 switch (*pcsv_template) 1022 { 1023 case '\\': 1024 csv_state |= ST_CS_ESCAPE; 1025 pcsv_template++; 1026 break; 1027 case '$': 1028 if (IN(csv_state, ST_CS_REGISTER)) 1029 { 1030 PRINTCSVIF("%c", '$'); 1031 csv_state &= ~ST_CS_REGISTER; 1032 } 1033 else 1034 csv_state |= ST_CS_REGISTER; 1035 pcsv_template++; 1036 break; 1037 case '#': 1038 if (IN(csv_state, ST_CS_HEADER)) 1039 { 1040 error(1, (u8*)"print_csv_row: Invalid header register mark"); 1041 csv_state &= ~(ST_CS_REGISTER | ST_CS_HEADER); 1042 } 1043 else if (IN(csv_state, ST_CS_REGISTER)) 1044 { 1045 csv_state &= ~ST_CS_REGISTER; 1046 csv_state |= ST_CS_HEADER; 1047 } 1048 else 1049 PRINTCSVIF("%c", '#'); 1050 pcsv_template++; 1051 break; 1052 case '?': 1053 if (IN(csv_state, ST_CS_COND)) 1054 { 1055 error(1, (u8*)"print_csv_row: Invalid conditional mark"); 1056 csv_state &= ~ST_CS_COND; 1057 } 1058 else if (IN(csv_state, ST_CS_REGISTER)) 1059 { 1060 csv_state &= ~ST_CS_REGISTER; 1061 csv_state |= ST_CS_COND; 1062 } 1063 else 1064 PRINTCSVIF("%c", '?'); 1065 pcsv_template++; 1066 break; 1067 case '!': 1068 if (ALL(csv_state, ST_CS_COND | ST_CS_COND_NONEMPTY)) 1069 { 1070 csv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY); 1071 csv_state |= ST_CS_COND_EMPTY; 1072 } 1073 else if (IN(csv_state, ST_CS_COND)) 1074 { 1075 error(1, 1076 (u8*)"print_csv_row: Empty conditional " 1077 "before/without nonempty" 1078 " conditional"); 1079 csv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY); 1080 } 1081 else 1082 PRINTCSVIF("%c", '!'); 1083 pcsv_template++; 1084 break; 1085 case '/': 1086 if (ALL(csv_state, ST_CS_COND | ST_CS_COND_EMPTY)) 1087 csv_state &= ~(ST_CS_COND | ST_CS_COND_EMPTY); 1088 else if (ALL(csv_state, ST_CS_COND)) 1089 csv_state &= ~(ST_CS_COND | ST_CS_COND_NONEMPTY); 1090 else 1091 PRINTCSVIF("%c", '/'); 1092 pcsv_template++; 1093 break; 1094 case '1': 1095 case '2': 1096 case '3': 1097 case '4': 1098 case '5': 1099 case '6': 1100 case '7': 1101 case '8': 1102 case '9': 1103 if (IN(csv_state, ST_CS_COND)) 1104 { 1105 conditional_index = *pcsv_template - '0'; 1106 csv_state &= ~ST_CS_COND; 1107 csv_state |= ST_CS_COND_NONEMPTY; 1108 } 1109 else if (IN(csv_state, ST_CS_HEADER)) 1110 { 1111 num = *pcsv_template - '0'; 1112 PRINTCSVIF("%s", csv_header[num - 1]); 1113 csv_state &= ~ST_CS_HEADER; 1114 } 1115 else if (IN(csv_state, ST_CS_REGISTER)) 1116 { 1117 num = *pcsv_template - '0'; 1118 PRINTCSVIF("%s", csv_register[num - 1]); 1119 csv_state &= ~ST_CS_REGISTER; 1120 } 1121 else 1122 PRINTCSVIF("%c", *pcsv_template); 1123 pcsv_template++; 1124 break; 1125 default: 1126 if (IN(csv_state, ST_CS_HEADER)) 1127 { 1128 error(1, (u8*)"print_csv_row: Invalid header register mark"); 1129 csv_state &= ~ST_CS_HEADER; 1130 } 1131 else if (IN(csv_state, ST_CS_REGISTER)) 1132 { 1133 error(1, (u8*)"print_csv_row: Invalid register mark"); 1134 csv_state &= ~ST_CS_REGISTER; 1135 } 1136 else 1137 PRINTCSVIF("%c", *pcsv_template); 1138 pcsv_template++; 1139 } 1140 goto csv_template_start; 1141 csv_template_done: 1142 return 0; 1143 } 1144 1145 int 1146 read_csv(FILE* output, const char* filename, csv_callback_t callback) 1147 { 1148 if (!callback) 1149 exit(error(EINVAL, (u8*)"read_csv: Invalid callback argument")); 1150 1151 FILE* csv = NULL; 1152 size_t csv_lineno = 0; 1153 u8* bufline = NULL; 1154 u8* pbufline = NULL; 1155 u8* token = NULL; 1156 u8* ptoken = NULL; 1157 size_t token_size = 0; 1158 UBYTE csv_state = ST_CS_NONE; 1159 u8* csv_header[MAX_CSV_REGISTERS]; 1160 UBYTE current_header = 0; 1161 u8* csv_register[MAX_CSV_REGISTERS]; 1162 UBYTE current_register = 0; 1163 u8* csv_delimiter 1164 = get_value(vars, vars_count, (u8*)"csv-delimiter", NULL); 1165 1166 if (!(csv = fopen(filename, "rt"))) 1167 { 1168 perror("fopen"); 1169 exit(error(ENOENT, (u8*)"read_csv: fopen failed: %s", filename)); 1170 } 1171 1172 CALLOC(bufline, u8, BUFSIZE); 1173 token_size = BUFSIZE; 1174 CALLOC(token, u8, token_size); 1175 for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) 1176 CALLOC(csv_header[i], u8, BUFSIZE); 1177 for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) 1178 CALLOC(csv_register[i], u8, BUFSIZE); 1179 1180 while (!feof(csv) && (!csv_iter || csv_lineno <= csv_iter)) 1181 { 1182 u8* eol = NULL; 1183 if (!fgets((char*)bufline, BUFSIZE, csv)) 1184 break; 1185 eol = (u8*)strchr((char*)bufline, '\n'); 1186 if (eol) 1187 *eol = 0; 1188 1189 pbufline = bufline; 1190 RESET_TOKEN(token, ptoken, token_size); 1191 current_register = 0; 1192 csv_bufline_start: 1193 if (!*pbufline) 1194 goto csv_bufline_done; 1195 switch (*pbufline) 1196 { 1197 case '"': 1198 if (IN(csv_state, ST_CS_QUOTE)) 1199 csv_state &= ~ST_CS_QUOTE; 1200 else 1201 csv_state |= ST_CS_QUOTE; 1202 pbufline++; 1203 break; 1204 case ';': 1205 case ',': 1206 if (IN(csv_state, ST_CS_QUOTE)) 1207 CHECKCOPY(token, ptoken, token_size, pbufline); 1208 else 1209 { 1210 *ptoken = 0; 1211 if (csv_lineno > 0 1212 && current_register < MAX_CSV_REGISTERS) 1213 { 1214 if (strlcpy((char*)csv_register 1215 [current_register], 1216 (char*)token, BUFSIZE) 1217 >= BUFSIZE) 1218 warning(1, (u8*)"strlcpy:%d: Overflow", 1219 __LINE__); 1220 current_register++; 1221 } 1222 else if (csv_lineno <= 0 1223 && current_header < MAX_CSV_REGISTERS) 1224 { 1225 if (strlcpy((char*)csv_header 1226 [current_header], 1227 (char*)token, BUFSIZE) 1228 >= BUFSIZE) 1229 warning(1, (u8*)"strlcpy:%d: Overflow", 1230 __LINE__); 1231 current_header++; 1232 } 1233 RESET_TOKEN(token, ptoken, token_size); 1234 pbufline++; 1235 } 1236 break; 1237 default: 1238 if (IN(csv_state, ST_CS_QUOTE)) 1239 CHECKCOPY(token, ptoken, token_size, pbufline); 1240 else if (csv_delimiter && *pbufline == *csv_delimiter) 1241 { 1242 *ptoken = 0; 1243 if (csv_lineno > 0 1244 && current_register < MAX_CSV_REGISTERS) 1245 { 1246 if (strlcpy((char*)csv_register 1247 [current_register], 1248 (char*)token, BUFSIZE) 1249 >= BUFSIZE) 1250 warning(1, (u8*)"strlcpy:%d: Overflow", 1251 __LINE__); 1252 current_register++; 1253 } 1254 else if (csv_lineno <= 0 1255 && current_header < MAX_CSV_REGISTERS) 1256 { 1257 if (strlcpy((char*)csv_header 1258 [current_header], 1259 (char*)token, BUFSIZE) 1260 >= BUFSIZE) 1261 warning(1, (u8*)"strlcpy:%d: Overflow", 1262 __LINE__); 1263 current_header++; 1264 } 1265 RESET_TOKEN(token, ptoken, token_size); 1266 pbufline++; 1267 } 1268 else 1269 CHECKCOPY(token, ptoken, token_size, pbufline); 1270 } 1271 goto csv_bufline_start; 1272 csv_bufline_done: 1273 *ptoken = 0; 1274 if (csv_lineno > 0) 1275 { 1276 if (current_register < MAX_CSV_REGISTERS) 1277 { 1278 if (strlcpy((char*)csv_register[current_register], 1279 (char*)token, BUFSIZE) 1280 >= BUFSIZE) 1281 warning(1, (u8*)"strlcpy:%d: Overflow", 1282 __LINE__); 1283 current_register++; 1284 } 1285 } 1286 else 1287 { 1288 if (current_header < MAX_CSV_REGISTERS) 1289 { 1290 if (strlcpy((char*)csv_header[current_header], 1291 (char*)token, BUFSIZE) 1292 >= BUFSIZE) 1293 warning(1, (u8*)"strlcpy:%d: Overflow", 1294 __LINE__); 1295 current_header++; 1296 } 1297 } 1298 RESET_TOKEN(token, ptoken, token_size); 1299 1300 if (csv_lineno > 0 && pbufline != bufline) 1301 (*callback)(output, csv_header, csv_register); 1302 1303 for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) 1304 *csv_register[i] = 0; 1305 csv_lineno++; 1306 } 1307 fclose(csv); 1308 for (UBYTE i = MAX_CSV_REGISTERS; i > 0; i--) 1309 free(csv_register[i - 1]); 1310 for (UBYTE i = MAX_CSV_REGISTERS; i > 0; i--) 1311 free(csv_header[i - 1]); 1312 free(token); 1313 free(bufline); 1314 1315 return 0; 1316 } 1317 1318 int 1319 process_csv(FILE* output, const u8* arg_token, 1320 const int read_yaml_macros_and_links, const int end_tag) 1321 { 1322 if (end_tag) 1323 { 1324 state &= ~ST_CSV_BODY; 1325 1326 if (read_yaml_macros_and_links) 1327 return 0; 1328 1329 read_csv(output, csv_filename, &print_csv_row); 1330 1331 free(csv_filename); 1332 csv_filename = NULL; 1333 free(csv_template); 1334 csv_template = NULL; 1335 csv_template_size = 0; 1336 } 1337 else 1338 { 1339 if (ANY(state, ST_CSV_BODY | ST_TSV_BODY)) 1340 exit(error(1, 1341 (u8*)"read_csv: Can't nest csv/tsv" 1342 " directives")); 1343 1344 state |= ST_CSV_BODY; 1345 1346 if (read_yaml_macros_and_links) 1347 return 0; 1348 1349 csv_iter = 0; 1350 u8* saveptr = NULL; 1351 u8* args = NULL; 1352 1353 (void)strtok_r((char*)arg_token, " ", (char**)&saveptr); 1354 args = (u8*)strtok_r(NULL, " ", (char**)&saveptr); 1355 if (!args) 1356 exit(error(EINVAL, (u8*)"read_csv: Arguments required")); 1357 size_t args_len = strlen((char*)args); 1358 if (*args != '"' || *(args + args_len - 1) != '"') 1359 exit(error(EINVAL, 1360 (u8*)"read_csv: First argument must be a " 1361 "string")); 1362 if (!csv_filename) 1363 CALLOC(csv_filename, char, BUFSIZE); 1364 u8* args_base = (u8*)strdup((char*)args + 1); 1365 *(args_base + strlen((char*)args_base) - 1) = 0; 1366 snprintf(csv_filename, BUFSIZE, "%s/%s.csv", input_dirname, 1367 (char*)args_base); 1368 free(args_base); 1369 args = (u8*)strtok_r(NULL, " ", (char**)&saveptr); 1370 if (args) 1371 { 1372 errno = 0; 1373 csv_iter = strtol((char*)args, NULL, 10); 1374 if (errno) 1375 exit(error(errno, 1376 (u8*)"read_csv: Invalid argument '%s'", 1377 args)); 1378 } 1379 } 1380 1381 return 0; 1382 } 1383 1384 int 1385 process_include(FILE* output, const u8* token, 1386 const int read_yaml_macros_and_links) 1387 { 1388 u8* ptoken; 1389 char* include_filename = NULL; 1390 char* pinclude_filename = NULL; 1391 pid_t pid = 0; 1392 int pstatus = 0; 1393 int arg_pipe_fds[2]; 1394 int output_pipe_fds[2]; 1395 1396 ptoken = (u8*)strchr((char*)token, ' '); 1397 1398 if (!input_filename) 1399 return warning(1, (u8*)"Cannot use 'include' in stdin"); 1400 1401 if (!ptoken) 1402 exit(error(1, 1403 (u8*)"process_include: Directive 'include' requires" 1404 " an argument")); 1405 1406 fflush(output); 1407 pipe(arg_pipe_fds); 1408 pipe(output_pipe_fds); 1409 pid = fork(); 1410 1411 if (pid > 0) /* parent */ 1412 { 1413 FILE* child_output = NULL; 1414 u8* child_output_line = NULL; 1415 close(arg_pipe_fds[PIPE_READ_INDEX]); 1416 close(output_pipe_fds[PIPE_WRITE_INDEX]); 1417 if (!(child_output 1418 = fdopen(output_pipe_fds[PIPE_READ_INDEX], "r"))) 1419 { 1420 wait(&pstatus); 1421 perror("fdopen"); 1422 exit(error(1, (u8*)"process_include: fdopen failed")); 1423 } 1424 1425 CALLOC(child_output_line, u8, BUFSIZE); 1426 while (!feof(child_output)) 1427 { 1428 char* eol = NULL; 1429 1430 if (!fgets((char*)child_output_line, BUFSIZE, 1431 child_output)) 1432 continue; 1433 1434 eol = strchr((char*)child_output_line, '\n'); 1435 if (eol) 1436 *eol = 0; 1437 simple_parse_yaml_line(child_output_line, &vars, 1438 &vars_count, &pvars); 1439 } 1440 free(child_output_line); 1441 fclose(child_output); 1442 1443 wait(&pstatus); 1444 } 1445 else if (pid == 0) /* child */ 1446 { 1447 char* filename = NULL; 1448 FILE* input = NULL; 1449 FILE* output = stdout; 1450 u8* buffer = NULL; 1451 size_t buffer_size = 0; 1452 int result = 0; 1453 FILE* child_output = NULL; 1454 1455 close(arg_pipe_fds[PIPE_WRITE_INDEX]); 1456 close(output_pipe_fds[PIPE_READ_INDEX]); 1457 if (!(child_output 1458 = fdopen(output_pipe_fds[PIPE_WRITE_INDEX], "w"))) 1459 { 1460 perror("fdopen"); 1461 exit(error(1, (u8*)"process_include: fdopen failed")); 1462 } 1463 1464 CALLOC(include_filename, char, BUFSIZE); 1465 pinclude_filename = include_filename; 1466 ptoken++; 1467 while (ptoken && *ptoken) 1468 if (*ptoken != '"') 1469 *pinclude_filename++ = *ptoken++; 1470 else 1471 ptoken++; 1472 1473 if (!strcmp(basedir, ".")) 1474 set_basedir(&basedir, &basedir_size, input_dirname); 1475 1476 CALLOC(filename, char, BUFSIZE); 1477 snprintf(filename, BUFSIZE, "%s/%s.slw", basedir, 1478 include_filename); 1479 free(include_filename); 1480 1481 read_file_into_buffer(&input, &buffer, &buffer_size, filename, 1482 &input_dirname); 1483 1484 free_keyvalue(&links, links_count); 1485 free(links); 1486 links = NULL; 1487 links_count = 0; 1488 1489 free_keyvalue(¶links, paralinks_count); 1490 free(paralinks); 1491 paralinks = NULL; 1492 paralinks_count = 0; 1493 1494 free_keyvalue(&footnotes, footnote_count); 1495 free(footnotes); 1496 footnotes = NULL; 1497 footnote_count = 0; 1498 current_footnote = 0; 1499 1500 while (inline_footnote_count--) 1501 free(inline_footnotes[inline_footnote_count]); 1502 free(inline_footnotes); 1503 inline_footnotes = NULL; 1504 inline_footnote_count = 0; 1505 current_inline_footnote = 0; 1506 state = ST_NONE; 1507 1508 /* First pass: read YAML, macros and links */ 1509 result = slweb_parse(output, filename, buffer, 1); 1510 1511 if (result || read_yaml_macros_and_links) 1512 goto process_include_child_cleanup; 1513 1514 state = ST_NONE; 1515 current_footnote = 0; 1516 current_inline_footnote = 0; 1517 1518 /* Second pass: parse and output */ 1519 result = slweb_parse(output, filename, buffer, 0); 1520 1521 process_include_child_cleanup: 1522 fflush(output); 1523 slweb_cleanup(); 1524 free(filename); 1525 free(buffer); 1526 fclose(child_output); 1527 exit(result); 1528 } 1529 else 1530 exit(error(1, (u8*)"process_include: fork failed")); 1531 1532 return 0; 1533 } 1534 1535 int 1536 process_list_start(FILE* output) 1537 { 1538 return 0; 1539 } 1540 1541 int 1542 process_list_item_start(FILE* output) 1543 { 1544 print_output(output, "%s* ", output_firstcol ? "" : "\n"); 1545 state |= ST_PARA_OPEN; 1546 return 0; 1547 } 1548 1549 int process_paralinks(FILE* output, const u8* link_prefix); 1550 1551 int 1552 process_list_item_end(FILE* output, const u8* link_prefix) 1553 { 1554 if (IN(state, ST_PARA_OPEN)) 1555 { 1556 state &= ~ST_PARA_OPEN; 1557 print_output(output, "\n\n"); 1558 output_firstcol = 1; 1559 process_paralinks(output, link_prefix); 1560 } 1561 return 0; 1562 } 1563 1564 int 1565 process_list_end(FILE* output) 1566 { 1567 return 0; 1568 } 1569 1570 int 1571 process_numlist_start(FILE* output) 1572 { 1573 return 0; 1574 } 1575 1576 int 1577 process_numlist_end(FILE* output) 1578 { 1579 return 0; 1580 } 1581 1582 int 1583 filter_subdirs(const struct dirent* node) 1584 { 1585 if (!node 1586 || ((*node->d_name == '.') 1587 && (!*(node->d_name + 1) 1588 || ((*(node->d_name + 1) == '.') 1589 && !(*(node->d_name + 2)))))) 1590 return 0; 1591 1592 struct stat st; 1593 char* nodename = NULL; 1594 1595 CALLOC(nodename, char, BUFSIZE); 1596 snprintf(nodename, BUFSIZE, "%s/%s", incdir, node->d_name); 1597 1598 if (lstat(nodename, &st) < 0 || !S_ISDIR(st.st_mode)) 1599 { 1600 free(nodename); 1601 return 0; 1602 } 1603 1604 free(nodename); 1605 1606 return 1; 1607 } 1608 1609 int 1610 filter_slw(const struct dirent* node) 1611 { 1612 if (!node 1613 || ((*node->d_name == '.') 1614 && (!*(node->d_name + 1) 1615 || ((*(node->d_name + 1) == '.') 1616 && !(*(node->d_name + 2)))))) 1617 return 0; 1618 1619 size_t node_len = strlen(node->d_name); 1620 size_t slw_len = strlen(".slw"); 1621 1622 if (slw_len >= node_len) 1623 return 0; 1624 return !strcmp(node->d_name + strlen(node->d_name) - slw_len, ".slw"); 1625 } 1626 1627 int 1628 reverse_alphacompare(const struct dirent** a, const struct dirent** b) 1629 { 1630 if (!a || !b) 1631 return 0; 1632 1633 return -1 * strcmp((*a)->d_name, (*b)->d_name); 1634 } 1635 1636 int process_timestamp(FILE* output, const u8* link_prefix, const char* link, 1637 const u8* permalink_macro, const u8* date, const u8* title, 1638 const int ext_in_permalink); 1639 1640 int 1641 process_incdir_subdir(FILE* output, const char* subdirname, 1642 const u8* link_prefix, const int details_open, const u8* macro_body, 1643 const u8* footer_permalink_text, const char* permalink_url, 1644 const int ext_in_permalink, const int list_only) 1645 { 1646 struct dirent** namelist; 1647 struct dirent** pnamelist; 1648 long names_total = 0; 1649 long names_output; 1650 char* abs_subdirname = NULL; 1651 1652 CALLOC(abs_subdirname, char, BUFSIZE); 1653 snprintf(abs_subdirname, BUFSIZE, "%s/%s/%s", basedir, incdir, 1654 subdirname); 1655 1656 if ((names_total = scandir(abs_subdirname, &namelist, &filter_slw, 1657 &reverse_alphacompare)) 1658 < 0) 1659 { 1660 perror("scandir"); 1661 exit(error(errno, 1662 (u8*)"process_incdir_subdir: scandir '%s' failed", 1663 abs_subdirname)); 1664 } 1665 1666 pnamelist = namelist; 1667 names_output = 0; 1668 while (names_output < names_total) 1669 { 1670 int pstatus = 0; 1671 1672 fflush(output); 1673 pid_t pid = fork(); 1674 1675 if (pid > 0) 1676 wait(&pstatus); 1677 else if (pid == 0) 1678 { 1679 FILE* input = NULL; 1680 FILE* output = stdout; 1681 u8* buffer = NULL; 1682 size_t buffer_size = 0; 1683 int result = 0; 1684 ULLONG saved_state = ST_NONE; 1685 char* filename = NULL; 1686 char* abs_filename = NULL; 1687 char* link = NULL; 1688 u8* title = NULL; 1689 u8* date = NULL; 1690 1691 if (!strcmp(basedir, ".")) 1692 set_basedir(&basedir, &basedir_size, 1693 abs_subdirname); 1694 CALLOC(filename, char, BUFSIZE); 1695 CALLOC(abs_filename, char, BUFSIZE); 1696 CALLOC(link, char, BUFSIZE); 1697 snprintf(filename, BUFSIZE, "%s/%s", subdirname, 1698 (*pnamelist)->d_name); 1699 snprintf(abs_filename, BUFSIZE, "%s/%s", abs_subdirname, 1700 (*pnamelist)->d_name); 1701 link = strip_ext(filename); 1702 1703 read_file_into_buffer(&input, &buffer, &buffer_size, 1704 abs_filename, &input_dirname); 1705 1706 free_keyvalue(&links, links_count); 1707 free(links); 1708 links = NULL; 1709 links_count = 0; 1710 1711 free_keyvalue(¶links, paralinks_count); 1712 free(paralinks); 1713 paralinks = 0; 1714 paralinks_count = 0; 1715 1716 free_keyvalue(&footnotes, footnote_count); 1717 free(footnotes); 1718 footnotes = NULL; 1719 footnote_count = 0; 1720 current_footnote = 0; 1721 1722 while (inline_footnote_count--) 1723 free(inline_footnotes[inline_footnote_count]); 1724 free(inline_footnotes); 1725 inline_footnotes = NULL; 1726 inline_footnote_count = 0; 1727 current_inline_footnote = 0; 1728 1729 saved_state = state; 1730 state = ST_INCDIR; 1731 output_firstcol = 1; 1732 skip_para_end = 0; 1733 1734 /* First pass: read YAML, macros and links */ 1735 result = slweb_parse(output, abs_filename, buffer, 1); 1736 if (result) 1737 { 1738 warning(1, 1739 (u8*)"process_incdir_subdir:" 1740 " slweb_parse error"); 1741 goto incdir_subdir_child_cleanup; 1742 } 1743 1744 title = get_value(vars, vars_count, (u8*)"title", NULL); 1745 date = get_value(vars, vars_count, (u8*)"date", NULL); 1746 1747 process_timestamp(output, link_prefix, link, NULL, date, 1748 title, ext_in_permalink); 1749 1750 state = saved_state; 1751 1752 incdir_subdir_child_cleanup: 1753 fflush(output); 1754 slweb_cleanup(); 1755 free(link); 1756 free(abs_filename); 1757 free(filename); 1758 free(buffer); 1759 free(abs_subdirname); 1760 while (names_total--) 1761 free(namelist[names_total]); 1762 free(namelist); 1763 exit(result); 1764 } 1765 else 1766 exit(error(1, 1767 (u8*)"process_incdir_subdir: fork failed")); 1768 1769 /*if (!list_only) 1770 print_output(output, "</article>\n");*/ 1771 1772 pnamelist++; 1773 names_output++; 1774 } 1775 1776 while (names_total--) 1777 free(namelist[names_total]); 1778 free(namelist); 1779 free(abs_subdirname); 1780 1781 /*if (list_only) 1782 ; / *print_output(output, "</ul>\n");* / 1783 else 1784 print_output(output, "</div>\n</details>\n</li>\n");*/ 1785 1786 output_firstcol = 0; 1787 return 0; 1788 } 1789 1790 int 1791 process_incdir(FILE* output, const u8* token, const u8* link_prefix, 1792 const u8* footer_permalink_text, const char* permalink_url, 1793 const int ext_in_permalink, const int read_yaml_macros_and_links) 1794 { 1795 u8* saveptr = NULL; 1796 /* skipping the first token (incdir) */ 1797 u8* arg = NULL; 1798 size_t arg_len = 0; 1799 long num = 5; 1800 u8* macro_body = NULL; 1801 int names_total = 0; 1802 struct dirent** namelist; 1803 struct dirent** pnamelist; 1804 long names_output; 1805 int details_open = 1; 1806 int list_only = 0; 1807 int pass; 1808 u8* parg = NULL; 1809 1810 if (read_yaml_macros_and_links) 1811 return 0; 1812 1813 (void)strtok_r((char*)token, " ", (char**)&saveptr); 1814 arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr); 1815 if (!arg) 1816 exit(error(1, (u8*)"process_incdir: Arguments required")); 1817 1818 arg_len = strlen((char*)arg); 1819 1820 if (*arg != '"' || *(arg + arg_len - 1) != '"') 1821 exit(error(1, (u8*)"process_incdir: First argument not string")); 1822 1823 incdir = strdup((char*)(arg + 1)); 1824 *(incdir + strlen(incdir) - 1) = 0; /* trim ending " */ 1825 1826 pass = 0; 1827 1828 incdir_next_arg: 1829 arg = (u8*)strtok_r(NULL, " ", (char**)&saveptr); 1830 if (!arg) 1831 goto incdir_done_args; 1832 1833 parg = arg; 1834 if (*parg == '=') 1835 macro_body = get_value(macros, macros_count, arg + 1, NULL); 1836 else if (!strcmp((const char*)arg, "listonly")) 1837 list_only = 1; 1838 else 1839 { 1840 while (parg && *parg) 1841 { 1842 if (*parg < '0' || *parg > '9') 1843 exit(error(1, 1844 (u8*)"process_incdir: Invalid " 1845 "argument")); 1846 parg++; 1847 } 1848 errno = 0; 1849 num = strtol((char*)arg, NULL, 10); 1850 if (errno) 1851 exit(error(errno, 1852 (u8*)"process_incdir: Invalid parameter " 1853 "'num'")); 1854 } 1855 pass++; 1856 if (pass < 3) 1857 goto incdir_next_arg; 1858 1859 incdir_done_args: 1860 /*print_output(output, "<ul class=\"incdir\">\n")*/; 1861 1862 if ((names_total = scandir(incdir, &namelist, &filter_subdirs, 1863 &reverse_alphacompare)) 1864 < 0) 1865 { 1866 perror("scandir"); 1867 exit(error(errno, (u8*)"process_incdir: scandir '%s' failed", 1868 incdir)); 1869 } 1870 1871 pnamelist = namelist; 1872 names_output = 0; 1873 while (names_output < MIN(names_total, num)) 1874 { 1875 /*if (list_only) 1876 print_output(output, "<li>%s\n", 1877 (*pnamelist)->d_name);*/ 1878 process_incdir_subdir(output, (*pnamelist)->d_name, link_prefix, 1879 details_open, macro_body, footer_permalink_text, 1880 permalink_url, ext_in_permalink, list_only); 1881 details_open = 0; 1882 pnamelist++; 1883 names_output++; 1884 /*if (list_only) 1885 print_output(output, "</li>\n");*/ 1886 } 1887 /*print_output(output, "\n");*/ 1888 output_firstcol = 1; 1889 1890 while (names_total--) 1891 free(namelist[names_total]); 1892 free(namelist); 1893 free(incdir); 1894 1895 return 0; 1896 } 1897 1898 int url_is_local(const char* url); 1899 1900 u8* 1901 format_date(const u8* date_arg, const char* timestamp_format) 1902 { 1903 const char* ptimestamp_format = NULL; 1904 u8* day = NULL; 1905 u8* month = NULL; 1906 u8* year = NULL; 1907 u8* formatted_date = NULL; 1908 u8* ptr = NULL; 1909 u8* date = NULL; 1910 1911 date = (u8*)strdup((const char*)date_arg); 1912 if (!date) 1913 goto format_date_cleanup; 1914 1915 CALLOC(formatted_date, u8, DATEBUFSIZE); 1916 ptr = NULL; 1917 year = (u8*)strtok_r((char*)date, "-", (char**)&ptr); 1918 if (!year) 1919 goto format_date_cleanup; 1920 month = (u8*)strtok_r(NULL, "-", (char**)&ptr); 1921 if (!month) 1922 goto format_date_cleanup; 1923 day = (u8*)strtok_r(NULL, "T", (char**)&ptr); 1924 if (!day) 1925 goto format_date_cleanup; 1926 ptimestamp_format = timestamp_format; 1927 while (*ptimestamp_format) 1928 { 1929 if (*ptimestamp_format == 'd' || *ptimestamp_format == 'D') 1930 { 1931 if (strlcat((char*)formatted_date, (char*)day, 1932 DATEBUFSIZE) 1933 > DATEBUFSIZE) 1934 warning(1, (u8*)"strlcat:%d: Overflow", 1935 __LINE__); 1936 } 1937 else if (*ptimestamp_format == 'm' || *ptimestamp_format == 'M') 1938 { 1939 if (strlcat((char*)formatted_date, (char*)month, 1940 DATEBUFSIZE) 1941 > DATEBUFSIZE) 1942 warning(1, (u8*)"strlcat:%d: Overflow", 1943 __LINE__); 1944 } 1945 else if (*ptimestamp_format == 'y' || *ptimestamp_format == 'Y') 1946 { 1947 if (strlcat((char*)formatted_date, (char*)year, 1948 DATEBUFSIZE) 1949 > DATEBUFSIZE) 1950 warning(1, (u8*)"strlcat:%d: Overflow", 1951 __LINE__); 1952 } 1953 else 1954 *(formatted_date + strlen((char*)formatted_date)) 1955 = *ptimestamp_format; 1956 1957 ptimestamp_format++; 1958 } 1959 return formatted_date; 1960 1961 format_date_cleanup: 1962 free(formatted_date); 1963 free(date); 1964 1965 return NULL; 1966 } 1967 1968 int 1969 process_timestamp(FILE* output, const u8* link_prefix, const char* link, 1970 const u8* permalink_macro, const u8* date, const u8* title, 1971 const int ext_in_permalink) 1972 { 1973 char* in_filename = NULL; 1974 char* in_line = NULL; 1975 u8* formatted_date = NULL; 1976 1977 formatted_date = format_date(date, timestamp_format); 1978 if (!formatted_date) 1979 goto process_timestamp_cleanup; 1980 1981 print_output(output, "=> %s%s%s%s\t%s %s\n", 1982 url_is_local(link) && global_link_prefix 1983 ? (const char*)global_link_prefix 1984 : "", 1985 url_is_local(link) && link_prefix ? (const char*)link_prefix 1986 : "", 1987 link, ext_in_permalink ? timestamp_output_ext : "", 1988 formatted_date, title ? (const char*)title : ""); 1989 output_firstcol = 1; 1990 1991 process_timestamp_cleanup: 1992 free(formatted_date); 1993 free(in_line); 1994 free(in_filename); 1995 1996 return 0; 1997 } 1998 1999 int 2000 process_macro_def(FILE* output, const u8* token, 2001 const int read_yaml_macros_and_links, const int end_tag) 2002 { 2003 if (end_tag) 2004 { 2005 fflush(output); 2006 state &= ~ST_MACRO_BODY; 2007 return 0; 2008 } 2009 2010 if (IN(state, ST_MACRO_BODY)) 2011 exit(error(1, 2012 (u8*)"process_macro_def: Cannot nest definitions")); 2013 2014 if (read_yaml_macros_and_links) 2015 { 2016 macros_count++; 2017 2018 if (!macros) 2019 { 2020 CALLOC(macros, KeyValue, macros_count); 2021 pmacros = macros; 2022 } 2023 else 2024 { 2025 REALLOC(macros, KeyValue, 2026 macros_count * sizeof(KeyValue)); 2027 pmacros = macros + macros_count - 1; 2028 } 2029 CALLOC(pmacros->key, u8, KEYSIZE); 2030 if (strlcpy((char*)pmacros->key, (char*)token + 2, KEYSIZE) 2031 >= KEYSIZE) 2032 warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__); 2033 pmacros->value = NULL; 2034 pmacros->value_size = 0; 2035 } 2036 fflush(output); 2037 state |= ST_MACRO_BODY; 2038 2039 return 0; 2040 } 2041 2042 int 2043 process_macro(FILE* output, const u8* token, const int end_tag) 2044 { 2045 u8* macro_body; 2046 u8* eol; 2047 size_t macro_body_len; 2048 2049 macro_body = get_value(macros, macros_count, token + 1, NULL); 2050 if (!macro_body) 2051 exit(error(1, (u8*)"process_macro: Macro '%s' undefined", 2052 token + 1)); 2053 2054 eol = (u8*)strrchr((char*)macro_body, '\n'); 2055 macro_body_len = strlen((char*)macro_body); 2056 if (eol) 2057 output_firstcol = macro_body + macro_body_len 2058 - (u8*)strrchr((char*)macro_body, '\n') 2059 > 0; 2060 else 2061 output_firstcol = !*macro_body 2062 || *(macro_body + macro_body_len - 1) == '\n'; 2063 print_output(output, "%s", macro_body); 2064 2065 return 0; 2066 } 2067 2068 int process_para_end(FILE* output, const u8* link_prefix, 2069 const int previous_line_blank, const int read_yaml_macros_and_links); 2070 2071 int 2072 process_tag(FILE* output, const u8* token, const char* source_filename, 2073 const u8* link_prefix, const int previous_line_blank, 2074 const int first_line_in_doc, const u8* footer_permalink_text, 2075 const char* permalink_url, const int ext_in_permalink, 2076 const int read_yaml_macros_and_links, int* skip_eol, const int end_tag) 2077 { 2078 if (!token || strlen((char*)token) < 1) 2079 return warning(1, (u8*)"%s:%ld:%ld: Empty tag name", 2080 input_filename, lineno, colno); 2081 2082 if (!strcmp((char*)token, "git-log") 2083 && !read_yaml_macros_and_links) /* {git-log} */ 2084 { 2085 process_git_log(output); 2086 } 2087 else if (startswith((char*)token, "csv")) /* {csv} */ 2088 process_csv(output, token, read_yaml_macros_and_links, end_tag); 2089 else if (startswith((char*)token, "tsv")) /* {csv} */ 2090 process_tsv(output, token, read_yaml_macros_and_links, end_tag); 2091 else if (startswith((char*)token, "include")) /* {include} */ 2092 { 2093 process_include(output, token, read_yaml_macros_and_links); 2094 *skip_eol = 1; 2095 } 2096 else if (startswith((char*)token, "incdir")) /* {incdir} */ 2097 { 2098 process_incdir(output, token, link_prefix, 2099 footer_permalink_text, permalink_url, ext_in_permalink, 2100 read_yaml_macros_and_links); 2101 *skip_eol = 1; 2102 } 2103 else if (*token == '=') /* {=macro} */ 2104 { 2105 if (*(token + 1) == '!') 2106 process_macro_def(output, token, 2107 read_yaml_macros_and_links, end_tag); 2108 else if ((*(token + 1) != '!') && !read_yaml_macros_and_links) 2109 process_macro(output, token, end_tag); 2110 *skip_eol = 1; 2111 } 2112 else if (startswith((char*)token, "p") && !read_yaml_macros_and_links) 2113 { 2114 skip_para_end = 1; 2115 *skip_eol = 1; 2116 if (end_tag) 2117 { 2118 process_para_end(output, link_prefix, 2119 previous_line_blank, 2120 read_yaml_macros_and_links); 2121 output_firstcol = 1; 2122 } 2123 } 2124 2125 return 0; 2126 } 2127 2128 int 2129 process_bold(FILE* output, int end_tag) 2130 { 2131 return 0; 2132 } 2133 2134 int 2135 process_italic(FILE* output, int end_tag) 2136 { 2137 return 0; 2138 } 2139 2140 int 2141 process_code(FILE* output, int end_tag) 2142 { 2143 return 0; 2144 } 2145 2146 int 2147 process_blockquote(FILE* output, int end_tag) 2148 { 2149 if (end_tag) 2150 print_output(output, "\n\n"); 2151 else 2152 print_output(output, "%s>", output_firstcol ? "" : "\n"); 2153 return 0; 2154 } 2155 2156 int 2157 process_kbd(FILE* output, int end_tag) 2158 { 2159 return 0; 2160 } 2161 2162 int 2163 process_madeby(FILE* output) 2164 { 2165 print_output(output, 2166 "Generated by slw2gmi © %s Strahinya Radich.\n" 2167 "=> %s slw2gmi\n", 2168 COPYRIGHTYEAR, MADEBY_URL); 2169 2170 output_firstcol = 1; 2171 return 0; 2172 } 2173 2174 int 2175 process_strike(FILE* output, int end_tag) 2176 { 2177 print_output(output, "~"); 2178 return 0; 2179 } 2180 2181 int 2182 process_table_start(FILE* output) 2183 { 2184 return 0; 2185 } 2186 2187 int 2188 process_table_header_cell(FILE* output) 2189 { 2190 return 0; 2191 } 2192 2193 int 2194 process_table_header_end(FILE* output) 2195 { 2196 print_output(output, "\n"); 2197 return 0; 2198 } 2199 2200 int 2201 process_table_body_start(FILE* output, int start_row) 2202 { 2203 return 0; 2204 } 2205 2206 int 2207 process_table_body_row_start(FILE* output) 2208 { 2209 return 0; 2210 } 2211 2212 int 2213 process_table_body_cell(FILE* output) 2214 { 2215 return 0; 2216 } 2217 2218 int 2219 process_table_body_row_end(FILE* output) 2220 { 2221 print_output(output, "\n"); 2222 return 0; 2223 } 2224 2225 int 2226 process_table_end(FILE* output) 2227 { 2228 return 0; 2229 } 2230 2231 int 2232 url_is_local(const char* url) 2233 { 2234 return !(startswith(url, "http://") || startswith(url, "https://") 2235 || startswith(url, "gemini://") || startswith(url, "ftp://") 2236 || startswith(url, "ftps://") || startswith(url, "mailto://")); 2237 } 2238 2239 int 2240 process_inline_link(FILE* output, const u8* link_text) 2241 { 2242 print_output(output, "%s", link_text); 2243 return 0; 2244 } 2245 2246 int 2247 process_link(FILE* output, const u8* link_text) 2248 { 2249 return process_inline_link(output, link_text); 2250 } 2251 2252 int 2253 process_inline_image(FILE* output, const u8* image_text) 2254 { 2255 print_output(output, "%s ", image_text); 2256 return 0; 2257 } 2258 2259 int 2260 process_image(FILE* output, const u8* image_text) 2261 { 2262 return process_inline_image(output, image_text); 2263 } 2264 2265 int 2266 process_paralinks(FILE* output, const u8* link_prefix) 2267 { 2268 KeyValue* pparalink = paralinks; 2269 if (!skip_para_end) 2270 { 2271 /* 2272 *if (!output_firstcol && (paralinks_count > 0)) 2273 * print_output(output, "\n"); 2274 */ 2275 2276 /*print_output(output, "\n");*/ 2277 } 2278 // fprintf(stderr, "PL_count=%ld\n", paralinks_count); 2279 while (pparalink < paralinks + paralinks_count) 2280 { 2281 if (pparalink->key) 2282 { 2283 /*fprintf(stderr, "PL{%s=%s}\n", pparalink->key, 2284 pparalink->value);*/ 2285 if (!output_firstcol) 2286 print_output(output, "\n"); 2287 print_output(output, "=> %s%s%s\t%s\n", 2288 url_is_local((const char*)pparalink->key) 2289 && global_link_prefix 2290 ? (const char*)global_link_prefix 2291 : "", 2292 url_is_local((const char*)pparalink->key) 2293 && link_prefix 2294 ? (const char*)link_prefix 2295 : "", 2296 pparalink->key, pparalink->value); 2297 output_firstcol = 1; 2298 } 2299 pparalink++; 2300 } 2301 if (paralinks_count > 0) 2302 print_output(output, "\n"); 2303 2304 free_keyvalue(¶links, paralinks_count); 2305 free(paralinks); 2306 paralinks = NULL; 2307 paralinks_count = 0; 2308 pparalinks = paralinks; 2309 2310 return 0; 2311 } 2312 2313 int 2314 process_para_end(FILE* output, const u8* link_prefix, 2315 const int previous_line_blank, const int read_yaml_macros_and_links) 2316 { 2317 if (!read_yaml_macros_and_links && !skip_para_end 2318 && !previous_line_blank) 2319 print_output(output, "\n"); 2320 process_paralinks(output, link_prefix); 2321 2322 return 0; 2323 } 2324 2325 int 2326 process_line_start(FILE* output, const u8* line, const u8* link_prefix, 2327 const int first_line_in_doc, const int previous_line_blank, 2328 int* previous_line_block, const int read_yaml_macros_and_links, 2329 const int list_para, const int new_para_ok) 2330 { 2331 if ((first_line_in_doc || previous_line_blank || *previous_line_block) 2332 && !(ANY(state, ST_BLOCKQUOTE | ST_PRE))) 2333 { 2334 if (!list_para) 2335 { 2336 if (IN(state, ST_LIST)) 2337 { 2338 state &= ~ST_LIST; 2339 if (!read_yaml_macros_and_links) 2340 { 2341 process_list_item_end(output, 2342 link_prefix); 2343 process_list_end(output); 2344 } 2345 } 2346 2347 if (IN(state, ST_NUMLIST)) 2348 { 2349 state &= ~ST_NUMLIST; 2350 if (!read_yaml_macros_and_links) 2351 { 2352 process_list_item_end(output, 2353 link_prefix); 2354 process_numlist_end(output); 2355 } 2356 } 2357 2358 if (IN(state, ST_FOOTNOTE_TEXT)) 2359 { 2360 if (!read_yaml_macros_and_links 2361 && (IN(state, ST_PARA_OPEN))) 2362 { 2363 process_para_end(output, link_prefix, 2364 previous_line_blank, 2365 read_yaml_macros_and_links); 2366 output_firstcol = 1; 2367 } 2368 state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN); 2369 } 2370 } 2371 if (new_para_ok 2372 && !ANY(state, 2373 ST_TABLE | ST_TABLE_HEADER | ST_TABLE_LINE)) 2374 { 2375 if (!read_yaml_macros_and_links) 2376 { 2377 print_output(output, "%s", 2378 output_firstcol && previous_line_blank 2379 ? "" 2380 : "\n"); 2381 *previous_line_block = 0; 2382 } 2383 output_firstcol = 1; 2384 skip_para_end = 0; 2385 state |= ST_PARA_OPEN; 2386 } 2387 } 2388 return 0; 2389 } 2390 2391 int 2392 process_text_token(FILE* output, const u8* line, const u8* link_prefix, 2393 const int first_line_in_doc, const int previous_line_blank, 2394 int* previous_line_block, const int processed_start_of_line, 2395 const int read_yaml_macros_and_links, const int list_para, u8** token, 2396 u8** ptoken, size_t* token_size, const int add_enclosing_paragraph, 2397 const int new_para_ok) 2398 { 2399 if (!(IN(state, ST_YAML))) 2400 { 2401 if (add_enclosing_paragraph && !processed_start_of_line) 2402 process_line_start(output, line, link_prefix, 2403 first_line_in_doc, previous_line_blank, 2404 previous_line_block, read_yaml_macros_and_links, 2405 list_para, new_para_ok); 2406 **ptoken = 0; 2407 if (**token && !read_yaml_macros_and_links 2408 && !(IN(state, ST_MACRO_BODY))) 2409 { 2410 print_output(output, "%s", *token); 2411 u8* eol = (u8*)strrchr((char*)*token, '\n'); 2412 if (eol) 2413 output_firstcol 2414 = *token + strlen((char*)*token) - eol 2415 > 0; 2416 else 2417 output_firstcol = 0; 2418 } 2419 } 2420 RESET_TOKEN(*token, *ptoken, *token_size); 2421 return 0; 2422 } 2423 2424 int 2425 process_inline_footnote(const u8* token, const int read_yaml_macros_and_links, 2426 FILE* output) 2427 { 2428 current_inline_footnote++; 2429 2430 if (read_yaml_macros_and_links) 2431 { 2432 size_t token_len = strlen((char*)token); 2433 2434 inline_footnote_count++; 2435 if (inline_footnote_count == 1 && footnote_count > 0) 2436 warning(1, 2437 (u8*)"Both inline and regular footnotes " 2438 "present"); 2439 else if (!inline_footnotes) 2440 { 2441 inline_footnote_count = 1; 2442 CALLOC(inline_footnotes, u8*, inline_footnote_count); 2443 } 2444 else 2445 REALLOC(inline_footnotes, u8*, 2446 sizeof(u8*) * inline_footnote_count); 2447 CALLOC(inline_footnotes[inline_footnote_count - 1], u8, 2448 token_len + 1); 2449 2450 if (strlcpy((char*)inline_footnotes[inline_footnote_count - 1], 2451 (char*)token, token_len + 1) 2452 >= token_len + 1) 2453 warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__); 2454 *(inline_footnotes[inline_footnote_count - 1] + token_len) = 0; 2455 } 2456 else 2457 { 2458 print_output(output, "(%d)", current_inline_footnote); 2459 output_firstcol = 0; 2460 } 2461 2462 return 0; 2463 } 2464 2465 int 2466 process_footnote(FILE* output, const u8* token, const int footnote_definition, 2467 const int footnote_output) 2468 { 2469 current_footnote++; 2470 2471 if (footnote_definition) 2472 { 2473 footnote_count++; 2474 if (footnote_count == 1 && inline_footnote_count > 0) 2475 warning(1, 2476 (u8*)"Both inline and regular footnotes " 2477 "present"); 2478 else if (!footnotes) 2479 { 2480 CALLOC(footnotes, KeyValue, footnote_count); 2481 pfootnotes = footnotes; 2482 } 2483 else 2484 { 2485 REALLOCARRAY(footnotes, KeyValue, footnote_count); 2486 pfootnotes = footnotes + footnote_count - 1; 2487 } 2488 CALLOC(pfootnotes->key, u8, KEYSIZE); 2489 if (strlcpy((char*)pfootnotes->key, (char*)token, KEYSIZE) 2490 >= KEYSIZE) 2491 warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__); 2492 pfootnotes->value = NULL; 2493 pfootnotes->value_size = 0; 2494 } 2495 2496 if (footnote_output) 2497 { 2498 print_output(output, "(%d)", current_footnote); 2499 output_firstcol = 0; 2500 } 2501 2502 return 0; 2503 } 2504 2505 int 2506 process_horizontal_rule(FILE* output, const u8* link_prefix, 2507 const int previous_line_blank, const int read_yaml_macros_and_links) 2508 { 2509 /* Temporarily break paragraph as hr is para-level */ 2510 if (IN(state, ST_PARA_OPEN)) 2511 process_para_end(output, link_prefix, previous_line_blank, 2512 read_yaml_macros_and_links); 2513 print_output(output, "————————————————————\n"); 2514 if (IN(state, ST_PARA_OPEN)) 2515 print_output(output, "\n"); 2516 return 0; 2517 } 2518 2519 int 2520 process_formula(FILE* output, const u8* token, int display_formula) 2521 { 2522 return 0; 2523 } 2524 2525 int 2526 begin_article(FILE* output, const char* source_filename, const u8* link_prefix, 2527 const int add_article_header, const u8* author, const u8* title, 2528 const u8* header_text, const char* title_heading_level, u8* date, 2529 const int ext_in_permalink, int first_line_in_doc, 2530 int* previous_line_blank, const char* permalink_url) 2531 { 2532 if (title) 2533 { 2534 process_heading_start(output, 1); 2535 process_heading(output, title); 2536 } 2537 int save_incdir = IN(state, ST_INCDIR); 2538 state &= ~ST_INCDIR; 2539 2540 if (header_text) 2541 print_output(output, "%s\n\n", (char*)header_text); 2542 2543 if (author) 2544 print_output(output, "%s\n\n", author); 2545 2546 if (date && source_filename) 2547 { 2548 char* link = strip_ext((const char*)source_filename); 2549 2550 if (ext_in_permalink 2551 && strlcat(link, timestamp_output_ext, sizeof link) 2552 > sizeof link) 2553 warning(1, (u8*)"strlcat:%d: Overflow", __LINE__); 2554 2555 free(link); 2556 } 2557 2558 /*if (author || date || header_text || title) 2559 print_output(output, "\n");*/ 2560 2561 output_firstcol = author || date || header_text || title; 2562 *previous_line_blank = author || header_text || title; 2563 2564 if (save_incdir) 2565 state |= ST_INCDIR; 2566 2567 return 0; 2568 } 2569 2570 int 2571 end_footnotes(FILE* output, const u8* link_prefix, const int add_footnote_div, 2572 const int previous_line_blank, const int read_yaml_macros_and_links) 2573 { 2574 size_t footnote = 0; 2575 2576 if (IN(state, ST_PARA_OPEN)) 2577 { 2578 output_firstcol = 1; 2579 state &= ~ST_PARA_OPEN; 2580 if (paralinks_count > 0) 2581 process_paralinks(output, link_prefix); 2582 } 2583 2584 process_horizontal_rule(output, link_prefix, previous_line_blank, 2585 read_yaml_macros_and_links); 2586 output_firstcol = 1; 2587 2588 for (footnote = 0; footnote < inline_footnote_count; footnote++) 2589 { 2590 print_output(output, "%d. %s\n\n", footnote + 1, 2591 (char*)inline_footnotes[footnote]); 2592 output_firstcol = 1; 2593 } 2594 2595 KeyValue* pfootnote = footnotes; 2596 footnote = 0; 2597 while (pfootnote && footnote < footnote_count) 2598 { 2599 print_output(output, "%d. %s\n\n", footnote + 1, 2600 (char*)pfootnote->value); 2601 output_firstcol = 1; 2602 pfootnote++; 2603 footnote++; 2604 } 2605 2606 return 0; 2607 } 2608 2609 int 2610 simple_parse_yaml_line(const u8* line, KeyValue** vars, size_t* vars_count, 2611 KeyValue** pvars) 2612 { 2613 u8* token = NULL; 2614 u8* ptoken = NULL; 2615 u8* var_key = NULL; 2616 const u8* pline = line; 2617 int in_arg = 0; 2618 2619 if (!line || !vars || !vars_count || !pvars) 2620 exit(error(EINVAL, 2621 (u8*)"simple_parse_yaml_line: Invalid argument")); 2622 2623 CALLOC(token, u8, BUFSIZE); 2624 ptoken = token; 2625 *ptoken = 0; 2626 while (*pline) 2627 { 2628 if (!in_arg && *pline == ':') 2629 { 2630 in_arg = 1; 2631 *ptoken = 0; 2632 var_key = (u8*)strdup((char*)token); 2633 if (!var_key) 2634 exit(error(ENOMEM, 2635 (u8*)"simple_parse_yaml_line: strdup " 2636 "failed (out of " 2637 "memory?)")); 2638 ptoken = token; 2639 *ptoken = 0; 2640 pline++; 2641 } 2642 else if (*pline == ' ' || *pline == '\t') 2643 pline++; 2644 else 2645 *ptoken++ = *pline++; 2646 } 2647 if (*token) 2648 *ptoken = 0; 2649 if (var_key && *var_key && *token) 2650 { 2651 int found = 0; 2652 KeyValue* cvar = NULL; 2653 2654 cvar = *vars; 2655 while (!found && cvar != *vars + *vars_count) 2656 { 2657 if (!strcmp((const char*)cvar->key, (const char*)var_key) 2658 && cvar->value_size 2659 && !strcmp((const char*)cvar->value, 2660 (const char*)token)) 2661 found = 1; 2662 cvar++; 2663 } 2664 if (!found) 2665 { 2666 (*vars_count)++; 2667 if (!*vars) 2668 { 2669 CALLOC(*vars, KeyValue, *vars_count); 2670 *pvars = *vars; 2671 } 2672 else 2673 { 2674 REALLOCARRAY(*vars, KeyValue, *vars_count); 2675 *pvars = *vars + *vars_count - 1; 2676 } 2677 CALLOC((*pvars)->key, u8, KEYSIZE); 2678 if (strlcpy((char*)(*pvars)->key, (char*)var_key, 2679 KEYSIZE) 2680 >= KEYSIZE) 2681 warning(1, (u8*)"strlcpy:%d: Overflow", 2682 __LINE__); 2683 (*pvars)->value_size = strlen((char*)token) + 1; 2684 CALLOC((*pvars)->value, u8, (*pvars)->value_size); 2685 if (strlcpy((char*)(*pvars)->value, (char*)token, 2686 (*pvars)->value_size) 2687 >= (*pvars)->value_size) 2688 warning(1, (u8*)"strlcpy:%d: Overflow", 2689 __LINE__); 2690 } 2691 } 2692 free(var_key); 2693 free(token); 2694 return 0; 2695 } 2696 2697 int 2698 slweb_parse(FILE* output, const char* source_filename, const u8* buffer, 2699 const int read_yaml_macros_and_links) 2700 { 2701 u8* title = NULL; 2702 u8* title_heading_level = NULL; 2703 u8* header_text = NULL; 2704 u8* author = NULL; 2705 u8* date = NULL; 2706 u8* permalink_url = NULL; 2707 u8* ext_in_permalink = NULL; 2708 u8* var_add_article_header = NULL; 2709 /*u8* var_add_image_links = NULL; 2710 u8* var_add_figcaption = NULL;*/ 2711 u8* var_add_footnote_div = NULL; 2712 u8* footer_permalink_text = NULL; 2713 u8* var_incdir_only_summary = NULL; 2714 /*u8* image_file_prefix = NULL;*/ 2715 u8* link_prefix = NULL; 2716 const u8* pbuffer = NULL; 2717 u8* line = NULL; 2718 u8* pline = NULL; 2719 size_t line_len = 0; 2720 size_t line_size = 0; 2721 u8* token = NULL; 2722 u8* ptoken = NULL; 2723 size_t token_size = 0; 2724 u8* link_text = NULL; 2725 size_t link_size = 0; 2726 u8* link_macro = NULL; 2727 UBYTE heading_level = 0; 2728 int end_tag = 0; 2729 int first_line_in_doc = 1; 2730 int skip_change_first_line_in_doc = 0; 2731 int skip_eol = 0; 2732 int keep_token = 0; 2733 int previous_line_blank = 0; 2734 int previous_line_block = 0; 2735 int processed_start_of_line = 0; 2736 /*int add_image_links = 1; 2737 int add_figcaption = 1;*/ 2738 int add_footnote_div = 0; 2739 int list_para = 0; 2740 int footnote_at_line_start = 0; 2741 size_t pline_len = 0; 2742 int madeby_present = 0; 2743 u8* eol = NULL; 2744 2745 if (!buffer) 2746 exit(error(1, (u8*)"slweb_parse: Empty buffer")); 2747 2748 title = get_value(vars, vars_count, (u8*)"title", NULL); 2749 title_heading_level 2750 = get_value(vars, vars_count, (u8*)"title-heading-level", NULL); 2751 header_text = get_value(vars, vars_count, (u8*)"header-text", NULL); 2752 author = get_value(vars, vars_count, (u8*)"author", NULL); 2753 date = get_value(vars, vars_count, (u8*)"date", NULL); 2754 permalink_url = get_value(vars, vars_count, (u8*)"permalink-url", NULL); 2755 /*image_file_prefix 2756 = get_value(vars, vars_count, (u8*)"image-file-prefix", NULL);*/ 2757 ext_in_permalink 2758 = get_value(vars, vars_count, (u8*)"ext-in-permalink", NULL); 2759 var_add_article_header 2760 = get_value(vars, vars_count, (u8*)"add-article-header", NULL); 2761 /*var_add_image_links 2762 = get_value(vars, vars_count, (u8*)"add-image-links", NULL); 2763 add_image_links = !(var_add_image_links && *var_add_image_links == '0'); 2764 var_add_figcaption 2765 = get_value(vars, vars_count, (u8*)"add-figcaption", NULL); 2766 add_figcaption = !(var_add_figcaption && *var_add_figcaption == '0');*/ 2767 var_add_footnote_div 2768 = get_value(vars, vars_count, (u8*)"add-footnote-div", NULL); 2769 add_footnote_div = var_add_footnote_div && *var_add_footnote_div == '1'; 2770 link_prefix = get_value(vars, vars_count, (u8*)"link-prefix", NULL); 2771 footer_permalink_text = get_value(vars, vars_count, 2772 (u8*)"incdir-footer-permalink-text", NULL); 2773 var_incdir_only_summary 2774 = get_value(vars, vars_count, (u8*)"incdir-only-summary", NULL); 2775 incdir_only_summary 2776 = var_incdir_only_summary && *var_incdir_only_summary == '1'; 2777 2778 line_size = BUFSIZE; 2779 CALLOC(line, u8, line_size); 2780 token_size = BUFSIZE; 2781 CALLOC(token, u8, BUFSIZE); 2782 CALLOC(link_macro, u8, BUFSIZE); 2783 2784 pbuffer = buffer; 2785 pvars = vars; 2786 pmacros = macros; 2787 plinks = links; 2788 pparalinks = paralinks; 2789 pfootnotes = footnotes; 2790 lineno = 0; 2791 2792 if (!read_yaml_macros_and_links) 2793 begin_article(output, source_filename, link_prefix, 2794 var_add_article_header && *var_add_article_header == '1', 2795 author, title, header_text, (char*)title_heading_level, 2796 date, ext_in_permalink && *ext_in_permalink != '0', 2797 first_line_in_doc, &previous_line_blank, 2798 (char*)permalink_url); 2799 2800 RESET_TOKEN(token, ptoken, token_size); 2801 2802 output_firstcol = 1; 2803 2804 do_buffer: 2805 eol = (u8*)strchr((char*)pbuffer, '\n'); 2806 if (!eol) 2807 goto done_buffer; 2808 2809 pline = line; 2810 while (pbuffer != eol) 2811 CHECKCOPY(line, pline, line_size, pbuffer); 2812 2813 if (pbuffer == eol) 2814 pbuffer++; 2815 2816 *pline = 0; 2817 pline = line; 2818 line_len = strlen((char*)line); 2819 2820 lineno++; 2821 colno = 1; 2822 processed_start_of_line = 0; 2823 skip_eol = 0; 2824 list_para = 0; 2825 2826 do_line: 2827 if (!pline || !*pline) 2828 goto done_line; 2829 switch (*pline) 2830 { 2831 case '-': 2832 if (ANY(state, 2833 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 2834 | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL)) 2835 { 2836 CHECKCOPY(token, ptoken, token_size, pline); 2837 colno++; 2838 } 2839 else if (IN(state, ST_HTML_TAG)) 2840 { 2841 pline++; 2842 colno++; 2843 break; 2844 } 2845 else if (colno == 1 && strlen((char*)pline) > 2 2846 && startswith((char*)pline, "---")) 2847 { 2848 skip_eol = 1; 2849 2850 if (!(IN(state, ST_YAML)) && (lineno > 1) 2851 && !read_yaml_macros_and_links) 2852 { 2853 process_horizontal_rule(output, link_prefix, 2854 previous_line_blank, 2855 read_yaml_macros_and_links); 2856 output_firstcol = 1; 2857 } 2858 else 2859 { 2860 if (lineno == 1) 2861 state |= ST_YAML; 2862 else 2863 state &= ~ST_YAML; 2864 2865 skip_change_first_line_in_doc = 1; 2866 } 2867 pline = NULL; 2868 } 2869 else if (colno == 1 && *(pline + 1) == ' ') 2870 { 2871 if (IN(state, ST_NUMLIST)) 2872 { 2873 state &= ~ST_NUMLIST; 2874 if (!read_yaml_macros_and_links) 2875 { 2876 process_list_item_end(output, 2877 link_prefix); 2878 process_numlist_end(output); 2879 } 2880 } 2881 if (!read_yaml_macros_and_links) 2882 { 2883 if (!(IN(state, ST_LIST))) 2884 process_list_start(output); 2885 else 2886 process_list_item_end(output, 2887 link_prefix); 2888 process_list_item_start(output); 2889 } 2890 2891 processed_start_of_line = 1; 2892 skip_eol = 0; 2893 2894 state |= ST_LIST; 2895 2896 pline += 2; 2897 colno += 2; 2898 } 2899 else if (IN(state, ST_SUMMARY) && *(pline + 1) == '/') 2900 { 2901 /* Output existing text up to -/ */ 2902 process_text_token(output, line, link_prefix, 2903 first_line_in_doc, previous_line_blank, 2904 &previous_line_block, processed_start_of_line, 2905 read_yaml_macros_and_links, list_para, &token, 2906 &ptoken, &token_size, 1, 1); 2907 processed_start_of_line = 1; 2908 2909 state &= ~ST_SUMMARY; 2910 2911 pline += 2; 2912 colno += 2; 2913 } 2914 else 2915 { 2916 CHECKCOPY(token, ptoken, token_size, pline); 2917 colno++; 2918 } 2919 break; 2920 2921 case ':': 2922 if (IN(state, ST_YAML) 2923 && !(ANY(state, 2924 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 2925 | ST_HEADING | ST_IMAGE | ST_MACRO_BODY 2926 | ST_PRE | ST_TAG | ST_YAML_VAL)) 2927 && read_yaml_macros_and_links) 2928 { 2929 *ptoken = 0; 2930 2931 vars_count++; 2932 2933 if (!vars) 2934 { 2935 CALLOC(vars, KeyValue, vars_count); 2936 pvars = vars; 2937 } 2938 else 2939 { 2940 REALLOCARRAY(vars, KeyValue, vars_count); 2941 pvars = vars + vars_count - 1; 2942 } 2943 CALLOC(pvars->key, u8, KEYSIZE); 2944 if (strlcpy((char*)pvars->key, (char*)token, KEYSIZE) 2945 >= KEYSIZE) 2946 warning(1, (u8*)"strlcpy:%d: Overflow", 2947 __LINE__); 2948 pvars->value = NULL; 2949 pvars->value_size = 0; 2950 2951 state |= ST_YAML_VAL; 2952 RESET_TOKEN(token, ptoken, token_size); 2953 pline++; 2954 colno++; 2955 2956 while (pline && (*pline == ' ' || *pline == '\t')) 2957 { 2958 pline++; 2959 colno++; 2960 } 2961 } 2962 else if (IN(state, ST_HTML_TAG)) 2963 { 2964 pline++; 2965 colno++; 2966 break; 2967 } 2968 else 2969 { 2970 CHECKCOPY(token, ptoken, token_size, pline); 2971 colno++; 2972 } 2973 break; 2974 2975 case '~': 2976 if (ANY(state, 2977 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 2978 | ST_HTML_TAG | ST_IMAGE | ST_KBD 2979 | ST_LINK_SECOND_ARG 2980 | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY 2981 | ST_PRE | ST_TAG | ST_YAML)) 2982 { 2983 CHECKCOPY(token, ptoken, token_size, pline); 2984 colno++; 2985 break; 2986 } 2987 2988 if (strlen((char*)pline) > 1 && *(pline + 1) == '~') 2989 { 2990 u8* entity = (u8*)" "; 2991 /* Handle ~~ within footnotes, headings 2992 * and link text specially */ 2993 if (ANY(state, 2994 ST_INLINE_FOOTNOTE | ST_HEADING 2995 | ST_FOOTNOTE_TEXT | ST_LINK)) 2996 { 2997 size_t entity_len = strlen((char*)entity); 2998 size_t token_len = 0; 2999 3000 *ptoken = 0; 3001 token_len = strlen((char*)token); 3002 3003 if (token_len + entity_len < BUFSIZE) 3004 { 3005 if (strlcat((char*)token, (char*)entity, 3006 token_size) 3007 > token_size) 3008 warning(1, (u8*)"strlcat:%d: Overflow", 3009 __LINE__); 3010 ptoken += entity_len; 3011 } 3012 } 3013 else 3014 { 3015 /* Output existing text up to ~~ */ 3016 process_text_token(output, line, link_prefix, 3017 first_line_in_doc, previous_line_blank, 3018 &previous_line_block, 3019 processed_start_of_line, 3020 read_yaml_macros_and_links, list_para, 3021 &token, &ptoken, &token_size, 1, 1); 3022 processed_start_of_line = 1; 3023 3024 if (!read_yaml_macros_and_links 3025 && !(ANY(state, 3026 ST_PRE | ST_CODE | ST_HEADING))) 3027 print_output(output, " "); 3028 } 3029 3030 pline += 2; 3031 colno += 2; 3032 } 3033 else if (strlen((char*)pline) > 1 && *(pline + 1) == '-') 3034 { 3035 u8* entity = (u8*)"-"; 3036 /* Handle ~- within footnotes, headings 3037 * and link text specially */ 3038 if (ANY(state, 3039 ST_INLINE_FOOTNOTE | ST_HEADING 3040 | ST_FOOTNOTE_TEXT | ST_LINK)) 3041 { 3042 size_t entity_len = strlen((char*)entity); 3043 size_t token_len = 0; 3044 3045 *ptoken = 0; 3046 token_len = strlen((char*)token); 3047 3048 if (token_len + entity_len < BUFSIZE) 3049 { 3050 if (strlcat((char*)token, (char*)entity, 3051 token_size) 3052 > token_size) 3053 warning(1, (u8*)"strlcat:%d: Overflow", 3054 __LINE__); 3055 ptoken += entity_len; 3056 } 3057 } 3058 else 3059 { 3060 /* Output existing text up to ~- */ 3061 process_text_token(output, line, link_prefix, 3062 first_line_in_doc, previous_line_blank, 3063 &previous_line_block, 3064 processed_start_of_line, 3065 read_yaml_macros_and_links, list_para, 3066 &token, &ptoken, &token_size, 1, 1); 3067 processed_start_of_line = 1; 3068 3069 if (!read_yaml_macros_and_links 3070 && !(ANY(state, 3071 ST_PRE | ST_CODE | ST_HEADING))) 3072 print_output(output, "-"); 3073 } 3074 3075 pline += 2; 3076 colno += 2; 3077 } 3078 else 3079 { 3080 /* Handle ~ within footnotes, headings 3081 * and link text specially */ 3082 if (ANY(state, 3083 ST_INLINE_FOOTNOTE | ST_HEADING 3084 | ST_FOOTNOTE_TEXT | ST_LINK)) 3085 { 3086 u8* tag = IN(state, ST_STRIKE) ? (u8*)"</s>" 3087 : (u8*)"<s>"; 3088 size_t tag_len = strlen((char*)tag); 3089 size_t token_len = 0; 3090 3091 *ptoken = 0; 3092 token_len = strlen((char*)token); 3093 3094 if (token_len + tag_len < BUFSIZE) 3095 { 3096 if (strlcat((char*)token, (char*)tag, 3097 token_size) 3098 > token_size) 3099 warning(1, (u8*)"strlcat:%d: Overflow", 3100 __LINE__); 3101 ptoken += tag_len; 3102 } 3103 } 3104 else 3105 { 3106 /* Output existing text up to ~ */ 3107 process_text_token(output, line, link_prefix, 3108 first_line_in_doc, previous_line_blank, 3109 &previous_line_block, 3110 processed_start_of_line, 3111 read_yaml_macros_and_links, list_para, 3112 &token, &ptoken, &token_size, 1, 1); 3113 processed_start_of_line = 1; 3114 3115 if (!read_yaml_macros_and_links 3116 && !(ANY(state, 3117 ST_PRE | ST_CODE | ST_HEADING))) 3118 process_strike(output, 3119 (IN(state, ST_STRIKE))); 3120 } 3121 3122 state ^= ST_STRIKE; 3123 pline++; 3124 colno++; 3125 } 3126 break; 3127 3128 case '`': 3129 if (ANY(state, 3130 ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE 3131 | ST_MACRO_BODY)) 3132 { 3133 CHECKCOPY(token, ptoken, token_size, pline); 3134 colno++; 3135 break; 3136 } 3137 else if (IN(state, ST_HTML_TAG)) 3138 { 3139 pline++; 3140 colno++; 3141 break; 3142 } 3143 3144 if (colno == 1 && strlen((char*)pline) > 2 3145 && startswith((char*)pline, "```")) 3146 { 3147 state ^= ST_PRE; 3148 3149 if (!read_yaml_macros_and_links) 3150 { 3151 print_output(output, "%s```%s", 3152 IN(state, ST_PRE) && !output_firstcol 3153 ? "\n" 3154 : "", 3155 IN(state, ST_PRE) ? "" : "\n\n"); 3156 output_firstcol = !IN(state, ST_PRE); 3157 } 3158 3159 /* Skip the rest of the line (language) */ 3160 pline = NULL; 3161 } 3162 else if (!ANY(state, ST_PRE | ST_TAG | ST_YAML)) 3163 { 3164 if (!ANY(state, 3165 ST_INLINE_FOOTNOTE | ST_HEADING 3166 | ST_FOOTNOTE_TEXT | ST_LINK)) 3167 { 3168 /* Output existing text up to ` */ 3169 process_text_token(output, line, link_prefix, 3170 first_line_in_doc, previous_line_blank, 3171 &previous_line_block, 3172 processed_start_of_line, 3173 read_yaml_macros_and_links, list_para, 3174 &token, &ptoken, &token_size, 1, 1); 3175 processed_start_of_line = 1; 3176 } 3177 3178 state ^= ST_CODE; 3179 3180 pline++; 3181 colno++; 3182 } 3183 else 3184 { 3185 CHECKCOPY(token, ptoken, token_size, pline); 3186 colno++; 3187 } 3188 break; 3189 3190 case '&': 3191 if (ANY(state, 3192 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 3193 | ST_HTML_TAG | ST_IMAGE | ST_KBD 3194 | ST_LINK_SECOND_ARG | ST_LINK_SECOND_ARG_END 3195 | ST_MACRO_BODY 3196 /*| ST_PRE*/ 3197 | ST_TAG | ST_YAML)) 3198 { 3199 CHECKCOPY(token, ptoken, token_size, pline); 3200 colno++; 3201 } 3202 else if (!IN(state, ST_ENT) && *(pline + 1) 3203 && isalpha(*(pline + 1)) && isascii(*(pline + 1))) 3204 { 3205 state |= ST_ENT; 3206 pline++; 3207 colno++; 3208 } 3209 else 3210 { 3211 CHECKCOPY(token, ptoken, token_size, pline); 3212 colno++; 3213 } 3214 break; 3215 3216 case ';': 3217 if (IN(state, ST_ENT)) 3218 { 3219 state &= ~ST_ENT; 3220 *ptoken = 0; 3221 if (!read_yaml_macros_and_links) 3222 { 3223 /* TODO: Use map */ 3224 if (endswith((char*)token, "amp")) 3225 { 3226 token[strlen((char*)token) 3227 - strlen("amp")] 3228 = 0; 3229 strcat((char*)token, "&"); 3230 } 3231 else if (endswith((char*)token, "lt")) 3232 { 3233 token[strlen((char*)token) - strlen("lt")] 3234 = 0; 3235 strcat((char*)token, "<"); 3236 } 3237 else if (endswith((char*)token, "gt")) 3238 { 3239 token[strlen((char*)token) - strlen("gt")] 3240 = 0; 3241 strcat((char*)token, ">"); 3242 } 3243 else if (endswith((char*)token, "quot")) 3244 { 3245 token[strlen((char*)token) 3246 - strlen("quot")] 3247 = 0; 3248 strcat((char*)token, "\""); 3249 } 3250 else if (endswith((char*)token, "ldquo")) 3251 { 3252 token[strlen((char*)token) 3253 - strlen("ldquo")] 3254 = 0; 3255 strcat((char*)token, "“"); 3256 } 3257 else if (endswith((char*)token, "rdquo")) 3258 { 3259 token[strlen((char*)token) 3260 - strlen("rdquo")] 3261 = 0; 3262 strcat((char*)token, "”"); 3263 } 3264 } 3265 ptoken = token + strlen((char*)token); 3266 pline++; 3267 colno++; 3268 } 3269 else 3270 { 3271 CHECKCOPY(token, ptoken, token_size, pline); 3272 colno++; 3273 } 3274 break; 3275 3276 case '#': 3277 if (IN(state, ST_HTML_TAG)) 3278 { 3279 pline++; 3280 colno++; 3281 break; 3282 } 3283 if (colno == 1 3284 && !(ANY(state, 3285 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 3286 | ST_MACRO_BODY | ST_PRE | ST_YAML))) 3287 { 3288 if (IN(state, ST_LIST)) 3289 { 3290 state &= ~ST_LIST; 3291 if (!read_yaml_macros_and_links) 3292 { 3293 process_list_item_end(output, 3294 link_prefix); 3295 process_list_end(output); 3296 } 3297 } 3298 3299 if (IN(state, ST_NUMLIST)) 3300 { 3301 state &= ~ST_NUMLIST; 3302 if (!read_yaml_macros_and_links) 3303 { 3304 process_list_item_end(output, 3305 link_prefix); 3306 process_numlist_end(output); 3307 } 3308 } 3309 3310 state |= ST_HEADING; 3311 heading_level = 1; 3312 pline++; 3313 colno++; 3314 } 3315 else if (IN(state, ST_HEADING) && *(pline - 1) == '#') 3316 { 3317 if (heading_level < MAX_HEADING_LEVEL) 3318 heading_level++; 3319 pline++; 3320 colno++; 3321 } 3322 else 3323 { 3324 CHECKCOPY(token, ptoken, token_size, pline); 3325 colno++; 3326 } 3327 break; 3328 3329 case '_': 3330 if (ANY(state, 3331 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 3332 | ST_HTML_TAG | ST_IMAGE | ST_LINK_SECOND_ARG 3333 | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY 3334 | ST_PRE | ST_TAG | ST_YAML)) 3335 { 3336 CHECKCOPY(token, ptoken, token_size, pline); 3337 colno++; 3338 break; 3339 } 3340 else if (IN(state, ST_HTML_TAG)) 3341 { 3342 pline++; 3343 colno++; 3344 break; 3345 } 3346 3347 if (strlen((char*)pline) > 1 && *(pline + 1) == '_') 3348 { 3349 /* Handle __ within footnotes, headings 3350 * and link text specially */ 3351 if (!ANY(state, 3352 ST_INLINE_FOOTNOTE | ST_HEADING 3353 | ST_FOOTNOTE_TEXT | ST_LINK)) 3354 { 3355 /* Output existing text up to __ */ 3356 process_text_token(output, line, link_prefix, 3357 first_line_in_doc, previous_line_blank, 3358 &previous_line_block, 3359 processed_start_of_line, 3360 read_yaml_macros_and_links, list_para, 3361 &token, &ptoken, &token_size, 1, 1); 3362 processed_start_of_line = 1; 3363 3364 if (!read_yaml_macros_and_links 3365 && !(ANY(state, 3366 ST_PRE | ST_CODE | ST_HEADING))) 3367 process_bold(output, IN(state, ST_BOLD)); 3368 } 3369 3370 state ^= ST_BOLD; 3371 pline += 2; 3372 colno += 2; 3373 } 3374 else 3375 { 3376 /* Handle _ within footnotes, headings 3377 * and link text specially */ 3378 if (!ANY(state, 3379 ST_INLINE_FOOTNOTE | ST_HEADING 3380 | ST_FOOTNOTE_TEXT | ST_LINK)) 3381 { 3382 /* Output existing text up to _ */ 3383 process_text_token(output, line, link_prefix, 3384 first_line_in_doc, previous_line_blank, 3385 &previous_line_block, 3386 processed_start_of_line, 3387 read_yaml_macros_and_links, list_para, 3388 &token, &ptoken, &token_size, 1, 1); 3389 processed_start_of_line = 1; 3390 3391 if (!read_yaml_macros_and_links 3392 && !(ANY(state, 3393 ST_PRE | ST_CODE | ST_HEADING))) 3394 process_italic(output, 3395 IN(state, ST_ITALIC)); 3396 } 3397 3398 state ^= ST_ITALIC; 3399 pline++; 3400 colno++; 3401 } 3402 break; 3403 3404 case 'o': 3405 if (ANY(state, 3406 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 3407 | ST_HTML_TAG | ST_IMAGE | ST_MACRO_BODY 3408 | ST_PRE | ST_YAML)) 3409 { 3410 CHECKCOPY(token, ptoken, token_size, pline); 3411 colno++; 3412 break; 3413 } 3414 else if (IN(state, ST_HTML_TAG)) 3415 { 3416 pline++; 3417 colno++; 3418 break; 3419 } 3420 3421 if (colno == 1 && strlen((char*)pline) > 1 3422 && *(pline + 1) == ' ') 3423 { 3424 if (IN(state, ST_NUMLIST)) 3425 { 3426 state &= ~ST_NUMLIST; 3427 if (!read_yaml_macros_and_links) 3428 { 3429 process_list_item_end(output, 3430 link_prefix); 3431 process_numlist_end(output); 3432 } 3433 } 3434 if (!read_yaml_macros_and_links) 3435 { 3436 if (!(IN(state, ST_LIST))) 3437 process_list_start(output); 3438 else 3439 process_list_item_end(output, 3440 link_prefix); 3441 process_list_item_start(output); 3442 } 3443 3444 processed_start_of_line = 1; 3445 skip_eol = 0; 3446 3447 state |= ST_LIST; 3448 3449 pline += 2; 3450 colno += 2; 3451 } 3452 else 3453 { 3454 CHECKCOPY(token, ptoken, token_size, pline); 3455 colno++; 3456 } 3457 break; 3458 3459 case '*': 3460 if (ANY(state, 3461 ST_CODE | ST_DISPLAY_FORMULA 3462 | ST_FORMULA 3463 /*| ST_HTML_TAG */ 3464 | ST_IMAGE | ST_MACRO_BODY | ST_PRE 3465 | ST_YAML)) 3466 { 3467 CHECKCOPY(token, ptoken, token_size, pline); 3468 colno++; 3469 break; 3470 } 3471 else if (IN(state, ST_HTML_TAG)) 3472 { 3473 pline++; 3474 colno++; 3475 break; 3476 } 3477 3478 pline_len = strlen((char*)pline); 3479 if (colno == 1 && pline_len > 1 && *(pline + 1) == '[') 3480 { 3481 process_line_start(output, line, link_prefix, 3482 first_line_in_doc, previous_line_blank, 3483 &previous_line_block, 3484 read_yaml_macros_and_links, list_para, 1); 3485 3486 /* Ignore abbreviations (for now) */ 3487 pline = NULL; 3488 } 3489 else if (colno == 1 && pline_len > 2 3490 && startswith((char*)pline, "***")) 3491 { 3492 skip_eol = 1; 3493 if (!read_yaml_macros_and_links) 3494 { 3495 process_horizontal_rule(output, link_prefix, 3496 previous_line_blank, 3497 read_yaml_macros_and_links); 3498 output_firstcol = 1; 3499 } 3500 pline = NULL; 3501 } 3502 else if (colno == 1 && strlen((char*)pline) > 1 3503 && *(pline + 1) == ' ') 3504 { 3505 if (IN(state, ST_NUMLIST)) 3506 { 3507 state &= ~ST_NUMLIST; 3508 if (!read_yaml_macros_and_links) 3509 { 3510 process_list_item_end(output, 3511 link_prefix); 3512 process_numlist_end(output); 3513 } 3514 } 3515 if (!read_yaml_macros_and_links) 3516 { 3517 if (!(IN(state, ST_LIST))) 3518 process_list_start(output); 3519 else 3520 process_list_item_end(output, 3521 link_prefix); 3522 process_list_item_start(output); 3523 } 3524 3525 processed_start_of_line = 1; 3526 skip_eol = 0; 3527 3528 state |= ST_LIST; 3529 3530 pline += 2; 3531 colno += 2; 3532 } 3533 else if (pline_len > 1 && *(pline + 1) == '*') 3534 { 3535 /* Handle ** within footnotes, headings 3536 * and link text specially */ 3537 if (!ANY(state, 3538 ST_INLINE_FOOTNOTE | ST_HEADING 3539 | ST_FOOTNOTE_TEXT | ST_LINK)) 3540 { 3541 /* Output existing text up to * */ 3542 process_text_token(output, line, link_prefix, 3543 first_line_in_doc, previous_line_blank, 3544 &previous_line_block, 3545 processed_start_of_line, 3546 read_yaml_macros_and_links, list_para, 3547 &token, &ptoken, &token_size, 1, 1); 3548 processed_start_of_line = 1; 3549 3550 if (!read_yaml_macros_and_links 3551 && !(ANY(state, 3552 ST_PRE | ST_CODE | ST_HEADING))) 3553 process_bold(output, IN(state, ST_BOLD)); 3554 } 3555 3556 state ^= ST_BOLD; 3557 pline += 2; 3558 colno += 2; 3559 } 3560 else 3561 { 3562 /* Handle * within footnotes, headings 3563 * and link text specially */ 3564 if (!ANY(state, 3565 ST_INLINE_FOOTNOTE | ST_HEADING 3566 | ST_FOOTNOTE_TEXT | ST_LINK)) 3567 3568 { 3569 /* Output existing text up to * */ 3570 process_text_token(output, line, link_prefix, 3571 first_line_in_doc, previous_line_blank, 3572 &previous_line_block, 3573 processed_start_of_line, 3574 read_yaml_macros_and_links, list_para, 3575 &token, &ptoken, &token_size, 1, 1); 3576 processed_start_of_line = 1; 3577 3578 if (!read_yaml_macros_and_links 3579 && !(ANY(state, 3580 ST_PRE | ST_CODE | ST_HEADING))) 3581 process_italic(output, 3582 IN(state, ST_ITALIC)); 3583 } 3584 3585 state ^= ST_ITALIC; 3586 pline++; 3587 colno++; 3588 } 3589 break; 3590 3591 case ' ': 3592 if (ANY(state, 3593 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE 3594 | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL)) 3595 { 3596 CHECKCOPY(token, ptoken, token_size, pline); 3597 colno++; 3598 break; 3599 } 3600 3601 if ((IN(state, ST_HEADING)) && !(IN(state, ST_HEADING_TEXT))) 3602 { 3603 if (!read_yaml_macros_and_links) 3604 process_heading_start(output, heading_level); 3605 state |= ST_HEADING_TEXT; 3606 pline++; 3607 colno++; 3608 break; 3609 } 3610 3611 if (colno == 1 && strlen((char*)pline) > 1 3612 && startswith((char*)pline, " ")) 3613 { 3614 list_para = 1; 3615 process_text_token(output, line, link_prefix, 3616 first_line_in_doc, previous_line_blank, 3617 &previous_line_block, processed_start_of_line, 3618 read_yaml_macros_and_links, list_para, &token, 3619 &ptoken, &token_size, 1, 1); 3620 processed_start_of_line = 1; 3621 while (*pline == ' ' || *pline == '\t') 3622 { 3623 pline++; 3624 colno++; 3625 } 3626 break; 3627 } 3628 3629 if (strlen((char*)pline) == 2 && *(pline + 1) == ' ') 3630 { 3631 *ptoken = 0; 3632 if (strlcat((char*)ptoken, "\n", 3633 token_size - (ptoken - token)) 3634 > token_size - (ptoken - token)) 3635 warning(1, (u8*)"strlcat:%d: Overflow", 3636 __LINE__); 3637 ptoken++; 3638 output_firstcol = 1; 3639 pline += 2; 3640 colno++; 3641 } 3642 else if (IN(state, ST_LINK_MACRO)) 3643 { 3644 *ptoken = 0; 3645 if (strlcpy((char*)link_macro, (char*)token, BUFSIZE) 3646 >= BUFSIZE) 3647 warning(1, (u8*)"strlcpy:%d: Overflow", 3648 __LINE__); 3649 *(link_macro + strlen((char*)token)) = 0; 3650 RESET_TOKEN(token, ptoken, token_size); 3651 state &= ~ST_LINK_MACRO; 3652 } 3653 else if (!(IN(state, ST_HEADING) && *(pline - 1) == '#')) 3654 CHECKCOPY(token, ptoken, token_size, pline); 3655 colno++; 3656 break; 3657 3658 case '{': 3659 if (ANY(state, 3660 ST_CODE | ST_DISPLAY_FORMULA 3661 | ST_FORMULA 3662 /*| ST_HTML_TAG*/ 3663 | ST_PRE | ST_YAML | ST_YAML_VAL)) 3664 { 3665 CHECKCOPY(token, ptoken, token_size, pline); 3666 colno++; 3667 break; 3668 } 3669 else if (IN(state, ST_HTML_TAG)) 3670 { 3671 pline++; 3672 colno++; 3673 break; 3674 } 3675 3676 if (IN(state, ST_MACRO_BODY)) 3677 { 3678 *ptoken = 0; 3679 size_t token_len = strlen((char*)token); 3680 3681 skip_eol = 1; 3682 if (read_yaml_macros_and_links) 3683 { 3684 if (!macros) 3685 { 3686 macros_count = 1; 3687 CALLOC(macros, KeyValue, macros_count); 3688 pmacros = macros; 3689 } 3690 if (pmacros->value) 3691 { 3692 size_t value_len 3693 = strlen((char*)pmacros->value); 3694 3695 if (pmacros->value_size 3696 < value_len + token_len + 1) 3697 { 3698 pmacros->value_size 3699 += token_len; 3700 REALLOC(pmacros->value, u8, 3701 pmacros->value_size); 3702 } 3703 if (strlcat((char*)pmacros->value, 3704 (char*)token, 3705 pmacros->value_size) 3706 > pmacros->value_size) 3707 warning(1, (u8*)"strlcat:%d: Overflow", 3708 __LINE__); 3709 } 3710 else 3711 { 3712 pmacros->value_size = token_len + 1; 3713 CALLOC(pmacros->value, u8, 3714 pmacros->value_size); 3715 if (strlcpy((char*)pmacros->value, 3716 (char*)token, 3717 pmacros->value_size) 3718 >= pmacros->value_size) 3719 warning(1, (u8*)"strlcpy:%d: Overflow", 3720 __LINE__); 3721 } 3722 } 3723 } 3724 else 3725 { 3726 /* Output existing text up to { */ 3727 process_text_token(output, line, link_prefix, 3728 first_line_in_doc, previous_line_blank, 3729 &previous_line_block, processed_start_of_line, 3730 read_yaml_macros_and_links, list_para, &token, 3731 &ptoken, &token_size, 0, 1); 3732 processed_start_of_line = 1; 3733 } 3734 3735 state |= ST_TAG; 3736 RESET_TOKEN(token, ptoken, token_size); 3737 3738 pline++; 3739 colno++; 3740 break; 3741 3742 case '/': 3743 if (ANY(state, 3744 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE 3745 | ST_PRE | ST_HEADING | ST_YAML_VAL)) 3746 { 3747 CHECKCOPY(token, ptoken, token_size, pline); 3748 colno++; 3749 break; 3750 } 3751 else if (IN(state, ST_HTML_TAG)) 3752 { 3753 if (pline != line && *(pline - 1) == '<') 3754 end_tag = 1; 3755 pline++; 3756 } 3757 else if ((IN(state, ST_TAG)) && pline != line 3758 && *(pline - 1) == '{') 3759 { 3760 end_tag = 1; 3761 pline++; 3762 } 3763 else if (*(pline + 1) == '-') 3764 { 3765 /* Output existing text up to /- */ 3766 process_text_token(output, line, link_prefix, 3767 first_line_in_doc, previous_line_blank, 3768 &previous_line_block, processed_start_of_line, 3769 read_yaml_macros_and_links, list_para, &token, 3770 &ptoken, &token_size, 1, 1); 3771 processed_start_of_line = 1; 3772 3773 state |= ST_SUMMARY; 3774 3775 pline += 2; 3776 colno++; 3777 } 3778 else 3779 CHECKCOPY(token, ptoken, token_size, pline); 3780 3781 colno++; 3782 break; 3783 3784 case '}': 3785 if (ANY(state, 3786 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE 3787 | ST_PRE | ST_YAML | ST_YAML_VAL)) 3788 { 3789 CHECKCOPY(token, ptoken, token_size, pline); 3790 colno++; 3791 break; 3792 } 3793 3794 if (IN(state, ST_TAG)) 3795 { 3796 state &= ~ST_TAG; 3797 *ptoken = 0; 3798 3799 if (!strcmp((char*)token, "made-by")) 3800 madeby_present = 1; 3801 else 3802 process_tag(output, token, source_filename, 3803 link_prefix, previous_line_blank, 3804 first_line_in_doc, 3805 footer_permalink_text, 3806 (const char*)permalink_url, 3807 ext_in_permalink 3808 && *ext_in_permalink != '0', 3809 read_yaml_macros_and_links, &skip_eol, 3810 end_tag); 3811 3812 RESET_TOKEN(token, ptoken, token_size); 3813 end_tag = 0; 3814 } 3815 3816 pline++; 3817 colno++; 3818 break; 3819 3820 case '|': 3821 if (ANY(state, 3822 ST_CODE | ST_DISPLAY_FORMULA 3823 | ST_FORMULA 3824 /*| ST_HTML_TAG*/ 3825 | ST_IMAGE | ST_PRE | ST_TAG | ST_YAML)) 3826 { 3827 CHECKCOPY(token, ptoken, token_size, pline); 3828 colno++; 3829 break; 3830 } 3831 else if (IN(state, ST_HTML_TAG)) 3832 { 3833 pline++; 3834 colno++; 3835 break; 3836 } 3837 3838 if (strlen((char*)pline) > 1 && *(pline + 1) == '|') 3839 { 3840 /* Handle || within footnotes, headings and link 3841 * text specially */ 3842 if (!ANY(state, 3843 ST_INLINE_FOOTNOTE | ST_HEADING 3844 | ST_FOOTNOTE_TEXT | ST_LINK)) 3845 { 3846 /* Output existing text up to || */ 3847 process_text_token(output, line, link_prefix, 3848 first_line_in_doc, previous_line_blank, 3849 &previous_line_block, 3850 processed_start_of_line, 3851 read_yaml_macros_and_links, list_para, 3852 &token, &ptoken, &token_size, 1, 1); 3853 processed_start_of_line = 1; 3854 } 3855 3856 state ^= ST_KBD; 3857 3858 pline += 2; 3859 colno += 2; 3860 } 3861 /* Partial tables (for templating) */ 3862 else if (!(IN(state, ST_PRE)) && colno == 1 3863 && strlen((char*)pline) > 1 && *(pline + 1) == '@') 3864 { 3865 skip_eol = 1; 3866 3867 pline += 2; 3868 colno += 2; 3869 3870 switch (*pline) 3871 { 3872 case '\\': 3873 if (!read_yaml_macros_and_links) 3874 process_table_start(output); 3875 pline = NULL; 3876 break; 3877 3878 case '#': 3879 state |= ST_TABLE_HEADER; 3880 pline++; 3881 colno++; 3882 break; 3883 3884 case '-': 3885 state &= ~ST_TABLE_HEADER; 3886 if (!read_yaml_macros_and_links) 3887 process_table_body_start(output, 0); 3888 pline = NULL; 3889 break; 3890 3891 case ' ': 3892 state |= ST_TABLE; 3893 if (!read_yaml_macros_and_links) 3894 process_table_body_row_start(output); 3895 break; 3896 3897 case '/': 3898 state &= ~ST_TABLE; 3899 if (!read_yaml_macros_and_links) 3900 { 3901 process_table_end(output); 3902 output_firstcol = 1; 3903 } 3904 pline = NULL; 3905 break; 3906 3907 default: 3908 warning(1, (u8*)"Invalid partial table line"); 3909 CHECKCOPY(token, ptoken, token_size, pline); 3910 colno++; 3911 } 3912 } 3913 else if (!(IN(state, ST_PRE)) && colno == 1) 3914 { 3915 skip_eol = 1; 3916 3917 if (IN(state, ST_TABLE_HEADER)) 3918 { 3919 state &= ~ST_TABLE_HEADER; 3920 state |= ST_TABLE_LINE; 3921 pline = NULL; 3922 break; 3923 } 3924 3925 if (IN(state, ST_TABLE_LINE)) 3926 { 3927 state &= ~ST_TABLE_LINE; 3928 state |= ST_TABLE; 3929 if (!read_yaml_macros_and_links) 3930 process_table_body_start(output, 1); 3931 } 3932 else if (IN(state, ST_TABLE)) 3933 { 3934 if (!read_yaml_macros_and_links) 3935 process_table_body_row_start(output); 3936 } 3937 else 3938 { 3939 state |= ST_TABLE_HEADER; 3940 if (!read_yaml_macros_and_links) 3941 process_table_start(output); 3942 } 3943 3944 pline++; 3945 colno++; 3946 } 3947 else if (IN(state, ST_TABLE_HEADER)) 3948 { 3949 *ptoken = 0; 3950 if (!read_yaml_macros_and_links) 3951 { 3952 print_output(output, "%s", token); 3953 if (*token) 3954 output_firstcol = 0; 3955 if (strlen((char*)pline) > 1) 3956 process_table_header_cell(output); 3957 else 3958 process_table_header_end(output); 3959 } 3960 RESET_TOKEN(token, ptoken, token_size); 3961 pline++; 3962 colno++; 3963 } 3964 else if (IN(state, ST_TABLE)) 3965 { 3966 *ptoken = 0; 3967 if (!read_yaml_macros_and_links) 3968 { 3969 print_output(output, "%s", token); 3970 if (*token) 3971 output_firstcol = 0; 3972 if (strlen((char*)pline) > 1) 3973 process_table_body_cell(output); 3974 else 3975 process_table_body_row_end(output); 3976 } 3977 RESET_TOKEN(token, ptoken, token_size); 3978 pline++; 3979 colno++; 3980 } 3981 else 3982 { 3983 CHECKCOPY(token, ptoken, token_size, pline); 3984 colno++; 3985 } 3986 break; 3987 3988 case '<': 3989 if (read_yaml_macros_and_links 3990 || ANY(state, 3991 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 3992 | ST_KBD | ST_IMAGE | ST_PRE)) 3993 { 3994 CHECKCOPY(token, ptoken, token_size, pline); 3995 colno++; 3996 break; 3997 } 3998 3999 state |= ST_HTML_TAG; 4000 pline++; 4001 colno++; 4002 break; 4003 4004 case '>': 4005 if (IN(state, ST_HTML_TAG)) 4006 { 4007 state &= ~ST_HTML_TAG; 4008 pline++; 4009 colno++; 4010 break; 4011 } 4012 4013 if (ANY(state, 4014 ST_DISPLAY_FORMULA | ST_FORMULA | ST_IMAGE 4015 | ST_MACRO_BODY)) 4016 { 4017 CHECKCOPY(token, ptoken, token_size, pline); 4018 colno++; 4019 break; 4020 } 4021 4022 if (!read_yaml_macros_and_links 4023 && !ANY(state, ST_CODE | ST_HEADING | ST_PRE) 4024 && colno == 1) 4025 { 4026 if (!read_yaml_macros_and_links 4027 && !(IN(state, ST_BLOCKQUOTE))) 4028 { 4029 if (IN(state, ST_PARA_OPEN)) 4030 { 4031 print_output(output, "\n"); 4032 state &= ~ST_PARA_OPEN; 4033 } 4034 process_blockquote(output, 0); 4035 output_firstcol = 0; 4036 } 4037 4038 state |= ST_BLOCKQUOTE; 4039 4040 pline++; 4041 colno++; 4042 } 4043 else 4044 { 4045 CHECKCOPY(token, ptoken, token_size, pline); 4046 colno++; 4047 } 4048 break; 4049 4050 case '!': 4051 if (ANY(state, 4052 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 4053 | ST_FOOTNOTE_TEXT | ST_HEADING | ST_IMAGE 4054 | ST_INLINE_FOOTNOTE | ST_LINK 4055 | ST_MACRO_BODY | ST_PRE)) 4056 { 4057 CHECKCOPY(token, ptoken, token_size, pline); 4058 colno++; 4059 break; 4060 } 4061 else if (IN(state, ST_HTML_TAG)) 4062 { 4063 pline++; 4064 colno++; 4065 break; 4066 } 4067 4068 if (strlen((char*)pline) > 1 && *(pline + 1) == '[') 4069 { 4070 /* Output existing text up to ! */ 4071 *ptoken = 0; 4072 process_text_token(output, line, link_prefix, 4073 first_line_in_doc, previous_line_blank, 4074 &previous_line_block, processed_start_of_line, 4075 read_yaml_macros_and_links, list_para, &token, 4076 &ptoken, &token_size, 0, 1); 4077 processed_start_of_line = 1; 4078 4079 RESET_TOKEN(token, ptoken, token_size); 4080 4081 state |= ST_IMAGE; 4082 keep_token = 1; 4083 pline += 2; 4084 colno += 2; 4085 } 4086 else 4087 { 4088 CHECKCOPY(token, ptoken, token_size, pline); 4089 colno++; 4090 } 4091 4092 break; 4093 4094 case '=': 4095 if (ANY(state, 4096 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 4097 | ST_MACRO_BODY | ST_PRE | ST_TAG)) 4098 { 4099 CHECKCOPY(token, ptoken, token_size, pline); 4100 colno++; 4101 break; 4102 } 4103 else if (IN(state, ST_HTML_TAG)) 4104 { 4105 pline++; 4106 colno++; 4107 break; 4108 } 4109 4110 if (colno != 1 && *(pline - 1) == '[') 4111 { 4112 state |= ST_LINK_MACRO; 4113 pline++; 4114 colno++; 4115 } 4116 else 4117 { 4118 CHECKCOPY(token, ptoken, token_size, pline); 4119 colno++; 4120 } 4121 break; 4122 4123 case '[': 4124 pline_len = strlen((char*)pline); 4125 4126 if (ANY(state, 4127 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 4128 | ST_HEADING | ST_IMAGE | ST_MACRO_BODY 4129 | ST_PRE)) 4130 { 4131 CHECKCOPY(token, ptoken, token_size, pline); 4132 colno++; 4133 break; 4134 } 4135 else if (IN(state, ST_HTML_TAG)) 4136 { 4137 pline++; 4138 colno++; 4139 break; 4140 } 4141 4142 if (IN(state, ST_FOOTNOTE_TEXT)) 4143 { 4144 if (!read_yaml_macros_and_links 4145 && (IN(state, ST_PARA_OPEN))) 4146 { 4147 process_para_end(output, link_prefix, 4148 previous_line_blank, 4149 read_yaml_macros_and_links); 4150 output_firstcol = 1; 4151 } 4152 state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN); 4153 } 4154 4155 if (pline_len > 1 && *(pline + 1) == '^') 4156 { 4157 footnote_at_line_start = colno == 1; 4158 if (*token) 4159 { 4160 /* Output existing text up to [^ */ 4161 *ptoken = 0; 4162 process_text_token(output, line, link_prefix, 4163 first_line_in_doc, previous_line_blank, 4164 &previous_line_block, 4165 processed_start_of_line, 4166 read_yaml_macros_and_links, list_para, 4167 &token, &ptoken, &token_size, 1, 1); 4168 } 4169 processed_start_of_line = 1; 4170 4171 RESET_TOKEN(token, ptoken, token_size); 4172 state |= ST_FOOTNOTE; 4173 pline += 2; 4174 colno += 2; 4175 break; 4176 } 4177 4178 if (*token) 4179 { 4180 /* Output existing text up to [ */ 4181 *ptoken = 0; 4182 process_text_token(output, line, link_prefix, 4183 first_line_in_doc, previous_line_blank, 4184 &previous_line_block, processed_start_of_line, 4185 read_yaml_macros_and_links, list_para, &token, 4186 &ptoken, &token_size, 1, 1); 4187 } 4188 processed_start_of_line = 1; 4189 4190 RESET_TOKEN(token, ptoken, token_size); 4191 state |= ST_LINK; 4192 keep_token = 1; 4193 *link_macro = 0; 4194 pline++; 4195 colno++; 4196 4197 if (*pline == '(') 4198 { 4199 *ptoken = 0; 4200 state |= ST_LINK_SPAN; 4201 pline++; 4202 colno++; 4203 } 4204 4205 break; 4206 4207 case '(': 4208 if (ANY(state, 4209 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 4210 | ST_FOOTNOTE_TEXT | ST_HEADING 4211 | ST_INLINE_FOOTNOTE | ST_LINK_SECOND_ARG_END 4212 | ST_MACRO_BODY | ST_PRE)) 4213 CHECKCOPY(token, ptoken, token_size, pline); 4214 else if (*link_macro && (IN(state, ST_LINK))) 4215 { 4216 /* 4217 * u8* 4218 * tag = (u8*)"<span>"; size_t tag_len = 4219 * strlen(tag); size_t token_len = 0; 4220 * 4221 * *ptoken 4222 * = 0; token_len = strlen(token); 4223 * 4224 * if 4225 * (token_len + tag_len < BUFSIZE) 4226 * { 4227 * strncat((char*)token, 4228 * tag, BUFSIZE-token_len-1); ptoken += 4229 * tag_len; 4230 * } 4231 */ 4232 4233 state |= ST_LINK_SPAN; 4234 pline++; 4235 } 4236 else 4237 CHECKCOPY(token, ptoken, token_size, pline); 4238 colno++; 4239 break; 4240 4241 case ')': 4242 if (ANY(state, 4243 ST_DISPLAY_FORMULA | ST_FORMULA 4244 | ST_LINK_SECOND_ARG_END)) 4245 { 4246 CHECKCOPY(token, ptoken, token_size, pline); 4247 colno++; 4248 break; 4249 } 4250 else if (IN(state, ST_HTML_TAG)) 4251 { 4252 pline++; 4253 colno++; 4254 break; 4255 } 4256 4257 pline_len = strlen((char*)pline); 4258 4259 if (IN(state, ST_LINK_SPAN) && pline_len > 1 4260 && *(pline + 1) == ']') 4261 { 4262 state &= ~ST_LINK_SPAN; 4263 pline++; 4264 } 4265 else if (ANY(state, ST_LINK_SECOND_ARG | ST_IMAGE_SECOND_ARG)) 4266 { 4267 *ptoken = 0; 4268 if (!read_yaml_macros_and_links) 4269 { 4270 paralinks_count++; 4271 if (!paralinks) 4272 { 4273 CALLOC(paralinks, KeyValue, 4274 paralinks_count); 4275 pparalinks = paralinks; 4276 } 4277 else 4278 { 4279 REALLOCARRAY(paralinks, KeyValue, 4280 paralinks_count); 4281 pparalinks = paralinks + paralinks_count 4282 - 1; 4283 } 4284 pparalinks->key = (u8*)strdup((char*)token); 4285 pparalinks->value_size 4286 = strlen((char*)link_text) + 1; 4287 pparalinks->value 4288 = (u8*)strdup((char*)link_text); 4289 4290 if (output_firstcol 4291 && ((*(line + line_len - 1) == ']') 4292 || *(line + line_len - 1) 4293 == ')')) 4294 { 4295 skip_eol = 1; 4296 skip_para_end = 1; 4297 pline = NULL; 4298 } 4299 else 4300 { 4301 if (IN(state, ST_LINK_SECOND_ARG)) 4302 process_inline_link(output, 4303 link_text); 4304 else 4305 process_inline_image(output, 4306 link_text); 4307 output_firstcol = !*link_text; 4308 } 4309 } 4310 RESET_TOKEN(token, ptoken, token_size); 4311 state &= ~(ST_LINK | ST_LINK_SECOND_ARG | ST_IMAGE 4312 | ST_IMAGE_SECOND_ARG); 4313 /* Difference from slweb: could be NULL */ 4314 if (pline) 4315 pline++; 4316 } 4317 else 4318 CHECKCOPY(token, ptoken, token_size, pline); 4319 colno++; 4320 break; 4321 4322 case ']': 4323 if (ANY(state, 4324 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 4325 | ST_HEADING | ST_MACRO_BODY | ST_PRE)) 4326 { 4327 CHECKCOPY(token, ptoken, token_size, pline); 4328 colno++; 4329 break; 4330 } 4331 else if (IN(state, ST_HTML_TAG)) 4332 { 4333 pline++; 4334 colno++; 4335 break; 4336 } 4337 4338 *ptoken = 0; 4339 if (IN(state, ST_INLINE_FOOTNOTE)) 4340 { 4341 process_inline_footnote(token, 4342 read_yaml_macros_and_links, output); 4343 4344 keep_token = 0; 4345 RESET_TOKEN(token, ptoken, token_size); 4346 state &= ~ST_INLINE_FOOTNOTE; 4347 pline++; 4348 colno++; 4349 } 4350 else if (IN(state, ST_FOOTNOTE)) 4351 { 4352 int footnote_definition = (strlen((char*)pline) > 1) 4353 && (*(pline + 1) == ':') 4354 && footnote_at_line_start; 4355 4356 process_footnote(output, token, 4357 footnote_definition 4358 && read_yaml_macros_and_links, 4359 !footnote_definition 4360 && !read_yaml_macros_and_links); 4361 4362 RESET_TOKEN(token, ptoken, token_size); 4363 state &= ~ST_FOOTNOTE; 4364 footnote_at_line_start = 0; 4365 pline++; 4366 colno++; 4367 if (footnote_definition) 4368 { 4369 pline++; 4370 colno++; 4371 while (*pline == ' ' || *pline == '\t') 4372 { 4373 pline++; 4374 colno++; 4375 } 4376 state |= ST_FOOTNOTE_TEXT; 4377 } 4378 } 4379 else if (IN(state, ST_LINK_SECOND_ARG)) 4380 { 4381 if (!read_yaml_macros_and_links) 4382 { 4383 u8* url = get_value(links, links_count, token, 4384 NULL); 4385 if (!url) 4386 exit(error(EINVAL, 4387 (u8*)"slweb_parse: get_" 4388 "value: " 4389 "Invalid " 4390 "argumen" 4391 "t")); 4392 paralinks_count++; 4393 if (!paralinks) 4394 { 4395 CALLOC(paralinks, KeyValue, 4396 paralinks_count); 4397 pparalinks = paralinks; 4398 } 4399 else 4400 { 4401 REALLOCARRAY(paralinks, KeyValue, 4402 paralinks_count); 4403 pparalinks = paralinks + paralinks_count 4404 - 1; 4405 } 4406 pparalinks->key = (u8*)strdup((char*)url); 4407 pparalinks->value_size 4408 = strlen((char*)link_text) + 1; 4409 pparalinks->value 4410 = (u8*)strdup((char*)link_text); 4411 4412 if (output_firstcol 4413 && ((*(line + line_len - 1) == ']') 4414 || *(line + line_len - 1) 4415 == ')')) 4416 { 4417 skip_eol = 1; 4418 pline = NULL; 4419 } 4420 else 4421 { 4422 process_link(output, link_text); 4423 output_firstcol = !*link_text; 4424 } 4425 } 4426 RESET_TOKEN(token, ptoken, token_size); 4427 state &= ~(ST_LINK | ST_LINK_SECOND_ARG); 4428 /* Difference from slweb: could be NULL */ 4429 if (pline) 4430 { 4431 pline++; 4432 colno++; 4433 } 4434 } 4435 else if (IN(state, ST_IMAGE_SECOND_ARG)) 4436 { 4437 if (!read_yaml_macros_and_links) 4438 { 4439 u8* url = get_value(links, links_count, token, 4440 NULL); 4441 if (!url) 4442 exit(error(EINVAL, 4443 (u8*)"slweb_parse: get_" 4444 "value: " 4445 "Invalid " 4446 "argumen" 4447 "t")); 4448 paralinks_count++; 4449 if (!paralinks) 4450 { 4451 CALLOC(paralinks, KeyValue, 4452 paralinks_count); 4453 pparalinks = paralinks; 4454 } 4455 else 4456 { 4457 REALLOCARRAY(paralinks, KeyValue, 4458 paralinks_count); 4459 pparalinks = paralinks + paralinks_count 4460 - 1; 4461 } 4462 pparalinks->key = (u8*)strdup((char*)url); 4463 pparalinks->value_size 4464 = strlen((char*)link_text) + 1; 4465 pparalinks->value 4466 = (u8*)strdup((char*)link_text); 4467 4468 if (output_firstcol 4469 && ((*(line + line_len - 1) == ']') 4470 || *(line + line_len - 1) 4471 == ')')) 4472 { 4473 skip_eol = 1; 4474 pline = NULL; 4475 } 4476 else 4477 { 4478 process_image(output, link_text); 4479 output_firstcol = !*link_text; 4480 } 4481 } 4482 RESET_TOKEN(token, ptoken, token_size); 4483 state &= ~(ST_IMAGE | ST_IMAGE_SECOND_ARG); 4484 /* Difference from slweb: could be NULL */ 4485 if (pline) 4486 { 4487 pline++; 4488 colno++; 4489 } 4490 } 4491 else if (strlen((char*)pline) > 1) 4492 { 4493 size_t token_len = 0; 4494 switch (*(pline + 1)) 4495 { 4496 case ':': 4497 links_count++; 4498 4499 if (!links) 4500 { 4501 CALLOC(links, KeyValue, links_count); 4502 plinks = links; 4503 } 4504 else 4505 { 4506 REALLOCARRAY(links, KeyValue, 4507 links_count); 4508 plinks = links + links_count - 1; 4509 } 4510 CALLOC(plinks->key, u8, KEYSIZE); 4511 if (strlcpy((char*)plinks->key, (char*)token, 4512 KEYSIZE) 4513 >= KEYSIZE) 4514 warning(1, (u8*)"strlcpy:%d: Overflow", 4515 __LINE__); 4516 plinks->value = NULL; 4517 plinks->value_size = 0; 4518 pline += 2; 4519 colno += 2; 4520 while (pline 4521 && (*pline == ' ' || *pline == '\t')) 4522 { 4523 pline++; 4524 colno++; 4525 } 4526 RESET_TOKEN(token, ptoken, token_size); 4527 keep_token = 0; 4528 state |= ST_LINK_SECOND_ARG_END; 4529 break; 4530 4531 case '[': 4532 case '(': 4533 token_len = strlen((char*)token); 4534 if (link_text && token_len + 1 > link_size) 4535 { 4536 link_size = token_len + 1; 4537 REALLOC(link_text, u8, link_size); 4538 } 4539 else if (!link_text) 4540 { 4541 link_size = token_len + 1; 4542 CALLOC(link_text, u8, link_size); 4543 } 4544 if (strlcpy((char*)link_text, (char*)token, 4545 link_size) 4546 >= link_size) 4547 warning(1, (u8*)"strlcpy:%d: Overflow", 4548 __LINE__); 4549 *(link_text + token_len) = 0; 4550 pline += 2; 4551 colno += 2; 4552 RESET_TOKEN(token, ptoken, token_size); 4553 keep_token = 0; 4554 if (IN(state, ST_LINK)) 4555 state |= ST_LINK_SECOND_ARG; 4556 else 4557 state |= ST_IMAGE_SECOND_ARG; 4558 break; 4559 default: 4560 CHECKCOPY(token, ptoken, token_size, pline); 4561 colno++; 4562 state &= ~(ST_LINK | ST_IMAGE); 4563 break; 4564 } 4565 } 4566 else 4567 { 4568 CHECKCOPY(token, ptoken, token_size, pline); 4569 colno++; 4570 } 4571 break; 4572 4573 case '^': 4574 if (ANY(state, 4575 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 4576 | ST_FOOTNOTE_TEXT | ST_IMAGE 4577 | ST_INLINE_FOOTNOTE | ST_PRE | ST_YAML)) 4578 { 4579 CHECKCOPY(token, ptoken, token_size, pline); 4580 colno++; 4581 break; 4582 } 4583 else if (IN(state, ST_HTML_TAG)) 4584 { 4585 pline++; 4586 colno++; 4587 break; 4588 } 4589 4590 if (strlen((char*)pline) > 1 && *(pline + 1) == '[') 4591 { 4592 /* Output existing text up to ^[ */ 4593 *ptoken = 0; 4594 process_text_token(output, line, link_prefix, 4595 first_line_in_doc, previous_line_blank, 4596 &previous_line_block, processed_start_of_line, 4597 read_yaml_macros_and_links, list_para, &token, 4598 &ptoken, &token_size, 1, 1); 4599 processed_start_of_line = 1; 4600 4601 RESET_TOKEN(token, ptoken, token_size); 4602 state |= ST_INLINE_FOOTNOTE; 4603 keep_token = 1; 4604 pline += 2; 4605 colno += 2; 4606 break; 4607 } 4608 4609 CHECKCOPY(token, ptoken, token_size, pline); 4610 colno++; 4611 break; 4612 4613 case '\\': 4614 *ptoken = 0; 4615 if (ANY(state, 4616 ST_DISPLAY_FORMULA 4617 | ST_FORMULA 4618 /*| ST_HTML_TAG*/ 4619 | ST_IMAGE | ST_LINK_SECOND_ARG 4620 | ST_LINK_SECOND_ARG_END | ST_MACRO_BODY)) 4621 { 4622 CHECKCOPY(token, ptoken, token_size, pline); 4623 colno++; 4624 break; 4625 } 4626 else if (IN(state, ST_HTML_TAG)) 4627 { 4628 pline++; 4629 colno++; 4630 break; 4631 } 4632 4633 if (strlen((char*)pline) > 1) 4634 { 4635 pline++; 4636 colno++; 4637 4638 CHECKCOPY(token, ptoken, token_size, pline); 4639 } 4640 else 4641 { 4642 pline = NULL; 4643 break; 4644 } 4645 4646 colno++; 4647 break; 4648 4649 case '$': 4650 if (ANY(state, 4651 ST_CODE | ST_CSV_BODY | ST_IMAGE | ST_PRE 4652 | ST_TSV_BODY | ST_YAML)) 4653 { 4654 CHECKCOPY(token, ptoken, token_size, pline); 4655 colno++; 4656 break; 4657 } 4658 else if (IN(state, ST_HTML_TAG)) 4659 { 4660 pline++; 4661 colno++; 4662 break; 4663 } 4664 4665 if (strlen((char*)pline) > 1 && *(pline + 1) == '$') 4666 { 4667 if (IN(state, ST_FORMULA)) 4668 exit(error(1, 4669 (u8*)"slweb_parse: Display formula " 4670 "within an" 4671 " open formula")); 4672 4673 if (!(IN(state, ST_DISPLAY_FORMULA))) 4674 { 4675 /* Output existing text up to $$ */ 4676 process_text_token(output, line, link_prefix, 4677 first_line_in_doc, previous_line_blank, 4678 &previous_line_block, 4679 processed_start_of_line, 4680 read_yaml_macros_and_links, list_para, 4681 &token, &ptoken, &token_size, 1, 1); 4682 } 4683 processed_start_of_line = 1; 4684 4685 state ^= ST_DISPLAY_FORMULA; 4686 4687 if (IN(state, ST_DISPLAY_FORMULA)) 4688 keep_token = 1; 4689 else 4690 { 4691 *ptoken = 0; 4692 4693 if (!read_yaml_macros_and_links) 4694 process_formula(output, token, 1); 4695 4696 keep_token = 0; 4697 RESET_TOKEN(token, ptoken, token_size); 4698 } 4699 4700 pline += 2; 4701 colno += 2; 4702 } 4703 else 4704 { 4705 if (IN(state, ST_DISPLAY_FORMULA)) 4706 exit(error(1, 4707 (u8*)"slweb_parse: Formula within an " 4708 "open" 4709 " display formula")); 4710 4711 if (!(IN(state, ST_FORMULA))) 4712 { 4713 /* Output existing text up to $ */ 4714 process_text_token(output, line, link_prefix, 4715 first_line_in_doc, previous_line_blank, 4716 &previous_line_block, 4717 processed_start_of_line, 4718 read_yaml_macros_and_links, list_para, 4719 &token, &ptoken, &token_size, 1, 1); 4720 } 4721 processed_start_of_line = 1; 4722 4723 state ^= ST_FORMULA; 4724 4725 if (IN(state, ST_FORMULA)) 4726 keep_token = 1; 4727 else 4728 { 4729 *ptoken = 0; 4730 4731 if (!read_yaml_macros_and_links) 4732 process_formula(output, token, 0); 4733 4734 keep_token = 0; 4735 RESET_TOKEN(token, ptoken, token_size); 4736 } 4737 4738 pline++; 4739 colno++; 4740 } 4741 break; 4742 4743 case '1': /* FALLTHROUGH */ 4744 case '2': 4745 case '3': 4746 case '4': 4747 case '5': 4748 case '6': 4749 case '7': 4750 case '8': 4751 case '9': 4752 case '0': 4753 if (ANY(state, 4754 ST_CODE | ST_DISPLAY_FORMULA | ST_FORMULA 4755 | ST_MACRO_BODY | ST_PRE | ST_YAML_VAL)) 4756 { 4757 CHECKCOPY(token, ptoken, token_size, pline); 4758 colno++; 4759 } 4760 else if (IN(state, ST_HTML_TAG)) 4761 { 4762 pline++; 4763 colno++; 4764 break; 4765 } 4766 else if ((colno == 1 || all_numeric(line, pline)) 4767 && strlen((char*)pline) > 1 4768 && (*(pline + 1) == '.' || *(pline + 1) == ')')) 4769 { 4770 ptoken = token; 4771 *ptoken = 0; 4772 4773 if (IN(state, ST_LIST)) 4774 { 4775 state &= ~ST_LIST; 4776 if (!read_yaml_macros_and_links) 4777 { 4778 process_list_item_end(output, 4779 link_prefix); 4780 process_list_end(output); 4781 } 4782 } 4783 if (!read_yaml_macros_and_links) 4784 { 4785 if (!(IN(state, ST_NUMLIST))) 4786 process_numlist_start(output); 4787 else 4788 process_list_item_end(output, 4789 link_prefix); 4790 process_list_item_start(output); 4791 } 4792 4793 processed_start_of_line = 1; 4794 skip_eol = 0; 4795 4796 state |= ST_NUMLIST; 4797 4798 pline += 2; 4799 colno += 2; 4800 } 4801 else 4802 { 4803 CHECKCOPY(token, ptoken, token_size, pline); 4804 colno++; 4805 } 4806 break; 4807 4808 default: 4809 if (IN(state, ST_HTML_TAG)) 4810 { 4811 pline++; 4812 colno++; 4813 } 4814 else 4815 { 4816 CHECKCOPY(token, ptoken, token_size, pline); 4817 colno++; 4818 } 4819 } 4820 goto do_line; 4821 4822 done_line: 4823 *ptoken = 0; 4824 4825 if (*token && read_yaml_macros_and_links && (IN(state, ST_YAML_VAL))) 4826 { 4827 *ptoken = 0; 4828 if (!vars) 4829 { 4830 vars_count = 1; 4831 CALLOC(vars, KeyValue, vars_count); 4832 pvars = vars; 4833 } 4834 pvars->value_size = strlen((char*)token) + 1; 4835 CALLOC(pvars->value, u8, pvars->value_size); 4836 if (strlcpy((char*)pvars->value, (char*)token, pvars->value_size) 4837 >= pvars->value_size) 4838 warning(1, (u8*)"strlcpy:%d: Overflow", __LINE__); 4839 } 4840 else if (keep_token) 4841 { 4842 if (ptoken + 2 > token + token_size) 4843 { 4844 size_t old_size = token_size; 4845 token_size += BUFSIZE; 4846 REALLOC(token, u8, token_size); 4847 ptoken = token + old_size - 1; 4848 } 4849 if (strlcat((char*)token, 4850 ANY(state, ST_IMAGE | ST_LINK) ? " " : "\n", 4851 token_size) 4852 > token_size) 4853 warning(1, (u8*)"strlcat:%d: Overflow", __LINE__); 4854 ptoken++; 4855 *ptoken = 0; 4856 } 4857 else 4858 { 4859 if (*token) 4860 { 4861 *ptoken = 0; 4862 size_t token_len = strlen((char*)token); 4863 if (IN(state, ST_MACRO_BODY)) 4864 { 4865 skip_eol = 1; 4866 if (!read_yaml_macros_and_links) 4867 goto skip_macro_read; 4868 if (!macros) 4869 { 4870 macros_count = 1; 4871 CALLOC(macros, KeyValue, macros_count); 4872 pmacros = macros; 4873 } 4874 if (pmacros->value) 4875 { 4876 size_t value_len 4877 = strlen((char*)pmacros->value); 4878 4879 if (pmacros->value_size 4880 < value_len + token_len + 1) 4881 { 4882 pmacros->value_size += BUFSIZE; 4883 REALLOC(pmacros->value, u8, 4884 pmacros->value_size); 4885 } 4886 if (strlcat((char*)pmacros->value, 4887 (char*)token, 4888 pmacros->value_size) 4889 > pmacros->value_size) 4890 warning(1, (u8*)"strlcat:%d: Overflow", 4891 __LINE__); 4892 if (strlcat((char*)pmacros->value, " ", 4893 pmacros->value_size) 4894 > pmacros->value_size) 4895 warning(1, (u8*)"strlcat:%d: Overflow", 4896 __LINE__); 4897 } 4898 else 4899 { 4900 pmacros->value_size = BUFSIZE; 4901 CALLOC(pmacros->value, u8, 4902 pmacros->value_size); 4903 if (strlcpy((char*)pmacros->value, 4904 (char*)token, 4905 pmacros->value_size) 4906 >= pmacros->value_size) 4907 warning(1, (u8*)"strlcpy:%d: Overflow", 4908 __LINE__); 4909 if (strlcat((char*)pmacros->value, " ", 4910 pmacros->value_size) 4911 > pmacros->value_size) 4912 warning(1, (u8*)"strlcat:%d: Overflow", 4913 __LINE__); 4914 } 4915 skip_macro_read:; 4916 } 4917 else if (IN(state, ST_FOOTNOTE_TEXT)) 4918 { 4919 skip_eol = 1; 4920 if (!read_yaml_macros_and_links) 4921 goto skip_footnote_read; 4922 if (!footnotes) 4923 { 4924 footnote_count = 1; 4925 CALLOC(footnotes, KeyValue, 4926 footnote_count); 4927 pfootnotes = footnotes; 4928 } 4929 if (pfootnotes->value) 4930 { 4931 size_t value_len = strlen( 4932 (char*)pfootnotes->value); 4933 4934 if (pfootnotes->value_size 4935 < value_len + token_len + 1) 4936 { 4937 pfootnotes->value_size 4938 += BUFSIZE; 4939 REALLOC(pfootnotes->value, u8, 4940 pfootnotes->value_size); 4941 } 4942 if (strlcat((char*)pfootnotes->value, 4943 (char*)token, 4944 pfootnotes->value_size) 4945 > pfootnotes->value_size) 4946 warning(1, (u8*)"strlcat:%d: Overflow", 4947 __LINE__); 4948 if (strlcat((char*)pfootnotes->value, 4949 " ", pfootnotes->value_size) 4950 > pfootnotes->value_size) 4951 warning(1, (u8*)"strlcat:%d: Overflow", 4952 __LINE__); 4953 } 4954 else 4955 { 4956 pfootnotes->value_size = BUFSIZE; 4957 CALLOC(pfootnotes->value, u8, 4958 pfootnotes->value_size); 4959 if (strlcpy((char*)pfootnotes->value, 4960 (char*)token, 4961 pfootnotes->value_size) 4962 >= pfootnotes->value_size) 4963 warning(1, (u8*)"strlcpy:%d: Overflow", 4964 __LINE__); 4965 if (strlcat((char*)pfootnotes->value, 4966 " ", pfootnotes->value_size) 4967 > pfootnotes->value_size) 4968 warning(1, (u8*)"strlcat:%d: Overflow", 4969 __LINE__); 4970 } 4971 *token = 0; 4972 ptoken = token; 4973 skip_footnote_read:; 4974 } 4975 else if (ANY(state, 4976 ST_LINK_SECOND_ARG 4977 | ST_LINK_SECOND_ARG_END)) 4978 { 4979 if (read_yaml_macros_and_links) 4980 { 4981 if (!links) 4982 { 4983 links_count = 1; 4984 CALLOC(links, KeyValue, 4985 links_count); 4986 plinks = links; 4987 } 4988 plinks->value_size 4989 = strlen((char*)token) + 1; 4990 CALLOC(plinks->value, u8, 4991 plinks->value_size); 4992 if (strlcpy((char*)plinks->value, 4993 (char*)token, 4994 plinks->value_size) 4995 >= plinks->value_size) 4996 warning(1, (u8*)"strlcpy:%d: Overflow", 4997 __LINE__); 4998 } 4999 } 5000 else if (IN(state, ST_HEADING)) 5001 { 5002 state &= ~(ST_HEADING | ST_HEADING_TEXT); 5003 if (!read_yaml_macros_and_links) 5004 process_heading(output, token); 5005 first_line_in_doc = 0; 5006 RESET_TOKEN(token, ptoken, token_size); 5007 heading_level = 0; 5008 } 5009 else if (!ANY(state, 5010 ST_DISPLAY_FORMULA | ST_FORMULA 5011 | ST_IMAGE | ST_LINK)) 5012 process_text_token(output, line, link_prefix, 5013 first_line_in_doc, previous_line_blank, 5014 &previous_line_block, 5015 processed_start_of_line, 5016 read_yaml_macros_and_links, list_para, 5017 &token, &ptoken, &token_size, 1, 1); 5018 } 5019 5020 if (!ANY(state, 5021 ST_PRE | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG 5022 | ST_LINK_SECOND_ARG_END) 5023 && (!*pbuffer || *pbuffer == '\n')) 5024 { 5025 if (IN(state, ST_PARA_OPEN)) 5026 { 5027 /*if (!read_yaml_macros_and_links) 5028 { 5029 print_output(output, "\n;"); 5030 output_firstcol = 1; 5031 }*/ 5032 if (IN(state, ST_LIST)) 5033 skip_eol = 1; 5034 state &= ~ST_PARA_OPEN; 5035 } 5036 5037 if (!*pbuffer && (IN(state, ST_LIST))) 5038 { 5039 if (!read_yaml_macros_and_links) 5040 { 5041 process_list_item_end(output, 5042 link_prefix); 5043 process_list_end(output); 5044 } 5045 state &= ~ST_LIST; 5046 } 5047 5048 if (!*pbuffer && (IN(state, ST_NUMLIST))) 5049 { 5050 if (!read_yaml_macros_and_links) 5051 { 5052 process_list_item_end(output, 5053 link_prefix); 5054 process_numlist_end(output); 5055 } 5056 state &= ~ST_NUMLIST; 5057 } 5058 5059 if (IN(state, ST_PARA_OPEN)) 5060 { 5061 if (!read_yaml_macros_and_links) 5062 { 5063 process_para_end(output, link_prefix, 5064 previous_line_blank, 5065 read_yaml_macros_and_links); 5066 output_firstcol = 1; 5067 } 5068 5069 if (IN(state, ST_LIST)) 5070 skip_eol = 1; 5071 5072 state &= ~ST_PARA_OPEN; 5073 } 5074 5075 if (!*pbuffer && (IN(state, ST_FOOTNOTE_TEXT))) 5076 { 5077 state &= ~ST_FOOTNOTE_TEXT; 5078 5079 *ptoken = 0; 5080 size_t token_len = strlen((char*)token); 5081 5082 skip_eol = 1; 5083 if (!read_yaml_macros_and_links) 5084 goto skip_footnote_text_read; 5085 if (!footnotes) 5086 { 5087 footnote_count = 1; 5088 CALLOC(footnotes, KeyValue, 5089 footnote_count); 5090 pfootnotes = footnotes; 5091 } 5092 if (pfootnotes->value) 5093 { 5094 size_t value_len = strlen( 5095 (char*)pfootnotes->value); 5096 5097 if (pfootnotes->value_size 5098 < value_len + token_len + 1) 5099 { 5100 pfootnotes->value_size 5101 = value_len + token_len 5102 + 1; 5103 REALLOC(pfootnotes->value, u8, 5104 pfootnotes->value_size); 5105 } 5106 if (strlcat((char*)pfootnotes->value, 5107 (char*)token, 5108 pfootnotes->value_size) 5109 > pfootnotes->value_size) 5110 warning(1, (u8*)"strlcat:%d: Overflow", 5111 __LINE__); 5112 } 5113 else 5114 { 5115 pfootnotes->value_size = token_len + 1; 5116 CALLOC(pfootnotes->value, u8, 5117 pfootnotes->value_size); 5118 if (strlcpy((char*)pfootnotes->value, 5119 (char*)token, 5120 pfootnotes->value_size) 5121 >= pfootnotes->value_size) 5122 warning(1, (u8*)"strlcpy:%d: Overflow", 5123 __LINE__); 5124 } 5125 *token = 0; 5126 ptoken = token; 5127 skip_footnote_text_read:; 5128 } 5129 } 5130 5131 if (IN(state, ST_BLOCKQUOTE) && (!*pbuffer || *pbuffer != '>')) 5132 { 5133 state &= ~ST_BLOCKQUOTE; 5134 if (!read_yaml_macros_and_links) 5135 { 5136 process_blockquote(output, 1); 5137 previous_line_block = 1; 5138 output_firstcol = 1; 5139 } 5140 } 5141 5142 if (ANY(state, ST_TABLE_HEADER | ST_TABLE_LINE) 5143 && (!line_len || !*pbuffer)) 5144 { 5145 if (!read_yaml_macros_and_links) 5146 { 5147 warning(1, (u8*)"Malformed table"); 5148 process_table_end(output); 5149 output_firstcol = 1; 5150 } 5151 state &= ~(ST_TABLE_HEADER | ST_TABLE_LINE); 5152 } 5153 5154 if ((IN(state, ST_TABLE)) && (!line_len || !*pbuffer)) 5155 { 5156 if (!read_yaml_macros_and_links) 5157 { 5158 process_table_end(output); 5159 output_firstcol = 1; 5160 } 5161 state &= ~ST_TABLE; 5162 } 5163 } 5164 5165 if (!skip_eol && !keep_token && !read_yaml_macros_and_links 5166 && !ANY(state, 5167 ST_YAML | ST_YAML_VAL | ST_LINK_SECOND_ARG 5168 | ST_LINK_SECOND_ARG_END)) 5169 { 5170 print_output(output, "%s", 5171 IN(state, ST_PRE) ? "\n" : (output_firstcol ? "" : " ")); 5172 output_firstcol = (IN(state, ST_PRE)) || output_firstcol; 5173 } 5174 5175 if (!keep_token) 5176 RESET_TOKEN(token, ptoken, token_size); 5177 5178 if (!skip_change_first_line_in_doc && !(IN(state, ST_YAML))) 5179 first_line_in_doc = 0; 5180 5181 skip_change_first_line_in_doc = 0; 5182 if (line_len == 0) 5183 { 5184 if (!previous_line_blank) 5185 { 5186 if (!read_yaml_macros_and_links 5187 && IN(state, ST_PARA_OPEN)) 5188 { 5189 if (IN(state, ST_LIST)) 5190 process_list_item_end(output, 5191 link_prefix); 5192 else 5193 { 5194 process_para_end(output, link_prefix, 5195 previous_line_blank, 5196 read_yaml_macros_and_links); 5197 output_firstcol = 1; 5198 } 5199 } 5200 else 5201 { 5202 process_para_end(output, link_prefix, 5203 previous_line_blank, 5204 read_yaml_macros_and_links); 5205 output_firstcol = 1; 5206 } 5207 } 5208 else 5209 { 5210 process_para_end(output, link_prefix, 5211 previous_line_blank, 5212 read_yaml_macros_and_links); 5213 output_firstcol = 1; 5214 } 5215 previous_line_blank = 1; 5216 } 5217 else 5218 previous_line_blank = 0; 5219 5220 /* Lasts until the end of line */ 5221 state &= ~(ST_YAML_VAL | ST_IMAGE_SECOND_ARG | ST_LINK_SECOND_ARG 5222 | ST_LINK_SECOND_ARG_END); 5223 5224 if (pbuffer && *pbuffer) 5225 goto do_buffer; 5226 done_buffer: 5227 if (!read_yaml_macros_and_links) 5228 { 5229 if (footnote_count > 0 || inline_footnote_count > 0) 5230 end_footnotes(output, link_prefix, add_footnote_div, 5231 previous_line_blank, 5232 read_yaml_macros_and_links); 5233 5234 if (paralinks_count > 0) 5235 process_paralinks(output, link_prefix); 5236 5237 if (madeby_present) 5238 { 5239 process_madeby(output); 5240 output_firstcol = 1; 5241 } 5242 } 5243 5244 free(link_text); 5245 free(link_macro); 5246 free(token); 5247 free(line); 5248 5249 return 0; 5250 } 5251 5252 int 5253 main(const int argc, const char** argv) 5254 { 5255 const char* arg; 5256 Command cmd = CMD_NONE; 5257 int result = 0; 5258 FILE* input = NULL; 5259 FILE* output = stdout; 5260 u8* buffer = NULL; 5261 size_t buffer_size = 0; 5262 5263 basedir_size = 2; 5264 CALLOC(basedir, char, basedir_size); 5265 *basedir = '.'; 5266 5267 while ((arg = *++argv)) 5268 { 5269 if (*arg == '-') 5270 { 5271 arg++; 5272 char c = *arg++; 5273 int result; 5274 5275 if (c == '-') 5276 { 5277 if (!strcmp(arg, "full-version")) 5278 cmd = CMD_FULL_VERSION; 5279 else if (!strcmp(arg, "version")) 5280 cmd = CMD_VERSION; 5281 else if (startswith(arg, "basedir")) 5282 { 5283 arg += strlen("basedir"); 5284 result = set_basedir(&basedir, 5285 &basedir_size, arg); 5286 if (result) 5287 return result; 5288 } 5289 else if (startswith(arg, "global-link-prefix")) 5290 { 5291 arg += strlen("global-link-prefix"); 5292 result = set_global_link_prefix( 5293 &global_link_prefix, 5294 &global_link_prefix_size, arg); 5295 if (result) 5296 return result; 5297 } 5298 else if (!strcmp(arg, "help")) 5299 return usage(); 5300 else 5301 { 5302 error(EINVAL, 5303 (u8*)"main: Invalid " 5304 "argument: --%s", 5305 arg); 5306 return usage(); 5307 } 5308 } 5309 else 5310 { 5311 switch (c) 5312 { 5313 case 'd': 5314 cmd = CMD_BASEDIR; 5315 break; 5316 case 'h': 5317 return usage(); 5318 break; 5319 case 'p': 5320 cmd = CMD_LINK_PREFIX; 5321 break; 5322 case 'V': 5323 cmd = CMD_FULL_VERSION; 5324 break; 5325 case 'v': 5326 cmd = CMD_VERSION; 5327 break; 5328 default: 5329 error(EINVAL, 5330 (u8*)"main: Invalid " 5331 "argument: -%c", 5332 c); 5333 return usage(); 5334 } 5335 } 5336 } 5337 else 5338 { 5339 int result; 5340 5341 if (cmd == CMD_BASEDIR) 5342 { 5343 result = set_basedir(&basedir, &basedir_size, 5344 arg); 5345 if (result) 5346 return result; 5347 } 5348 else if (cmd == CMD_LINK_PREFIX) 5349 { 5350 result = set_global_link_prefix(&global_link_prefix, 5351 &global_link_prefix_size, arg); 5352 if (result) 5353 return result; 5354 } 5355 else 5356 input_filename = arg; 5357 cmd = CMD_NONE; 5358 } 5359 } 5360 5361 if (cmd == CMD_BASEDIR) 5362 return error(1, (u8*)"main: set_basedir: Argument required"); 5363 5364 if (cmd == CMD_FULL_VERSION) 5365 return version(1); 5366 else if (cmd == CMD_VERSION) 5367 return version(0); 5368 5369 if (input_filename) 5370 { 5371 int result = read_file_into_buffer(&input, &buffer, 5372 &buffer_size, input_filename, &input_dirname); 5373 if (result) 5374 exit(result); 5375 } 5376 else 5377 { 5378 u8* bufline = NULL; 5379 size_t buffer_len = 0; 5380 size_t buffer_size = 0; 5381 size_t bufline_len = 0; 5382 5383 input = stdin; 5384 5385 CALLOC(input_dirname, char, 2); 5386 *input_dirname = '.'; 5387 5388 CALLOC(bufline, u8, BUFSIZE); 5389 buffer_size = BUFSIZE; 5390 CALLOC(buffer, u8, buffer_size); 5391 5392 while (!feof(input)) 5393 { 5394 u8* eol = NULL; 5395 if (!fgets((char*)bufline, BUFSIZE - 1, input)) 5396 break; 5397 eol = (u8*)strchr((char*)bufline, '\n'); 5398 if (eol) 5399 *(eol + 1) = 0; 5400 bufline_len = strlen((char*)bufline); 5401 if (buffer_len + bufline_len + 1 > buffer_size) 5402 { 5403 buffer_size += BUFSIZE; 5404 REALLOC(buffer, u8, buffer_size); 5405 } 5406 if (strlcat((char*)buffer, (char*)bufline, buffer_size) 5407 > buffer_size) 5408 warning(1, (u8*)"strlcat:%d: Overflow", 5409 __LINE__); 5410 buffer_len += bufline_len; 5411 } 5412 free(bufline); 5413 } 5414 5415 vars = NULL; 5416 vars_count = 0; 5417 macros = NULL; 5418 macros_count = 0; 5419 links = NULL; 5420 links_count = 0; 5421 paralinks = NULL; 5422 paralinks_count = 0; 5423 footnotes = NULL; 5424 footnote_count = 0; 5425 inline_footnotes = NULL; 5426 inline_footnote_count = 0; 5427 5428 /* First pass: read YAML, macros and links */ 5429 result = slweb_parse(output, input_filename, buffer, 1); 5430 5431 if (result) 5432 { 5433 slweb_cleanup(); 5434 free(buffer); 5435 return result; 5436 } 5437 5438 state = ST_NONE; 5439 current_footnote = 0; 5440 current_inline_footnote = 0; 5441 5442 /* Second pass: parse and output */ 5443 result = slweb_parse(output, input_filename, buffer, 0); 5444 5445 slweb_cleanup(); 5446 free(buffer); 5447 5448 return result; 5449 }