|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1983 Regents of the University of California. ! 3: * All rights reserved. The Berkeley software License Agreement ! 4: * specifies the terms and conditions for redistribution. ! 5: */ ! 6: ! 7: #ifndef lint ! 8: char copyright[] = ! 9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\ ! 10: All rights reserved.\n"; ! 11: #endif not lint ! 12: ! 13: #ifndef lint ! 14: static char sccsid[] = "@(#)passwd.c 4.24 (Berkeley) 5/28/86"; ! 15: #endif not lint ! 16: ! 17: /* ! 18: * Modify a field in the password file (either ! 19: * password, login shell, or gecos field). ! 20: * This program should be suid with an owner ! 21: * with write permission on /etc/passwd. ! 22: */ ! 23: #include <sys/types.h> ! 24: #include <sys/file.h> ! 25: #include <sys/time.h> ! 26: #include <sys/resource.h> ! 27: ! 28: #include <stdio.h> ! 29: #include <signal.h> ! 30: #include <pwd.h> ! 31: #include <ndbm.h> ! 32: #include <errno.h> ! 33: #include <strings.h> ! 34: #include <ctype.h> ! 35: ! 36: /* ! 37: * This should be the first thing returned from a getloginshells() ! 38: * but too many programs know that it is /bin/sh. ! 39: */ ! 40: #define DEFSHELL "/bin/sh" ! 41: ! 42: char temp[] = "/etc/ptmp"; ! 43: char passwd[] = "/etc/passwd"; ! 44: char *getpass(); ! 45: char *getlogin(); ! 46: char *getfingerinfo(); ! 47: char *getloginshell(); ! 48: char *getnewpasswd(); ! 49: char *malloc(); ! 50: char *getusershell(); ! 51: extern int errno; ! 52: ! 53: main(argc, argv) ! 54: char *argv[]; ! 55: { ! 56: struct passwd *pwd; ! 57: char *cp, *uname, *progname; ! 58: int fd, u, dochfn, dochsh, err; ! 59: FILE *tf; ! 60: DBM *dp; ! 61: ! 62: if ((progname = rindex(argv[0], '/')) == NULL) ! 63: progname = argv[0]; ! 64: else ! 65: progname++; ! 66: dochfn = 0, dochsh = 0; ! 67: argc--, argv++; ! 68: while (argc > 0 && argv[0][0] == '-') { ! 69: for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { ! 70: ! 71: case 'f': ! 72: if (dochsh) ! 73: goto bad; ! 74: dochfn = 1; ! 75: break; ! 76: ! 77: case 's': ! 78: if (dochfn) { ! 79: bad: ! 80: fprintf(stderr, ! 81: "passwd: Only one of -f and -s allowed.\n"); ! 82: exit(1); ! 83: } ! 84: dochsh = 1; ! 85: break; ! 86: ! 87: default: ! 88: fprintf(stderr, "passwd: -%c: unknown option.\n", *cp); ! 89: exit(1); ! 90: } ! 91: argc--, argv++; ! 92: } ! 93: if (!dochfn && !dochsh) { ! 94: if (strcmp(progname, "chfn") == 0) ! 95: dochfn = 1; ! 96: else if (strcmp(progname, "chsh") == 0) ! 97: dochsh = 1; ! 98: } ! 99: if (argc < 1) { ! 100: if ((uname = getlogin()) == NULL) { ! 101: fprintf(stderr, "Usage: %s [-f] [-s] [user]\n", progname); ! 102: exit(1); ! 103: } ! 104: printf("Changing %s for %s.\n", ! 105: dochfn ? "finger information" : ! 106: dochsh ? "login shell" : "password", ! 107: uname); ! 108: } else ! 109: uname = *argv++; ! 110: pwd = getpwnam(uname); ! 111: if (pwd == NULL) { ! 112: fprintf(stderr, "passwd: %s: unknown user.\n", uname); ! 113: exit(1); ! 114: } ! 115: u = getuid(); ! 116: if (u != 0 && u != pwd->pw_uid) { ! 117: printf("Permission denied.\n"); ! 118: exit(1); ! 119: } ! 120: if (dochfn) ! 121: cp = getfingerinfo(pwd); ! 122: else if (dochsh) ! 123: cp = getloginshell(pwd, u, *argv); ! 124: else ! 125: cp = getnewpasswd(pwd, u); ! 126: (void) signal(SIGHUP, SIG_IGN); ! 127: (void) signal(SIGINT, SIG_IGN); ! 128: (void) signal(SIGQUIT, SIG_IGN); ! 129: (void) signal(SIGTSTP, SIG_IGN); ! 130: (void) umask(0); ! 131: fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0644); ! 132: if (fd < 0) { ! 133: err = errno; ! 134: ! 135: fprintf(stderr, "passwd: "); ! 136: if (err == EEXIST) ! 137: fprintf(stderr, "password file busy - try again.\n"); ! 138: else { ! 139: errno = err; ! 140: perror(temp); ! 141: } ! 142: exit(1); ! 143: } ! 144: if ((tf = fdopen(fd, "w")) == NULL) { ! 145: fprintf(stderr, "passwd: fdopen failed?\n"); ! 146: exit(1); ! 147: } ! 148: if ((dp = dbm_open(passwd, O_RDWR, 0644)) == NULL) { ! 149: err = errno; ! 150: fprintf(stderr, "Warning: dbm_open failed: "); ! 151: errno = err; ! 152: perror(passwd); ! 153: } else if (flock(dp->dbm_dirf, LOCK_EX) < 0) { ! 154: perror("Warning: lock failed"); ! 155: dbm_close(dp); ! 156: dp = NULL; ! 157: } ! 158: unlimit(RLIMIT_CPU); ! 159: unlimit(RLIMIT_FSIZE); ! 160: /* ! 161: * Copy passwd to temp, replacing matching lines ! 162: * with new password. ! 163: */ ! 164: while ((pwd = getpwent()) != NULL) { ! 165: if (strcmp(pwd->pw_name, uname) == 0) { ! 166: if (u && u != pwd->pw_uid) { ! 167: fprintf(stderr, "passwd: permission denied.\n"); ! 168: goto out; ! 169: } ! 170: if (dochfn) ! 171: pwd->pw_gecos = cp; ! 172: else if (dochsh) ! 173: pwd->pw_shell = cp; ! 174: else ! 175: pwd->pw_passwd = cp; ! 176: if (pwd->pw_gecos[0] == '*') /* ??? */ ! 177: pwd->pw_gecos++; ! 178: replace(dp, pwd); ! 179: } ! 180: fprintf(tf,"%s:%s:%d:%d:%s:%s:%s\n", ! 181: pwd->pw_name, ! 182: pwd->pw_passwd, ! 183: pwd->pw_uid, ! 184: pwd->pw_gid, ! 185: pwd->pw_gecos, ! 186: pwd->pw_dir, ! 187: pwd->pw_shell); ! 188: } ! 189: endpwent(); ! 190: if (dp != NULL && dbm_error(dp)) ! 191: fprintf(stderr, "Warning: dbm_store failed\n"); ! 192: (void) fflush(tf); ! 193: if (ferror(tf)) { ! 194: fprintf(stderr, "Warning: %s write error, %s not updated\n", ! 195: temp, passwd); ! 196: goto out; ! 197: } ! 198: (void) fclose(tf); ! 199: if (dp != NULL) ! 200: dbm_close(dp); ! 201: if (rename(temp, passwd) < 0) { ! 202: perror("passwd: rename"); ! 203: out: ! 204: (void) unlink(temp); ! 205: exit(1); ! 206: } ! 207: exit(0); ! 208: } ! 209: ! 210: unlimit(lim) ! 211: { ! 212: struct rlimit rlim; ! 213: ! 214: rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; ! 215: (void) setrlimit(lim, &rlim); ! 216: } ! 217: ! 218: /* ! 219: * Replace the password entry in the dbm data base with pwd. ! 220: */ ! 221: replace(dp, pwd) ! 222: DBM *dp; ! 223: struct passwd *pwd; ! 224: { ! 225: datum key, content; ! 226: register char *cp, *tp; ! 227: char buf[BUFSIZ]; ! 228: ! 229: if (dp == NULL) ! 230: return; ! 231: ! 232: cp = buf; ! 233: #define COMPACT(e) tp = pwd->pw_/**/e; while (*cp++ = *tp++); ! 234: COMPACT(name); ! 235: COMPACT(passwd); ! 236: bcopy((char *)&pwd->pw_uid, cp, sizeof (int)); ! 237: cp += sizeof (int); ! 238: bcopy((char *)&pwd->pw_gid, cp, sizeof (int)); ! 239: cp += sizeof (int); ! 240: bcopy((char *)&pwd->pw_quota, cp, sizeof (int)); ! 241: cp += sizeof (int); ! 242: COMPACT(comment); ! 243: COMPACT(gecos); ! 244: COMPACT(dir); ! 245: COMPACT(shell); ! 246: content.dptr = buf; ! 247: content.dsize = cp - buf; ! 248: key.dptr = pwd->pw_name; ! 249: key.dsize = strlen(pwd->pw_name); ! 250: dbm_store(dp, key, content, DBM_REPLACE); ! 251: key.dptr = (char *)&pwd->pw_uid; ! 252: key.dsize = sizeof (int); ! 253: dbm_store(dp, key, content, DBM_REPLACE); ! 254: } ! 255: ! 256: char * ! 257: getnewpasswd(pwd, u) ! 258: register struct passwd *pwd; ! 259: int u; ! 260: { ! 261: char saltc[2]; ! 262: long salt; ! 263: int i, insist = 0, ok, flags; ! 264: int c, pwlen; ! 265: static char pwbuf[10]; ! 266: long time(); ! 267: char *crypt(), *pw, *p; ! 268: ! 269: if (pwd->pw_passwd[0] && u != 0) { ! 270: (void) strcpy(pwbuf, getpass("Old password:")); ! 271: pw = crypt(pwbuf, pwd->pw_passwd); ! 272: if (strcmp(pw, pwd->pw_passwd) != 0) { ! 273: printf("Sorry.\n"); ! 274: exit(1); ! 275: } ! 276: } ! 277: tryagain: ! 278: (void) strcpy(pwbuf, getpass("New password:")); ! 279: pwlen = strlen(pwbuf); ! 280: if (pwlen == 0) { ! 281: printf("Password unchanged.\n"); ! 282: exit(1); ! 283: } ! 284: /* ! 285: * Insure password is of reasonable length and ! 286: * composition. If we really wanted to make things ! 287: * sticky, we could check the dictionary for common ! 288: * words, but then things would really be slow. ! 289: */ ! 290: ok = 0; ! 291: flags = 0; ! 292: p = pwbuf; ! 293: while (c = *p++) { ! 294: if (c >= 'a' && c <= 'z') ! 295: flags |= 2; ! 296: else if (c >= 'A' && c <= 'Z') ! 297: flags |= 4; ! 298: else if (c >= '0' && c <= '9') ! 299: flags |= 1; ! 300: else ! 301: flags |= 8; ! 302: } ! 303: if (flags >= 7 && pwlen >= 4) ! 304: ok = 1; ! 305: if ((flags == 2 || flags == 4) && pwlen >= 6) ! 306: ok = 1; ! 307: if ((flags == 3 || flags == 5 || flags == 6) && pwlen >= 5) ! 308: ok = 1; ! 309: if (!ok && insist < 2) { ! 310: printf("Please use %s.\n", flags == 1 ? ! 311: "at least one non-numeric character" : ! 312: "a longer password"); ! 313: insist++; ! 314: goto tryagain; ! 315: } ! 316: if (strcmp(pwbuf, getpass("Retype new password:")) != 0) { ! 317: printf("Mismatch - password unchanged.\n"); ! 318: exit(1); ! 319: } ! 320: (void) time(&salt); ! 321: salt = 9 * getpid(); ! 322: saltc[0] = salt & 077; ! 323: saltc[1] = (salt>>6) & 077; ! 324: for (i = 0; i < 2; i++) { ! 325: c = saltc[i] + '.'; ! 326: if (c > '9') ! 327: c += 7; ! 328: if (c > 'Z') ! 329: c += 6; ! 330: saltc[i] = c; ! 331: } ! 332: return (crypt(pwbuf, saltc)); ! 333: } ! 334: ! 335: char * ! 336: getloginshell(pwd, u, arg) ! 337: struct passwd *pwd; ! 338: int u; ! 339: char *arg; ! 340: { ! 341: static char newshell[BUFSIZ]; ! 342: char *cp, *valid, *getusershell(); ! 343: ! 344: if (pwd->pw_shell == 0 || *pwd->pw_shell == '\0') ! 345: pwd->pw_shell = DEFSHELL; ! 346: if (u != 0) { ! 347: for (valid = getusershell(); valid; valid = getusershell()) ! 348: if (strcmp(pwd->pw_shell, valid) == 0) ! 349: break; ! 350: if (valid == NULL) { ! 351: printf("Cannot change from restricted shell %s\n", ! 352: pwd->pw_shell); ! 353: exit(1); ! 354: } ! 355: } ! 356: if (arg != 0) { ! 357: (void) strncpy(newshell, arg, sizeof newshell - 1); ! 358: newshell[sizeof newshell - 1] = 0; ! 359: } else { ! 360: printf("Old shell: %s\nNew shell: ", pwd->pw_shell); ! 361: (void)fgets(newshell, sizeof (newshell) - 1, stdin); ! 362: cp = index(newshell, '\n'); ! 363: if (cp) ! 364: *cp = '\0'; ! 365: } ! 366: if (newshell[0] == '\0' || strcmp(newshell, pwd->pw_shell) == 0) { ! 367: printf("Login shell unchanged.\n"); ! 368: exit(1); ! 369: } ! 370: /* ! 371: * Allow user to give shell name w/o preceding pathname. ! 372: */ ! 373: if (u == 0) { ! 374: valid = newshell; ! 375: } else { ! 376: for (valid = getusershell(); valid; valid = getusershell()) { ! 377: if (newshell[0] == '/') { ! 378: cp = valid; ! 379: } else { ! 380: cp = rindex(valid, '/'); ! 381: if (cp == 0) ! 382: cp = valid; ! 383: else ! 384: cp++; ! 385: } ! 386: if (strcmp(newshell, cp) == 0) ! 387: break; ! 388: } ! 389: } ! 390: if (valid == 0) { ! 391: printf("%s is unacceptable as a new shell.\n", ! 392: newshell); ! 393: exit(1); ! 394: } ! 395: if (access(valid, X_OK) < 0) { ! 396: printf("%s is unavailable.\n", valid); ! 397: exit(1); ! 398: } ! 399: if (strcmp(valid, DEFSHELL) == 0) ! 400: valid[0] = '\0'; ! 401: return (valid); ! 402: } ! 403: ! 404: struct default_values { ! 405: char *name; ! 406: char *office_num; ! 407: char *office_phone; ! 408: char *home_phone; ! 409: }; ! 410: ! 411: /* ! 412: * Get name, room number, school phone, and home phone. ! 413: */ ! 414: char * ! 415: getfingerinfo(pwd) ! 416: struct passwd *pwd; ! 417: { ! 418: char in_str[BUFSIZ]; ! 419: struct default_values *defaults, *get_defaults(); ! 420: static char answer[4*BUFSIZ]; ! 421: ! 422: answer[0] = '\0'; ! 423: defaults = get_defaults(pwd->pw_gecos); ! 424: printf("Default values are printed inside of '[]'.\n"); ! 425: printf("To accept the default, type <return>.\n"); ! 426: printf("To have a blank entry, type the word 'none'.\n"); ! 427: /* ! 428: * Get name. ! 429: */ ! 430: do { ! 431: printf("\nName [%s]: ", defaults->name); ! 432: (void) fgets(in_str, BUFSIZ, stdin); ! 433: if (special_case(in_str, defaults->name)) ! 434: break; ! 435: } while (illegal_input(in_str)); ! 436: (void) strcpy(answer, in_str); ! 437: /* ! 438: * Get room number. ! 439: */ ! 440: do { ! 441: printf("Room number (Exs: 597E or 197C) [%s]: ", ! 442: defaults->office_num); ! 443: (void) fgets(in_str, BUFSIZ, stdin); ! 444: if (special_case(in_str, defaults->office_num)) ! 445: break; ! 446: } while (illegal_input(in_str) || illegal_building(in_str)); ! 447: (void) strcat(strcat(answer, ","), in_str); ! 448: /* ! 449: * Get office phone number. ! 450: * Remove hyphens. ! 451: */ ! 452: do { ! 453: printf("Office Phone (Ex: 6426000) [%s]: ", ! 454: defaults->office_phone); ! 455: (void) fgets(in_str, BUFSIZ, stdin); ! 456: if (special_case(in_str, defaults->office_phone)) ! 457: break; ! 458: remove_hyphens(in_str); ! 459: } while (illegal_input(in_str) || not_all_digits(in_str)); ! 460: (void) strcat(strcat(answer, ","), in_str); ! 461: /* ! 462: * Get home phone number. ! 463: * Remove hyphens if present. ! 464: */ ! 465: do { ! 466: printf("Home Phone (Ex: 9875432) [%s]: ", defaults->home_phone); ! 467: (void) fgets(in_str, BUFSIZ, stdin); ! 468: if (special_case(in_str, defaults->home_phone)) ! 469: break; ! 470: remove_hyphens(in_str); ! 471: } while (illegal_input(in_str) || not_all_digits(in_str)); ! 472: (void) strcat(strcat(answer, ","), in_str); ! 473: if (strcmp(answer, pwd->pw_gecos) == 0) { ! 474: printf("Finger information unchanged.\n"); ! 475: exit(1); ! 476: } ! 477: return (answer); ! 478: } ! 479: ! 480: /* ! 481: * Prints an error message if a ':' or a newline is found in the string. ! 482: * A message is also printed if the input string is too long. ! 483: * The password file uses :'s as seperators, and are not allowed in the "gcos" ! 484: * field. Newlines serve as delimiters between users in the password file, ! 485: * and so, those too, are checked for. (I don't think that it is possible to ! 486: * type them in, but better safe than sorry) ! 487: * ! 488: * Returns '1' if a colon or newline is found or the input line is too long. ! 489: */ ! 490: illegal_input(input_str) ! 491: char *input_str; ! 492: { ! 493: char *ptr; ! 494: int error_flag = 0; ! 495: int length = strlen(input_str); ! 496: ! 497: if (index(input_str, ':')) { ! 498: printf("':' is not allowed.\n"); ! 499: error_flag = 1; ! 500: } ! 501: if (input_str[length-1] != '\n') { ! 502: /* the newline and the '\0' eat up two characters */ ! 503: printf("Maximum number of characters allowed is %d\n", ! 504: BUFSIZ-2); ! 505: /* flush the rest of the input line */ ! 506: while (getchar() != '\n') ! 507: /* void */; ! 508: error_flag = 1; ! 509: } ! 510: /* ! 511: * Delete newline by shortening string by 1. ! 512: */ ! 513: input_str[length-1] = '\0'; ! 514: /* ! 515: * Don't allow control characters, etc in input string. ! 516: */ ! 517: for (ptr=input_str; *ptr != '\0'; ptr++) { ! 518: if ((int) *ptr < 040) { ! 519: printf("Control characters are not allowed.\n"); ! 520: error_flag = 1; ! 521: break; ! 522: } ! 523: } ! 524: return (error_flag); ! 525: } ! 526: ! 527: /* ! 528: * Removes '-'s from the input string. ! 529: */ ! 530: remove_hyphens(str) ! 531: char *str; ! 532: { ! 533: char *hyphen; ! 534: ! 535: while ((hyphen = index(str, '-')) != NULL) ! 536: (void) strcpy(hyphen, hyphen+1); ! 537: } ! 538: ! 539: /* ! 540: * Checks to see if 'str' contains only digits (0-9). If not, then ! 541: * an error message is printed and '1' is returned. ! 542: */ ! 543: not_all_digits(str) ! 544: char *str; ! 545: { ! 546: char *ptr; ! 547: ! 548: for (ptr = str; *ptr != '\0'; ++ptr) ! 549: if (!isdigit(*ptr)) { ! 550: printf("Phone numbers can only contain digits.\n"); ! 551: return (1); ! 552: } ! 553: return (0); ! 554: } ! 555: ! 556: /* ! 557: * Deal with Berkeley buildings. Abbreviating Cory to C and Evans to E. ! 558: * Correction changes "str". ! 559: * ! 560: * Returns 1 if incorrect room format. ! 561: * ! 562: * Note: this function assumes that the newline has been removed from str. ! 563: */ ! 564: illegal_building(str) ! 565: register char *str; ! 566: { ! 567: int length = strlen(str); ! 568: register char *ptr; ! 569: ! 570: /* ! 571: * If the string is [Ee]vans or [Cc]ory or ends in ! 572: * [ \t0-9][Ee]vans or [ \t0-9M][Cc]ory, then contract the name ! 573: * into 'E' or 'C', as the case may be, and delete leading blanks. ! 574: */ ! 575: if (length >= 5 && strcmp(ptr = str + length - 4, "vans") == 0 && ! 576: (*--ptr == 'e' || *ptr == 'E') && ! 577: (--ptr < str || isspace(*ptr) || isdigit(*ptr))) { ! 578: for (; ptr > str && isspace(*ptr); ptr--) ! 579: ; ! 580: ptr++; ! 581: *ptr++ = 'E'; ! 582: *ptr = '\0'; ! 583: } else ! 584: if (length >= 4 && strcmp(ptr = str + length - 3, "ory") == 0 && ! 585: (*--ptr == 'c' || *ptr == 'C') && ! 586: (--ptr < str || *ptr == 'M' || isspace(*ptr) || isdigit(*ptr))) { ! 587: for (; ptr > str && isspace(*ptr); ptr--) ! 588: ; ! 589: ptr++; ! 590: *ptr++ = 'C'; ! 591: *ptr = '\0'; ! 592: } ! 593: return (0); ! 594: } ! 595: ! 596: /* ! 597: * get_defaults picks apart "str" and returns a structure points. ! 598: * "str" contains up to 4 fields separated by commas. ! 599: * Any field that is missing is set to blank. ! 600: */ ! 601: struct default_values * ! 602: get_defaults(str) ! 603: char *str; ! 604: { ! 605: struct default_values *answer; ! 606: ! 607: answer = (struct default_values *) ! 608: malloc((unsigned)sizeof(struct default_values)); ! 609: if (answer == (struct default_values *) NULL) { ! 610: fprintf(stderr, ! 611: "\nUnable to allocate storage in get_defaults!\n"); ! 612: exit(1); ! 613: } ! 614: /* ! 615: * Values if no corresponding string in "str". ! 616: */ ! 617: answer->name = str; ! 618: answer->office_num = ""; ! 619: answer->office_phone = ""; ! 620: answer->home_phone = ""; ! 621: str = index(answer->name, ','); ! 622: if (str == 0) ! 623: return (answer); ! 624: *str = '\0'; ! 625: answer->office_num = str + 1; ! 626: str = index(answer->office_num, ','); ! 627: if (str == 0) ! 628: return (answer); ! 629: *str = '\0'; ! 630: answer->office_phone = str + 1; ! 631: str = index(answer->office_phone, ','); ! 632: if (str == 0) ! 633: return (answer); ! 634: *str = '\0'; ! 635: answer->home_phone = str + 1; ! 636: return (answer); ! 637: } ! 638: ! 639: /* ! 640: * special_case returns true when either the default is accepted ! 641: * (str = '\n'), or when 'none' is typed. 'none' is accepted in ! 642: * either upper or lower case (or any combination). 'str' is modified ! 643: * in these two cases. ! 644: */ ! 645: special_case(str,default_str) ! 646: char *str, *default_str; ! 647: { ! 648: static char word[] = "none\n"; ! 649: char *ptr, *wordptr; ! 650: ! 651: /* ! 652: * If the default is accepted, then change the old string do the ! 653: * default string. ! 654: */ ! 655: if (*str == '\n') { ! 656: (void) strcpy(str, default_str); ! 657: return (1); ! 658: } ! 659: /* ! 660: * Check to see if str is 'none'. (It is questionable if case ! 661: * insensitivity is worth the hair). ! 662: */ ! 663: wordptr = word-1; ! 664: for (ptr = str; *ptr != '\0'; ++ptr) { ! 665: ++wordptr; ! 666: if (*wordptr == '\0') /* then words are different sizes */ ! 667: return (0); ! 668: if (*ptr == *wordptr) ! 669: continue; ! 670: if (isupper(*ptr) && (tolower(*ptr) == *wordptr)) ! 671: continue; ! 672: /* ! 673: * At this point we have a mismatch, so we return ! 674: */ ! 675: return (0); ! 676: } ! 677: /* ! 678: * Make sure that words are the same length. ! 679: */ ! 680: if (*(wordptr+1) != '\0') ! 681: return (0); ! 682: /* ! 683: * Change 'str' to be the null string ! 684: */ ! 685: *str = '\0'; ! 686: return (1); ! 687: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.