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