jcs's openbsd hax
openbsd
at jcs 967 lines 19 kB view raw
1/* $OpenBSD: misc.c,v 1.21 2024/11/09 18:03:44 op Exp $ */ 2 3/* misc - miscellaneous flex routines */ 4 5/* Copyright (c) 1990 The Regents of the University of California. */ 6/* All rights reserved. */ 7 8/* This code is derived from software contributed to Berkeley by */ 9/* Vern Paxson. */ 10 11/* The United States Government has rights in this work pursuant */ 12/* to contract no. DE-AC03-76SF00098 between the United States */ 13/* Department of Energy and the University of California. */ 14 15/* This file is part of flex. */ 16 17/* Redistribution and use in source and binary forms, with or without */ 18/* modification, are permitted provided that the following conditions */ 19/* are met: */ 20 21/* 1. Redistributions of source code must retain the above copyright */ 22/* notice, this list of conditions and the following disclaimer. */ 23/* 2. Redistributions in binary form must reproduce the above copyright */ 24/* notice, this list of conditions and the following disclaimer in the */ 25/* documentation and/or other materials provided with the distribution. */ 26 27/* Neither the name of the University nor the names of its contributors */ 28/* may be used to endorse or promote products derived from this software */ 29/* without specific prior written permission. */ 30 31/* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */ 32/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ 33/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ 34/* PURPOSE. */ 35 36#include "flexdef.h" 37#include "tables.h" 38 39#define CMD_IF_TABLES_SER "%if-tables-serialization" 40#define CMD_TABLES_YYDMAP "%tables-yydmap" 41#define CMD_DEFINE_YYTABLES "%define-yytables" 42#define CMD_IF_CPP_ONLY "%if-c++-only" 43#define CMD_IF_C_ONLY "%if-c-only" 44#define CMD_IF_C_OR_CPP "%if-c-or-c++" 45#define CMD_NOT_FOR_HEADER "%not-for-header" 46#define CMD_OK_FOR_HEADER "%ok-for-header" 47#define CMD_PUSH "%push" 48#define CMD_POP "%pop" 49#define CMD_IF_REENTRANT "%if-reentrant" 50#define CMD_IF_NOT_REENTRANT "%if-not-reentrant" 51#define CMD_IF_BISON_BRIDGE "%if-bison-bridge" 52#define CMD_IF_NOT_BISON_BRIDGE "%if-not-bison-bridge" 53#define CMD_ENDIF "%endif" 54 55/* we allow the skeleton to push and pop. */ 56struct sko_state { 57 bool dc; /**< do_copy */ 58}; 59static struct sko_state *sko_stack = 0; 60static int sko_len = 0, sko_sz = 0; 61static void 62sko_push(bool dc) 63{ 64 if (!sko_stack) { 65 sko_sz = 1; 66 sko_stack = malloc(sizeof(struct sko_state) * sko_sz); 67 if (!sko_stack) 68 flexfatal(_("allocation of sko_stack failed")); 69 sko_len = 0; 70 } 71 if (sko_len >= sko_sz) { 72 sko_sz *= 2; 73 sko_stack = realloc(sko_stack, sizeof(struct sko_state) * sko_sz); 74 } 75 /* initialize to zero and push */ 76 sko_stack[sko_len].dc = dc; 77 sko_len++; 78} 79static void 80sko_peek(bool * dc) 81{ 82 if (sko_len <= 0) 83 flex_die("peek attempt when sko stack is empty"); 84 if (dc) 85 *dc = sko_stack[sko_len - 1].dc; 86} 87static void 88sko_pop(bool * dc) 89{ 90 sko_peek(dc); 91 sko_len--; 92 if (sko_len < 0) 93 flex_die("popped too many times in skeleton."); 94} 95 96/* Append "#define defname value\n" to the running buffer. */ 97void 98action_define(const char *defname, int value) 99{ 100 char buf[MAXLINE]; 101 char *cpy; 102 103 if ((int) strlen(defname) > MAXLINE / 2) { 104 format_pinpoint_message(_ 105 ("name \"%s\" ridiculously long"), 106 defname); 107 return; 108 } 109 snprintf(buf, sizeof(buf), "#define %s %d\n", defname, value); 110 add_action(buf); 111 112 /* track #defines so we can undef them when we're done. */ 113 cpy = copy_string(defname); 114 buf_append(&defs_buf, &cpy, 1); 115} 116 117 118/* Append "new_text" to the running buffer. */ 119void 120add_action(const char *new_text) 121{ 122 int len = strlen(new_text); 123 124 while (len + action_index >= action_size - 10 /* slop */ ) { 125 int new_size = action_size * 2; 126 127 if (new_size <= 0) 128 /* 129 * Increase just a little, to try to avoid overflow 130 * on 16-bit machines. 131 */ 132 action_size += action_size / 8; 133 else 134 action_size = new_size; 135 136 action_array = 137 reallocate_character_array(action_array, 138 action_size); 139 } 140 141 strlcpy(&action_array[action_index], new_text, 142 action_size - action_index); 143 144 action_index += len; 145} 146 147 148/* allocate_array - allocate memory for an integer array of the given size */ 149 150void * 151allocate_array(int size, size_t element_size) 152{ 153 void *mem; 154 size_t num_bytes = element_size * size; 155 156 mem = malloc(num_bytes); 157 if (!mem) 158 flexfatal(_ 159 ("memory allocation failed in allocate_array()")); 160 161 return mem; 162} 163 164 165/* all_lower - true if a string is all lower-case */ 166 167int 168all_lower(char *str) 169{ 170 while (*str) { 171 if (!isascii((u_char) * str) || !islower((u_char) * str)) 172 return 0; 173 ++str; 174 } 175 176 return 1; 177} 178 179 180/* all_upper - true if a string is all upper-case */ 181 182int 183all_upper(char *str) 184{ 185 while (*str) { 186 if (!isascii((u_char) * str) || !isupper((u_char) * str)) 187 return 0; 188 ++str; 189 } 190 191 return 1; 192} 193 194 195/* intcmp - compares two integers for use by qsort. */ 196 197int 198intcmp(const void *a, const void *b) 199{ 200 return *(const int *) a - *(const int *) b; 201} 202 203 204/* check_char - checks a character to make sure it's within the range 205 * we're expecting. If not, generates fatal error message 206 * and exits. 207 */ 208 209void 210check_char(int c) 211{ 212 if (c >= CSIZE) 213 lerrsf(_("bad character '%s' detected in check_char()"), 214 readable_form(c)); 215 216 if (c >= csize) 217 lerrsf(_ 218 ("scanner requires -8 flag to use the character %s"), 219 readable_form(c)); 220} 221 222 223 224/* clower - replace upper-case letter to lower-case */ 225 226u_char 227clower(int c) 228{ 229 return (u_char) ((isascii(c) && isupper(c)) ? tolower(c) : c); 230} 231 232 233/* copy_string - returns a dynamically allocated copy of a string */ 234 235char * 236copy_string(const char *str) 237{ 238 const char *c1; 239 char *c2; 240 char *copy; 241 unsigned int size; 242 243 /* find length */ 244 for (c1 = str; *c1; ++c1); 245 246 size = (c1 - str + 1) * sizeof(char); 247 248 copy = (char *) malloc(size); 249 250 if (copy == NULL) 251 flexfatal(_("dynamic memory failure in copy_string()")); 252 253 for (c2 = copy; (*c2++ = *str++) != 0;); 254 255 return copy; 256} 257 258 259/* copy_unsigned_string - 260 * returns a dynamically allocated copy of a (potentially) unsigned string 261 */ 262 263u_char * 264copy_unsigned_string(unsigned char *str) 265{ 266 u_char *c; 267 u_char *copy; 268 269 /* find length */ 270 for (c = str; *c; ++c); 271 272 copy = allocate_Character_array(c - str + 1); 273 274 for (c = copy; (*c++ = *str++) != 0;); 275 276 return copy; 277} 278 279 280/* cclcmp - compares two characters for use by qsort with '\0' sorting last. */ 281 282int 283cclcmp(const void *a, const void *b) 284{ 285 if (!*(const u_char *) a) 286 return 1; 287 else if (!*(const u_char *) b) 288 return -1; 289 else 290 return *(const u_char *) a - *(const u_char *) b; 291} 292 293 294/* dataend - finish up a block of data declarations */ 295 296void 297dataend(void) 298{ 299 /* short circuit any output */ 300 if (gentables) { 301 302 if (datapos > 0) 303 dataflush(); 304 305 /* add terminator for initialization; { for vi */ 306 outn(" } ;\n"); 307 } 308 dataline = 0; 309 datapos = 0; 310} 311 312 313/* dataflush - flush generated data statements */ 314 315void 316dataflush(void) 317{ 318 /* short circuit any output */ 319 if (!gentables) 320 return; 321 322 outc('\n'); 323 324 if (++dataline >= NUMDATALINES) { 325 /* 326 * Put out a blank line so that the table is grouped into 327 * large blocks that enable the user to find elements easily. 328 */ 329 outc('\n'); 330 dataline = 0; 331 } 332 /* Reset the number of characters written on the current line. */ 333 datapos = 0; 334} 335 336 337/* flexerror - report an error message and terminate */ 338 339void 340flexerror(const char *msg) 341{ 342 fprintf(stderr, "%s: %s\n", program_name, msg); 343 flexend(1); 344} 345 346 347/* flexfatal - report a fatal error message and terminate */ 348 349void 350flexfatal(const char *msg) 351{ 352 fprintf(stderr, _("%s: fatal internal error, %s\n"), 353 program_name, msg); 354 FLEX_EXIT(1); 355} 356 357 358/* htoi - convert a hexadecimal digit string to an integer value */ 359 360int 361htoi(unsigned char str[]) 362{ 363 unsigned int result; 364 365 (void) sscanf((char *) str, "%x", &result); 366 367 return result; 368} 369 370 371/* lerrif - report an error message formatted with one integer argument */ 372 373void 374lerrif(const char *msg, int arg) 375{ 376 char errmsg[MAXLINE]; 377 378 snprintf(errmsg, sizeof(errmsg), msg, arg); 379 flexerror(errmsg); 380} 381 382 383/* lerrsf - report an error message formatted with one string argument */ 384 385void 386lerrsf(const char *msg, const char arg[]) 387{ 388 char errmsg[MAXLINE]; 389 390 snprintf(errmsg, sizeof(errmsg) - 1, msg, arg); 391 errmsg[sizeof(errmsg) - 1] = 0; /* ensure NULL termination */ 392 flexerror(errmsg); 393} 394 395 396/* lerrsf_fatal - as lerrsf, but call flexfatal */ 397 398void 399lerrsf_fatal(const char *msg, const char arg[]) 400{ 401 char errmsg[MAXLINE]; 402 403 snprintf(errmsg, sizeof(errmsg) - 1, msg, arg); 404 errmsg[sizeof(errmsg) - 1] = 0; /* ensure NULL termination */ 405 flexfatal(errmsg); 406} 407 408 409/* line_directive_out - spit out a "#line" statement */ 410 411void 412line_directive_out(FILE *output_file, int do_infile) 413{ 414 char directive[MAXLINE], filename[MAXLINE]; 415 char *s1, *s2, *s3; 416 static const char *line_fmt = "#line %d \"%s\"\n"; 417 418 if (!gen_line_dirs) 419 return; 420 421 s1 = do_infile ? infilename : "M4_YY_OUTFILE_NAME"; 422 423 if (do_infile && !s1) 424 s1 = "<stdin>"; 425 426 s2 = filename; 427 s3 = &filename[sizeof(filename) - 2]; 428 429 while (s2 < s3 && *s1) { 430 if (*s1 == '\\') 431 /* Escape the '\' */ 432 *s2++ = '\\'; 433 434 *s2++ = *s1++; 435 } 436 437 *s2 = '\0'; 438 439 if (do_infile) 440 snprintf(directive, sizeof(directive), line_fmt, linenum, filename); 441 else { 442 snprintf(directive, sizeof(directive), line_fmt, 0, filename); 443 } 444 445 /* 446 * If output_file is nil then we should put the directive in the 447 * accumulated actions. 448 */ 449 if (output_file) { 450 fputs(directive, output_file); 451 } else 452 add_action(directive); 453} 454 455 456/* mark_defs1 - mark the current position in the action array as 457 * representing where the user's section 1 definitions end 458 * and the prolog begins 459 */ 460void 461mark_defs1(void) 462{ 463 defs1_offset = 0; 464 action_array[action_index++] = '\0'; 465 action_offset = prolog_offset = action_index; 466 action_array[action_index] = '\0'; 467} 468 469 470/* mark_prolog - mark the current position in the action array as 471 * representing the end of the action prolog 472 */ 473void 474mark_prolog(void) 475{ 476 action_array[action_index++] = '\0'; 477 action_offset = action_index; 478 action_array[action_index] = '\0'; 479} 480 481 482/* mk2data - generate a data statement for a two-dimensional array 483 * 484 * Generates a data statement initializing the current 2-D array to "value". 485 */ 486void 487mk2data(int value) 488{ 489 /* short circuit any output */ 490 if (!gentables) 491 return; 492 493 if (datapos >= NUMDATAITEMS) { 494 outc(','); 495 dataflush(); 496 } 497 if (datapos == 0) 498 /* Indent. */ 499 out(" "); 500 501 else 502 outc(','); 503 504 ++datapos; 505 506 out_dec("%5d", value); 507} 508 509 510/* mkdata - generate a data statement 511 * 512 * Generates a data statement initializing the current array element to 513 * "value". 514 */ 515void 516mkdata(int value) 517{ 518 /* short circuit any output */ 519 if (!gentables) 520 return; 521 522 if (datapos >= NUMDATAITEMS) { 523 outc(','); 524 dataflush(); 525 } 526 if (datapos == 0) 527 /* Indent. */ 528 out(" "); 529 else 530 outc(','); 531 532 ++datapos; 533 534 out_dec("%5d", value); 535} 536 537 538/* myctoi - return the integer represented by a string of digits */ 539 540int 541myctoi(const char *array) 542{ 543 int val = 0; 544 545 (void) sscanf(array, "%d", &val); 546 547 return val; 548} 549 550 551/* myesc - return character corresponding to escape sequence */ 552 553u_char 554myesc(unsigned char array[]) 555{ 556 u_char c, esc_char; 557 558 switch (array[1]) { 559 case 'b': 560 return '\b'; 561 case 'f': 562 return '\f'; 563 case 'n': 564 return '\n'; 565 case 'r': 566 return '\r'; 567 case 't': 568 return '\t'; 569 570#if defined (__STDC__) 571 case 'a': 572 return '\a'; 573 case 'v': 574 return '\v'; 575#else 576 case 'a': 577 return '\007'; 578 case 'v': 579 return '\013'; 580#endif 581 582 case '0': 583 case '1': 584 case '2': 585 case '3': 586 case '4': 587 case '5': 588 case '6': 589 case '7': 590 { /* \<octal> */ 591 int sptr = 1; 592 593 while (isascii(array[sptr]) && 594 isdigit(array[sptr])) 595 /* 596 * Don't increment inside loop control 597 * because if isdigit() is a macro it might 598 * expand into multiple increments ... 599 */ 600 ++sptr; 601 602 c = array[sptr]; 603 array[sptr] = '\0'; 604 605 esc_char = otoi(array + 1); 606 607 array[sptr] = c; 608 609 return esc_char; 610 } 611 612 case 'x': 613 { /* \x<hex> */ 614 int sptr = 2; 615 616 while (isascii(array[sptr]) && 617 isxdigit(array[sptr])) 618 /* 619 * Don't increment inside loop control 620 * because if isdigit() is a macro it might 621 * expand into multiple increments ... 622 */ 623 ++sptr; 624 625 c = array[sptr]; 626 array[sptr] = '\0'; 627 628 esc_char = htoi(array + 2); 629 630 array[sptr] = c; 631 632 return esc_char; 633 } 634 635 default: 636 return array[1]; 637 } 638} 639 640 641/* otoi - convert an octal digit string to an integer value */ 642 643int 644otoi(unsigned char str[]) 645{ 646 unsigned int result; 647 648 (void) sscanf((char *) str, "%o", &result); 649 return result; 650} 651 652 653/* out - various flavors of outputting a (possibly formatted) string for the 654 * generated scanner, keeping track of the line count. 655 */ 656 657void 658out(const char *str) 659{ 660 fputs(str, stdout); 661} 662 663void 664out_dec(const char *fmt, int n) 665{ 666 fprintf(stdout, fmt, n); 667} 668 669void 670out_dec2(const char *fmt, int n1, int n2) 671{ 672 fprintf(stdout, fmt, n1, n2); 673} 674 675void 676out_hex(const char *fmt, unsigned int x) 677{ 678 fprintf(stdout, fmt, x); 679} 680 681void 682out_str(const char *fmt, const char str[]) 683{ 684 fprintf(stdout, fmt, str); 685} 686 687void 688out_str3(const char *fmt, const char s1[], const char s2[], const char s3[]) 689{ 690 fprintf(stdout, fmt, s1, s2, s3); 691} 692 693void 694out_str_dec(const char *fmt, const char str[], int n) 695{ 696 fprintf(stdout, fmt, str, n); 697} 698 699void 700outc(int c) 701{ 702 fputc(c, stdout); 703} 704 705void 706outn(const char *str) 707{ 708 fputs(str, stdout); 709 fputc('\n', stdout); 710} 711 712/** Print "m4_define( [[def]], [[val]])m4_dnl\n". 713 * @param def The m4 symbol to define. 714 * @param val The definition; may be NULL. 715 * @return buf 716 */ 717void 718out_m4_define(const char *def, const char *val) 719{ 720 const char *fmt = "m4_define( [[%s]], [[%s]])m4_dnl\n"; 721 fprintf(stdout, fmt, def, val ? val : ""); 722} 723 724 725/* readable_form - return the human-readable form of a character 726 * 727 * The returned string is in static storage. 728 */ 729 730char * 731readable_form(int c) 732{ 733 static char rform[10]; 734 735 if ((c >= 0 && c < 32) || c >= 127) { 736 switch (c) { 737 case '\b': 738 return "\\b"; 739 case '\f': 740 return "\\f"; 741 case '\n': 742 return "\\n"; 743 case '\r': 744 return "\\r"; 745 case '\t': 746 return "\\t"; 747 748#if defined (__STDC__) 749 case '\a': 750 return "\\a"; 751 case '\v': 752 return "\\v"; 753#endif 754 755 default: 756 snprintf(rform, sizeof(rform), "\\%.3o", (unsigned int) c); 757 return rform; 758 } 759 } else if (c == ' ') 760 return "' '"; 761 762 else { 763 rform[0] = c; 764 rform[1] = '\0'; 765 766 return rform; 767 } 768} 769 770 771/* reallocate_array - increase the size of a dynamic array */ 772 773void * 774reallocate_array(void *array, int size, size_t element_size) 775{ 776 void *new_array; 777 size_t num_bytes = element_size * size; 778 779 new_array = realloc(array, num_bytes); 780 if (!new_array) 781 flexfatal(_("attempt to increase array size failed")); 782 783 return new_array; 784} 785 786 787/* skelout - write out one section of the skeleton file 788 * 789 * Description 790 * Copies skelfile or skel array to stdout until a line beginning with 791 * "%%" or EOF is found. 792 */ 793void 794skelout(void) 795{ 796 char buf_storage[MAXLINE]; 797 char *buf = buf_storage; 798 bool do_copy = true; 799 800 /* "reset" the state by clearing the buffer and pushing a '1' */ 801 if (sko_len > 0) 802 sko_peek(&do_copy); 803 sko_len = 0; 804 sko_push(do_copy = true); 805 806 807 /* 808 * Loop pulling lines either from the skelfile, if we're using one, 809 * or from the skel[] array. 810 */ 811 while (skelfile ? 812 (fgets(buf, MAXLINE, skelfile) != NULL) : 813 ((buf = (char *) skel[skel_ind++]) != 0)) { 814 815 if (skelfile) 816 chomp(buf); 817 818 /* copy from skel array */ 819 if (buf[0] == '%') { /* control line */ 820 /* print the control line as a comment. */ 821 if (ddebug && buf[1] != '#') { 822 if (buf[strlen(buf) - 1] == '\\') 823 out_str("/* %s */\\\n", buf); 824 else 825 out_str("/* %s */\n", buf); 826 } 827 /* 828 * We've been accused of using cryptic markers in the 829 * skel. So we'll use 830 * emacs-style-hyphenated-commands. We might consider 831 * a hash if this if-else-if-else chain gets too 832 * large. 833 */ 834#define cmd_match(s) (strncmp(buf,(s),strlen(s))==0) 835 836 if (buf[1] == '%') { 837 /* %% is a break point for skelout() */ 838 return; 839 } else if (cmd_match(CMD_PUSH)) { 840 sko_push(do_copy); 841 if (ddebug) { 842 out_str("/*(state = (%s) */", do_copy ? "true" : "false"); 843 } 844 out_str("%s\n", buf[strlen(buf) - 1] == '\\' ? "\\" : ""); 845 } else if (cmd_match(CMD_POP)) { 846 sko_pop(&do_copy); 847 if (ddebug) { 848 out_str("/*(state = (%s) */", do_copy ? "true" : "false"); 849 } 850 out_str("%s\n", buf[strlen(buf) - 1] == '\\' ? "\\" : ""); 851 } else if (cmd_match(CMD_IF_REENTRANT)) { 852 sko_push(do_copy); 853 do_copy = reentrant && do_copy; 854 } else if (cmd_match(CMD_IF_NOT_REENTRANT)) { 855 sko_push(do_copy); 856 do_copy = !reentrant && do_copy; 857 } else if (cmd_match(CMD_IF_BISON_BRIDGE)) { 858 sko_push(do_copy); 859 do_copy = bison_bridge_lval && do_copy; 860 } else if (cmd_match(CMD_IF_NOT_BISON_BRIDGE)) { 861 sko_push(do_copy); 862 do_copy = !bison_bridge_lval && do_copy; 863 } else if (cmd_match(CMD_ENDIF)) { 864 sko_pop(&do_copy); 865 } else if (cmd_match(CMD_IF_TABLES_SER)) { 866 do_copy = do_copy && tablesext; 867 } else if (cmd_match(CMD_TABLES_YYDMAP)) { 868 if (tablesext && yydmap_buf.elts) 869 outn((char *) (yydmap_buf.elts)); 870 } else if (cmd_match(CMD_DEFINE_YYTABLES)) { 871 out_str("#define YYTABLES_NAME \"%s\"\n", 872 tablesname ? tablesname : "yytables"); 873 } else if (cmd_match(CMD_IF_CPP_ONLY)) { 874 /* only for C++ */ 875 sko_push(do_copy); 876 do_copy = C_plus_plus; 877 } else if (cmd_match(CMD_IF_C_ONLY)) { 878 /* %- only for C */ 879 sko_push(do_copy); 880 do_copy = !C_plus_plus; 881 } else if (cmd_match(CMD_IF_C_OR_CPP)) { 882 /* %* for C and C++ */ 883 sko_push(do_copy); 884 do_copy = true; 885 } else if (cmd_match(CMD_NOT_FOR_HEADER)) { 886 /* %c begin linkage-only (non-header) code. */ 887 OUT_BEGIN_CODE(); 888 } else if (cmd_match(CMD_OK_FOR_HEADER)) { 889 /* %e end linkage-only code. */ 890 OUT_END_CODE(); 891 } else if (buf[1] == '#') { 892 /* %# a comment in the skel. ignore. */ 893 } else { 894 flexfatal(_("bad line in skeleton file")); 895 } 896 } else if (do_copy) 897 outn(buf); 898 } /* end while */ 899} 900 901 902/* transition_struct_out - output a yy_trans_info structure 903 * 904 * outputs the yy_trans_info structure with the two elements, element_v and 905 * element_n. Formats the output with spaces and carriage returns. 906 */ 907 908void 909transition_struct_out(int element_v, int element_n) 910{ 911 912 /* short circuit any output */ 913 if (!gentables) 914 return; 915 916 out_dec2(" {%4d,%4d },", element_v, element_n); 917 918 datapos += TRANS_STRUCT_PRINT_LENGTH; 919 920 if (datapos >= 79 - TRANS_STRUCT_PRINT_LENGTH) { 921 outc('\n'); 922 923 if (++dataline % 10 == 0) 924 outc('\n'); 925 926 datapos = 0; 927 } 928} 929 930 931/* The following is only needed when building flex's parser using certain 932 * broken versions of bison. 933 */ 934void * 935yy_flex_xmalloc(int size) 936{ 937 void *result = malloc((size_t) size); 938 939 if (!result) 940 flexfatal(_ 941 ("memory allocation failed in yy_flex_xmalloc()")); 942 943 return result; 944} 945 946 947/* Remove all '\n' and '\r' characters, if any, from the end of str. 948 * str can be any null-terminated string, or NULL. 949 * returns str. */ 950char * 951chomp(char *str) 952{ 953 char *p = str; 954 955 if (!str || !*str) /* s is null or empty string */ 956 return str; 957 958 /* find end of string minus one */ 959 while (*p) 960 ++p; 961 --p; 962 963 /* eat newlines */ 964 while (p >= str && (*p == '\r' || *p == '\n')) 965 *p-- = 0; 966 return str; 967}