source: trunk/third/findutils/lib/ansi2knr.c @ 18890

Revision 18890, 20.7 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18889, which included commits to RCS files with non-trunk default branches.
Line 
1/* Copyright (C) 1989, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved. */
2
3/*$Id: ansi2knr.c,v 1.1.1.1 2003-02-19 07:56:32 zacheiss Exp $*/
4/* Convert ANSI C function definitions to K&R ("traditional C") syntax */
5
6/*
7ansi2knr is distributed in the hope that it will be useful, but WITHOUT ANY
8WARRANTY.  No author or distributor accepts responsibility to anyone for the
9consequences of using it or for whether it serves any particular purpose or
10works at all, unless he says so in writing.  Refer to the GNU General Public
11License (the "GPL") for full details.
12
13Everyone is granted permission to copy, modify and redistribute ansi2knr,
14but only under the conditions described in the GPL.  A copy of this license
15is supposed to have been given to you along with ansi2knr so you can know
16your rights and responsibilities.  It should be in a file named COPYLEFT,
17or, if there is no file named COPYLEFT, a file named COPYING.  Among other
18things, the copyright notice and this notice must be preserved on all
19copies.
20
21We explicitly state here what we believe is already implied by the GPL: if
22the ansi2knr program is distributed as a separate set of sources and a
23separate executable file which are aggregated on a storage medium together
24with another program, this in itself does not bring the other program under
25the GPL, nor does the mere fact that such a program or the procedures for
26constructing it invoke the ansi2knr executable bring any other part of the
27program under the GPL.
28*/
29
30/*
31 * Usage:
32        ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]
33 * --filename provides the file name for the #line directive in the output,
34 * overriding input_file (if present).
35 * If no input_file is supplied, input is read from stdin.
36 * If no output_file is supplied, output goes to stdout.
37 * There are no error messages.
38 *
39 * ansi2knr recognizes function definitions by seeing a non-keyword
40 * identifier at the left margin, followed by a left parenthesis, with a
41 * right parenthesis as the last character on the line, and with a left
42 * brace as the first token on the following line (ignoring possible
43 * intervening comments and/or preprocessor directives), except that a line
44 * consisting of only
45 *      identifier1(identifier2)
46 * will not be considered a function definition unless identifier2 is
47 * the word "void", and a line consisting of
48 *      identifier1(identifier2, <<arbitrary>>)
49 * will not be considered a function definition.
50 * ansi2knr will recognize a multi-line header provided that no intervening
51 * line ends with a left or right brace or a semicolon.  These algorithms
52 * ignore whitespace, comments, and preprocessor directives, except that
53 * the function name must be the first thing on the line.  The following
54 * constructs will confuse it:
55 *      - Any other construct that starts at the left margin and
56 *          follows the above syntax (such as a macro or function call).
57 *      - Some macros that tinker with the syntax of function headers.
58 */
59
60/*
61 * The original and principal author of ansi2knr is L. Peter Deutsch
62 * <ghost@aladdin.com>.  Other authors are noted in the change history
63 * that follows (in reverse chronological order):
64        lpd 1999-08-17 added code to allow preprocessor directives
65                wherever comments are allowed
66        lpd 1999-04-12 added minor fixes from Pavel Roskin
67                <pavel_roskin@geocities.com> for clean compilation with
68                gcc -W -Wall
69        lpd 1999-03-22 added hack to recognize lines consisting of
70                identifier1(identifier2, xxx) as *not* being procedures
71        lpd 1999-02-03 made indentation of preprocessor commands consistent
72        lpd 1999-01-28 fixed two bugs: a '/' in an argument list caused an
73                endless loop; quoted strings within an argument list
74                confused the parser
75        lpd 1999-01-24 added a check for write errors on the output,
76                suggested by Jim Meyering <meyering@ascend.com>
77        lpd 1998-11-09 added further hack to recognize identifier(void)
78                as being a procedure
79        lpd 1998-10-23 added hack to recognize lines consisting of
80                identifier1(identifier2) as *not* being procedures
81        lpd 1997-12-08 made input_file optional; only closes input and/or
82                output file if not stdin or stdout respectively; prints
83                usage message on stderr rather than stdout; adds
84                --filename switch (changes suggested by
85                <ceder@lysator.liu.se>)
86        lpd 1996-01-21 added code to cope with not HAVE_CONFIG_H and with
87                compilers that don't understand void, as suggested by
88                Tom Lane
89        lpd 1996-01-15 changed to require that the first non-comment token
90                on the line following a function header be a left brace,
91                to reduce sensitivity to macros, as suggested by Tom Lane
92                <tgl@sss.pgh.pa.us>
93        lpd 1995-06-22 removed #ifndefs whose sole purpose was to define
94                undefined preprocessor symbols as 0; changed all #ifdefs
95                for configuration symbols to #ifs
96        lpd 1995-04-05 changed copyright notice to make it clear that
97                including ansi2knr in a program does not bring the entire
98                program under the GPL
99        lpd 1994-12-18 added conditionals for systems where ctype macros
100                don't handle 8-bit characters properly, suggested by
101                Francois Pinard <pinard@iro.umontreal.ca>;
102                removed --varargs switch (this is now the default)
103        lpd 1994-10-10 removed CONFIG_BROKETS conditional
104        lpd 1994-07-16 added some conditionals to help GNU `configure',
105                suggested by Francois Pinard <pinard@iro.umontreal.ca>;
106                properly erase prototype args in function parameters,
107                contributed by Jim Avera <jima@netcom.com>;
108                correct error in writeblanks (it shouldn't erase EOLs)
109        lpd 1989-xx-xx original version
110 */
111
112/* Most of the conditionals here are to make ansi2knr work with */
113/* or without the GNU configure machinery. */
114
115#if HAVE_CONFIG_H
116# include <config.h>
117#endif
118
119#include <stdio.h>
120#include <ctype.h>
121
122#if HAVE_CONFIG_H
123
124/*
125   For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
126   This will define HAVE_CONFIG_H and so, activate the following lines.
127 */
128
129# if STDC_HEADERS || HAVE_STRING_H
130#  include <string.h>
131# else
132#  include <strings.h>
133# endif
134
135#else /* not HAVE_CONFIG_H */
136
137/* Otherwise do it the hard way */
138
139# ifdef BSD
140#  include <strings.h>
141# else
142#  ifdef VMS
143    extern int strlen(), strncmp();
144#  else
145#   include <string.h>
146#  endif
147# endif
148
149#endif /* not HAVE_CONFIG_H */
150
151#if STDC_HEADERS
152# include <stdlib.h>
153#else
154/*
155   malloc and free should be declared in stdlib.h,
156   but if you've got a K&R compiler, they probably aren't.
157 */
158# ifdef MSDOS
159#  include <malloc.h>
160# else
161#  ifdef VMS
162     extern char *malloc();
163     extern void free();
164#  else
165     extern char *malloc();
166     extern int free();
167#  endif
168# endif
169
170#endif
171
172/* Define NULL (for *very* old compilers). */
173#ifndef NULL
174# define NULL (0)
175#endif
176
177/*
178 * The ctype macros don't always handle 8-bit characters correctly.
179 * Compensate for this here.
180 */
181#ifdef isascii
182# undef HAVE_ISASCII            /* just in case */
183# define HAVE_ISASCII 1
184#else
185#endif
186#if STDC_HEADERS || !HAVE_ISASCII
187# define is_ascii(c) 1
188#else
189# define is_ascii(c) isascii(c)
190#endif
191
192#define is_space(c) (is_ascii(c) && isspace(c))
193#define is_alpha(c) (is_ascii(c) && isalpha(c))
194#define is_alnum(c) (is_ascii(c) && isalnum(c))
195
196/* Scanning macros */
197#define isidchar(ch) (is_alnum(ch) || (ch) == '_')
198#define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
199
200/* Forward references */
201char *ppdirforward();
202char *ppdirbackward();
203char *skipspace();
204char *scanstring();
205int writeblanks();
206int test1();
207int convert1();
208int concatlits();
209
210/* The main program */
211int
212main(argc, argv)
213    int argc;
214    char *argv[];
215{       FILE *in = stdin;
216        FILE *out = stdout;
217        char *filename = 0;
218        char *program_name = argv[0];
219        char *output_name = 0;
220#define bufsize 5000                    /* arbitrary size */
221        char *buf;
222        char *line;
223        char *more;
224        char *usage =
225          "Usage: ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]\n";
226        /*
227         * In previous versions, ansi2knr recognized a --varargs switch.
228         * If this switch was supplied, ansi2knr would attempt to convert
229         * a ... argument to va_alist and va_dcl; if this switch was not
230         * supplied, ansi2knr would simply drop any such arguments.
231         * Now, ansi2knr always does this conversion, and we only
232         * check for this switch for backward compatibility.
233         */
234        int convert_varargs = 1;
235        int output_error;
236
237        while ( argc > 1 && argv[1][0] == '-' ) {
238          if ( !strcmp(argv[1], "--varargs") ) {
239            convert_varargs = 1;
240            argc--;
241            argv++;
242            continue;
243          }
244          if ( !strcmp(argv[1], "--filename") && argc > 2 ) {
245            filename = argv[2];
246            argc -= 2;
247            argv += 2;
248            continue;
249          }
250          fprintf(stderr, "%s: Unrecognized switch: %s\n", program_name,
251                  argv[1]);
252          fprintf(stderr, usage);
253          exit(1);
254        }
255        switch ( argc )
256           {
257        default:
258                fprintf(stderr, usage);
259                exit(0);
260        case 3:
261                output_name = argv[2];
262                out = fopen(output_name, "w");
263                if ( out == NULL ) {
264                  fprintf(stderr, "%s: Cannot open output file %s\n",
265                          program_name, output_name);
266                  exit(1);
267                }
268                /* falls through */
269        case 2:
270                in = fopen(argv[1], "r");
271                if ( in == NULL ) {
272                  fprintf(stderr, "%s: Cannot open input file %s\n",
273                          program_name, argv[1]);
274                  exit(1);
275                }
276                if ( filename == 0 )
277                  filename = argv[1];
278                /* falls through */
279        case 1:
280                break;
281           }
282        if ( filename )
283          fprintf(out, "#line 1 \"%s\"\n", filename);
284        buf = malloc(bufsize);
285        if ( buf == NULL )
286           {
287                fprintf(stderr, "Unable to allocate read buffer!\n");
288                exit(1);
289           }
290        line = buf;
291        while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
292           {
293test:           line += strlen(line);
294                switch ( test1(buf) )
295                   {
296                case 2:                 /* a function header */
297                        convert1(buf, out, 1, convert_varargs);
298                        break;
299                case 1:                 /* a function */
300                        /* Check for a { at the start of the next line. */
301                        more = ++line;
302f:                      if ( line >= buf + (bufsize - 1) ) /* overflow check */
303                          goto wl;
304                        if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL )
305                          goto wl;
306                        switch ( *skipspace(ppdirforward(more), 1) )
307                          {
308                          case '{':
309                            /* Definitely a function header. */
310                            convert1(buf, out, 0, convert_varargs);
311                            fputs(more, out);
312                            break;
313                          case 0:
314                            /* The next line was blank or a comment: */
315                            /* keep scanning for a non-comment. */
316                            line += strlen(line);
317                            goto f;
318                          default:
319                            /* buf isn't a function header, but */
320                            /* more might be. */
321                            fputs(buf, out);
322                            strcpy(buf, more);
323                            line = buf;
324                            goto test;
325                          }
326                        break;
327                case -1:                /* maybe the start of a function */
328                        if ( line != buf + (bufsize - 1) ) /* overflow check */
329                          continue;
330                        /* falls through */
331                default:                /* not a function */
332                        concatlits(buf, line, buf + bufsize, in);
333wl:                     fputs(buf, out);
334                        break;
335                   }
336                line = buf;
337           }
338        if ( line != buf )
339          fputs(buf, out);
340        free(buf);
341        if ( output_name ) {
342          output_error = ferror(out);
343          output_error |= fclose(out);
344        } else {                /* out == stdout */
345          fflush(out);
346          output_error = ferror(out);
347        }
348        if ( output_error ) {
349          fprintf(stderr, "%s: error writing to %s\n", program_name,
350                  (output_name ? output_name : "stdout"));
351          exit(1);
352        }
353        if ( in != stdin )
354          fclose(in);
355        return 0;
356}
357
358/*
359 * Skip forward or backward over one or more preprocessor directives.
360 */
361char *
362ppdirforward(p)
363    char *p;
364{
365    for (; *p == '#'; ++p) {
366        for (; *p != '\r' && *p != '\n'; ++p)
367            if (*p == 0)
368                return p;
369        if (*p == '\r' && p[1] == '\n')
370            ++p;
371    }
372    return p;
373}
374char *
375ppdirbackward(p, limit)
376    char *p;
377    char *limit;
378{
379    char *np = p;
380
381    for (;; p = --np) {
382        if (*np == '\n' && np[-1] == '\r')
383            --np;
384        for (; np > limit && np[-1] != '\r' && np[-1] != '\n'; --np)
385            if (np[-1] == 0)
386                return np;
387        if (*np != '#')
388            return p;
389    }
390}
391
392/* Skip over whitespace and comments, in either direction. */
393char *
394skipspace(p, dir)
395    char *p;
396    int dir;                    /* 1 for forward, -1 for backward */
397{
398    for ( ; ; ) {
399        while ( is_space(*p) )
400            p += dir;
401        if ( !(*p == '/' && p[dir] == '*') )
402            break;
403        p += dir;  p += dir;
404        while ( !(*p == '*' && p[dir] == '/') ) {
405            if ( *p == 0 )
406                return p;       /* multi-line comment?? */
407            p += dir;
408        }
409        p += dir;  p += dir;
410    }
411    return p;
412}
413
414/* Scan over a quoted string, in either direction. */
415char *
416scanstring(p, dir)
417    char *p;
418    int dir;
419{
420    char quote = *p;
421    for (p += dir; *p; p += dir) {
422        if (*p == quote) {
423            char *q = p;
424            int backslashed;
425            for (backslashed = 0; ; backslashed ^= 1) {
426                for (q--; *q == '\n' && q[-1] == '\\'; q -= 2)
427                    continue;
428                if (*q != '\\')
429                    break;
430            }
431            if (!backslashed)
432                return p + dir;
433        }
434    }
435    return p; /* unterminated string */
436}
437
438/*
439 * Write blanks over part of a string.
440 * Don't overwrite end-of-line characters.
441 */
442int
443writeblanks(start, end)
444    char *start;
445    char *end;
446{       char *p;
447        for ( p = start; p < end; p++ )
448          if ( *p != '\r' && *p != '\n' )
449            *p = ' ';
450        return 0;
451}
452
453/*
454 * Test whether the string in buf is a function definition.
455 * The string may contain and/or end with a newline.
456 * Return as follows:
457 *      0 - definitely not a function definition;
458 *      1 - definitely a function definition;
459 *      2 - definitely a function prototype (NOT USED);
460 *      -1 - may be the beginning of a function definition,
461 *              append another line and look again.
462 * The reason we don't attempt to convert function prototypes is that
463 * Ghostscript's declaration-generating macros look too much like
464 * prototypes, and confuse the algorithms.
465 */
466int
467test1(buf)
468    char *buf;
469{       char *p = buf;
470        char *bend;
471        char *endfn;
472        int contin;
473
474        if ( !isidfirstchar(*p) )
475          return 0;             /* no name at left margin */
476        bend = skipspace(ppdirbackward(buf + strlen(buf) - 1, buf), -1);
477        switch ( *bend )
478           {
479           case ';': contin = 0 /*2*/; break;
480           case ')': contin = 1; break;
481           case '{': return 0;          /* not a function */
482           case '}': return 0;          /* not a function */
483           default: contin = -1;
484           }
485        while ( isidchar(*p) )
486          p++;
487        endfn = p;
488        p = skipspace(p, 1);
489        if ( *p++ != '(' )
490          return 0;             /* not a function */
491        p = skipspace(p, 1);
492        if ( *p == ')' )
493          return 0;             /* no parameters */
494        /* Check that the apparent function name isn't a keyword. */
495        /* We only need to check for keywords that could be followed */
496        /* by a left parenthesis (which, unfortunately, is most of them). */
497           {    static char *words[] =
498                   {    "asm", "auto", "case", "char", "const", "double",
499                        "extern", "float", "for", "if", "int", "long",
500                        "register", "return", "short", "signed", "sizeof",
501                        "static", "switch", "typedef", "unsigned",
502                        "void", "volatile", "while", 0
503                   };
504                char **key = words;
505                char *kp;
506                unsigned len = endfn - buf;
507
508                while ( (kp = *key) != 0 )
509                   {    if ( strlen(kp) == len && !strncmp(kp, buf, len) )
510                          return 0;     /* name is a keyword */
511                        key++;
512                   }
513           }
514           {
515               char *id = p;
516               int len;
517               /*
518                * Check for identifier1(identifier2) and not
519                * identifier1(void), or identifier1(identifier2, xxxx).
520                */
521
522               while ( isidchar(*p) )
523                   p++;
524               len = p - id;
525               p = skipspace(p, 1);
526               if (*p == ',' ||
527                   (*p == ')' && (len != 4 || strncmp(id, "void", 4)))
528                   )
529                   return 0;    /* not a function */
530           }
531        /*
532         * If the last significant character was a ), we need to count
533         * parentheses, because it might be part of a formal parameter
534         * that is a procedure.
535         */
536        if (contin > 0) {
537            int level = 0;
538
539            for (p = skipspace(buf, 1); *p; p = skipspace(p + 1, 1))
540                level += (*p == '(' ? 1 : *p == ')' ? -1 : 0);
541            if (level > 0)
542                contin = -1;
543        }
544        return contin;
545}
546
547/* Convert a recognized function definition or header to K&R syntax. */
548int
549convert1(buf, out, header, convert_varargs)
550    char *buf;
551    FILE *out;
552    int header;                 /* Boolean */
553    int convert_varargs;        /* Boolean */
554{       char *endfn;
555        char *p;
556        /*
557         * The breaks table contains pointers to the beginning and end
558         * of each argument.
559         */
560        char **breaks;
561        unsigned num_breaks = 2;        /* for testing */
562        char **btop;
563        char **bp;
564        char **ap;
565        char *vararg = 0;
566
567        /* Pre-ANSI implementations don't agree on whether strchr */
568        /* is called strchr or index, so we open-code it here. */
569        for ( endfn = buf; *(endfn++) != '('; )
570          ;
571top:    p = endfn;
572        breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
573        if ( breaks == NULL )
574           {    /* Couldn't allocate break table, give up */
575                fprintf(stderr, "Unable to allocate break table!\n");
576                fputs(buf, out);
577                return -1;
578           }
579        btop = breaks + num_breaks * 2 - 2;
580        bp = breaks;
581        /* Parse the argument list */
582        do
583           {    int level = 0;
584                char *lp = NULL;
585                char *rp = NULL;
586                char *end = NULL;
587
588                if ( bp >= btop )
589                   {    /* Filled up break table. */
590                        /* Allocate a bigger one and start over. */
591                        free((char *)breaks);
592                        num_breaks <<= 1;
593                        goto top;
594                   }
595                *bp++ = p;
596                /* Find the end of the argument */
597                for ( ; end == NULL; p++ )
598                   {    switch(*p)
599                           {
600                           case ',':
601                                if ( !level ) end = p;
602                                break;
603                           case '(':
604                                if ( !level ) lp = p;
605                                level++;
606                                break;
607                           case ')':
608                                if ( --level < 0 ) end = p;
609                                else rp = p;
610                                break;
611                           case '/':
612                                if (p[1] == '*')
613                                    p = skipspace(p, 1) - 1;
614                                break;
615                           case '"': case '\'':
616                               p = scanstring(p, 1) - 1;
617                               break;
618                           default:
619                                ;
620                           }
621                   }
622                /* Erase any embedded prototype parameters. */
623                if ( lp && rp )
624                  writeblanks(lp + 1, rp);
625                p--;                    /* back up over terminator */
626                /* Find the name being declared. */
627                /* This is complicated because of procedure and */
628                /* array modifiers. */
629                for ( ; ; )
630                   {    p = skipspace(p - 1, -1);
631                        switch ( *p )
632                           {
633                           case ']':    /* skip array dimension(s) */
634                           case ')':    /* skip procedure args OR name */
635                           {    int level = 1;
636                                while ( level )
637                                 switch ( *--p )
638                                   {
639                                   case ']': case ')':
640                                       level++;
641                                       break;
642                                   case '[': case '(':
643                                       level--;
644                                       break;
645                                   case '/':
646                                       if (p > buf && p[-1] == '*')
647                                           p = skipspace(p, -1) + 1;
648                                       break;
649                                   case '"': case '\'':
650                                       p = scanstring(p, -1) + 1;
651                                       break;
652                                   default: ;
653                                   }
654                           }
655                                if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
656                                   {    /* We found the name being declared */
657                                        while ( !isidfirstchar(*p) )
658                                          p = skipspace(p, 1) + 1;
659                                        goto found;
660                                   }
661                                break;
662                           default:
663                                goto found;
664                           }
665                   }
666found:          if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
667                  {     if ( convert_varargs )
668                          {     *bp++ = "va_alist";
669                                vararg = p-2;
670                          }
671                        else
672                          {     p++;
673                                if ( bp == breaks + 1 ) /* sole argument */
674                                  writeblanks(breaks[0], p);
675                                else
676                                  writeblanks(bp[-1] - 1, p);
677                                bp--;
678                          }
679                   }
680                else
681                   {    while ( isidchar(*p) ) p--;
682                        *bp++ = p+1;
683                   }
684                p = end;
685           }
686        while ( *p++ == ',' );
687        *bp = p;
688        /* Make a special check for 'void' arglist */
689        if ( bp == breaks+2 )
690           {    p = skipspace(breaks[0], 1);
691                if ( !strncmp(p, "void", 4) )
692                   {    p = skipspace(p+4, 1);
693                        if ( p == breaks[2] - 1 )
694                           {    bp = breaks;    /* yup, pretend arglist is empty */
695                                writeblanks(breaks[0], p + 1);
696                           }
697                   }
698           }
699        /* Put out the function name and left parenthesis. */
700        p = buf;
701        while ( p != endfn ) putc(*p, out), p++;
702        /* Put out the declaration. */
703        if ( header )
704          {     fputs(");", out);
705                for ( p = breaks[0]; *p; p++ )
706                  if ( *p == '\r' || *p == '\n' )
707                    putc(*p, out);
708          }
709        else
710          {     for ( ap = breaks+1; ap < bp; ap += 2 )
711                  {     p = *ap;
712                        while ( isidchar(*p) )
713                          putc(*p, out), p++;
714                        if ( ap < bp - 1 )
715                          fputs(", ", out);
716                  }
717                fputs(")  ", out);
718                /* Put out the argument declarations */
719                for ( ap = breaks+2; ap <= bp; ap += 2 )
720                  (*ap)[-1] = ';';
721                if ( vararg != 0 )
722                  {     *vararg = 0;
723                        fputs(breaks[0], out);          /* any prior args */
724                        fputs("va_dcl", out);           /* the final arg */
725                        fputs(bp[0], out);
726                  }
727                else
728                  fputs(breaks[0], out);
729          }
730        free((char *)breaks);
731        return 0;
732}
733
734/* Append a line to a buffer.  Return the end of the appended line.  */
735char *
736appendline(lineend, bufend, in)
737    char *lineend;
738    char *bufend;
739    FILE *in;
740{
741    if (bufend == lineend)
742        return NULL;
743    if (fgets(lineend, (unsigned)(bufend - lineend), in) == NULL)
744        return NULL;
745    return lineend + strlen(lineend);
746}
747
748/*
749 * Concatenate string literals in a non-function line, appending
750 * new lines if a string literal crosses a line boundary or ends a line.
751 */
752int
753concatlits(line, lineend, bufend, in)
754    char *line;
755    char *lineend;
756    FILE *in;
757{
758    char *d = line;
759    char *s = line;
760    char *s1;
761    int pending_newlines = 0;
762    if (lineend[-1] != '\n')
763        return 0;
764    if (*skipspace(s, 1) == '#')
765        return 0;
766    while (*s) {
767        switch ((*d++ = *s++))
768          {
769          case '"': case '\'':
770              for (;;) {
771                  while ((s1 = scanstring(s - 1, 1)) == lineend)
772                      if (!(lineend = appendline(lineend, bufend, in)))
773                          goto finish;
774                  do *d++ = *s++;
775                  while (s != s1);
776                  if (s[-1] != '"')
777                      break;
778                  while ((s1 = skipspace(s, 1)) == lineend)
779                      if (!(lineend = appendline(lineend, bufend, in)))
780                          goto finish;
781                  if (*s1 != '"')
782                      break;
783                  d--;
784                  do pending_newlines += *s++ == '\n';
785                  while (s <= s1);
786              }
787          }
788    }
789    while (pending_newlines--)
790        *d++ = '\n';
791finish:
792    strcpy(d, s);
793    return 0;
794}
Note: See TracBrowser for help on using the repository browser.