~ubuntu-branches/ubuntu/precise/photopc/precise

1 by John Goerzen
Import upstream version 3.04
1
/* Copyright (C) 1989, 1997, 1998 Aladdin Enterprises.  All rights reserved. */
2
3
/*$Id: ansi2knr.c,v 1.10 1998/12/02 12:42:23 tromey Exp $*/
4
/* Convert ANSI C function definitions to K&R ("traditional C") syntax */
5
6
/*
7
ansi2knr is distributed in the hope that it will be useful, but WITHOUT ANY
8
WARRANTY.  No author or distributor accepts responsibility to anyone for the
9
consequences of using it or for whether it serves any particular purpose or
10
works at all, unless he says so in writing.  Refer to the GNU General Public
11
License (the "GPL") for full details.
12
13
Everyone is granted permission to copy, modify and redistribute ansi2knr,
14
but only under the conditions described in the GPL.  A copy of this license
15
is supposed to have been given to you along with ansi2knr so you can know
16
your rights and responsibilities.  It should be in a file named COPYLEFT,
17
or, if there is no file named COPYLEFT, a file named COPYING.  Among other
18
things, the copyright notice and this notice must be preserved on all
19
copies.
20
21
We explicitly state here what we believe is already implied by the GPL: if
22
the ansi2knr program is distributed as a separate set of sources and a
23
separate executable file which are aggregated on a storage medium together
24
with another program, this in itself does not bring the other program under
25
the GPL, nor does the mere fact that such a program or the procedures for
26
constructing it invoke the ansi2knr executable bring any other part of the
27
program 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,
41
 * with a right parenthesis as the last character on the line,
42
 * and with a left brace as the first token on the following line
43
 * (ignoring possible intervening comments), 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".  ansi2knr will recognize a multi-line header provided
48
 * that no intervening line ends with a left or right brace or a semicolon.
49
 * These algorithms ignore whitespace and comments, except that
50
 * the function name must be the first thing on the line.
51
 * The following constructs will confuse it:
52
 *	- Any other construct that starts at the left margin and
53
 *	    follows the above syntax (such as a macro or function call).
54
 *	- Some macros that tinker with the syntax of the function header.
55
 */
56
57
/*
58
 * The original and principal author of ansi2knr is L. Peter Deutsch
59
 * <ghost@aladdin.com>.  Other authors are noted in the change history
60
 * that follows (in reverse chronological order):
61
	lpd 1998-11-09 added further hack to recognize identifier(void)
62
		as being a procedure
63
	lpd 1998-10-23 added hack to recognize lines consisting of
64
		identifier1(identifier2) as *not* being procedures
65
	lpd 1997-12-08 made input_file optional; only closes input and/or
66
		output file if not stdin or stdout respectively; prints
67
		usage message on stderr rather than stdout; adds
68
		--filename switch (changes suggested by
69
		<ceder@lysator.liu.se>)
70
	lpd 1996-01-21 added code to cope with not HAVE_CONFIG_H and with
71
		compilers that don't understand void, as suggested by
72
		Tom Lane
73
	lpd 1996-01-15 changed to require that the first non-comment token
74
		on the line following a function header be a left brace,
75
		to reduce sensitivity to macros, as suggested by Tom Lane
76
		<tgl@sss.pgh.pa.us>
77
	lpd 1995-06-22 removed #ifndefs whose sole purpose was to define
78
		undefined preprocessor symbols as 0; changed all #ifdefs
79
		for configuration symbols to #ifs
80
	lpd 1995-04-05 changed copyright notice to make it clear that
81
		including ansi2knr in a program does not bring the entire
82
		program under the GPL
83
	lpd 1994-12-18 added conditionals for systems where ctype macros
84
		don't handle 8-bit characters properly, suggested by
85
		Francois Pinard <pinard@iro.umontreal.ca>;
86
		removed --varargs switch (this is now the default)
87
	lpd 1994-10-10 removed CONFIG_BROKETS conditional
88
	lpd 1994-07-16 added some conditionals to help GNU `configure',
89
		suggested by Francois Pinard <pinard@iro.umontreal.ca>;
90
		properly erase prototype args in function parameters,
91
		contributed by Jim Avera <jima@netcom.com>;
92
		correct error in writeblanks (it shouldn't erase EOLs)
93
	lpd 1989-xx-xx original version
94
 */
95
96
/* Most of the conditionals here are to make ansi2knr work with */
97
/* or without the GNU configure machinery. */
98
99
#if HAVE_CONFIG_H
100
# include <config.h>
101
#endif
102
103
#include <stdio.h>
104
#include <ctype.h>
105
106
#if HAVE_CONFIG_H
107
108
/*
109
   For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
110
   This will define HAVE_CONFIG_H and so, activate the following lines.
111
 */
112
113
# if STDC_HEADERS || HAVE_STRING_H
114
#  include <string.h>
115
# else
116
#  include <strings.h>
117
# endif
118
119
#else /* not HAVE_CONFIG_H */
120
121
/* Otherwise do it the hard way */
122
123
# ifdef BSD
124
#  include <strings.h>
125
# else
126
#  ifdef VMS
127
    extern int strlen(), strncmp();
128
#  else
129
#   include <string.h>
130
#  endif
131
# endif
132
133
#endif /* not HAVE_CONFIG_H */
134
135
#if STDC_HEADERS
136
# include <stdlib.h>
137
#else
138
/*
139
   malloc and free should be declared in stdlib.h,
140
   but if you've got a K&R compiler, they probably aren't.
141
 */
142
# ifdef MSDOS
143
#  include <malloc.h>
144
# else
145
#  ifdef VMS
146
     extern char *malloc();
147
     extern void free();
148
#  else
149
     extern char *malloc();
150
     extern int free();
151
#  endif
152
# endif
153
154
#endif
155
156
/*
157
 * The ctype macros don't always handle 8-bit characters correctly.
158
 * Compensate for this here.
159
 */
160
#ifdef isascii
161
#  undef HAVE_ISASCII		/* just in case */
162
#  define HAVE_ISASCII 1
163
#else
164
#endif
165
#if STDC_HEADERS || !HAVE_ISASCII
166
#  define is_ascii(c) 1
167
#else
168
#  define is_ascii(c) isascii(c)
169
#endif
170
171
#define is_space(c) (is_ascii(c) && isspace(c))
172
#define is_alpha(c) (is_ascii(c) && isalpha(c))
173
#define is_alnum(c) (is_ascii(c) && isalnum(c))
174
175
/* Scanning macros */
176
#define isidchar(ch) (is_alnum(ch) || (ch) == '_')
177
#define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
178
179
/* Forward references */
180
char *skipspace();
181
int writeblanks();
182
int test1();
183
int convert1();
184
185
/* The main program */
186
int
187
main(argc, argv)
188
    int argc;
189
    char *argv[];
190
{	FILE *in = stdin;
191
	FILE *out = stdout;
192
	char *filename = 0;
193
#define bufsize 5000			/* arbitrary size */
194
	char *buf;
195
	char *line;
196
	char *more;
197
	char *usage =
198
	  "Usage: ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]\n";
199
	/*
200
	 * In previous versions, ansi2knr recognized a --varargs switch.
201
	 * If this switch was supplied, ansi2knr would attempt to convert
202
	 * a ... argument to va_alist and va_dcl; if this switch was not
203
	 * supplied, ansi2knr would simply drop any such arguments.
204
	 * Now, ansi2knr always does this conversion, and we only
205
	 * check for this switch for backward compatibility.
206
	 */
207
	int convert_varargs = 1;
208
209
	while ( argc > 1 && argv[1][0] == '-' ) {
210
	  if ( !strcmp(argv[1], "--varargs") ) {
211
	    convert_varargs = 1;
212
	    argc--;
213
	    argv++;
214
	    continue;
215
	  }
216
	  if ( !strcmp(argv[1], "--filename") && argc > 2 ) {
217
	    filename = argv[2];
218
	    argc -= 2;
219
	    argv += 2;
220
	    continue;
221
	  }
222
	  fprintf(stderr, "Unrecognized switch: %s\n", argv[1]);
223
	  fprintf(stderr, usage);
224
	  exit(1);
225
	}
226
	switch ( argc )
227
	   {
228
	default:
229
		fprintf(stderr, usage);
230
		exit(0);
231
	case 3:
232
		out = fopen(argv[2], "w");
233
		if ( out == NULL ) {
234
		  fprintf(stderr, "Cannot open output file %s\n", argv[2]);
235
		  exit(1);
236
		}
237
		/* falls through */
238
	case 2:
239
		in = fopen(argv[1], "r");
240
		if ( in == NULL ) {
241
		  fprintf(stderr, "Cannot open input file %s\n", argv[1]);
242
		  exit(1);
243
		}
244
		if ( filename == 0 )
245
		  filename = argv[1];
246
		/* falls through */
247
	case 1:
248
		break;
249
	   }
250
	if ( filename )
251
	  fprintf(out, "#line 1 \"%s\"\n", filename);
252
	buf = malloc(bufsize);
253
	line = buf;
254
	while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
255
	   {
256
test:		line += strlen(line);
257
		switch ( test1(buf) )
258
		   {
259
		case 2:			/* a function header */
260
			convert1(buf, out, 1, convert_varargs);
261
			break;
262
		case 1:			/* a function */
263
			/* Check for a { at the start of the next line. */
264
			more = ++line;
265
f:			if ( line >= buf + (bufsize - 1) ) /* overflow check */
266
			  goto wl;
267
			if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL )
268
			  goto wl;
269
			switch ( *skipspace(more, 1) )
270
			  {
271
			  case '{':
272
			    /* Definitely a function header. */
273
			    convert1(buf, out, 0, convert_varargs);
274
			    fputs(more, out);
275
			    break;
276
			  case 0:
277
			    /* The next line was blank or a comment: */
278
			    /* keep scanning for a non-comment. */
279
			    line += strlen(line);
280
			    goto f;
281
			  default:
282
			    /* buf isn't a function header, but */
283
			    /* more might be. */
284
			    fputs(buf, out);
285
			    strcpy(buf, more);
286
			    line = buf;
287
			    goto test;
288
			  }
289
			break;
290
		case -1:		/* maybe the start of a function */
291
			if ( line != buf + (bufsize - 1) ) /* overflow check */
292
			  continue;
293
			/* falls through */
294
		default:		/* not a function */
295
wl:			fputs(buf, out);
296
			break;
297
		   }
298
		line = buf;
299
	   }
300
	if ( line != buf )
301
	  fputs(buf, out);
302
	free(buf);
303
	if ( out != stdout )
304
	  fclose(out);
305
	if ( in != stdin )
306
	  fclose(in);
307
	return 0;
308
}
309
310
/* Skip over space and comments, in either direction. */
311
char *
312
skipspace(p, dir)
313
    register char *p;
314
    register int dir;			/* 1 for forward, -1 for backward */
315
{	for ( ; ; )
316
	   {	while ( is_space(*p) )
317
		  p += dir;
318
		if ( !(*p == '/' && p[dir] == '*') )
319
		  break;
320
		p += dir;  p += dir;
321
		while ( !(*p == '*' && p[dir] == '/') )
322
		   {	if ( *p == 0 )
323
			  return p;	/* multi-line comment?? */
324
			p += dir;
325
		   }
326
		p += dir;  p += dir;
327
	   }
328
	return p;
329
}
330
331
/*
332
 * Write blanks over part of a string.
333
 * Don't overwrite end-of-line characters.
334
 */
335
int
336
writeblanks(start, end)
337
    char *start;
338
    char *end;
339
{	char *p;
340
	for ( p = start; p < end; p++ )
341
	  if ( *p != '\r' && *p != '\n' )
342
	    *p = ' ';
343
	return 0;
344
}
345
346
/*
347
 * Test whether the string in buf is a function definition.
348
 * The string may contain and/or end with a newline.
349
 * Return as follows:
350
 *	0 - definitely not a function definition;
351
 *	1 - definitely a function definition;
352
 *	2 - definitely a function prototype (NOT USED);
353
 *	-1 - may be the beginning of a function definition,
354
 *		append another line and look again.
355
 * The reason we don't attempt to convert function prototypes is that
356
 * Ghostscript's declaration-generating macros look too much like
357
 * prototypes, and confuse the algorithms.
358
 */
359
int
360
test1(buf)
361
    char *buf;
362
{	register char *p = buf;
363
	char *bend;
364
	char *endfn;
365
	int contin;
366
367
	if ( !isidfirstchar(*p) )
368
	  return 0;		/* no name at left margin */
369
	bend = skipspace(buf + strlen(buf) - 1, -1);
370
	switch ( *bend )
371
	   {
372
	   case ';': contin = 0 /*2*/; break;
373
	   case ')': contin = 1; break;
374
	   case '{': return 0;		/* not a function */
375
	   case '}': return 0;		/* not a function */
376
	   default: contin = -1;
377
	   }
378
	while ( isidchar(*p) )
379
	  p++;
380
	endfn = p;
381
	p = skipspace(p, 1);
382
	if ( *p++ != '(' )
383
	  return 0;		/* not a function */
384
	p = skipspace(p, 1);
385
	if ( *p == ')' )
386
	  return 0;		/* no parameters */
387
	/* Check that the apparent function name isn't a keyword. */
388
	/* We only need to check for keywords that could be followed */
389
	/* by a left parenthesis (which, unfortunately, is most of them). */
390
	   {	static char *words[] =
391
		   {	"asm", "auto", "case", "char", "const", "double",
392
			"extern", "float", "for", "if", "int", "long",
393
			"register", "return", "short", "signed", "sizeof",
394
			"static", "switch", "typedef", "unsigned",
395
			"void", "volatile", "while", 0
396
		   };
397
		char **key = words;
398
		char *kp;
399
		int len = endfn - buf;
400
401
		while ( (kp = *key) != 0 )
402
		   {	if ( strlen(kp) == len && !strncmp(kp, buf, len) )
403
			  return 0;	/* name is a keyword */
404
			key++;
405
		   }
406
	   }
407
	   {
408
	       char *id = p;
409
	       int len;
410
	       /*
411
		* Check for identifier1(identifier2) and not
412
		* identifier1(void).
413
		*/
414
415
	       while ( isidchar(*p) )
416
		   p++;
417
	       len = p - id;
418
	       p = skipspace(p, 1);
419
	       if ( *p == ')' && (len != 4 || strncmp(id, "void", 4)) )
420
		   return 0;	/* not a function */
421
	   }
422
	/*
423
	 * If the last significant character was a ), we need to count
424
	 * parentheses, because it might be part of a formal parameter
425
	 * that is a procedure.
426
	 */
427
	if (contin > 0) {
428
	    int level = 0;
429
430
	    for (p = skipspace(buf, 1); *p; p = skipspace(p + 1, 1))
431
		level += (*p == '(' ? 1 : *p == ')' ? -1 : 0);
432
	    if (level > 0)
433
		contin = -1;
434
	}
435
	return contin;
436
}
437
438
/* Convert a recognized function definition or header to K&R syntax. */
439
int
440
convert1(buf, out, header, convert_varargs)
441
    char *buf;
442
    FILE *out;
443
    int header;			/* Boolean */
444
    int convert_varargs;	/* Boolean */
445
{	char *endfn;
446
	register char *p;
447
	/*
448
	 * The breaks table contains pointers to the beginning and end
449
	 * of each argument.
450
	 */
451
	char **breaks;
452
	unsigned num_breaks = 2;	/* for testing */
453
	char **btop;
454
	char **bp;
455
	char **ap;
456
	char *vararg = 0;
457
458
	/* Pre-ANSI implementations don't agree on whether strchr */
459
	/* is called strchr or index, so we open-code it here. */
460
	for ( endfn = buf; *(endfn++) != '('; )
461
	  ;
462
top:	p = endfn;
463
	breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
464
	if ( breaks == 0 )
465
	   {	/* Couldn't allocate break table, give up */
466
		fprintf(stderr, "Unable to allocate break table!\n");
467
		fputs(buf, out);
468
		return -1;
469
	   }
470
	btop = breaks + num_breaks * 2 - 2;
471
	bp = breaks;
472
	/* Parse the argument list */
473
	do
474
	   {	int level = 0;
475
		char *lp = NULL;
476
		char *rp;
477
		char *end = NULL;
478
479
		if ( bp >= btop )
480
		   {	/* Filled up break table. */
481
			/* Allocate a bigger one and start over. */
482
			free((char *)breaks);
483
			num_breaks <<= 1;
484
			goto top;
485
		   }
486
		*bp++ = p;
487
		/* Find the end of the argument */
488
		for ( ; end == NULL; p++ )
489
		   {	switch(*p)
490
			   {
491
			   case ',':
492
				if ( !level ) end = p;
493
				break;
494
			   case '(':
495
				if ( !level ) lp = p;
496
				level++;
497
				break;
498
			   case ')':
499
				if ( --level < 0 ) end = p;
500
				else rp = p;
501
				break;
502
			   case '/':
503
				p = skipspace(p, 1) - 1;
504
				break;
505
			   default:
506
				;
507
			   }
508
		   }
509
		/* Erase any embedded prototype parameters. */
510
		if ( lp )
511
		  writeblanks(lp + 1, rp);
512
		p--;			/* back up over terminator */
513
		/* Find the name being declared. */
514
		/* This is complicated because of procedure and */
515
		/* array modifiers. */
516
		for ( ; ; )
517
		   {	p = skipspace(p - 1, -1);
518
			switch ( *p )
519
			   {
520
			   case ']':	/* skip array dimension(s) */
521
			   case ')':	/* skip procedure args OR name */
522
			   {	int level = 1;
523
				while ( level )
524
				 switch ( *--p )
525
				   {
526
				   case ']': case ')': level++; break;
527
				   case '[': case '(': level--; break;
528
				   case '/': p = skipspace(p, -1) + 1; break;
529
				   default: ;
530
				   }
531
			   }
532
				if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
533
				   {	/* We found the name being declared */
534
					while ( !isidfirstchar(*p) )
535
					  p = skipspace(p, 1) + 1;
536
					goto found;
537
				   }
538
				break;
539
			   default:
540
				goto found;
541
			   }
542
		   }
543
found:		if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
544
		  {	if ( convert_varargs )
545
			  {	*bp++ = "va_alist";
546
				vararg = p-2;
547
			  }
548
			else
549
			  {	p++;
550
				if ( bp == breaks + 1 )	/* sole argument */
551
				  writeblanks(breaks[0], p);
552
				else
553
				  writeblanks(bp[-1] - 1, p);
554
				bp--;
555
			  }
556
		   }
557
		else
558
		   {	while ( isidchar(*p) ) p--;
559
			*bp++ = p+1;
560
		   }
561
		p = end;
562
	   }
563
	while ( *p++ == ',' );
564
	*bp = p;
565
	/* Make a special check for 'void' arglist */
566
	if ( bp == breaks+2 )
567
	   {	p = skipspace(breaks[0], 1);
568
		if ( !strncmp(p, "void", 4) )
569
		   {	p = skipspace(p+4, 1);
570
			if ( p == breaks[2] - 1 )
571
			   {	bp = breaks;	/* yup, pretend arglist is empty */
572
				writeblanks(breaks[0], p + 1);
573
			   }
574
		   }
575
	   }
576
	/* Put out the function name and left parenthesis. */
577
	p = buf;
578
	while ( p != endfn ) putc(*p, out), p++;
579
	/* Put out the declaration. */
580
	if ( header )
581
	  {	fputs(");", out);
582
		for ( p = breaks[0]; *p; p++ )
583
		  if ( *p == '\r' || *p == '\n' )
584
		    putc(*p, out);
585
	  }
586
	else
587
	  {	for ( ap = breaks+1; ap < bp; ap += 2 )
588
		  {	p = *ap;
589
			while ( isidchar(*p) )
590
			  putc(*p, out), p++;
591
			if ( ap < bp - 1 )
592
			  fputs(", ", out);
593
		  }
594
		fputs(")  ", out);
595
		/* Put out the argument declarations */
596
		for ( ap = breaks+2; ap <= bp; ap += 2 )
597
		  (*ap)[-1] = ';';
598
		if ( vararg != 0 )
599
		  {	*vararg = 0;
600
			fputs(breaks[0], out);		/* any prior args */
601
			fputs("va_dcl", out);		/* the final arg */
602
			fputs(bp[0], out);
603
		  }
604
		else
605
		  fputs(breaks[0], out);
606
	  }
607
	free((char *)breaks);
608
	return 0;
609
}