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