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