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