File: proc2.c

package info (click to toggle)
scsh 0.5.1-2
  • links: PTS
  • area: non-free
  • in suites: potato, slink
  • size: 6,540 kB
  • ctags: 8,656
  • sloc: lisp: 39,346; ansic: 13,466; sh: 1,669; makefile: 624
file content (377 lines) | stat: -rw-r--r-- 12,171 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/* Copyright (c) 1993 by Olin Shivers.
**
** Please imagine a long, tedious, legalistic 5-page gnu-style copyright
** notice appearing here to the effect that you may use this code any
** way you like, as long as you don't charge money for it, remove this
** notice, or hold me liable for its results.
*/

/* If the above copyright notice is a problem for your app, send me mail. */

/* Using the #! interpreter hack in Unix for making scripts has a big
** problem: you only get 1 argument after the interpreter on the #! line. 
** This subroutine helps to fix that.
**
** Below is a procedure that will arrange for a command-line switch of the 
** form \ <script> to stand for reading more args from line 2 of the file
** <script>. Replace the \ arg with these args. Now you can have Scheme,
** Postscript, Forth, Lisp, Smalltalk, tcl, etc. scripts that look like:
**
** File foo:
**         #!/usr/local/bin/scheme \
**         -heap 4000000 -batch -script
**         !#
**         (define foo ...) ; Scheme code from here on.
**         ...
**
** With this program definition, executing
**     foo arg1 arg2 arg3
** will turn into
**     /usr/local/bin/scheme \ foo arg1 arg2 arg3
** which your Scheme interpreter main() (using this routine) will expand during
** argv processing into:
**     /usr/local/bin/scheme -heap 4000000 -batch -script foo arg1 arg2 arg3
** That is, the argument processing in main() will *replace* the \ argument
** with the arguments read in from line 2 of foo. So we have dodged the
** only-one-argument-on-the-#!-line constraint.
**
** The only other thing that needs to be done in this case is arrange for the
** interpreter to ignore these initial few non-Scheme lines. We can arrange
** for this in our Scheme example by defining a Scheme read macro #! that
** skips characters until newline, bang, splat (somewhat like the ; read macro
** skips characters until newline).
**
** Using backslash as the meta-argument switch is handy for two reasons:
** - It is only one character. Since many Unix systems limit the #!
**   line to 32 characters total, this is important.
** - It is a helpful visual pun -- implying a continuation line for the
**   arguments.
** It is also very unlikely to be an already-used switch. However, -2
** is also a reasonable choice.
**
** All you have to do to get this second-line meta-argument functionality is
** link this file in with your interpreter.  You can tweak this routine for
** various interpreters if you need to have it, for example, skip an initial
** comment character when it begins to scan the second line.
**
** Arguments are parsed from the second line as follows:
** - The only special chars are space, tab, newline, and \.
** - Every space char terminates an argument. 
**   Multiple spaces therefore introduce empty-string arguments.
** - A newline terminates an argument, and also terminates the argument list.
**   This means that an empty line parses to the singleton list whose one
**   element is the empty string: (""). The grammar doesn't admit the empty 
**   list.
** - Tab is not allowed.
**   This is to prevent you from being screwed by thinking you had several
**   spaces where you really had a tab, and vice-versa.
** - The only other special character is \, the knock-down character. 
**   \ escapes \, space, tab, and newline, turning off their special 
**   functions. The ANSI C escape sequences (\b, \n, \r and \t) are 
**   supported; these also produce argument-constituents -- \n doesn't act 
**   like a terminating newline. \nnn for *exactly* three octal digits reads 
**   as the char whose ASCII code is nnn. It is an error if \ is followed by 
**   just 1 or 2 octal digits: \3Q is an error. Octal-escapes are always 
**   constituent chars. \ followed by other chars is not allowed (so we can
**   extend the escape-code space later if we like).
**
** You have to construct these line-2 arg lines carefully. For example,
** beware of trailing spaces at the end of the line. They'll give you
** extra trailing empty-string args.
**
** You should also beware of including nul bytes into your arguments, since
** C's pathetic excuse for a string data-type will lose if you try this.
**
**
** Another way to get this sort of multiple-argument functionality, with
** the extra cost of starting up a shell, is to simply have the following
** trampoline at the beginning of your script:
**     #!/bin/sh -
**     exec /usr/local/bin/scheme -heap 4000000 -batch -script $0 $*
**     !#
** (or use the indir program, same rough idea). This is less appropriate
** for interpreters intended to replace the shell.
**
** Possible extensions:
** - I considered making the argument line syntax hairier -- adding ~user
**   directory expansion and $(envvar) expansion. But I didn't do it.
**
** - Not much error information. If something is wrong -- file can't
**   be read, no second line, illegal syntax on second line, malloc
**   loses -- you just get a NULL return value. You can examine errno
**   if the problem is a Unix error (e.g., file error). But if the call
**   fails for another reason (e.g., bad arg syntax on the second line),
**   then errno won't help. This code could be modified to take an additional
**   &error_code argument, and assign an integer into the var indicating
**   just exactly what the problem was, if that's important to your 
**   application. 
**
** This code is fairly robust, careful code. ANSI standard C. No dependencies 
** on fixed-size buffers. It won't blow up if the inputs are pathological.
** It all type-checks. No core leaks. Feel free to customise it for the
** particular needs of a given interpreter; the core functionality is there.
**
** See the end of this file for a sample program with an arg processing loop.
** Please send me bug reports, fixes, and improvements.
**
** Some interpreters that might use this: tcl (wish, hope), perl, Smalltalk,
** little Schemes (scm, elk, s48, ...), big Schemes, Postscript, emacs, 
** Dylan, Lisp, Prolog.
**     -Olin Shivers 2/93
**     shivers@cs.cmu.edu
**     shivers@csd.hku.hk
*/

#include <stdio.h>
#include <stdlib.h> /* malloc */
#include <ctype.h>

#define Alloc(type) 	((type *) malloc(sizeof(type)))
#define Malloc(type,n)	((type *) malloc(sizeof(type)*(n)))
#define Realloc(type,ptr,size) \
	((type *) realloc((void *)ptr, sizeof(type)*(size)))
#define Free(p)		(free((void *)(p)))

/* Is character c an octal digit? */
#define isodigit(c) (isdigit(c) && (c) != '8' && (c) != '9')

/* Double the vector if we've overflowed it. Return the vector.
** If we double the vector, lenptr is updated with the new length.
** If we fail, return NULL.
*/

static void *maybe_grow_vec(void *vec, int *lenptr, int index, int elt_size)
{
    int len = *lenptr;
    if( index < len ) return vec;
    len *= 2;
    *lenptr = len; /* Update the length pointer. */
    return realloc(vec, len*elt_size);
    }

/* The do ... while(0) is a trick to make this macro accept a terminating
** semicolon.
*/
#define Maybe_Grow_Vec(vec, size, index, elt_t, lose) \
    do {elt_t *mgv_tmp =(elt_t*)maybe_grow_vec((void*)vec, &size, \
					       index, sizeof(elt_t)); \
	if(mgv_tmp) vec = mgv_tmp; else goto lose;} while (0);


/* process_meta_arg(fname, av)
** -----------------------
** The main routine.
**
** Expand a \ <fname> switch. Return NULL on error, otherwise a new arg
** vector composed of (1) the args scanned in from line 2 of fname, followed
** by (2) the arguments in av. The argument vector av starts with the
** argument following the \ switch, i.e., the <fname> argument.
*/

static char* read_arg(FILE*);

char **process_meta_arg(char **av)
{
    char **argv, *arg, **ap;
    int c;
    FILE *script;
    char *fname;
    int av_len;
    int argv_i=0, argv_len=100;

    if( !*av ) return NULL;
    fname = *av;
    script = fopen(fname, "r");
    if( !script ) return NULL;

    /* Skip line 1. */
    while( '\n' != getc(script) )
	if( feof(script) || ferror(script) ) goto lose3;

    argv = Malloc(char*, argv_len);
    if( !argv ) goto lose3;

    do {
	char *arg;
	arg = read_arg(script);
	if( !arg ) goto lose2;
	Maybe_Grow_Vec(argv, argv_len, argv_i, char*, lose1);
	argv[argv_i++] = arg;
	}
    while( EOF != (c=getc(script)) && '\n' != c );

    for(av_len=0; av[av_len]; av_len++);	/* Compute length of av. */

    /* Precisely re-size argv. */
    if( NULL == (ap=Realloc(char*, argv, argv_len + av_len + 1)) ) goto lose2;
    argv = ap;

    while( argv[argv_i++] = *av++ );	/* Copy over av & null terminate. */

    fclose(script);
    return argv;


    /* Exception handlers: free storage and lose. */
  lose1:
    Free(arg);
  lose2:
    while( argv_i ) Free(argv[--argv_i]);
    Free(argv);
 lose3:
    fclose(script);
    return NULL;
    }

/* Read in one arg, but not its terminating space or newline.
** Return a newly-allocated string containing the arg; 
** NULL if there's an error.
*/
static char *read_arg(FILE *f)
{
    char *buf, *tmp;
    int buflen, i;

    /* Allocate a buffer for the arg. */
    i = 0;
    buflen=20;
    if( !(buf = Malloc(char, buflen)) ) return NULL;

    /* Read in the arg. */
    while(1) {
	int c = getc(f);

	if( c == EOF || c == ' ' || c == '\n' ) {ungetc(c, f); break;}

	/* Do knock-down processing. */
	if( c == '\\' ) {
	    int c1, c2, c3;
	    switch (c1=getc(f)) {
	      case EOF:
		goto lose;

		/* \nnn octal escape. */
	      case '0':		case '1':
	      case '2':		case '3':
	      case '4':		case '5':
	      case '6':		case '7':
		if( EOF == (c2=getc(f)) || !isodigit(c2) ) goto lose;
		if( EOF == (c3=getc(f)) || !isodigit(c3) ) goto lose;
		c = ((c1-'0')<<6) | ((c2-'0')<<3) | (c3-'0');
		break;

		/* ANSI C escapes. */
	      case 'n':	c='\n'; break;
	      case 'r':	c='\r'; break;
	      case 't':	c='\t'; break;
	      case 'b':	c='\b'; break;

		/* Simple knock-down: \, space, tab, newline. */
	      case '\\':	case ' ':
	      case '\t':	case '\n':
		c=c1; break;

		/* Nothing else allowed. */
	      default: goto lose;
		}
	    }

	/* No tab allowed. */
	else if( c == '\t' ) goto lose;

	Maybe_Grow_Vec(buf, buflen, i, char, lose);
	buf[i++] = c;
	}

    /* Null terminate the arg. */
    Maybe_Grow_Vec(buf, buflen, i, char, lose);
    buf[i++] = '\0';

    /* Precisely re-size buf and return. */
    if( tmp=Realloc(char,buf,i) ) return tmp;

  lose:
    Free(buf);
    return NULL;
    }


/*****************************************************************************/
#if 0
/*
** Debugging test stub and example argument scanner. 
** Like echo, but with \ <fname> expansion.
**/

char *prog_name;

static void usage(void)
{
    fprintf(stderr,
	    "Usage: %s [\\ <fname>] [-n] [--] arg1 ... argn\n",
	    prog_name);
    exit(1);
    }

/* Expand away a leading meta-arg if there is one. Die informatively on error.
** I can't think of a reason why you might want to have recursive meta
** arguments, but we handle this case to be complete.
*/
static char **maybe_expand_meta_arg(char **argv)
{
    if( *argv )
	while( strcmp(*argv, "\\") == 0 ) {
	    argv++;
	    if( !*argv ) {
		fprintf(stderr, "%s: \\ switch without following filename.\n",
			prog_name);
		usage();
		}
	    argv = process_meta_arg(argv);
	    if( !argv ) {
		fprintf(stderr, "%s: unable to expand \\ <filename> switch.\n",
			prog_name);
		usage();
		}
	    }
    return argv;
    }

main(int argc, char **argv)
{
    int n_flag=0;

    prog_name = *argv++;

    /* Handle an initial meta-arg expansion. */
    argv = maybe_expand_meta_arg(argv);

    /* Process switches. */
    for(;*argv;argv++) {
	/* Process arg. */
	if( argv[0][0] == '-' )
	    switch( argv[0][1] ) {
	      /* -n means no terminating newline. */
	      case 'n':
		n_flag++;
		break;

	      /* -- terminates args, so you can echo \, -n, -- args. */
	      case '-':
		argv++;
		goto args_done;
		break;

	      default:
		fprintf(stderr, "%s: unknown flag %s.\n", prog_name, *argv);
		usage();
	      }
	else goto args_done; /* Not a switch. We are done. */
	}

  args_done:
    if( *argv ) printf("\"%s\"", *argv++);
    while( *argv ) printf(" \"%s\"", *argv++);
    if( !n_flag ) putchar('\n');
    }
#endif /* 0 */