process.c revision e8ac26b0
1/*
2 *
3Copyright 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 * *
25 * Original Author of "xauth" : Jim Fulton, MIT X Consortium
26 * Modified into "iceauth"    : Ralph Mor, X Consortium
27 */
28
29#include "iceauth.h"
30#include <ctype.h>
31#include <errno.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <signal.h>
35
36#define SECURERPC "SUN-DES-1"
37#define K5AUTH "KERBEROS-V5-1"
38
39#define ICEAUTH_DEFAULT_RETRIES 10	/* number of competitors we expect */
40#define ICEAUTH_DEFAULT_TIMEOUT 2	/* in seconds, be quick */
41#define ICEAUTH_DEFAULT_DEADTIME 600L	/* 10 minutes in seconds */
42
43typedef struct _AuthList {		/* linked list of entries */
44    struct _AuthList *next;
45    IceAuthFileEntry *auth;
46} AuthList;
47
48#define add_to_list(h,t,e) {if (t) (t)->next = (e); else (h) = (e); (t) = (e);}
49
50typedef int (*ProcessFunc)(const char *, int, int, const char **);
51typedef int (*DoFunc)(const char *, int, IceAuthFileEntry *, void *);
52
53typedef struct _CommandTable {		/* commands that are understood */
54    const char *name;			/* full name */
55    unsigned int minlen;		/* unique prefix */
56    unsigned int maxlen;		/* strlen(name) */
57    ProcessFunc processfunc;		/* handler */
58    const char *helptext;		/* what to print for help */
59} CommandTable;
60
61struct _extract_data {			/* for iterating */
62    FILE *fp;				/* input source */
63    const char *filename;		/* name of input */
64    Bool used_stdout;			/* whether or not need to close */
65    int nwritten;			/* number of entries written */
66    const char *cmd;			/* for error messages */
67};
68
69struct _list_data {			/* for iterating */
70    FILE *fp;				/* output file */
71};
72
73
74/*
75 * private data
76 */
77static const char *stdin_filename = "(stdin)";  /* for messages */
78static const char *stdout_filename = "(stdout)";  /* for messages */
79static const char *Yes = "yes";		/* for messages */
80static const char *No = "no";			/* for messages */
81
82static int binaryEqual ( const char *a, const char *b, unsigned len );
83static void prefix ( const char *fn, int n );
84static void badcommandline ( const char *cmd );
85static char *skip_space ( char *s );
86static char *skip_nonspace ( char *s );
87static char **split_into_words ( char *src, int *argcp );
88static FILE *open_file ( const char **filenamep, const char *mode, Bool *usedstdp, const char *srcfn, int srcln, const char *cmd );
89static int read_auth_entries ( FILE *fp, AuthList **headp, AuthList **tailp );
90static int cvthexkey ( const char *hexstr, char **ptrp );
91static int dispatch_command ( const char *inputfilename, int lineno, int argc, const char **argv, const CommandTable *tab, int *statusp );
92static void die ( int sig ) _X_NORETURN;
93static void catchsig ( int sig ) _X_NORETURN;
94static void register_signals ( void );
95static int write_auth_file ( char *tmp_nam, size_t tmp_nam_len );
96static void fprintfhex ( FILE *fp, unsigned int len, const char *cp );
97static int dump_entry ( const char *inputfilename, int lineno, IceAuthFileEntry *auth, void *data );
98static int extract_entry ( const char *inputfilename, int lineno, IceAuthFileEntry *auth, void *data );
99static int match_auth ( IceAuthFileEntry *a, IceAuthFileEntry *b, int *authDataSame );
100static int merge_entries ( AuthList **firstp, AuthList *second, int *nnewp, int *nreplp, int *ndupp );
101static int search_and_do ( const char *inputfilename, int lineno, int start, int argc, const char *argv[], DoFunc do_func, void *data );
102static int remove_entry ( const char *inputfilename, int lineno, IceAuthFileEntry *auth, void *data );
103static int do_help ( const char *inputfilename, int lineno, int argc, const char **argv );
104static int do_questionmark ( const char *inputfilename, int lineno, int argc, const char **argv );
105static int do_list ( const char *inputfilename, int lineno, int argc, const char **argv );
106static int do_merge ( const char *inputfilename, int lineno, int argc, const char **argv );
107static int do_extract ( const char *inputfilename, int lineno, int argc, const char **argv );
108static int do_add ( const char *inputfilename, int lineno, int argc, const char **argv );
109static int do_remove ( const char *inputfilename, int lineno, int argc, const char **argv );
110static int do_info ( const char *inputfilename, int lineno, int argc, const char **argv );
111static int do_exit ( const char *inputfilename, int lineno, int argc, const char **argv );
112static int do_quit ( const char *inputfilename, int lineno, int argc, const char **argv );
113static int do_source ( const char *inputfilename, int lineno, int argc, const char **argv );
114
115static const CommandTable command_table[] = {	/* table of known commands */
116{ "add", 2, 3, do_add,
117"\
118add       add an entry\n\
119          add protoname protodata netid authname authdata"
120},
121
122{ "exit", 3, 4, do_exit,
123"\
124exit      save changes and exit program"
125},
126
127{ "extract", 3, 7, do_extract,
128"\
129extract   extract entries into file\n\
130          extract filename <protoname=$> <protodata=$> <netid=$> <authname=$>"
131},
132
133{ "help", 1, 4, do_help,
134"\
135help      print help\n\
136          help <topic>"
137},
138
139{ "info", 1, 4, do_info,
140"\
141info      print information about entries"
142},
143
144{ "list", 1, 4, do_list,
145"\
146list      list entries\n\
147          list <protoname=$> <protodata=$> <netid=$> <authname=$>"
148},
149
150{ "merge", 1, 5, do_merge,
151"\
152merge     merge entries from files\n\
153          merge filename1 <filename2> <filename3> ..."
154},
155
156{ "quit", 1, 4, do_quit,
157"\
158quit      abort changes and exit program" },
159
160{ "remove", 1, 6, do_remove,
161"\
162remove    remove entries\n\
163          remove <protoname=$> <protodata=$> <netid=$> <authname=$>"
164},
165
166{ "source", 1, 6, do_source,
167"\
168source    read commands from file\n\
169          source filename"
170},
171
172{ "?", 1, 1, do_questionmark,
173"\
174?         list available commands" },
175
176{ NULL, 0, 0, NULL, NULL },
177};
178
179#define COMMAND_NAMES_PADDED_WIDTH 10	/* wider than anything above */
180
181
182static Bool okay_to_use_stdin = True;	/* set to false after using */
183
184static const char * const hex_table[] = {	/* for printing hex digits */
185    "00", "01", "02", "03", "04", "05", "06", "07",
186    "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
187    "10", "11", "12", "13", "14", "15", "16", "17",
188    "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
189    "20", "21", "22", "23", "24", "25", "26", "27",
190    "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
191    "30", "31", "32", "33", "34", "35", "36", "37",
192    "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
193    "40", "41", "42", "43", "44", "45", "46", "47",
194    "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
195    "50", "51", "52", "53", "54", "55", "56", "57",
196    "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
197    "60", "61", "62", "63", "64", "65", "66", "67",
198    "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
199    "70", "71", "72", "73", "74", "75", "76", "77",
200    "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
201    "80", "81", "82", "83", "84", "85", "86", "87",
202    "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
203    "90", "91", "92", "93", "94", "95", "96", "97",
204    "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
205    "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
206    "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
207    "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7",
208    "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
209    "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7",
210    "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
211    "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
212    "d8", "d9", "da", "db", "dc", "dd", "de", "df",
213    "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7",
214    "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
215    "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
216    "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff",
217};
218
219static unsigned int hexvalues[256];	/* for parsing hex input */
220
221static mode_t original_umask = 0;	/* for restoring */
222
223
224/*
225 * private utility procedures
226 */
227
228#define copystring(s)	( s != NULL ? strdup(s) : NULL )
229
230static int
231binaryEqual (
232    register const char	*a,
233    register const char	*b,
234    register unsigned	len)
235
236{
237    while (len--)
238	if (*a++ != *b++)
239	    return 0;
240    return 1;
241}
242
243static void prefix (const char *fn, int n)
244{
245    fprintf (stderr, "%s: %s:%d:  ", ProgramName, fn, n);
246}
247
248static void badcommandline (const char *cmd)
249{
250    fprintf (stderr, "bad \"%s\" command line\n", cmd);
251}
252
253static char *skip_space (register char *s)
254{
255    if (!s) return NULL;
256
257    for ( ; *s && isascii(*s) && isspace(*s); s++)
258	;
259    return s;
260}
261
262
263static char *skip_nonspace (register char *s)
264{
265    if (!s) return NULL;
266
267    /* put quoting into loop if need be */
268    for ( ; *s && isascii(*s) && !isspace(*s); s++)
269	;
270    return s;
271}
272
273static char **split_into_words (  /* argvify string */
274    char *src,
275    int *argcp)
276{
277    char *jword;
278    char savec;
279    char **argv;
280    int cur, total;
281
282    *argcp = 0;
283#define WORDSTOALLOC 4			/* most lines are short */
284    argv = (char **) malloc (WORDSTOALLOC * sizeof (char *));
285    if (!argv) return NULL;
286    cur = 0;
287    total = WORDSTOALLOC;
288
289    /*
290     * split the line up into separate, nul-terminated tokens; the last
291     * "token" will point to the empty string so that it can be bashed into
292     * a null pointer.
293     */
294
295    do {
296	jword = skip_space (src);
297	src = skip_nonspace (jword);
298	savec = *src;
299	*src = '\0';
300	if (cur == total) {
301	    char **prevargv = argv;
302	    total += WORDSTOALLOC;
303	    argv = (char **) realloc (argv, total * sizeof (char *));
304	    if (!argv) {
305		free (prevargv);
306		return NULL;
307	    }
308	}
309	argv[cur++] = jword;
310	if (savec) src++;		/* if not last on line advance */
311    } while (jword != src);
312
313    argv[--cur] = NULL;			/* smash empty token to end list */
314    *argcp = cur;
315    return argv;
316}
317
318
319static FILE *open_file (
320    const char **filenamep,
321    const char *mode,
322    Bool *usedstdp,
323    const char *srcfn,
324    int srcln,
325    const char *cmd)
326{
327    FILE *fp;
328
329    if (strcmp (*filenamep, "-") == 0) {
330	*usedstdp = True;
331					/* select std descriptor to use */
332	if (mode[0] == 'r') {
333	    if (okay_to_use_stdin) {
334		okay_to_use_stdin = False;
335		*filenamep = stdin_filename;
336		return stdin;
337	    } else {
338		prefix (srcfn, srcln);
339		fprintf (stderr, "%s:  stdin already in use\n", cmd);
340		return NULL;
341	    }
342	} else {
343	    *filenamep = stdout_filename;
344	    return stdout;		/* always okay to use stdout */
345	}
346    }
347
348    fp = fopen (*filenamep, mode);
349    if (!fp) {
350	prefix (srcfn, srcln);
351	fprintf (stderr, "%s:  unable to open file %s\n", cmd, *filenamep);
352    }
353    return fp;
354}
355
356
357static int read_auth_entries (FILE *fp, AuthList **headp, AuthList **tailp)
358{
359    IceAuthFileEntry *auth;
360    AuthList *head, *tail;
361    int n;
362
363    head = tail = NULL;
364    n = 0;
365					/* put all records into linked list */
366    while ((auth = IceReadAuthFileEntry (fp)) != NULL) {
367	AuthList *l = (AuthList *) malloc (sizeof (AuthList));
368	if (!l) {
369	    fprintf (stderr,
370		     "%s:  unable to alloc entry reading auth file\n",
371		     ProgramName);
372	    exit (1);
373	}
374	l->next = NULL;
375	l->auth = auth;
376	if (tail) 			/* if not first time through append */
377	  tail->next = l;
378	else
379	  head = l;			/* first time through, so assign */
380	tail = l;
381	n++;
382    }
383    *headp = head;
384    *tailp = tail;
385    return n;
386}
387
388
389static int cvthexkey (	/* turn hex key string into octets */
390    const char *hexstr,
391    char **ptrp)
392{
393    unsigned int i;
394    unsigned int len = 0;
395    char *retval;
396    const char *s;
397    unsigned char *us;
398    char c;
399    char savec = '\0';
400
401    /* count */
402    for (s = hexstr; *s; s++) {
403	if (!isascii(*s)) return -1;
404	if (isspace(*s)) continue;
405	if (!isxdigit(*s)) return -1;
406	len++;
407    }
408
409    /* if 0 or odd, then there was an error */
410    if (len == 0 || (len & 1) == 1) return -1;
411
412
413    /* now we know that the input is good */
414    len >>= 1;
415    retval = malloc (len);
416    if (!retval) {
417	fprintf (stderr, "%s:  unable to allocate %d bytes for hexkey\n",
418		 ProgramName, len);
419	return -1;
420    }
421
422    for (us = (unsigned char *) retval, i = len; i > 0; hexstr++) {
423	c = *hexstr;
424	if (isspace(c)) continue;	 /* already know it is ascii */
425	if (isupper(c))
426	    c = tolower(c);
427	if (savec) {
428#define atoh(c) ((c) - (((c) >= '0' && (c) <= '9') ? '0' : ('a'-10)))
429	    *us = (unsigned char)((atoh(savec) << 4) + atoh(c));
430#undef atoh
431	    savec = 0;		/* ready for next character */
432	    us++;
433	    i--;
434	} else {
435	    savec = c;
436	}
437    }
438    *ptrp = retval;
439    return (int) len;
440}
441
442static int dispatch_command (
443    const char *inputfilename,
444    int lineno,
445    int argc,
446    const char **argv,
447    const CommandTable *tab,
448    int *statusp)
449{
450    const CommandTable *ct;
451    const char *cmd;
452    size_t n;
453					/* scan table for command */
454    cmd = argv[0];
455    n = strlen (cmd);
456    for (ct = tab; ct->name; ct++) {
457					/* look for unique prefix */
458	if (n >= ct->minlen && n <= ct->maxlen &&
459	    strncmp (cmd, ct->name, n) == 0) {
460	    *statusp = (*(ct->processfunc))(inputfilename, lineno, argc, argv);
461	    return 1;
462	}
463    }
464
465    *statusp = 1;
466    return 0;
467}
468
469
470static AuthList *iceauth_head = NULL;	/* list of auth entries */
471static Bool iceauth_existed = False;	/* if was present at initialize */
472static Bool iceauth_modified = False;	/* if added, removed, or merged */
473static Bool iceauth_allowed = True;	/* if allowed to write auth file */
474static char *iceauth_filename = NULL;
475static volatile Bool dieing = False;
476
477/* poor man's puts(), for under signal handlers */
478#define WRITES(fd, S) (void)write((fd), (S), strlen((S)))
479
480/* ARGSUSED */
481static void die (_X_UNUSED int sig)
482{
483    dieing = True;
484    _exit (auth_finalize ());
485    /* NOTREACHED */
486}
487
488static void catchsig (int sig)
489{
490#ifdef SYSV
491    if (sig > 0) signal (sig, die);	/* re-establish signal handler */
492#endif
493    /*
494     * fileno() might not be reentrant, avoid it if possible, and use
495     * stderr instead of stdout
496     */
497#ifdef STDERR_FILENO
498    if (verbose && iceauth_modified) WRITES(STDERR_FILENO, "\r\n");
499#else
500    if (verbose && iceauth_modified) WRITES(fileno(stderr), "\r\n");
501#endif
502    die (sig);
503    /* NOTREACHED */
504}
505
506static void register_signals (void)
507{
508    signal (SIGINT, catchsig);
509    signal (SIGTERM, catchsig);
510#ifdef SIGHUP
511    signal (SIGHUP, catchsig);
512#endif
513    return;
514}
515
516
517/*
518 * public procedures for parsing lines of input
519 */
520
521int auth_initialize ( char *authfilename )
522{
523    int n;
524    AuthList *head, *tail;
525    FILE *authfp;
526    Bool exists;
527
528    register_signals ();
529
530    bzero ((char *) hexvalues, sizeof hexvalues);
531    hexvalues['0'] = 0;
532    hexvalues['1'] = 1;
533    hexvalues['2'] = 2;
534    hexvalues['3'] = 3;
535    hexvalues['4'] = 4;
536    hexvalues['5'] = 5;
537    hexvalues['6'] = 6;
538    hexvalues['7'] = 7;
539    hexvalues['8'] = 8;
540    hexvalues['9'] = 9;
541    hexvalues['a'] = hexvalues['A'] = 0xa;
542    hexvalues['b'] = hexvalues['B'] = 0xb;
543    hexvalues['c'] = hexvalues['C'] = 0xc;
544    hexvalues['d'] = hexvalues['D'] = 0xd;
545    hexvalues['e'] = hexvalues['E'] = 0xe;
546    hexvalues['f'] = hexvalues['F'] = 0xf;
547
548    if (break_locks && verbose) {
549	printf ("Attempting to break locks on authority file %s\n",
550		authfilename);
551    }
552
553    iceauth_filename = strdup(authfilename);
554
555    if (ignore_locks) {
556	if (break_locks) IceUnlockAuthFile (authfilename);
557    } else {
558	n = IceLockAuthFile (authfilename, ICEAUTH_DEFAULT_RETRIES,
559			 ICEAUTH_DEFAULT_TIMEOUT,
560			 (break_locks ? 0L : ICEAUTH_DEFAULT_DEADTIME));
561	if (n != IceAuthLockSuccess) {
562	    const char *reason = "unknown error";
563	    switch (n) {
564	      case IceAuthLockError:
565		reason = "error";
566		break;
567	      case IceAuthLockTimeout:
568		reason = "timeout";
569		break;
570	    }
571	    fprintf (stderr, "%s:  %s in locking authority file %s\n",
572		     ProgramName, reason, authfilename);
573	    return -1;
574	}
575    }
576
577    /* these checks can only be done reliably after the file is locked */
578    exists = (access (authfilename, F_OK) == 0);
579    if (exists && access (authfilename, W_OK) != 0) {
580	fprintf (stderr,
581	 "%s:  %s not writable, changes will be ignored\n",
582		 ProgramName, authfilename);
583	iceauth_allowed = False;
584    }
585
586    original_umask = umask (0077);	/* disallow non-owner access */
587
588    authfp = fopen (authfilename, "rb");
589    if (!authfp) {
590	int olderrno = errno;
591
592					/* if file there then error */
593	if (access (authfilename, F_OK) == 0) {	 /* then file does exist! */
594	    errno = olderrno;
595	    return -1;
596	}				/* else ignore it */
597	fprintf (stderr,
598		 "%s:  creating new authority file %s\n",
599		 ProgramName, authfilename);
600    } else {
601	iceauth_existed = True;
602	n = read_auth_entries (authfp, &head, &tail);
603	(void) fclose (authfp);
604	if (n < 0) {
605	    fprintf (stderr,
606		     "%s:  unable to read auth entries from file \"%s\"\n",
607		     ProgramName, authfilename);
608	    return -1;
609	}
610	iceauth_head = head;
611    }
612
613    iceauth_modified = False;
614
615    if (verbose) {
616	printf ("%s authority file %s\n",
617		ignore_locks ? "Ignoring locks on" : "Using", authfilename);
618    }
619    return 0;
620}
621
622static int write_auth_file (char *tmp_nam, size_t tmp_nam_len)
623{
624    FILE *fp;
625    AuthList *list;
626
627    if ((strlen(iceauth_filename) + 3) > tmp_nam_len) {
628	strncpy(tmp_nam, "filename too long", tmp_nam_len);
629	tmp_nam[tmp_nam_len - 1] = '\0';
630	return -1;
631    }
632
633    strcpy (tmp_nam, iceauth_filename);
634    strcat (tmp_nam, "-n");		/* for new */
635    (void) unlink (tmp_nam);
636    fp = fopen (tmp_nam, "wb");		/* umask is still set to 0077 */
637    if (!fp) {
638	fprintf (stderr, "%s:  unable to open tmp file \"%s\"\n",
639		 ProgramName, tmp_nam);
640	return -1;
641    }
642
643    for (list = iceauth_head; list; list = list->next)
644	IceWriteAuthFileEntry (fp, list->auth);
645
646    (void) fclose (fp);
647    return 0;
648}
649
650int auth_finalize (void)
651{
652    char temp_name[1024];			/* large filename size */
653
654    if (iceauth_modified) {
655	if (dieing) {
656	    if (verbose) {
657		/*
658		 * called from a signal handler -- printf is *not* reentrant; also
659		 * fileno() might not be reentrant, avoid it if possible, and use
660		 * stderr instead of stdout
661		 */
662#ifdef STDERR_FILENO
663		WRITES(STDERR_FILENO, "\nAborting changes to authority file ");
664		WRITES(STDERR_FILENO, iceauth_filename);
665		WRITES(STDERR_FILENO, "\n");
666#else
667		WRITES(fileno(stderr), "\nAborting changes to authority file ");
668		WRITES(fileno(stderr), iceauth_filename);
669		WRITES(fileno(stderr), "\n");
670#endif
671	    }
672	} else if (!iceauth_allowed) {
673	    fprintf (stderr,
674		     "%s:  %s not writable, changes ignored\n",
675		     ProgramName, iceauth_filename);
676	} else {
677	    if (verbose) {
678		printf ("%s authority file %s\n",
679			ignore_locks ? "Ignoring locks and writing" :
680			"Writing", iceauth_filename);
681	    }
682	    temp_name[0] = '\0';
683	    if (write_auth_file (temp_name, sizeof(temp_name)) == -1) {
684		fprintf (stderr,
685			 "%s:  unable to write authority file %s\n",
686			 ProgramName, temp_name);
687	    } else {
688		(void) unlink (iceauth_filename);
689#if defined(WIN32) || defined(__UNIXOS2__)
690		if (rename(temp_name, iceauth_filename) == -1)
691#else
692		/* Attempt to rename() if link() fails, since this may be on a FS that does not support hard links */
693		if (link (temp_name, iceauth_filename) == -1 && rename(temp_name, iceauth_filename) == -1)
694#endif
695		{
696		    fprintf (stderr,
697		     "%s:  unable to link authority file %s, use %s\n",
698			     ProgramName, iceauth_filename, temp_name);
699		} else {
700		    (void) unlink (temp_name);
701		}
702	    }
703	}
704    }
705
706    if (!ignore_locks && (iceauth_filename != NULL)) {
707	IceUnlockAuthFile (iceauth_filename);
708    }
709    (void) umask (original_umask);
710    return 0;
711}
712
713int process_command (
714    const char *inputfilename,
715    int lineno,
716    int argc,
717    const char **argv)
718{
719    int status;
720
721    if (argc < 1 || !argv || !argv[0]) return 1;
722
723    if (dispatch_command (inputfilename, lineno, argc, argv,
724			  command_table, &status))
725      return status;
726
727    prefix (inputfilename, lineno);
728    fprintf (stderr, "unknown command \"%s\"\n", argv[0]);
729    return 1;
730}
731
732
733/*
734 * utility routines
735 */
736
737static void fprintfhex (
738    register FILE *fp,
739    unsigned int len,
740    const char *cp)
741{
742    const unsigned char *ucp = (const unsigned char *) cp;
743
744    for (; len > 0; len--, ucp++) {
745	register const char *s = hex_table[*ucp];
746	putc (s[0], fp);
747	putc (s[1], fp);
748    }
749    return;
750}
751
752/* ARGSUSED */
753static int dump_entry (
754    const char *inputfilename _X_UNUSED,
755    int lineno _X_UNUSED,
756    IceAuthFileEntry *auth,
757    void *data)
758{
759    struct _list_data *ld = (struct _list_data *) data;
760    FILE *fp = ld->fp;
761
762    fprintf (fp, "%s", auth->protocol_name);
763    putc (' ', fp);
764    if (auth->protocol_data_length > 0)
765	fprintfhex (fp, auth->protocol_data_length, auth->protocol_data);
766    else
767	fprintf (fp, "\"\"");
768    putc (' ', fp);
769    fprintf (fp, "%s", auth->network_id);
770    putc (' ', fp);
771    fprintf (fp, "%s", auth->auth_name);
772    putc (' ', fp);
773
774    if (auth->auth_data_length == 0)
775	fprintf (fp, "\"\"");
776    else if (!strcmp(auth->auth_name, SECURERPC) ||
777	!strcmp(auth->auth_name, K5AUTH))
778	fwrite (auth->auth_data, sizeof (char), auth->auth_data_length, fp);
779    else
780	fprintfhex (fp, auth->auth_data_length, auth->auth_data);
781    putc ('\n', fp);
782
783    return 0;
784}
785
786static int extract_entry (
787    const char *inputfilename,
788    int lineno,
789    IceAuthFileEntry *auth,
790    void *data)
791{
792    struct _extract_data *ed = (struct _extract_data *) data;
793
794    if (!ed->fp) {
795	ed->fp = open_file (&ed->filename, "wb",
796			    &ed->used_stdout,
797			    inputfilename, lineno, ed->cmd);
798	if (!ed->fp) {
799	    prefix (inputfilename, lineno);
800	    fprintf (stderr,
801		     "unable to open extraction file \"%s\"\n",
802		     ed->filename);
803	    return -1;
804	}
805    }
806    IceWriteAuthFileEntry (ed->fp, auth);
807    ed->nwritten++;
808
809    return 0;
810}
811
812
813static int match_auth (
814    register IceAuthFileEntry *a,
815    register IceAuthFileEntry *b,
816    int *authDataSame)
817{
818    int match = strcmp (a->protocol_name, b->protocol_name) == 0 &&
819	    strcmp (a->network_id, b->network_id) == 0 &&
820            strcmp (a->auth_name, b->auth_name) == 0;
821
822    if (match)
823    {
824	*authDataSame = (a->auth_data_length == b->auth_data_length &&
825	    binaryEqual (a->auth_data, b->auth_data, a->auth_data_length));
826    }
827    else
828	*authDataSame = 0;
829
830    return (match);
831}
832
833
834static int merge_entries (
835    AuthList **firstp, AuthList *second,
836    int *nnewp, int *nreplp, int *ndupp)
837{
838    AuthList *a, *b, *first, *tail;
839    int n = 0, nnew = 0, nrepl = 0, ndup = 0;
840
841    if (!second) return 0;
842
843    if (!*firstp) {			/* if nothing to merge into */
844	*firstp = second;
845	for (tail = *firstp, n = 1; tail->next; n++, tail = tail->next) ;
846	*nnewp = n;
847	*nreplp = 0;
848	*ndupp = 0;
849	return n;
850    }
851
852    first = *firstp;
853    /*
854     * find end of first list and stick second list on it
855     */
856    for (tail = first; tail->next; tail = tail->next) ;
857    tail->next = second;
858
859    /*
860     * run down list freeing duplicate entries; if an entry is okay, then
861     * bump the tail up to include it, otherwise, cut the entry out of
862     * the chain.
863     */
864    for (b = second; b; ) {
865	AuthList *next = b->next;	/* in case we free it */
866	int duplicate;
867
868	duplicate = 0;
869	a = first;
870	for (;;) {
871	    int authDataSame;
872	    if (match_auth (a->auth, b->auth, &authDataSame)) {
873		if (authDataSame)
874		{
875		    /* found a complete duplicate, ignore */
876		    duplicate = 1;
877		    break;
878		}
879		else
880		{
881		    /* found a duplicate, but auth data differs */
882
883		    AuthList tmp;		/* swap it in for old one */
884		    tmp = *a;
885		    *a = *b;
886		    *b = tmp;
887		    a->next = b->next;
888		    IceFreeAuthFileEntry (b->auth);
889		    free ((char *) b);
890		    b = NULL;
891		    tail->next = next;
892		    nrepl++;
893		    nnew--;
894		    break;
895		}
896	    }
897	    if (a == tail) break;	/* if have looked at left side */
898	    a = a->next;
899	}
900	if (!duplicate && b) {		/* if we didn't remove it */
901	    tail = b;			/* bump end of first list */
902	}
903	b = next;
904
905	if (duplicate)
906	    ndup++;
907	else
908	{
909	    n++;
910	    nnew++;
911	}
912    }
913
914    *nnewp = nnew;
915    *nreplp = nrepl;
916    *ndupp = ndup;
917    return n;
918
919}
920
921
922static int search_and_do (
923    const char *inputfilename,
924    int lineno,
925    int start,
926    int argc,
927    const char *argv[],
928    DoFunc do_func,
929    void *data)
930{
931    int i;
932    int status = 0;
933    int errors = 0;
934    AuthList *l, *next;
935    const char *protoname, *protodata, *netid, *authname;
936
937    for (l = iceauth_head; l; l = next)
938    {
939	next = l->next;
940
941	protoname = protodata = netid = authname = NULL;
942
943	for (i = start; i < argc; i++)
944	{
945	    if (!strncmp ("protoname=", argv[i], 10))
946		protoname = argv[i] + 10;
947	    else if (!strncmp ("protodata=", argv[i], 10))
948		protodata = argv[i] + 10;
949	    else if (!strncmp ("netid=", argv[i], 6))
950		netid = argv[i] + 6;
951	    else if (!strncmp ("authname=", argv[i], 9))
952		authname = argv[i] + 9;
953	}
954
955	status = 0;
956
957	if (protoname || protodata || netid || authname)
958	{
959	    if (protoname && strcmp (protoname, l->auth->protocol_name))
960		continue;
961
962	    if (protodata && !binaryEqual (protodata,
963		l->auth->protocol_data, l->auth->protocol_data_length))
964		continue;
965
966	    if (netid && strcmp (netid, l->auth->network_id))
967		continue;
968
969	    if (authname && strcmp (authname, l->auth->auth_name))
970		continue;
971
972	    status = (*do_func) (inputfilename, lineno, l->auth, data);
973
974	    if (status < 0)
975		break;
976	}
977    }
978
979    if (status < 0)
980	errors -= status;		/* since status is negative */
981
982    return (errors);
983}
984
985
986/* ARGSUSED */
987static int remove_entry (
988    const char *inputfilename _X_UNUSED,
989    int lineno _X_UNUSED,
990    IceAuthFileEntry *auth,
991    void *data)
992{
993    int *nremovedp = (int *) data;
994    AuthList **listp = &iceauth_head;
995    AuthList *list;
996
997    /*
998     * unlink the auth we were asked to
999     */
1000    while ((list = *listp)->auth != auth)
1001	listp = &list->next;
1002    *listp = list->next;
1003    IceFreeAuthFileEntry (list->auth);                    /* free the auth */
1004    free (list);				    /* free the link */
1005    iceauth_modified = True;
1006    (*nremovedp)++;
1007    return 1;
1008}
1009
1010/*
1011 * action routines
1012 */
1013
1014/*
1015 * help
1016 */
1017int print_help (
1018    FILE *fp,
1019    const char *cmd)
1020{
1021    const CommandTable *ct;
1022    int n = 0;
1023
1024    fprintf (fp, "\n");
1025    if (!cmd) {				/* if no cmd, print all help */
1026	for (ct = command_table; ct->name; ct++) {
1027	    fprintf (fp, "%s\n\n", ct->helptext);
1028	    n++;
1029	}
1030    } else {
1031	size_t len = strlen (cmd);
1032	for (ct = command_table; ct->name; ct++) {
1033	    if (strncmp (cmd, ct->name, len) == 0) {
1034		fprintf (fp, "%s\n\n", ct->helptext);
1035		n++;
1036	    }
1037	}
1038    }
1039
1040    return n;
1041}
1042
1043static int do_help (
1044    const char *inputfilename,
1045    int lineno,
1046    int argc,
1047    const char **argv)
1048{
1049    const char *cmd = (argc > 1 ? argv[1] : NULL);
1050    int n;
1051
1052    n = print_help (stdout, cmd);
1053
1054    if (n < 0 || (n == 0 && !cmd)) {
1055	prefix (inputfilename, lineno);
1056	fprintf (stderr, "internal error with help");
1057	if (cmd) {
1058	    fprintf (stderr, " on command \"%s\"", cmd);
1059	}
1060	fprintf (stderr, "\n");
1061	return 1;
1062    }
1063
1064    if (n == 0) {
1065	prefix (inputfilename, lineno);
1066	/* already know that cmd is set in this case */
1067	fprintf (stderr, "no help for nonexistent command \"%s\"\n", cmd);
1068    }
1069
1070    return 0;
1071}
1072
1073/*
1074 * questionmark
1075 */
1076/* ARGSUSED */
1077static int do_questionmark (
1078    const char *inputfilename _X_UNUSED,
1079    int lineno _X_UNUSED,
1080    int argc _X_UNUSED,
1081    const char **argv _X_UNUSED)
1082{
1083    const CommandTable *ct;
1084    unsigned int i;
1085#define WIDEST_COLUMN 72
1086    unsigned int col = WIDEST_COLUMN;
1087
1088    printf ("Commands:\n");
1089    for (ct = command_table; ct->name; ct++) {
1090	if ((col + ct->maxlen) > WIDEST_COLUMN) {
1091	    if (ct != command_table) {
1092		putc ('\n', stdout);
1093	    }
1094	    fputs ("        ", stdout);
1095	    col = 8;			/* length of string above */
1096	}
1097	fputs (ct->name, stdout);
1098	col += ct->maxlen;
1099	for (i = ct->maxlen; i < COMMAND_NAMES_PADDED_WIDTH; i++) {
1100	    putc (' ', stdout);
1101	    col++;
1102	}
1103    }
1104    if (col != 0) {
1105	putc ('\n', stdout);
1106    }
1107
1108    /* allow bad lines since this is help */
1109    return 0;
1110}
1111
1112/*
1113 * list [displayname ...]
1114 */
1115static int do_list (
1116    const char *inputfilename,
1117    int lineno,
1118    int argc,
1119    const char **argv)
1120{
1121    struct _list_data ld;
1122
1123    ld.fp = stdout;
1124
1125    if (argc == 1) {
1126	register AuthList *l;
1127
1128	if (iceauth_head) {
1129	    for (l = iceauth_head; l; l = l->next) {
1130		dump_entry (inputfilename, lineno, l->auth, &ld);
1131	    }
1132	}
1133	return 0;
1134    }
1135    else
1136    {
1137	return (search_and_do (inputfilename, lineno, 1, argc, argv,
1138	    dump_entry, &ld));
1139    }
1140}
1141
1142/*
1143 * merge filename [filename ...]
1144 */
1145static int do_merge (
1146    const char *inputfilename,
1147    int lineno,
1148    int argc,
1149    const char **argv)
1150{
1151    int i;
1152    int errors = 0;
1153    AuthList *head, *tail, *listhead, *listtail;
1154    int nentries, nnew, nrepl, ndup;
1155
1156    if (argc < 2) {
1157	prefix (inputfilename, lineno);
1158	badcommandline (argv[0]);
1159	return 1;
1160    }
1161
1162    listhead = listtail = NULL;
1163
1164    for (i = 1; i < argc; i++) {
1165	const char *filename = argv[i];
1166	FILE *fp;
1167	Bool used_stdin = False;
1168
1169	fp = open_file (&filename, "rb",
1170			&used_stdin, inputfilename, lineno,
1171			argv[0]);
1172	if (!fp) {
1173	    errors++;
1174	    continue;
1175	}
1176
1177	head = tail = NULL;
1178	nentries = read_auth_entries (fp, &head, &tail);
1179	if (nentries == 0) {
1180	    prefix (inputfilename, lineno);
1181	    fprintf (stderr, "unable to read any entries from file \"%s\"\n",
1182		     filename);
1183	    errors++;
1184	} else {			/* link it in */
1185	    add_to_list (listhead, listtail, head);
1186 	}
1187
1188	if (!used_stdin) (void) fclose (fp);
1189    }
1190
1191    /*
1192     * if we have new entries, merge them in (freeing any duplicates)
1193     */
1194    if (listhead) {
1195	nentries = merge_entries (&iceauth_head, listhead,
1196	    &nnew, &nrepl, &ndup);
1197	if (verbose)
1198	  printf ("%d entries read in:  %d new, %d replacement%s\n",
1199	  	  nentries, nnew, nrepl, nrepl != 1 ? "s" : "");
1200	if (nentries > 0) iceauth_modified = True;
1201    }
1202
1203    return 0;
1204}
1205
1206/*
1207 * extract filename displayname [displayname ...]
1208 */
1209static int do_extract (
1210    const char *inputfilename,
1211    int lineno,
1212    int argc,
1213    const char **argv)
1214{
1215    int errors;
1216    struct _extract_data ed;
1217
1218    if (argc < 3) {
1219	prefix (inputfilename, lineno);
1220	badcommandline (argv[0]);
1221	return 1;
1222    }
1223
1224    ed.fp = NULL;
1225    ed.filename = argv[1];
1226    ed.nwritten = 0;
1227    ed.cmd = argv[0];
1228
1229    errors = search_and_do (inputfilename, lineno, 2, argc, argv,
1230	extract_entry, &ed);
1231
1232    if (!ed.fp) {
1233	fprintf (stderr,
1234		 "No matches found, authority file \"%s\" not written\n",
1235		 ed.filename);
1236    } else {
1237	if (verbose) {
1238	    printf ("%d entries written to \"%s\"\n",
1239		    ed.nwritten, ed.filename);
1240	}
1241	if (!ed.used_stdout) {
1242	    (void) fclose (ed.fp);
1243	}
1244    }
1245
1246    return errors;
1247}
1248
1249
1250/*
1251 * add protoname protodata netid authname authdata
1252 */
1253static int do_add (
1254    const char *inputfilename,
1255    int lineno,
1256    int argc,
1257    const char **argv)
1258{
1259    int n, nnew, nrepl, ndup;
1260    const char *protoname;
1261    const char *protodata_hex;
1262    char *protodata = NULL; /* not required */
1263    const char *netid;
1264    const char *authname;
1265    const char *authdata_hex;
1266    char *authdata = NULL;
1267    int protodata_len, authdata_len;
1268    IceAuthFileEntry *auth = NULL;
1269    AuthList *list;
1270    int status = 0;
1271
1272    if (argc != 6 || !argv[1] || !argv[2] ||
1273	!argv[3] || !argv[4] || !argv[5])
1274    {
1275	prefix (inputfilename, lineno);
1276	badcommandline (argv[0]);
1277	return 1;
1278    }
1279
1280    protoname = argv[1];
1281    protodata_hex = argv[2];
1282    netid = argv[3];
1283    authname = argv[4];
1284    authdata_hex = argv[5];
1285
1286    protodata_len = strlen (protodata_hex);
1287    if (protodata_len > 0)
1288    {
1289	if (protodata_hex[0] == '"' && protodata_hex[protodata_len - 1] == '"')
1290	{
1291	    protodata = malloc (protodata_len - 1);
1292	    if (protodata)
1293	    {
1294		strncpy (protodata, protodata_hex + 1, protodata_len - 2);
1295		protodata_len -= 2;
1296	    }
1297	    else
1298		goto add_bad_malloc;
1299	}
1300	else
1301	{
1302	    protodata_len = cvthexkey (protodata_hex, &protodata);
1303	    if (protodata_len < 0)
1304	    {
1305		prefix (inputfilename, lineno);
1306		fprintf (stderr,
1307	       "protodata_hex contains odd number of or non-hex characters\n");
1308		return (1);
1309	    }
1310	}
1311    }
1312
1313    authdata_len = strlen (authdata_hex);
1314    if (authdata_hex[0] == '"' && authdata_hex[authdata_len - 1] == '"')
1315    {
1316	authdata = malloc (authdata_len - 1);
1317	if (authdata)
1318	{
1319	    strncpy (authdata, authdata_hex + 1, authdata_len - 2);
1320	    authdata_len -= 2;
1321	}
1322	else
1323	    goto add_bad_malloc;
1324    }
1325    else if (!strcmp (protoname, SECURERPC) || !strcmp (protoname, K5AUTH))
1326    {
1327	authdata = malloc (authdata_len + 1);
1328	if (authdata)
1329	    strcpy (authdata, authdata_hex);
1330	else
1331	    goto add_bad_malloc;
1332    }
1333    else
1334    {
1335	authdata_len = cvthexkey (authdata_hex, &authdata);
1336	if (authdata_len < 0)
1337	{
1338	    prefix (inputfilename, lineno);
1339	    fprintf (stderr,
1340	       "authdata_hex contains odd number of or non-hex characters\n");
1341	    free (protodata);
1342	    return (1);
1343	}
1344    }
1345
1346    auth = (IceAuthFileEntry *) malloc (sizeof (IceAuthFileEntry));
1347
1348    if (!auth)
1349	goto add_bad_malloc;
1350
1351    auth->protocol_name = copystring (protoname);
1352    auth->protocol_data_length = protodata_len;
1353    auth->protocol_data = protodata;
1354    auth->network_id = copystring (netid);
1355    auth->auth_name = copystring (authname);
1356    auth->auth_data_length = authdata_len;
1357    auth->auth_data = authdata;
1358
1359    if (!auth->protocol_name ||
1360	(!auth->protocol_data && auth->protocol_data_length > 0) ||
1361        !auth->network_id || !auth->auth_name ||
1362	(!auth->auth_data && auth->auth_data_length > 0))
1363    {
1364	goto add_bad_malloc;
1365    }
1366
1367    list = (AuthList *) malloc (sizeof (AuthList));
1368
1369    if (!list)
1370	goto add_bad_malloc;
1371
1372    list->next = NULL;
1373    list->auth = auth;
1374
1375    /*
1376     * merge it in; note that merge will deal with allocation
1377     */
1378
1379    n = merge_entries (&iceauth_head, list, &nnew, &nrepl, &ndup);
1380
1381    if (n > 0)
1382	iceauth_modified = True;
1383    else
1384    {
1385	prefix (inputfilename, lineno);
1386	if (ndup > 0)
1387	{
1388	    status = 0;
1389	    fprintf (stderr, "no records added - all duplicate\n");
1390	}
1391	else
1392	{
1393	    status = 1;
1394	    fprintf (stderr, "unable to merge in added record\n");
1395	}
1396	goto cant_add;
1397    }
1398
1399    return 0;
1400
1401
1402add_bad_malloc:
1403
1404    status = 1;
1405    prefix (inputfilename, lineno);
1406    fprintf (stderr, "unable to allocate memory to add an entry\n");
1407
1408cant_add:
1409
1410    if (protodata)
1411	free (protodata);
1412    if (authdata)
1413	free (authdata);
1414    if (auth)
1415    {
1416	if (auth->protocol_name)
1417	    free (auth->protocol_name);
1418	/* auth->protocol_data already freed,
1419	   since it's the same as protodata */
1420	if (auth->network_id)
1421	    free (auth->network_id);
1422	if (auth->auth_name)
1423	    free (auth->auth_name);
1424	/* auth->auth_data already freed,
1425	   since it's the same as authdata */
1426	free ((char *) auth);
1427    }
1428
1429    return status;
1430}
1431
1432/*
1433 * remove displayname
1434 */
1435static int do_remove (
1436    const char *inputfilename,
1437    int lineno,
1438    int argc,
1439    const char **argv)
1440{
1441    int nremoved = 0;
1442    int errors;
1443
1444    if (argc < 2) {
1445	prefix (inputfilename, lineno);
1446	badcommandline (argv[0]);
1447	return 1;
1448    }
1449
1450    errors = search_and_do (inputfilename, lineno, 1, argc, argv,
1451	remove_entry, &nremoved);
1452    if (verbose) printf ("%d entries removed\n", nremoved);
1453    return errors;
1454}
1455
1456/*
1457 * info
1458 */
1459static int do_info (
1460    const char *inputfilename,
1461    int lineno,
1462    int argc,
1463    const char **argv)
1464{
1465    int n;
1466    AuthList *l;
1467
1468    if (argc != 1) {
1469	prefix (inputfilename, lineno);
1470	badcommandline (argv[0]);
1471	return 1;
1472    }
1473
1474    for (l = iceauth_head, n = 0; l; l = l->next, n++) ;
1475
1476    printf ("Authority file:       %s\n",
1477	    iceauth_filename ? iceauth_filename : "(none)");
1478    printf ("File new:             %s\n", iceauth_existed ? No : Yes);
1479    printf ("File locked:          %s\n", ignore_locks ? No : Yes);
1480    printf ("Number of entries:    %d\n", n);
1481    printf ("Changes honored:      %s\n", iceauth_allowed ? Yes : No);
1482    printf ("Changes made:         %s\n", iceauth_modified ? Yes : No);
1483    printf ("Current input:        %s:%d\n", inputfilename, lineno);
1484    return 0;
1485}
1486
1487
1488/*
1489 * exit
1490 */
1491static Bool alldone = False;
1492
1493/* ARGSUSED */
1494static int do_exit (
1495    const char *inputfilename _X_UNUSED,
1496    int lineno _X_UNUSED,
1497    int argc _X_UNUSED,
1498    const char **argv _X_UNUSED)
1499{
1500    /* allow bogus stuff */
1501    alldone = True;
1502    return 0;
1503}
1504
1505/*
1506 * quit
1507 */
1508/* ARGSUSED */
1509static int do_quit (
1510    const char *inputfilename _X_UNUSED,
1511    int lineno _X_UNUSED,
1512    int argc _X_UNUSED,
1513    const char **argv _X_UNUSED)
1514{
1515    /* allow bogus stuff */
1516    die (0);
1517    /* NOTREACHED */
1518    return -1;				/* for picky compilers */
1519}
1520
1521
1522/*
1523 * source filename
1524 */
1525static int do_source (
1526    const char *inputfilename,
1527    int lineno,
1528    int argc,
1529    const char **argv)
1530{
1531    const char *script;
1532    char buf[BUFSIZ];
1533    FILE *fp;
1534    Bool used_stdin = False;
1535    size_t len;
1536    int errors = 0, status;
1537    int sublineno = 0;
1538    char **subargv;
1539    int subargc;
1540    Bool prompt = False;		/* only true if reading from tty */
1541
1542    if (argc != 2 || !argv[1]) {
1543	prefix (inputfilename, lineno);
1544	badcommandline (argv[0]);
1545	return 1;
1546    }
1547
1548    script = argv[1];
1549
1550    fp = open_file (&script, "r", &used_stdin, inputfilename, lineno, argv[0]);
1551    if (!fp) {
1552	return 1;
1553    }
1554
1555    if (verbose && used_stdin && isatty (fileno (fp))) prompt = True;
1556
1557    while (!alldone) {
1558	buf[0] = '\0';
1559	if (prompt) {
1560	    printf ("iceauth> ");
1561	    fflush (stdout);
1562	}
1563	if (fgets (buf, sizeof buf, fp) == NULL) break;
1564	sublineno++;
1565	len = strlen (buf);
1566	if (len == 0 || buf[0] == '#') continue;
1567	if (buf[len-1] != '\n') {
1568	    prefix (script, sublineno);
1569	    fprintf (stderr, "line too long\n");
1570	    errors++;
1571	    break;
1572	}
1573	buf[--len] = '\0';		/* remove new line */
1574	subargv = split_into_words (buf, &subargc);
1575	if (subargv) {
1576	    status = process_command (script, sublineno, subargc,
1577                                      (const char **) subargv);
1578	    free ((char *) subargv);
1579	    errors += status;
1580	} else {
1581	    prefix (script, sublineno);
1582	    fprintf (stderr, "unable to break line into words\n");
1583	    errors++;
1584	}
1585    }
1586
1587    if (!used_stdin) {
1588	(void) fclose (fp);
1589    }
1590    return errors;
1591}
1592