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