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