process.c revision 90b6713c
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, char **);
51typedef int (*DoFunc)(const char *, int, IceAuthFileEntry *, char *);
52
53typedef struct _CommandTable {		/* commands that are understood */
54    char *name;				/* full name */
55    int minlen;				/* unique prefix */
56    int maxlen;				/* strlen(name) */
57    ProcessFunc processfunc;		/* handler */
58    char *helptext;			/* what to print for help */
59} CommandTable;
60
61struct _extract_data {			/* for iterating */
62    FILE *fp;				/* input source */
63    char *filename;			/* name of input */
64    Bool used_stdout;			/* whether or not need to close */
65    int nwritten;			/* number of entries written */
66    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 char *stdin_filename = "(stdin)";  /* for messages */
78static 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 ( 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 ( char *hexstr, char **ptrp );
91static int dispatch_command ( const char *inputfilename, int lineno, int argc, char **argv, const CommandTable *tab, int *statusp );
92static void die ( int sig );
93static void catchsig ( int sig );
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, char *data );
98static int extract_entry ( const char *inputfilename, int lineno, IceAuthFileEntry *auth, char *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, char *argv[], DoFunc do_func, char *data );
102static int remove_entry ( const char *inputfilename, int lineno, IceAuthFileEntry *auth, char *data );
103static int do_help ( const char *inputfilename, int lineno, int argc, char **argv );
104static int do_questionmark ( const char *inputfilename, int lineno, int argc, char **argv );
105static int do_list ( const char *inputfilename, int lineno, int argc, char **argv );
106static int do_merge ( const char *inputfilename, int lineno, int argc, char **argv );
107static int do_extract ( const char *inputfilename, int lineno, int argc, char **argv );
108static int do_add ( const char *inputfilename, int lineno, int argc, char **argv );
109static int do_remove ( const char *inputfilename, int lineno, int argc, char **argv );
110static int do_info ( const char *inputfilename, int lineno, int argc, char **argv );
111static int do_exit ( const char *inputfilename, int lineno, int argc, char **argv );
112static int do_quit ( const char *inputfilename, int lineno, int argc, char **argv );
113static int do_source ( const char *inputfilename, int lineno, int argc, 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	    total += WORDSTOALLOC;
302	    argv = (char **) realloc (argv, total * sizeof (char *));
303	    if (!argv) return NULL;
304	}
305	argv[cur++] = jword;
306	if (savec) src++;		/* if not last on line advance */
307    } while (jword != src);
308
309    argv[--cur] = NULL;			/* smash empty token to end list */
310    *argcp = cur;
311    return argv;
312}
313
314
315static FILE *open_file (
316    char **filenamep,
317    const char *mode,
318    Bool *usedstdp,
319    const char *srcfn,
320    int srcln,
321    const char *cmd)
322{
323    FILE *fp;
324
325    if (strcmp (*filenamep, "-") == 0) {
326	*usedstdp = True;
327					/* select std descriptor to use */
328	if (mode[0] == 'r') {
329	    if (okay_to_use_stdin) {
330		okay_to_use_stdin = False;
331		*filenamep = stdin_filename;
332		return stdin;
333	    } else {
334		prefix (srcfn, srcln);
335		fprintf (stderr, "%s:  stdin already in use\n", cmd);
336		return NULL;
337	    }
338	} else {
339	    *filenamep = stdout_filename;
340	    return stdout;		/* always okay to use stdout */
341	}
342    }
343
344    fp = fopen (*filenamep, mode);
345    if (!fp) {
346	prefix (srcfn, srcln);
347	fprintf (stderr, "%s:  unable to open file %s\n", cmd, *filenamep);
348    }
349    return fp;
350}
351
352
353static int read_auth_entries (FILE *fp, AuthList **headp, AuthList **tailp)
354{
355    IceAuthFileEntry *auth;
356    AuthList *head, *tail;
357    int n;
358
359    head = tail = NULL;
360    n = 0;
361					/* put all records into linked list */
362    while ((auth = IceReadAuthFileEntry (fp)) != NULL) {
363	AuthList *l = (AuthList *) malloc (sizeof (AuthList));
364	if (!l) {
365	    fprintf (stderr,
366		     "%s:  unable to alloc entry reading auth file\n",
367		     ProgramName);
368	    exit (1);
369	}
370	l->next = NULL;
371	l->auth = auth;
372	if (tail) 			/* if not first time through append */
373	  tail->next = l;
374	else
375	  head = l;			/* first time through, so assign */
376	tail = l;
377	n++;
378    }
379    *headp = head;
380    *tailp = tail;
381    return n;
382}
383
384
385static int cvthexkey (	/* turn hex key string into octets */
386    char *hexstr,
387    char **ptrp)
388{
389    int i;
390    int len = 0;
391    char *retval, *s;
392    unsigned char *us;
393    char c;
394    char savec = '\0';
395
396    /* count */
397    for (s = hexstr; *s; s++) {
398	if (!isascii(*s)) return -1;
399	if (isspace(*s)) continue;
400	if (!isxdigit(*s)) return -1;
401	len++;
402    }
403
404    /* if 0 or odd, then there was an error */
405    if (len == 0 || (len & 1) == 1) return -1;
406
407
408    /* now we know that the input is good */
409    len >>= 1;
410    retval = malloc (len);
411    if (!retval) {
412	fprintf (stderr, "%s:  unable to allocate %d bytes for hexkey\n",
413		 ProgramName, len);
414	return -1;
415    }
416
417    for (us = (unsigned char *) retval, i = len; i > 0; hexstr++) {
418	c = *hexstr;
419	if (isspace(c)) continue;	 /* already know it is ascii */
420	if (isupper(c))
421	    c = tolower(c);
422	if (savec) {
423#define atoh(c) ((c) - (((c) >= '0' && (c) <= '9') ? '0' : ('a'-10)))
424	    *us = (unsigned char)((atoh(savec) << 4) + atoh(c));
425#undef atoh
426	    savec = 0;		/* ready for next character */
427	    us++;
428	    i--;
429	} else {
430	    savec = c;
431	}
432    }
433    *ptrp = retval;
434    return len;
435}
436
437static int dispatch_command (
438    const char *inputfilename,
439    int lineno,
440    int argc,
441    char **argv,
442    const CommandTable *tab,
443    int *statusp)
444{
445    const CommandTable *ct;
446    char *cmd;
447    int n;
448					/* scan table for command */
449    cmd = argv[0];
450    n = strlen (cmd);
451    for (ct = tab; ct->name; ct++) {
452					/* look for unique prefix */
453	if (n >= ct->minlen && n <= ct->maxlen &&
454	    strncmp (cmd, ct->name, n) == 0) {
455	    *statusp = (*(ct->processfunc))(inputfilename, lineno, argc, argv);
456	    return 1;
457	}
458    }
459
460    *statusp = 1;
461    return 0;
462}
463
464
465static AuthList *iceauth_head = NULL;	/* list of auth entries */
466static Bool iceauth_existed = False;	/* if was present at initialize */
467static Bool iceauth_modified = False;	/* if added, removed, or merged */
468static Bool iceauth_allowed = True;	/* if allowed to write auth file */
469static char *iceauth_filename = NULL;
470static volatile Bool dieing = False;
471
472#ifdef RETSIGTYPE /* autoconf AC_TYPE_SIGNAL */
473# define _signal_t RETSIGTYPE
474#else /* Imake */
475#ifdef SIGNALRETURNSINT
476#define _signal_t int
477#else
478#define _signal_t void
479#endif
480#endif /* RETSIGTYPE */
481
482/* poor man's puts(), for under signal handlers */
483#define WRITES(fd, S) (void)write((fd), (S), strlen((S)))
484
485/* ARGSUSED */
486static _signal_t die (int sig)
487{
488    dieing = True;
489    _exit (auth_finalize ());
490    /* NOTREACHED */
491#ifdef SIGNALRETURNSINT
492    return -1;				/* for picky compilers */
493#endif
494}
495
496static _signal_t catchsig (int sig)
497{
498#ifdef SYSV
499    if (sig > 0) signal (sig, die);	/* re-establish signal handler */
500#endif
501    /*
502     * fileno() might not be reentrant, avoid it if possible, and use
503     * stderr instead of stdout
504     */
505#ifdef STDERR_FILENO
506    if (verbose && iceauth_modified) WRITES(STDERR_FILENO, "\r\n");
507#else
508    if (verbose && iceauth_modified) WRITES(fileno(stderr), "\r\n");
509#endif
510    die (sig);
511    /* NOTREACHED */
512#ifdef SIGNALRETURNSINT
513    return -1;				/* for picky compilers */
514#endif
515}
516
517static void register_signals (void)
518{
519    signal (SIGINT, catchsig);
520    signal (SIGTERM, catchsig);
521#ifdef SIGHUP
522    signal (SIGHUP, catchsig);
523#endif
524    return;
525}
526
527
528/*
529 * public procedures for parsing lines of input
530 */
531
532int auth_initialize ( char *authfilename )
533{
534    int n;
535    AuthList *head, *tail;
536    FILE *authfp;
537    Bool exists;
538
539    register_signals ();
540
541    bzero ((char *) hexvalues, sizeof hexvalues);
542    hexvalues['0'] = 0;
543    hexvalues['1'] = 1;
544    hexvalues['2'] = 2;
545    hexvalues['3'] = 3;
546    hexvalues['4'] = 4;
547    hexvalues['5'] = 5;
548    hexvalues['6'] = 6;
549    hexvalues['7'] = 7;
550    hexvalues['8'] = 8;
551    hexvalues['9'] = 9;
552    hexvalues['a'] = hexvalues['A'] = 0xa;
553    hexvalues['b'] = hexvalues['B'] = 0xb;
554    hexvalues['c'] = hexvalues['C'] = 0xc;
555    hexvalues['d'] = hexvalues['D'] = 0xd;
556    hexvalues['e'] = hexvalues['E'] = 0xe;
557    hexvalues['f'] = hexvalues['F'] = 0xf;
558
559    if (break_locks && verbose) {
560	printf ("Attempting to break locks on authority file %s\n",
561		authfilename);
562    }
563
564    iceauth_filename = strdup(authfilename);
565
566    if (ignore_locks) {
567	if (break_locks) IceUnlockAuthFile (authfilename);
568    } else {
569	n = IceLockAuthFile (authfilename, ICEAUTH_DEFAULT_RETRIES,
570			 ICEAUTH_DEFAULT_TIMEOUT,
571			 (break_locks ? 0L : ICEAUTH_DEFAULT_DEADTIME));
572	if (n != IceAuthLockSuccess) {
573	    char *reason = "unknown error";
574	    switch (n) {
575	      case IceAuthLockError:
576		reason = "error";
577		break;
578	      case IceAuthLockTimeout:
579		reason = "timeout";
580		break;
581	    }
582	    fprintf (stderr, "%s:  %s in locking authority file %s\n",
583		     ProgramName, reason, authfilename);
584	    return -1;
585	}
586    }
587
588    /* these checks can only be done reliably after the file is locked */
589    exists = (access (authfilename, F_OK) == 0);
590    if (exists && access (authfilename, W_OK) != 0) {
591	fprintf (stderr,
592	 "%s:  %s not writable, changes will be ignored\n",
593		 ProgramName, authfilename);
594	iceauth_allowed = False;
595    }
596
597    original_umask = umask (0077);	/* disallow non-owner access */
598
599    authfp = fopen (authfilename, "rb");
600    if (!authfp) {
601	int olderrno = errno;
602
603					/* if file there then error */
604	if (access (authfilename, F_OK) == 0) {	 /* then file does exist! */
605	    errno = olderrno;
606	    return -1;
607	}				/* else ignore it */
608	fprintf (stderr,
609		 "%s:  creating new authority file %s\n",
610		 ProgramName, authfilename);
611    } else {
612	iceauth_existed = True;
613	n = read_auth_entries (authfp, &head, &tail);
614	(void) fclose (authfp);
615	if (n < 0) {
616	    fprintf (stderr,
617		     "%s:  unable to read auth entries from file \"%s\"\n",
618		     ProgramName, authfilename);
619	    return -1;
620	}
621	iceauth_head = head;
622    }
623
624    iceauth_modified = False;
625
626    if (verbose) {
627	printf ("%s authority file %s\n",
628		ignore_locks ? "Ignoring locks on" : "Using", authfilename);
629    }
630    return 0;
631}
632
633static int write_auth_file (char *tmp_nam, size_t tmp_nam_len)
634{
635    FILE *fp;
636    AuthList *list;
637
638    if ((strlen(iceauth_filename) + 3) > tmp_nam_len) {
639	strncpy(tmp_nam, "filename too long", tmp_nam_len);
640	tmp_nam[tmp_nam_len - 1] = '\0';
641	return -1;
642    }
643
644    strcpy (tmp_nam, iceauth_filename);
645    strcat (tmp_nam, "-n");		/* for new */
646    (void) unlink (tmp_nam);
647    fp = fopen (tmp_nam, "wb");		/* umask is still set to 0077 */
648    if (!fp) {
649	fprintf (stderr, "%s:  unable to open tmp file \"%s\"\n",
650		 ProgramName, tmp_nam);
651	return -1;
652    }
653
654    for (list = iceauth_head; list; list = list->next)
655	IceWriteAuthFileEntry (fp, list->auth);
656
657    (void) fclose (fp);
658    return 0;
659}
660
661int auth_finalize (void)
662{
663    char temp_name[1024];			/* large filename size */
664
665    if (iceauth_modified) {
666	if (dieing) {
667	    if (verbose) {
668		/*
669		 * called from a signal handler -- printf is *not* reentrant; also
670		 * fileno() might not be reentrant, avoid it if possible, and use
671		 * stderr instead of stdout
672		 */
673#ifdef STDERR_FILENO
674		WRITES(STDERR_FILENO, "\nAborting changes to authority file ");
675		WRITES(STDERR_FILENO, iceauth_filename);
676		WRITES(STDERR_FILENO, "\n");
677#else
678		WRITES(fileno(stderr), "\nAborting changes to authority file ");
679		WRITES(fileno(stderr), iceauth_filename);
680		WRITES(fileno(stderr), "\n");
681#endif
682	    }
683	} else if (!iceauth_allowed) {
684	    fprintf (stderr,
685		     "%s:  %s not writable, changes ignored\n",
686		     ProgramName, iceauth_filename);
687	} else {
688	    if (verbose) {
689		printf ("%s authority file %s\n",
690			ignore_locks ? "Ignoring locks and writing" :
691			"Writing", iceauth_filename);
692	    }
693	    temp_name[0] = '\0';
694	    if (write_auth_file (temp_name, sizeof(temp_name)) == -1) {
695		fprintf (stderr,
696			 "%s:  unable to write authority file %s\n",
697			 ProgramName, temp_name);
698	    } else {
699		(void) unlink (iceauth_filename);
700#if defined(WIN32) || defined(__UNIXOS2__)
701		if (rename(temp_name, iceauth_filename) == -1)
702#else
703		/* Attempt to rename() if link() fails, since this may be on a FS that does not support hard links */
704		if (link (temp_name, iceauth_filename) == -1 && rename(temp_name, iceauth_filename) == -1)
705#endif
706		{
707		    fprintf (stderr,
708		     "%s:  unable to link authority file %s, use %s\n",
709			     ProgramName, iceauth_filename, temp_name);
710		} else {
711		    (void) unlink (temp_name);
712		}
713	    }
714	}
715    }
716
717    if (!ignore_locks && (iceauth_filename != NULL)) {
718	IceUnlockAuthFile (iceauth_filename);
719    }
720    (void) umask (original_umask);
721    return 0;
722}
723
724int process_command (
725    const char *inputfilename,
726    int lineno,
727    int argc,
728    char **argv)
729{
730    int status;
731
732    if (argc < 1 || !argv || !argv[0]) return 1;
733
734    if (dispatch_command (inputfilename, lineno, argc, argv,
735			  command_table, &status))
736      return status;
737
738    prefix (inputfilename, lineno);
739    fprintf (stderr, "unknown command \"%s\"\n", argv[0]);
740    return 1;
741}
742
743
744/*
745 * utility routines
746 */
747
748static void fprintfhex (
749    register FILE *fp,
750    unsigned int len,
751    const char *cp)
752{
753    const unsigned char *ucp = (const unsigned char *) cp;
754
755    for (; len > 0; len--, ucp++) {
756	register const char *s = hex_table[*ucp];
757	putc (s[0], fp);
758	putc (s[1], fp);
759    }
760    return;
761}
762
763/* ARGSUSED */
764static int dump_entry (
765    const char *inputfilename,
766    int lineno,
767    IceAuthFileEntry *auth,
768    char *data)
769{
770    struct _list_data *ld = (struct _list_data *) data;
771    FILE *fp = ld->fp;
772
773    fprintf (fp, "%s", auth->protocol_name);
774    putc (' ', fp);
775    if (auth->protocol_data_length > 0)
776	fprintfhex (fp, auth->protocol_data_length, auth->protocol_data);
777    else
778	fprintf (fp, "\"\"");
779    putc (' ', fp);
780    fprintf (fp, "%s", auth->network_id);
781    putc (' ', fp);
782    fprintf (fp, "%s", auth->auth_name);
783    putc (' ', fp);
784
785    if (auth->auth_data_length == 0)
786	fprintf (fp, "\"\"");
787    else if (!strcmp(auth->auth_name, SECURERPC) ||
788	!strcmp(auth->auth_name, K5AUTH))
789	fwrite (auth->auth_data, sizeof (char), auth->auth_data_length, fp);
790    else
791	fprintfhex (fp, auth->auth_data_length, auth->auth_data);
792    putc ('\n', fp);
793
794    return 0;
795}
796
797static int extract_entry (
798    const char *inputfilename,
799    int lineno,
800    IceAuthFileEntry *auth,
801    char *data)
802{
803    struct _extract_data *ed = (struct _extract_data *) data;
804
805    if (!ed->fp) {
806	ed->fp = open_file (&ed->filename, "wb",
807			    &ed->used_stdout,
808			    inputfilename, lineno, ed->cmd);
809	if (!ed->fp) {
810	    prefix (inputfilename, lineno);
811	    fprintf (stderr,
812		     "unable to open extraction file \"%s\"\n",
813		     ed->filename);
814	    return -1;
815	}
816    }
817    IceWriteAuthFileEntry (ed->fp, auth);
818    ed->nwritten++;
819
820    return 0;
821}
822
823
824static int match_auth (
825    register IceAuthFileEntry *a,
826    register IceAuthFileEntry *b,
827    int *authDataSame)
828{
829    int match = strcmp (a->protocol_name, b->protocol_name) == 0 &&
830	    strcmp (a->network_id, b->network_id) == 0 &&
831            strcmp (a->auth_name, b->auth_name) == 0;
832
833    if (match)
834    {
835	*authDataSame = (a->auth_data_length == b->auth_data_length &&
836	    binaryEqual (a->auth_data, b->auth_data, a->auth_data_length));
837    }
838    else
839	*authDataSame = 0;
840
841    return (match);
842}
843
844
845static int merge_entries (
846    AuthList **firstp, AuthList *second,
847    int *nnewp, int *nreplp, int *ndupp)
848{
849    AuthList *a, *b, *first, *tail;
850    int n = 0, nnew = 0, nrepl = 0, ndup = 0;
851
852    if (!second) return 0;
853
854    if (!*firstp) {			/* if nothing to merge into */
855	*firstp = second;
856	for (tail = *firstp, n = 1; tail->next; n++, tail = tail->next) ;
857	*nnewp = n;
858	*nreplp = 0;
859	*ndupp = 0;
860	return n;
861    }
862
863    first = *firstp;
864    /*
865     * find end of first list and stick second list on it
866     */
867    for (tail = first; tail->next; tail = tail->next) ;
868    tail->next = second;
869
870    /*
871     * run down list freeing duplicate entries; if an entry is okay, then
872     * bump the tail up to include it, otherwise, cut the entry out of
873     * the chain.
874     */
875    for (b = second; b; ) {
876	AuthList *next = b->next;	/* in case we free it */
877	int duplicate;
878
879	duplicate = 0;
880	a = first;
881	for (;;) {
882	    int authDataSame;
883	    if (match_auth (a->auth, b->auth, &authDataSame)) {
884		if (authDataSame)
885		{
886		    /* found a complete duplicate, ignore */
887		    duplicate = 1;
888		    break;
889		}
890		else
891		{
892		    /* found a duplicate, but auth data differs */
893
894		    AuthList tmp;		/* swap it in for old one */
895		    tmp = *a;
896		    *a = *b;
897		    *b = tmp;
898		    a->next = b->next;
899		    IceFreeAuthFileEntry (b->auth);
900		    free ((char *) b);
901		    b = NULL;
902		    tail->next = next;
903		    nrepl++;
904		    nnew--;
905		    break;
906		}
907	    }
908	    if (a == tail) break;	/* if have looked at left side */
909	    a = a->next;
910	}
911	if (!duplicate && b) {		/* if we didn't remove it */
912	    tail = b;			/* bump end of first list */
913	}
914	b = next;
915
916	if (duplicate)
917	    ndup++;
918	else
919	{
920	    n++;
921	    nnew++;
922	}
923    }
924
925    *nnewp = nnew;
926    *nreplp = nrepl;
927    *ndupp = ndup;
928    return n;
929
930}
931
932
933static int search_and_do (
934    const char *inputfilename,
935    int lineno,
936    int start,
937    int argc,
938    char *argv[],
939    DoFunc do_func,
940    char *data)
941{
942    int i;
943    int status = 0;
944    int errors = 0;
945    AuthList *l, *next;
946    char *protoname, *protodata, *netid, *authname;
947
948    for (l = iceauth_head; l; l = next)
949    {
950	next = l->next;
951
952	protoname = protodata = netid = authname = NULL;
953
954	for (i = start; i < argc; i++)
955	{
956	    if (!strncmp ("protoname=", argv[i], 10))
957		protoname = argv[i] + 10;
958	    else if (!strncmp ("protodata=", argv[i], 10))
959		protodata = argv[i] + 10;
960	    else if (!strncmp ("netid=", argv[i], 6))
961		netid = argv[i] + 6;
962	    else if (!strncmp ("authname=", argv[i], 9))
963		authname = argv[i] + 9;
964	}
965
966	status = 0;
967
968	if (protoname || protodata || netid || authname)
969	{
970	    if (protoname && strcmp (protoname, l->auth->protocol_name))
971		continue;
972
973	    if (protodata && !binaryEqual (protodata,
974		l->auth->protocol_data, l->auth->protocol_data_length))
975		continue;
976
977	    if (netid && strcmp (netid, l->auth->network_id))
978		continue;
979
980	    if (authname && strcmp (authname, l->auth->auth_name))
981		continue;
982
983	    status = (*do_func) (inputfilename, lineno, l->auth, data);
984
985	    if (status < 0)
986		break;
987	}
988    }
989
990    if (status < 0)
991	errors -= status;		/* since status is negative */
992
993    return (errors);
994}
995
996
997/* ARGSUSED */
998static int remove_entry (
999    const char *inputfilename,
1000    int lineno,
1001    IceAuthFileEntry *auth,
1002    char *data)
1003{
1004    int *nremovedp = (int *) data;
1005    AuthList **listp = &iceauth_head;
1006    AuthList *list;
1007
1008    /*
1009     * unlink the auth we were asked to
1010     */
1011    while ((list = *listp)->auth != auth)
1012	listp = &list->next;
1013    *listp = list->next;
1014    IceFreeAuthFileEntry (list->auth);                    /* free the auth */
1015    free (list);				    /* free the link */
1016    iceauth_modified = True;
1017    (*nremovedp)++;
1018    return 1;
1019}
1020
1021/*
1022 * action routines
1023 */
1024
1025/*
1026 * help
1027 */
1028int print_help (
1029    FILE *fp,
1030    const char *cmd)
1031{
1032    const CommandTable *ct;
1033    int n = 0;
1034
1035    fprintf (fp, "\n");
1036    if (!cmd) {				/* if no cmd, print all help */
1037	for (ct = command_table; ct->name; ct++) {
1038	    fprintf (fp, "%s\n\n", ct->helptext);
1039	    n++;
1040	}
1041    } else {
1042	int len = strlen (cmd);
1043	for (ct = command_table; ct->name; ct++) {
1044	    if (strncmp (cmd, ct->name, len) == 0) {
1045		fprintf (fp, "%s\n\n", ct->helptext);
1046		n++;
1047	    }
1048	}
1049    }
1050
1051    return n;
1052}
1053
1054static int do_help (
1055    const char *inputfilename,
1056    int lineno,
1057    int argc,
1058    char **argv)
1059{
1060    char *cmd = (argc > 1 ? argv[1] : NULL);
1061    int n;
1062
1063    n = print_help (stdout, cmd);
1064
1065    if (n < 0 || (n == 0 && !cmd)) {
1066	prefix (inputfilename, lineno);
1067	fprintf (stderr, "internal error with help");
1068	if (cmd) {
1069	    fprintf (stderr, " on command \"%s\"", cmd);
1070	}
1071	fprintf (stderr, "\n");
1072	return 1;
1073    }
1074
1075    if (n == 0) {
1076	prefix (inputfilename, lineno);
1077	/* already know that cmd is set in this case */
1078	fprintf (stderr, "no help for noexistent command \"%s\"\n", cmd);
1079    }
1080
1081    return 0;
1082}
1083
1084/*
1085 * questionmark
1086 */
1087/* ARGSUSED */
1088static int do_questionmark (
1089    const char *inputfilename,
1090    int lineno,
1091    int argc,
1092    char **argv)
1093{
1094    const CommandTable *ct;
1095    int i;
1096#define WIDEST_COLUMN 72
1097    int col = WIDEST_COLUMN;
1098
1099    printf ("Commands:\n");
1100    for (ct = command_table; ct->name; ct++) {
1101	if ((col + ct->maxlen) > WIDEST_COLUMN) {
1102	    if (ct != command_table) {
1103		putc ('\n', stdout);
1104	    }
1105	    fputs ("        ", stdout);
1106	    col = 8;			/* length of string above */
1107	}
1108	fputs (ct->name, stdout);
1109	col += ct->maxlen;
1110	for (i = ct->maxlen; i < COMMAND_NAMES_PADDED_WIDTH; i++) {
1111	    putc (' ', stdout);
1112	    col++;
1113	}
1114    }
1115    if (col != 0) {
1116	putc ('\n', stdout);
1117    }
1118
1119    /* allow bad lines since this is help */
1120    return 0;
1121}
1122
1123/*
1124 * list [displayname ...]
1125 */
1126static int do_list (
1127    const char *inputfilename,
1128    int lineno,
1129    int argc,
1130    char **argv)
1131{
1132    struct _list_data ld;
1133
1134    ld.fp = stdout;
1135
1136    if (argc == 1) {
1137	register AuthList *l;
1138
1139	if (iceauth_head) {
1140	    for (l = iceauth_head; l; l = l->next) {
1141		dump_entry (inputfilename, lineno, l->auth, (char *) &ld);
1142	    }
1143	}
1144	return 0;
1145    }
1146    else
1147    {
1148	return (search_and_do (inputfilename, lineno, 1, argc, argv,
1149	    dump_entry, (char *) &ld));
1150    }
1151}
1152
1153/*
1154 * merge filename [filename ...]
1155 */
1156static int do_merge (
1157    const char *inputfilename,
1158    int lineno,
1159    int argc,
1160    char **argv)
1161{
1162    int i;
1163    int errors = 0;
1164    AuthList *head, *tail, *listhead, *listtail;
1165    int nentries, nnew, nrepl, ndup;
1166
1167    if (argc < 2) {
1168	prefix (inputfilename, lineno);
1169	badcommandline (argv[0]);
1170	return 1;
1171    }
1172
1173    listhead = listtail = NULL;
1174
1175    for (i = 1; i < argc; i++) {
1176	char *filename = argv[i];
1177	FILE *fp;
1178	Bool used_stdin = False;
1179
1180	fp = open_file (&filename, "rb",
1181			&used_stdin, inputfilename, lineno,
1182			argv[0]);
1183	if (!fp) {
1184	    errors++;
1185	    continue;
1186	}
1187
1188	head = tail = NULL;
1189	nentries = read_auth_entries (fp, &head, &tail);
1190	if (nentries == 0) {
1191	    prefix (inputfilename, lineno);
1192	    fprintf (stderr, "unable to read any entries from file \"%s\"\n",
1193		     filename);
1194	    errors++;
1195	} else {			/* link it in */
1196	    add_to_list (listhead, listtail, head);
1197 	}
1198
1199	if (!used_stdin) (void) fclose (fp);
1200    }
1201
1202    /*
1203     * if we have new entries, merge them in (freeing any duplicates)
1204     */
1205    if (listhead) {
1206	nentries = merge_entries (&iceauth_head, listhead,
1207	    &nnew, &nrepl, &ndup);
1208	if (verbose)
1209	  printf ("%d entries read in:  %d new, %d replacement%s\n",
1210	  	  nentries, nnew, nrepl, nrepl != 1 ? "s" : "");
1211	if (nentries > 0) iceauth_modified = True;
1212    }
1213
1214    return 0;
1215}
1216
1217/*
1218 * extract filename displayname [displayname ...]
1219 */
1220static int do_extract (
1221    const char *inputfilename,
1222    int lineno,
1223    int argc,
1224    char **argv)
1225{
1226    int errors;
1227    struct _extract_data ed;
1228
1229    if (argc < 3) {
1230	prefix (inputfilename, lineno);
1231	badcommandline (argv[0]);
1232	return 1;
1233    }
1234
1235    ed.fp = NULL;
1236    ed.filename = argv[1];
1237    ed.nwritten = 0;
1238    ed.cmd = argv[0];
1239
1240    errors = search_and_do (inputfilename, lineno, 2, argc, argv,
1241	extract_entry, (char *) &ed);
1242
1243    if (!ed.fp) {
1244	fprintf (stderr,
1245		 "No matches found, authority file \"%s\" not written\n",
1246		 ed.filename);
1247    } else {
1248	if (verbose) {
1249	    printf ("%d entries written to \"%s\"\n",
1250		    ed.nwritten, ed.filename);
1251	}
1252	if (!ed.used_stdout) {
1253	    (void) fclose (ed.fp);
1254	}
1255    }
1256
1257    return errors;
1258}
1259
1260
1261/*
1262 * add protoname protodata netid authname authdata
1263 */
1264static int do_add (
1265    const char *inputfilename,
1266    int lineno,
1267    int argc,
1268    char **argv)
1269{
1270    int n, nnew, nrepl, ndup;
1271    char *protoname;
1272    char *protodata_hex;
1273    char *protodata = NULL; /* not required */
1274    char *netid;
1275    char *authname;
1276    char *authdata_hex;
1277    char *authdata = NULL;
1278    int protodata_len, authdata_len;
1279    IceAuthFileEntry *auth = NULL;
1280    AuthList *list;
1281    int status = 0;
1282
1283    if (argc != 6 || !argv[1] || !argv[2] ||
1284	!argv[3] || !argv[4] || !argv[5])
1285    {
1286	prefix (inputfilename, lineno);
1287	badcommandline (argv[0]);
1288	return 1;
1289    }
1290
1291    protoname = argv[1];
1292    protodata_hex = argv[2];
1293    netid = argv[3];
1294    authname = argv[4];
1295    authdata_hex = argv[5];
1296
1297    protodata_len = strlen (protodata_hex);
1298    if (protodata_len > 0)
1299    {
1300	if (protodata_hex[0] == '"' && protodata_hex[protodata_len - 1] == '"')
1301	{
1302	    protodata = malloc (protodata_len - 1);
1303	    if (protodata)
1304	    {
1305		strncpy (protodata, protodata_hex + 1, protodata_len - 2);
1306		protodata_len -= 2;
1307	    }
1308	    else
1309		goto add_bad_malloc;
1310	}
1311	else
1312	{
1313	    protodata_len = cvthexkey (protodata_hex, &protodata);
1314	    if (protodata_len < 0)
1315	    {
1316		prefix (inputfilename, lineno);
1317		fprintf (stderr,
1318	       "protodata_hex contains odd number of or non-hex characters\n");
1319		return (1);
1320	    }
1321	}
1322    }
1323
1324    authdata_len = strlen (authdata_hex);
1325    if (authdata_hex[0] == '"' && authdata_hex[authdata_len - 1] == '"')
1326    {
1327	authdata = malloc (authdata_len - 1);
1328	if (authdata)
1329	{
1330	    strncpy (authdata, authdata_hex + 1, authdata_len - 2);
1331	    authdata_len -= 2;
1332	}
1333	else
1334	    goto add_bad_malloc;
1335    }
1336    else if (!strcmp (protoname, SECURERPC) || !strcmp (protoname, K5AUTH))
1337    {
1338	authdata = malloc (authdata_len + 1);
1339	if (authdata)
1340	    strcpy (authdata, authdata_hex);
1341	else
1342	    goto add_bad_malloc;
1343    }
1344    else
1345    {
1346	authdata_len = cvthexkey (authdata_hex, &authdata);
1347	if (authdata_len < 0)
1348	{
1349	    prefix (inputfilename, lineno);
1350	    fprintf (stderr,
1351	       "authdata_hex contains odd number of or non-hex characters\n");
1352	    free (protodata);
1353	    return (1);
1354	}
1355    }
1356
1357    auth = (IceAuthFileEntry *) malloc (sizeof (IceAuthFileEntry));
1358
1359    if (!auth)
1360	goto add_bad_malloc;
1361
1362    auth->protocol_name = copystring (protoname);
1363    auth->protocol_data_length = protodata_len;
1364    auth->protocol_data = protodata;
1365    auth->network_id = copystring (netid);
1366    auth->auth_name = copystring (authname);
1367    auth->auth_data_length = authdata_len;
1368    auth->auth_data = authdata;
1369
1370    if (!auth->protocol_name ||
1371	(!auth->protocol_data && auth->protocol_data_length > 0) ||
1372        !auth->network_id || !auth->auth_name ||
1373	(!auth->auth_data && auth->auth_data_length > 0))
1374    {
1375	goto add_bad_malloc;
1376    }
1377
1378    list = (AuthList *) malloc (sizeof (AuthList));
1379
1380    if (!list)
1381	goto add_bad_malloc;
1382
1383    list->next = NULL;
1384    list->auth = auth;
1385
1386    /*
1387     * merge it in; note that merge will deal with allocation
1388     */
1389
1390    n = merge_entries (&iceauth_head, list, &nnew, &nrepl, &ndup);
1391
1392    if (n > 0)
1393	iceauth_modified = True;
1394    else
1395    {
1396	prefix (inputfilename, lineno);
1397	if (ndup > 0)
1398	{
1399	    status = 0;
1400	    fprintf (stderr, "no records added - all duplicate\n");
1401	}
1402	else
1403	{
1404	    status = 1;
1405	    fprintf (stderr, "unable to merge in added record\n");
1406	}
1407	goto cant_add;
1408    }
1409
1410    return 0;
1411
1412
1413add_bad_malloc:
1414
1415    status = 1;
1416    prefix (inputfilename, lineno);
1417    fprintf (stderr, "unable to allocate memory to add an entry\n");
1418
1419cant_add:
1420
1421    if (protodata)
1422	free (protodata);
1423    if (authdata)
1424	free (authdata);
1425    if (auth)
1426    {
1427	if (auth->protocol_name)
1428	    free (auth->protocol_name);
1429	/* auth->protocol_data already freed,
1430	   since it's the same as protodata */
1431	if (auth->network_id)
1432	    free (auth->network_id);
1433	if (auth->auth_name)
1434	    free (auth->auth_name);
1435	/* auth->auth_data already freed,
1436	   since it's the same as authdata */
1437	free ((char *) auth);
1438    }
1439
1440    return status;
1441}
1442
1443/*
1444 * remove displayname
1445 */
1446static int do_remove (
1447    const char *inputfilename,
1448    int lineno,
1449    int argc,
1450    char **argv)
1451{
1452    int nremoved = 0;
1453    int errors;
1454
1455    if (argc < 2) {
1456	prefix (inputfilename, lineno);
1457	badcommandline (argv[0]);
1458	return 1;
1459    }
1460
1461    errors = search_and_do (inputfilename, lineno, 1, argc, argv,
1462	remove_entry, (char *) &nremoved);
1463    if (verbose) printf ("%d entries removed\n", nremoved);
1464    return errors;
1465}
1466
1467/*
1468 * info
1469 */
1470static int do_info (
1471    const char *inputfilename,
1472    int lineno,
1473    int argc,
1474    char **argv)
1475{
1476    int n;
1477    AuthList *l;
1478
1479    if (argc != 1) {
1480	prefix (inputfilename, lineno);
1481	badcommandline (argv[0]);
1482	return 1;
1483    }
1484
1485    for (l = iceauth_head, n = 0; l; l = l->next, n++) ;
1486
1487    printf ("Authority file:       %s\n",
1488	    iceauth_filename ? iceauth_filename : "(none)");
1489    printf ("File new:             %s\n", iceauth_existed ? No : Yes);
1490    printf ("File locked:          %s\n", ignore_locks ? No : Yes);
1491    printf ("Number of entries:    %d\n", n);
1492    printf ("Changes honored:      %s\n", iceauth_allowed ? Yes : No);
1493    printf ("Changes made:         %s\n", iceauth_modified ? Yes : No);
1494    printf ("Current input:        %s:%d\n", inputfilename, lineno);
1495    return 0;
1496}
1497
1498
1499/*
1500 * exit
1501 */
1502static Bool alldone = False;
1503
1504/* ARGSUSED */
1505static int do_exit (
1506    const char *inputfilename,
1507    int lineno,
1508    int argc,
1509    char **argv)
1510{
1511    /* allow bogus stuff */
1512    alldone = True;
1513    return 0;
1514}
1515
1516/*
1517 * quit
1518 */
1519/* ARGSUSED */
1520static int do_quit (
1521    const char *inputfilename,
1522    int lineno,
1523    int argc,
1524    char **argv)
1525{
1526    /* allow bogus stuff */
1527    die (0);
1528    /* NOTREACHED */
1529    return -1;				/* for picky compilers */
1530}
1531
1532
1533/*
1534 * source filename
1535 */
1536static int do_source (
1537    const char *inputfilename,
1538    int lineno,
1539    int argc,
1540    char **argv)
1541{
1542    char *script;
1543    char buf[BUFSIZ];
1544    FILE *fp;
1545    Bool used_stdin = False;
1546    int len;
1547    int errors = 0, status;
1548    int sublineno = 0;
1549    char **subargv;
1550    int subargc;
1551    Bool prompt = False;		/* only true if reading from tty */
1552
1553    if (argc != 2 || !argv[1]) {
1554	prefix (inputfilename, lineno);
1555	badcommandline (argv[0]);
1556	return 1;
1557    }
1558
1559    script = argv[1];
1560
1561    fp = open_file (&script, "r", &used_stdin, inputfilename, lineno, argv[0]);
1562    if (!fp) {
1563	return 1;
1564    }
1565
1566    if (verbose && used_stdin && isatty (fileno (fp))) prompt = True;
1567
1568    while (!alldone) {
1569	buf[0] = '\0';
1570	if (prompt) {
1571	    printf ("iceauth> ");
1572	    fflush (stdout);
1573	}
1574	if (fgets (buf, sizeof buf, fp) == NULL) break;
1575	sublineno++;
1576	len = strlen (buf);
1577	if (len == 0 || buf[0] == '#') continue;
1578	if (buf[len-1] != '\n') {
1579	    prefix (script, sublineno);
1580	    fprintf (stderr, "line too long\n");
1581	    errors++;
1582	    break;
1583	}
1584	buf[--len] = '\0';		/* remove new line */
1585	subargv = split_into_words (buf, &subargc);
1586	if (subargv) {
1587	    status = process_command (script, sublineno, subargc, subargv);
1588	    free ((char *) subargv);
1589	    errors += status;
1590	} else {
1591	    prefix (script, sublineno);
1592	    fprintf (stderr, "unable to break line into words\n");
1593	    errors++;
1594	}
1595    }
1596
1597    if (!used_stdin) {
1598	(void) fclose (fp);
1599    }
1600    return errors;
1601}
1602