home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume11 / getty-enable < prev    next >
Text File  |  1987-08-12  |  36KB  |  1,283 lines

  1. Path: uunet!rs
  2. From: rs@uunet.UU.NET (Rich Salz)
  3. Newsgroups: comp.sources.unix
  4. Subject: v11i008:  Getty on/off programs for 4.[23] BSD
  5. Message-ID: <898@uunet.UU.NET>
  6. Date: 13 Aug 87 22:41:13 GMT
  7. Organization: UUNET Communications Services, Arlington, VA
  8. Lines: 1272
  9. Approved: rs@uunet.UU.NET
  10.  
  11. Submitted-by: tron@sc.nsc.com (Ronald S. Karr)
  12. Posting-number: Volume 11, Issue 8
  13. Archive-name: getty-enable
  14.  
  15. [  Since UUNET is not about to give me write permission to /etc/ttys :-),
  16.    I just compiled this and haven't run it.  -r$  ]
  17.  
  18. The 4.3BSD ttys file format increases versatility, but the simple act
  19. of enabling or disabling getty on a line is no longer easily writable
  20. as a quick shell script.  Thus, I wrote this C program to do this for
  21. me.  I modified it for 4.2BSD as well, just for fun.
  22.  
  23.    tron  |-<=>-|    ARPAnet: nsc!tron@sun.COM
  24.   tron@sc.nsc.com   UUCPnet: {amdahl,decwrl,hplabs,pyramid,sun}!nsc!tron
  25.  
  26. #!/bin/sh
  27. #
  28. # This is a shell archive.
  29. # Send this to /bin/sh to create the following files:
  30. #    README
  31. #    Makefile
  32. #    enable.man
  33. #    enable.c
  34. #    enable42.c
  35. #
  36.  
  37. echo "Extracting from shar archive number 1..."
  38.  
  39. if test ! -f "README"; then
  40.     echo "    file - \"README\""
  41.     sed 's/^X//' > "README" <<\EOF
  42. XIn versions of UN*X before 4.3BSD, it was relatively simple to write
  43. Xa quick shell script to turn gettys on or off on tty lines.  But under
  44. X4.3BSD the more complex /etc/ttys file format makes it more difficult
  45. Xto make a simple shell script for this, or at least one that can handle
  46. Xerrors in a reasonable fashion.  Thus, I wrote this little program
  47. Xto handle the new format.  Then, since I had written the program, anyway,
  48. Xand much of the code is independent of file format, I wrote a second
  49. Xversion to handle 4.2BSD consistently.  The version for 4.2BSD may
  50. Xwork elsewhere if the code for AUTHGROUP and the rename(2) call are
  51. Xreplaced with something appropriate for a different environment.
  52. X
  53. X   tron  |-<=>-|    ARPAnet: nsc!tron@sun.com
  54. X  tron@sc.nsc.com   UUCPnet: {amdahl,decwrl,hplabs,pyramid,sun}!nsc!tron
  55. EOF
  56.     if test $? -ne 0; then
  57.         echo "    WARNING:  status $? returned by sed"
  58.     fi
  59. else
  60.     echo "    WARNING:  \"README\" already exists, will not overwrite"
  61. fi
  62.  
  63. if test ! -f "Makefile"; then
  64.     echo "    file - \"Makefile\""
  65.     sed 's/^X//' > "Makefile" <<\EOF
  66. X#!/bin/make -f
  67. X# ENABLE/DISABLE TTYLINES (makefile)
  68. X#
  69. X# $Header: Makefile,v 1.1 87/03/06 20:44:14 tron Exp $
  70. X# $Log:    Makefile,v $
  71. X# Revision 1.1  87/03/06  20:44:14  tron
  72. X# Remove system V comments.
  73. X# 
  74. X# Revision 1.0  87/03/06  19:36:43  tron
  75. X# Initial revision
  76. X# 
  77. X
  78. X# 4.3BSD systems use enable.c, which handles the new format
  79. X# /etc/ttys file.  4.2 systems use enable42.c which handles
  80. X# the older, less interesting, format.
  81. XSRC=enable.c
  82. X
  83. XDESTDIR=/etc
  84. XMANDIR=/usr/local/man/man8
  85. XMANSUFFIX=8
  86. X
  87. X# users in this group are authorized to use these programs
  88. XAUTHGROUP = "operator"
  89. X
  90. XCFLAGS=-O
  91. X
  92. Xall:    enable
  93. X
  94. Xenable:    ${SRC}
  95. X    cc ${CFLAGS} -o enable ${SRC}
  96. X
  97. Xinstall: enable
  98. X    rm -f ${DESTDIR}/disable
  99. X    install -s -m 4755 -o root enable ${DESTDIR}/enable
  100. X    ln ${DESTDIR}/enable ${DESTDIR}/disable
  101. X
  102. Xinstallman: enable.man
  103. X    install -c enable.man ${MANDIR}/enable.${MANSUFFIX}
  104. X
  105. Xlint:    ${SRC}
  106. X    lint ${SRC}
  107. X
  108. Xclean:;    rm *.o core a.out enable disable
  109. EOF
  110.     if test $? -ne 0; then
  111.         echo "    WARNING:  status $? returned by sed"
  112.     fi
  113. else
  114.     echo "    WARNING:  \"Makefile\" already exists, will not overwrite"
  115. fi
  116.  
  117. if test ! -f "enable.man"; then
  118.     echo "    file - \"enable.man\""
  119.     sed 's/^X//' > "enable.man" <<\EOF
  120. X'\"
  121. X'\" $Header: enable.man,v 1.1 87/03/06 20:16:01 tron Exp $
  122. X'\" $Log:    enable.man,v $
  123. X'\" Revision 1.1  87/03/06  20:16:01  tron
  124. X'\" Added information about the "operator" group permissions in BSD.
  125. X'\" 
  126. X'\" Revision 1.0  87/03/06  19:37:52  tron
  127. X'\" Initial revision
  128. X'\" 
  129. X.TH ENABLE 8 "March 6, 1987"
  130. X.UC 4
  131. X.SH NAME
  132. Xenable, disable \- enable, disable getty on tty lines
  133. X.SH SYNOPSIS
  134. X.B enable
  135. Xtty ...
  136. X.br
  137. X.B disable
  138. Xtty ...
  139. X.br
  140. X.SH DESCRIPTION
  141. X.I Enable
  142. Xsets the getty enable flag in
  143. X.I /etc/ttys
  144. Xfor the specified tty lines.
  145. X.I Disable
  146. Xresets the getty enable flag.
  147. XBoth programs send the SIGHUP signal to init(8) when all changes
  148. Xhave been made so init can respond to the configuration changes.
  149. X.PP
  150. XUnder 4BSD systems,
  151. Xusers in the group "operator" can use
  152. X.I enable
  153. Xand
  154. X.I disable
  155. Xwithout needing to
  156. X.I su(1)
  157. Xto root.
  158. X.SH DIAGNOSTICS
  159. XComplains about anything that seems wrong,
  160. Xand does a reasonably large amount
  161. Xof checking.
  162. X.SH FILES
  163. X.ta 2i
  164. X/etc/ttys    terminal information file
  165. X.br
  166. X/etc/nttys    temp file for making changes
  167. X.br
  168. X/etc/ottys    backup ttys file
  169. X.br
  170. X.SH AUTHOR
  171. XRonald S. Karr
  172. X.br
  173. Xtron@sc.nsc.com
  174. X.SH "SEE ALSO"
  175. Xinit(8), ttys(5), getty(8), su(1)
  176. X.SH BUGS
  177. XOn 4.3BSD systems, there must be on "on" or "off" string in the
  178. Xstatus field for this to work.
  179. X.I Enable
  180. Xand
  181. X.I Disable
  182. Xwill change this field,
  183. Xbut will not create it if it does not exist.
  184. EOF
  185.     if test $? -ne 0; then
  186.         echo "    WARNING:  status $? returned by sed"
  187.     fi
  188. else
  189.     echo "    WARNING:  \"enable.man\" already exists, will not overwrite"
  190. fi
  191.  
  192. if test ! -f "enable.c"; then
  193.     echo "    file - \"enable.c\""
  194.     sed 's/^X//' > "enable.c" <<\EOF
  195. X/*
  196. X * ENABLE/DISABLE -- turn on/off tty lines (4.2BSD version).
  197. X *
  198. X * $Header: enable.c,v 1.3 87/03/06 20:49:13 tron Exp $
  199. X * $Log:    enable.c,v $
  200. X * Revision 1.3  87/03/06  20:49:13  tron
  201. X * Cleaned up panic exitcodes.
  202. X * 
  203. X * Revision 1.2  87/03/06  19:48:57  tron
  204. X * cleanup, readied for release, added code for handling signals.
  205. X * 
  206. X * Revision 1.1  87/03/06  05:13:43  tron
  207. X * found one bug, incorrectly computing basename of program.
  208. X * 
  209. X * Revision 1.0  87/03/06  05:09:29  tron
  210. X * Initial revision
  211. X * 
  212. X * usage:
  213. X *    enable ttyline ...
  214. X *    disable ttyline ...
  215. X *
  216. X * ttyline may be of the form "ttyname" or "/dev/ttyname" in which case
  217. X * "/dev/" is stripped off.
  218. X */
  219. X
  220. X#ifndef lint
  221. Xstatic char rcsid[] = "$Header: enable.c,v 1.3 87/03/06 20:49:13 tron Exp $";
  222. X#endif
  223. X
  224. X#include <stdio.h>
  225. X#include <sysexits.h>
  226. X#include <sys/types.h>
  227. X#include <sys/file.h>
  228. X#include <sys/param.h>
  229. X#include <sys/stat.h>
  230. X#include <signal.h>
  231. X#include <grp.h>
  232. X#include <strings.h>
  233. X#include <ctype.h>
  234. X
  235. Xchar dev_prefix[] = "/dev/";    /* prefix for tty special files */
  236. Xchar ttysfile[] = "/etc/ttys";    /* the file we are interested in */
  237. Xchar tempfile[] = "/etc/nttys";    /* lock file, temp file for changes */
  238. Xchar backup[] = "/etc/ottys";    /* in case of error, retain previous copy */
  239. X
  240. X#ifndef AUTHGROUP
  241. X# define AUTHGROUP    "operator"
  242. X#endif AUTHGROUP
  243. X
  244. Xchar auth_group[] = AUTHGROUP;    /* only root or users in this group are ok */
  245. X
  246. X/*
  247. X * advance to the next char in a string.  If there is
  248. X * not enough space allocated in the string to advance to
  249. X * the next char, make it M_BUMP-chars larger.
  250. X * M_INIT is a reasonable initial malloc region.
  251. X *
  252. X *    s - the string
  253. X *    i - current index into string, range from 0 to a-1.
  254. X *    a - current allocation size
  255. X *
  256. X * usage:
  257. X *    NEXT(string,index,alloc_size) = new_character;
  258. X */
  259. X#define M_BUMP    64
  260. X#define NEXT(s,i,a)  ((i)>=(a)?((a)+=M_BUMP,s=xrealloc((s),(a))):0),(s)[(i)++]
  261. X#define M_INIT    (64-sizeof (int))
  262. X
  263. Xchar *program;            /* name this program was run as */
  264. X
  265. Xchar *xmalloc();        /* malloc(3) with panic on error */
  266. Xchar *xrealloc();        /* realloc(3) with panic on error */
  267. Xchar *getfield();        /* get next field from the ttys file */
  268. Xvoid putfield();        /* write out field to the temp file */
  269. Xvoid panic();            /* write error message and quit */
  270. Xvoid security_check();        /* panic if user is not authorized for this */
  271. Xint interupt();            /* routine to handle signals */
  272. XFILE *temp = NULL;        /* temp file; if open, unlink on error */
  273. X
  274. Xextern int errno;        /* see intro(2) */
  275. Xextern int sys_nerr;        /* number of known errors */
  276. Xextern char *sys_errlist[];    /* description of known errors */
  277. X
  278. Xvoid
  279. Xmain(argc, argv)
  280. X    int argc;            /* number of arguments */
  281. X    char *argv[];        /* vector of arguments */
  282. X{
  283. X    register char **ap;        /* pointer to arguments list */
  284. X    char *argmap;        /* map of hits in tty file */
  285. X    FILE *ttys;            /* ttys file */
  286. X    int fd;            /* used for opening temp file */
  287. X    struct stat statbuf;    /* status on ttys file */
  288. X    register char *field;    /* current field from the ttys file */
  289. X    int fieldno;        /* field number in the ttys file */
  290. X    int match;            /* does the current line match anything? */
  291. X    char *from;            /* status field to change on matching line */
  292. X    char *to;            /* what to change that field to */
  293. X    char *bl;            /* hold blanks from getfield() */
  294. X    int i;            /* index */
  295. X
  296. X    /* first things first, trap signals */
  297. X    (void) signal(SIGHUP, interupt);
  298. X    (void) signal(SIGINT, interupt);
  299. X    (void) signal(SIGTERM, interupt);
  300. X
  301. X    /* get basename of argv[0] for the name of the program */
  302. X    program = rindex(*argv, '/');
  303. X    if (program == NULL) {
  304. X    program = *argv;
  305. X    } else {
  306. X    program++;
  307. X    }
  308. X
  309. X    /* make sure the user is authorized to do this */
  310. X    security_check();
  311. X
  312. X    /* what are we doing, anyway? */
  313. X    if (strcmp(program, "enable") == 0) {
  314. X    /* enable changes from off to on */
  315. X    from = "off";
  316. X    to = "on";
  317. X    } else {
  318. X    /* otherwise assume disable, changes from on to off */
  319. X    from = "on";
  320. X    to = "off";
  321. X    }
  322. X    argv++;            /* we don't care about argv[0] anymore */
  323. X    argc--;
  324. X
  325. X    if (*argv == NULL) {
  326. X    panic(EX_USAGE, "usage: %s ttyline ...\n", program);
  327. X    /*NOTREACHED*/
  328. X    }
  329. X
  330. X    /* allocate ttys hit map and zero it out */
  331. X    argmap = xmalloc((unsigned)argc);
  332. X    (void) bzero(argmap, argc);
  333. X
  334. X    /*
  335. X     * cleanup the argument list
  336. X     */
  337. X    for (ap = argv; *ap; ap++) {
  338. X    /*
  339. X     * if in dev directory, get basename
  340. X     */
  341. X    if ((*ap)[0] == '/') {
  342. X        if (strncmp(*ap, dev_prefix, sizeof(dev_prefix) - 1) != 0) {
  343. X        panic(EX_DATAERR, "%s: illegal argument: %s\n", program, *ap);
  344. X        /*NOTREACHED*/
  345. X        }
  346. X        *ap += sizeof(dev_prefix)-1;
  347. X    }
  348. X    }
  349. X
  350. X    /*
  351. X     * open the ttys file and get its mode
  352. X     */
  353. X    ttys = fopen(ttysfile, "r+");
  354. X    if (ttys == NULL) {
  355. X    panic(EX_OSFILE, "%s: could not open %s: %m\n", program, ttysfile);
  356. X    /*NOTREACHED*/
  357. X    }
  358. X    if (fstat(fileno(ttys), &statbuf) < 0) {
  359. X    panic(EX_OSFILE, "%s: could not stat %s: %m\n", program, ttysfile);
  360. X    /*NOTREACHED*/
  361. X    }
  362. X
  363. X    /*
  364. X     * open temp file exclusively to prevent simultaneous edits
  365. X     */
  366. X    fd = open(tempfile, O_WRONLY|O_CREAT|O_EXCL, statbuf.st_mode);
  367. X    if (fd < 0) {
  368. X    panic(EX_OSFILE, "%s: open failed on %s: %m\n", program, tempfile);
  369. X    /*NOTREACHED*/
  370. X    }
  371. X    temp = fdopen(fd, "w");
  372. X    if (temp == NULL) {
  373. X    panic(EX_OSERR, "%s: fdopen failed on %s: %m\n", program, tempfile);
  374. X    /*NOTREACHED*/
  375. X    }
  376. X
  377. X    /*
  378. X     * loop through, grabbing fields from the ttys file and copying
  379. X     * them to the temp file, altering the status fields as necessary
  380. X     */
  381. X    fieldno = 0;        /* field number within the current line */
  382. X    match = 0;            /* is current line a match? */
  383. X
  384. X    while (field = getfield(&bl, ttys)) {
  385. X    if (field[0] == '\n' || field[0] == '#') {
  386. X        /* end of a line, initialize for the next line */
  387. X        fieldno = 0;
  388. X        match = 0;
  389. X    } else {
  390. X        fieldno++;
  391. X        if (fieldno == 1) {
  392. X        /* does first field match one of the ttys to change */
  393. X        for (ap = argv; *ap; ap++) {
  394. X            if (strcmp(*ap, field) == 0) {
  395. X            match = 1; /* yes */
  396. X            if (argmap[ap-argv] > 0) {
  397. X                (void)fprintf(stderr,
  398. X                      "%s: warning: %s in %s twice\n",
  399. X                      program, *ap, ttysfile);
  400. X            }
  401. X            argmap[ap-argv] = 1;
  402. X            break;
  403. X            }
  404. X        }
  405. X        } else if (fieldno > 3 && match) {
  406. X        /* does status field match "on" or "off"? */
  407. X        if (strcmp(from, field) == 0) {
  408. X            field = to;    /* change if so */
  409. X            argmap[ap-argv] |= 2; /* signify that field found */
  410. X        } else if (strcmp(to, field) == 0) {
  411. X            (void) fprintf(stderr, "%s: %s is already %s\n",
  412. X                   program, *ap, to);
  413. X        }
  414. X        }
  415. X    }
  416. X
  417. X    /* output the field to the temp file */
  418. X    putfield(bl, field, temp);
  419. X    }
  420. X    putfield(bl, "", temp);    /* there could be stray spaces at the end */
  421. X
  422. X    /*
  423. X     * check map for anything that wasn't found or changed properly
  424. X     */
  425. X    for (i = 0; i < argc; i++) {
  426. X    if (argmap[i] == 0) {
  427. X        (void) fprintf(stderr, "%s: line %s not found in %s\n",
  428. X               program, argv[i], ttysfile);
  429. X    } else if ((argmap[i] & 2) == 0) {
  430. X        (void) fprintf(stderr, "%s: line %s not turned %s\n",
  431. X               program, argv[i], to);
  432. X    }
  433. X    }
  434. X
  435. X    /* finish everything up */
  436. X    (void) fclose(ttys);    /* close files */
  437. X    if (fclose(temp) == EOF) {
  438. X        panic(EX_OSERR, "%s: could not close %s: %m\n", program, tempfile);
  439. X        /*NOTREACHED*/
  440. X    }
  441. X    (void) unlink(backup);    /* remove previous backup */
  442. X    if (link(ttysfile, backup) < 0) { /* create a new backup */
  443. X    panic(EX_OSERR, "%s: link from %s to %s failed: %m\n",
  444. X          program, ttysfile, backup);
  445. X    /*NOTREACHED*/
  446. X    }                /* install new ttys file */
  447. X    if (rename(tempfile, ttysfile)) {
  448. X    panic(EX_OSERR, "%s: rename from %s to %s failed: %m\n",
  449. X          program, tempfile, ttysfile);
  450. X    /*NOTREACHED*/
  451. X    }
  452. X    if (kill(1, SIGHUP) < 0) {    /* inform init of changes */
  453. X    panic(EX_OSERR, "%s: failed to notify init: %m\n", program);
  454. X    /*NOTREACHED*/
  455. X    }
  456. X    exit(0);            /* all done, everything went okay (?) */
  457. X    /*NOTREACHED*/
  458. X}
  459. X
  460. X/*
  461. X * the user must either be root or be in the auth_group.  Otherwise
  462. X * panic with a security violation.
  463. X */
  464. Xvoid
  465. Xsecurity_check()
  466. X{
  467. X    register i;            /* index */
  468. X    register n;            /* index limit */
  469. X    int gidset[NGROUPS];    /* set of user's groups */
  470. X    struct group *gr;        /* group file entry */
  471. X    register int gid;        /* group we are searching for in gidset */
  472. X    uid_t getuid();        /* let's make lint happy */
  473. X
  474. X    /* are we root? */
  475. X    if (getuid() == 0) return;    /* yes */
  476. X
  477. X    /* get id of authorized group */
  478. X    gr = getgrnam(auth_group);
  479. X    if (gr == NULL) {
  480. X    panic(EX_SOFTWARE, "%s: no such group %s\n", program, auth_group);
  481. X    /*NOTREACHED*/
  482. X    }
  483. X    gid = gr->gr_gid;
  484. X
  485. X    n = getgroups(NGROUPS, gidset);
  486. X    /* search for authorized group id */
  487. X    for (i = 0; i < n; i++) {
  488. X    if (gidset[i] == gid) return; /* user looks okay to me */
  489. X    }
  490. X    /* not found */
  491. X    panic(EX_NOPERM, "%s: you are not allowed to run this program\n", program);
  492. X    /*NOTREACHED*/
  493. X}
  494. X
  495. X/*
  496. X * return the next field from the input.  A field is defined
  497. X * as a sequence of non-space chars followed by a space char
  498. X * or the comment char '#'.
  499. X *
  500. X * a field beginning with # contains a comment which ends in newline.
  501. X * a field beginning with '\n' signals the end of an uncommented line.
  502. X * a return value of NULL signifies end of file.
  503. X * this routine panics on errors.
  504. X */
  505. Xchar *
  506. Xgetfield(bl, stream)
  507. X    char **bl;            /* return string of blanks here */
  508. X    FILE *stream;        /* file to take the fields from */
  509. X{
  510. X    register int c;        /* current input char */
  511. X    register char *s;        /* malloc region to store into */
  512. X    static char *buf = NULL;    /* retained between calls */
  513. X    register int i = 0;        /* index into s */
  514. X    static unsigned a = M_INIT;    /* allocation region */
  515. X    int inquote = 0;        /* set if we are inside of a quote */
  516. X    int field;            /* index to start of field */
  517. X
  518. X    if (buf == NULL) {
  519. X    s = buf = xmalloc(a);    /* first time, get the initial region */
  520. X    } else {
  521. X    s = buf;        /* get the region */
  522. X    }
  523. X
  524. X    /*
  525. X     * advance to next non space or tab
  526. X     */
  527. X    while ((c = getc(stream)) != EOF && (c == ' ' || c == '\t')) {
  528. X    NEXT(s,i,a) = c;
  529. X    }
  530. X    NEXT(s,i,a) = '\0';        /* terminate string of blanks */
  531. X
  532. X    if (c == EOF) {
  533. X    /* end of file */
  534. X    *bl = buf = s;        /* return blanks */
  535. X    return NULL;
  536. X    }
  537. X
  538. X    field = i;            /* field starts at next char */
  539. X    NEXT(s,i,a) = c;        /* all other cases will want this char */
  540. X
  541. X    /*
  542. X     * first handle trivial cases
  543. X     */
  544. X    if (c == '\n') {
  545. X    /* end of line */
  546. X    NEXT(s,i,a) = '\0';    /* terminate string */
  547. X    *bl = s;
  548. X    buf = s;
  549. X    return s+field;        /* return the newline */
  550. X    }
  551. X    if (c == '#') {
  552. X    /* comment to end of line */
  553. X    do {
  554. X        NEXT(s,i,a) = c = getc(stream);
  555. X    } while (c != '\n');
  556. X    NEXT(s,i,a) = '\0';    /* terminate the string */
  557. X    buf = s;
  558. X    *bl = s;
  559. X    return s+field;        /* return the entire comment, plus newline */
  560. X    }
  561. X    /*
  562. X     * now scan for the end of the field, as signified by a character
  563. X     * which is a space, tab, newline or '#'.  Characters inside double
  564. X     * quotes don't count, and " can be escaped in a quote as \".
  565. X     */
  566. X    if (c == '"') {
  567. X    inquote = 1;        /* first char is a quote */
  568. X    }
  569. X    for (;;) {
  570. X    c = getc(stream);    /* get next character */
  571. X    if (inquote) {
  572. X        /* in a quote */
  573. X        if (c == '"') {
  574. X        inquote = 0;    /* end of quote */
  575. X        } else if (c == '\\') {
  576. X        /* escape next char in quote */
  577. X        NEXT(s,i,a) = c;
  578. X        if ((c = getc(stream)) == EOF || c == '\n') {
  579. X            break;    /* though watch out for EOF and newline */
  580. X        }
  581. X        } else if (c == EOF || c == '\n') {
  582. X        break;        /* EOF or end of line in string */
  583. X        }
  584. X    } else {
  585. X        /* not in a quote: EOF, space or comment ends field */
  586. X        /* trap error on read */
  587. X        if ((c == EOF && !ferror(stream)) || isspace(c) || c == '#') {
  588. X        NEXT(s,i,a) = '\0';
  589. X        (void) ungetc(c, stream); /* put character back */
  590. X        buf = s;
  591. X        *bl = s;
  592. X        return s+field;
  593. X        }
  594. X        if (c == EOF) {
  595. X        break;        /* must have been a read error */
  596. X        }
  597. X        if (c == '"') {
  598. X        inquote = 1;
  599. X        }
  600. X    }
  601. X    NEXT(s,i,a) = c;    /* put the char in the field */
  602. X    }
  603. X
  604. X    /*
  605. X     * if we reach this point, then a quote was not terminated, or there was
  606. X     * a read error.
  607. X     */
  608. X    if (ferror(stream)) {
  609. X    panic(EX_IOERR, "%s: read error in %s: %m\n", program, ttysfile);
  610. X    /*NOTREACHED*/
  611. X    }
  612. X    panic(EX_DATAERR, "%s: unterminated quote in %s\n", program, ttysfile);
  613. X    /*NOTREACHED*/
  614. X}
  615. X
  616. X/*
  617. X * write out the blanks, followed by the field, to the stream
  618. X */
  619. Xvoid
  620. Xputfield(bl, field, stream)
  621. X    char *bl;
  622. X    char *field;
  623. X    register FILE *stream;
  624. X{
  625. X    (void) fputs(bl, stream);    /* blanks go first */
  626. X    (void) fputs(field, stream);
  627. X    if (ferror(stream)) {
  628. X    panic(EX_IOERR, "%s: error writing to %s: %m\n", program, tempfile);
  629. X    /*NOTREACHED*/
  630. X    }
  631. X}
  632. X
  633. X/*
  634. X * malloc and realloc with panics on error
  635. X */
  636. Xchar *
  637. Xxmalloc(size)
  638. X    unsigned size;
  639. X{
  640. X    register char *buf;
  641. X    extern char *malloc();
  642. X
  643. X    buf = malloc(size);
  644. X    if (buf == NULL) {
  645. X    panic(EX_OSERR, "%s: could not malloc: %m\n", program);
  646. X    /*NOTREACHED*/
  647. X    }
  648. X    return buf;
  649. X}
  650. X
  651. Xchar *
  652. Xxrealloc(oldbuf, size)
  653. X    char *oldbuf;
  654. X    unsigned size;
  655. X{
  656. X    register char *newbuf;
  657. X    extern char *realloc();
  658. X
  659. X    newbuf = realloc(oldbuf, size);
  660. X    if (newbuf == NULL) {
  661. X    panic(EX_OSERR, "%s: could not realloc: %m\n", program);
  662. X    /*NOTREACHED*/
  663. X    }
  664. X    return newbuf;
  665. X}
  666. X
  667. X/*
  668. X * signals are sent here to generate an error.
  669. X */
  670. Xint
  671. Xinterupt()
  672. X{
  673. X    panic(EX_SOFTWARE, "\nsignal\n");
  674. X    /*NOTREACHED*/
  675. X}
  676. X
  677. X/*
  678. X * process a panic string and exit with the given exit code
  679. X * format is as with printf, only there are no numeric codes
  680. X * within fields, and the %m field is replaced by a string
  681. X * representing the current error.
  682. X */
  683. X/*VARARGS2*/
  684. Xvoid
  685. Xpanic(exitstat, fmt, args)
  686. X    int exitstat;        /* call exit with this value */
  687. X    char *fmt;            /* printf-like format string */
  688. X    char args;            /* anchor for the rest of the args */
  689. X{
  690. X    register int c;        /* current format char */
  691. X    register char *ap = &args;    /* pointer to args */
  692. X    register int i;        /* index */
  693. X    char *itoa();
  694. X
  695. X    /*
  696. X     * process the format string
  697. X     */
  698. X    while (c = *fmt++) if (c != '%') {
  699. X    /* not a percent field, just output the current char */
  700. X    (void) putc(c, stderr);
  701. X    } else {
  702. X    switch(*fmt++) {
  703. X    case 'm':        /* print the current error */
  704. X        if (errno >= sys_nerr || errno < 0) {
  705. X        /* outside of the range of known errors */
  706. X        (void) fputs("Error ", stderr);
  707. X        (void) fputs(itoa((unsigned)errno, 10), stderr);
  708. X        } else {
  709. X        (void) fputs(sys_errlist[errno], stderr);
  710. X        }
  711. X        break;
  712. X    case 's':        /* output a string */
  713. X        (void) fputs(*(char **)ap, stderr);
  714. X        ap += sizeof(char *);
  715. X        break;
  716. X    case 'c':        /* output a single character */
  717. X        (void) putc(*(int *)ap, stderr);
  718. X        ap += sizeof(int);
  719. X        break;
  720. X    case 'd':        /* output a signed decimal integer */
  721. X        i = *(int *)ap;
  722. X        if (i < 0) {
  723. X        (void) putc('-', stderr);
  724. X        i = -i;
  725. X        }
  726. X        (void) fputs(itoa((unsigned)i, 10), stderr);
  727. X        ap += sizeof(int);
  728. X        break;
  729. X    case 'u':        /* output an unsigned decimal interger */
  730. X        (void) fputs(itoa(*(unsigned *)ap, 10), stderr);
  731. X        ap += sizeof(unsigned);
  732. X        break;
  733. X    case 'o':        /* an unsigned octal integer */
  734. X        (void) fputs(itoa(*(unsigned *)ap, 8), stderr);
  735. X        ap += sizeof(unsigned);
  736. X        break;
  737. X    case 'x':        /* an unsigned hexadecimal integer */
  738. X        (void) fputs(itoa(*(unsigned *)ap, 16), stderr);
  739. X        ap += sizeof(char *);
  740. X        break;
  741. X    case '%':        /* a percent sign */
  742. X        (void) putc('%', stderr);
  743. X    }
  744. X    }
  745. X    /*
  746. X     * if temp file open, remove it.
  747. X     */
  748. X    if (temp) {
  749. X    (void) fclose(temp);
  750. X    (void) unlink(tempfile);
  751. X    }
  752. X    exit(exitstat);
  753. X    /*NOTREACHED*/
  754. X}
  755. X
  756. X/*
  757. X * convert an integer to a string representation in the given base
  758. X */
  759. Xchar *
  760. Xitoa(n, base)
  761. X    register unsigned n;    /* number to print */
  762. X    register int base;        /* base to print it in */
  763. X{
  764. X    static char buf[64];    /* big enough to store any reasonable number */
  765. X    register char *p;        /* pointer to buf */
  766. X
  767. X    p = buf+64;            /* point to end, and work back */
  768. X    *--p = '\0';        /* null terminate the string */
  769. X
  770. X    /*
  771. X     * fill in buf until we are done
  772. X     */
  773. X    while (n) {
  774. X    *--p = "0123456789abcdefghijklmnopqrstuvwxyz"[n%base];
  775. X    n /= base;
  776. X    }
  777. X    if (*p == '\0') {
  778. X    *--p = '0';        /* handle case of the number 0 */
  779. X    }
  780. X    return p;            /* return the value; */
  781. X}
  782. EOF
  783.     if test $? -ne 0; then
  784.         echo "    WARNING:  status $? returned by sed"
  785.     fi
  786. else
  787.     echo "    WARNING:  \"enable.c\" already exists, will not overwrite"
  788. fi
  789.  
  790. if test ! -f "enable42.c"; then
  791.     echo "    file - \"enable42.c\""
  792.     sed 's/^X//' > "enable42.c" <<\EOF
  793. X/*
  794. X * ENABLE/DISABLE -- turn on/off tty lines (non-4.3BSD version)
  795. X *
  796. X * $Header: enable42.c,v 1.3 87/03/06 21:14:23 tron Exp $
  797. X * $Log:    enable42.c,v $
  798. X * Revision 1.3  87/03/06  21:14:23  tron
  799. X * no uid_t type in 4.2BSD.
  800. X * 
  801. X * Revision 1.2  87/03/06  20:47:51  tron
  802. X * Cleaned up panic exitcodes.
  803. X * 
  804. X * Revision 1.1  87/03/06  19:47:30  tron
  805. X * cleanup, readied for release, installed code to handle signals.
  806. X * 
  807. X * Revision 1.0  87/03/06  18:24:48  tron
  808. X * 4.3BSD enable modified for other UNIX systems
  809. X * 
  810. X * Revision 1.1  87/03/06  05:13:43  tron
  811. X * found one bug, incorrectly computing basename of program.
  812. X * 
  813. X * Revision 1.0  87/03/06  05:09:29  tron
  814. X * Initial revision
  815. X * 
  816. X * usage:
  817. X *    enable ttyline ...
  818. X *    disable ttyline ...
  819. X *
  820. X * ttyline may be of the form "ttyname" or "/dev/ttyname" in which case
  821. X * "/dev/" is stripped off.
  822. X */
  823. X
  824. X#ifndef lint
  825. Xstatic char rcsid[] = "$Header: enable42.c,v 1.3 87/03/06 21:14:23 tron Exp $";
  826. X#endif
  827. X
  828. X#include <stdio.h>
  829. X#include <sysexits.h>
  830. X#include <sys/types.h>
  831. X#include <sys/file.h>
  832. X#include <sys/param.h>
  833. X#include <sys/stat.h>
  834. X#include <signal.h>
  835. X#include <grp.h>
  836. X#include <strings.h>
  837. X#include <ctype.h>
  838. X
  839. Xchar dev_prefix[] = "/dev/";    /* prefix for tty special files */
  840. Xchar ttysfile[] = "/etc/ttys";    /* the file we are interested in */
  841. Xchar tempfile[] = "/etc/nttys";    /* lock file, temp file for changes */
  842. Xchar backup[] = "/etc/ottys";    /* in case of error, retain previous copy */
  843. X
  844. X#ifndef AUTHGROUP
  845. X# define AUTHGROUP    "operator"
  846. X#endif AUTHGROUP
  847. X
  848. Xchar auth_group[] = AUTHGROUP;    /* only root or users in this group are ok */
  849. X
  850. X#define MAXLINE    64        /* maximum size for a ttys file line */
  851. X
  852. Xchar *program;            /* name this program was run as */
  853. X
  854. Xchar *xmalloc();        /* malloc(3) with panic on error */
  855. Xchar *getline();        /* get next line from the ttys file */
  856. Xvoid putline();            /* write out line to the temp file */
  857. Xvoid panic();            /* write error message and quit */
  858. Xvoid security_check();        /* panic if user is not authorized for this */
  859. Xint interupt();            /* routine to handle signals */
  860. XFILE *temp = NULL;        /* temp file; if open, unlink on error */
  861. X
  862. Xextern int errno;        /* see intro(2) */
  863. Xextern int sys_nerr;        /* number of known errors */
  864. Xextern char *sys_errlist[];    /* description of known errors */
  865. X
  866. Xvoid
  867. Xmain(argc, argv)
  868. X    int argc;            /* number of arguments */
  869. X    char *argv[];        /* vector of arguments */
  870. X{
  871. X    register char **ap;        /* pointer to arguments list */
  872. X    char *argmap;        /* map of hits in tty file */
  873. X    FILE *ttys;            /* ttys file */
  874. X    int fd;            /* used for opening temp file */
  875. X    struct stat statbuf;    /* status on ttys file */
  876. X    register char *line;    /* current line from the ttys file */
  877. X    char to;            /* how to set the status line */
  878. X    int i;            /* index */
  879. X
  880. X    /* first things first, trap signals */
  881. X    (void) signal(SIGHUP, interupt);
  882. X    (void) signal(SIGINT, interupt);
  883. X    (void) signal(SIGTERM, interupt);
  884. X
  885. X    /* get basename of argv[0] for the name of the program */
  886. X    program = rindex(*argv, '/');
  887. X    if (program == NULL) {
  888. X    program = *argv;
  889. X    } else {
  890. X    program++;
  891. X    }
  892. X
  893. X    /* make sure the user is authorized to do this */
  894. X    security_check();
  895. X
  896. X    /* what are we doing, anyway? */
  897. X    if (strcmp(program, "enable") == 0) {
  898. X    /* enable changes from off to on */
  899. X    to = '1';
  900. X    } else {
  901. X    /* otherwise assume disable, changes from on to off */
  902. X    to = '0';
  903. X    }
  904. X    argv++;            /* we don't care about argv[0] anymore */
  905. X    argc--;
  906. X
  907. X    if (*argv == NULL) {
  908. X    panic(EX_USAGE, "usage: %s ttyline ...\n", program);
  909. X    /*NOTREACHED*/
  910. X    }
  911. X
  912. X    /* allocate ttys hit map and zero it out */
  913. X    argmap = xmalloc((unsigned)argc);
  914. X    (void) bzero(argmap, argc);
  915. X
  916. X    /*
  917. X     * cleanup the argument list
  918. X     */
  919. X    for (ap = argv; *ap; ap++) {
  920. X    /*
  921. X     * if in dev directory, get basename
  922. X     */
  923. X    if ((*ap)[0] == '/') {
  924. X        if (strncmp(*ap, dev_prefix, sizeof(dev_prefix) - 1) != 0) {
  925. X        panic(EX_DATAERR, "%s: illegal argument: %s\n", program, *ap);
  926. X        /*NOTREACHED*/
  927. X        }
  928. X        *ap += sizeof(dev_prefix)-1;
  929. X    }
  930. X    }
  931. X
  932. X    /*
  933. X     * open the ttys file and get its mode
  934. X     */
  935. X    ttys = fopen(ttysfile, "r+");
  936. X    if (ttys == NULL) {
  937. X    panic(EX_OSFILE, "%s: could not open %s: %m\n", program, ttysfile);
  938. X    /*NOTREACHED*/
  939. X    }
  940. X    if (fstat(fileno(ttys), &statbuf) < 0) {
  941. X    panic(EX_OSFILE, "%s: could not stat %s: %m\n", program, ttysfile);
  942. X    /*NOTREACHED*/
  943. X    }
  944. X
  945. X    /*
  946. X     * open temp file exclusively to prevent simultaneous edits
  947. X     */
  948. X    fd = open(tempfile, O_WRONLY|O_CREAT|O_EXCL, statbuf.st_mode);
  949. X    if (fd < 0) {
  950. X    panic(EX_OSFILE, "%s: open failed on %s: %m\n", program, tempfile);
  951. X    /*NOTREACHED*/
  952. X    }
  953. X    temp = fdopen(fd, "w");
  954. X    if (temp == NULL) {
  955. X    panic(EX_OSERR, "%s: fdopen failed on %s: %m\n", program, tempfile);
  956. X    /*NOTREACHED*/
  957. X    }
  958. X
  959. X    /*
  960. X     * loop through, grabbing fields from the ttys file and copying
  961. X     * them to the temp file, altering the status fields as necessary
  962. X     */
  963. X    while (line = getline(ttys)) {
  964. X    /* is this one of the lines we are interested in? */
  965. X    for (ap = argv; *ap; ap++) {
  966. X        if (strcmp(*ap, line+2) == 0) {
  967. X        /* yes */
  968. X        if (argmap[ap-argv] > 0) {
  969. X            (void)fprintf(stderr,
  970. X                  "%s: warning: %s in %s twice\n",
  971. X                  program, *ap, ttysfile);
  972. X        }
  973. X        argmap[ap-argv] = 1;
  974. X        if (line[0] == to) {
  975. X            (void) fprintf(stderr, "%s: %s is already %s\n",
  976. X                   program, *ap, to=='1'? "on": "off");
  977. X        }
  978. X        line[0] = to;
  979. X        break;
  980. X        }
  981. X    }
  982. X
  983. X    /* output the field to the temp file */
  984. X    putline(line, temp);
  985. X    }
  986. X
  987. X    /*
  988. X     * check map for anything that wasn't found
  989. X     */
  990. X    for (i = 0; i < argc; i++) {
  991. X    if (argmap[i] == 0) {
  992. X        (void) fprintf(stderr, "%s: line %s not found in %s\n",
  993. X               program, argv[i], ttysfile);
  994. X    }
  995. X    }
  996. X
  997. X    /* finish everything up */
  998. X    (void) fclose(ttys);    /* close files */
  999. X    if (fclose(temp) == EOF) {
  1000. X    panic(EX_OSERR, "%s: could not close %s: %m\n", program, temp);
  1001. X    /*NOTREACHED*/
  1002. X    }
  1003. X    (void) unlink(backup);    /* remove previous backup */
  1004. X    if (link(ttysfile, backup) < 0) { /* create a new backup */
  1005. X    panic(EX_OSERR, "%s: link from %s to %s failed: %m\n",
  1006. X          program, ttysfile, backup);
  1007. X    /*NOTREACHED*/
  1008. X    }                /* install new ttys file */
  1009. X    if (rename(tempfile, ttysfile)) {
  1010. X    panic(EX_OSERR, "%s: rename from %s to %s failed: %m\n",
  1011. X          program, tempfile, ttysfile);
  1012. X    /*NOTREACHED*/
  1013. X    }
  1014. X    if (kill(1, SIGHUP) < 0) {    /* inform init of changes */
  1015. X    panic(EX_OSERR, "%s: failed to notify init: %m\n", program);
  1016. X    /*NOTREACHED*/
  1017. X    }
  1018. X    exit(0);            /* all done, everything went okay (?) */
  1019. X    /*NOTREACHED*/
  1020. X}
  1021. X
  1022. X/*
  1023. X * the user must either be root or be in the auth_group.  Otherwise
  1024. X * panic with a security violation.
  1025. X */
  1026. Xvoid
  1027. Xsecurity_check()
  1028. X{
  1029. X    register i;            /* index */
  1030. X    register n;            /* index limit */
  1031. X    int gidset[NGROUPS];    /* set of user's groups */
  1032. X    struct group *gr;        /* group file entry */
  1033. X    register int gid;        /* group we are searching for in gidset */
  1034. X
  1035. X    /* are we root? */
  1036. X    if (getuid() == 0) return;    /* yes */
  1037. X
  1038. X    /* get id of authorized group */
  1039. X    gr = getgrnam(auth_group);
  1040. X    if (gr == NULL) {
  1041. X    panic(EX_SOFTWARE, "%s: no such group %s\n", program, auth_group);
  1042. X    /*NOTREACHED*/
  1043. X    }
  1044. X    gid = gr->gr_gid;
  1045. X
  1046. X    n = getgroups(NGROUPS, gidset);
  1047. X    /* search for authorized group id */
  1048. X    for (i = 0; i < n; i++) {
  1049. X    if (gidset[i] == gid) return; /* user looks okay to me */
  1050. X    }
  1051. X    /* not found */
  1052. X    panic(EX_NOPERM, "%s: you are not allowed to run this program\n", program);
  1053. X    /*NOTREACHED*/
  1054. X}
  1055. X
  1056. X/*
  1057. X * return the next line from the ttys file.
  1058. X * panic if there is anything obviously wrong with it.
  1059. X * return NULL on end of file.
  1060. X */
  1061. Xchar *
  1062. Xgetline(stream)
  1063. X    FILE *stream;        /* file to take the fields from */
  1064. X{
  1065. X    static char buf[MAXLINE+1];    /* put the input line here */
  1066. X    register char *s;        /* return value from fgets */
  1067. X    register int l;
  1068. X
  1069. X    s = fgets(buf, MAXLINE, stream);
  1070. X    if (s == NULL && ferror(stream)) {
  1071. X    panic(EX_IOERR, "%s: read error in %s: %m\n", program, ttysfile);
  1072. X    /*NOTREACHED*/
  1073. X    }
  1074. X    if (s) {
  1075. X    l = strlen(s);
  1076. X    if (s[l-1] != '\n' || (l < 4 && (s[l-1] = '\0', 1))) {
  1077. X        panic(EX_DATAERR, "%s: illegal line in %s: %s\n",
  1078. X          program, ttysfile, s);
  1079. X        /*NOTREACHED*/
  1080. X    }
  1081. X    }
  1082. X    s[l-1] = '\0';        /* strip the newline */
  1083. X    return s;
  1084. X}
  1085. X
  1086. X/*
  1087. X * write out the given string to the file.  panic on error.
  1088. X */
  1089. Xvoid
  1090. Xputline(line, stream)
  1091. X    char *line;
  1092. X    FILE *stream;
  1093. X{
  1094. X    (void) fputs(line, stream);
  1095. X    (void) putc('\n', stream);
  1096. X    if (ferror(stream)) {
  1097. X    panic(EX_IOERR, "%s: error writing to %s: %m\n", program, tempfile);
  1098. X    /*NOTREACHED*/
  1099. X    }
  1100. X}
  1101. X
  1102. X/*
  1103. X * malloc with panic on error
  1104. X */
  1105. Xchar *
  1106. Xxmalloc(size)
  1107. X    unsigned size;
  1108. X{
  1109. X    register char *buf;
  1110. X    extern char *malloc();
  1111. X
  1112. X    buf = malloc(size);
  1113. X    if (buf == NULL) {
  1114. X    panic(EX_OSERR, "%s: could not malloc: %m\n", program);
  1115. X    /*NOTREACHED*/
  1116. X    }
  1117. X    return buf;
  1118. X}
  1119. X
  1120. X/*
  1121. X * signals are sent here to generate an error.
  1122. X */
  1123. Xint
  1124. Xinterupt()
  1125. X{
  1126. X    panic(EX_SOFTWARE, "\nsignal\n");
  1127. X    /*NOTREACHED*/
  1128. X}
  1129. X
  1130. X/*
  1131. X * process a panic string and exit with the given exit code
  1132. X * format is as with printf, only there are no numeric codes
  1133. X * within fields, and the %m field is replaced by a string
  1134. X * representing the current error.
  1135. X */
  1136. X/*VARARGS2*/
  1137. Xvoid
  1138. Xpanic(exitstat, fmt, args)
  1139. X    int exitstat;        /* call exit with this value */
  1140. X    char *fmt;            /* printf-like format string */
  1141. X    char args;            /* anchor for the rest of the args */
  1142. X{
  1143. X    register int c;        /* current format char */
  1144. X    register char *ap = &args;    /* pointer to args */
  1145. X    register int i;        /* index */
  1146. X    char *itoa();
  1147. X
  1148. X    /*
  1149. X     * process the format string
  1150. X     */
  1151. X    while (c = *fmt++) if (c != '%') {
  1152. X    /* not a percent field, just output the current char */
  1153. X    (void) putc(c, stderr);
  1154. X    } else {
  1155. X    switch(*fmt++) {
  1156. X    case 'm':        /* print the current error */
  1157. X        if (errno >= sys_nerr || errno < 0) {
  1158. X        /* outside of the range of known errors */
  1159. X        (void) fputs("Error ", stderr);
  1160. X        (void) fputs(itoa((unsigned)errno, 10), stderr);
  1161. X        } else {
  1162. X        (void) fputs(sys_errlist[errno], stderr);
  1163. X        }
  1164. X        break;
  1165. X    case 's':        /* output a string */
  1166. X        (void) fputs(*(char **)ap, stderr);
  1167. X        ap += sizeof(char *);
  1168. X        break;
  1169. X    case 'c':        /* output a single character */
  1170. X        (void) putc(*(int *)ap, stderr);
  1171. X        ap += sizeof(int);
  1172. X        break;
  1173. X    case 'd':        /* output a signed decimal integer */
  1174. X        i = *(int *)ap;
  1175. X        if (i < 0) {
  1176. X        (void) putc('-', stderr);
  1177. X        i = -i;
  1178. X        }
  1179. X        (void) fputs(itoa((unsigned)i, 10), stderr);
  1180. X        ap += sizeof(int);
  1181. X        break;
  1182. X    case 'u':        /* output an unsigned decimal interger */
  1183. X        (void) fputs(itoa(*(unsigned *)ap, 10), stderr);
  1184. X        ap += sizeof(unsigned);
  1185. X        break;
  1186. X    case 'o':        /* an unsigned octal integer */
  1187. X        (void) fputs(itoa(*(unsigned *)ap, 8), stderr);
  1188. X        ap += sizeof(unsigned);
  1189. X        break;
  1190. X    case 'x':        /* an unsigned hexadecimal integer */
  1191. X        (void) fputs(itoa(*(unsigned *)ap, 16), stderr);
  1192. X        ap += sizeof(char *);
  1193. X        break;
  1194. X    case '%':        /* a percent sign */
  1195. X        (void) putc('%', stderr);
  1196. X    }
  1197. X    }
  1198. X    /*
  1199. X     * if temp file open, remove it.
  1200. X     */
  1201. X    if (temp) {
  1202. X    (void) fclose(temp);
  1203. X    (void) unlink(tempfile);
  1204. X    }
  1205. X    exit(exitstat);
  1206. X    /*NOTREACHED*/
  1207. X}
  1208. X
  1209. X/*
  1210. X * convert an integer to a string representation in the given base
  1211. X */
  1212. Xchar *
  1213. Xitoa(n, base)
  1214. X    register unsigned n;    /* number to print */
  1215. X    register int base;        /* base to print it in */
  1216. X{
  1217. X    static char buf[64];    /* big enough to store any reasonable number */
  1218. X    register char *p;        /* pointer to buf */
  1219. X
  1220. X    p = buf+64;            /* point to end, and work back */
  1221. X    *--p = '\0';        /* null terminate the string */
  1222. X
  1223. X    /*
  1224. X     * fill in buf until we are done
  1225. X     */
  1226. X    while (n) {
  1227. X    *--p = "0123456789abcdefghijklmnopqrstuvwxyz"[n%base];
  1228. X    n /= base;
  1229. X    }
  1230. X    if (*p == '\0') {
  1231. X    *--p = '0';        /* handle case of the number 0 */
  1232. X    }
  1233. X    return p;            /* return the value; */
  1234. X}
  1235. EOF
  1236.     if test $? -ne 0; then
  1237.         echo "    WARNING:  status $? returned by sed"
  1238.     fi
  1239. else
  1240.     echo "    WARNING:  \"enable42.c\" already exists, will not overwrite"
  1241. fi
  1242.  
  1243. echo "Inspecting for damage in transit..."
  1244. ERROR=
  1245. EXPECT=`wc -c "README" 2>&1 | awk '{print 0 + $1}'`
  1246. if test $EXPECT -ne 812; then
  1247.     echo "    ERROR:  \"README\":  expected 812 chars, got $EXPECT"
  1248.     ERROR=1
  1249. fi
  1250. EXPECT=`wc -c "Makefile" 2>&1 | awk '{print 0 + $1}'`
  1251. if test $EXPECT -ne 928; then
  1252.     echo "    ERROR:  \"Makefile\":  expected 928 chars, got $EXPECT"
  1253.     ERROR=1
  1254. fi
  1255. EXPECT=`wc -c "enable.man" 2>&1 | awk '{print 0 + $1}'`
  1256. if test $EXPECT -ne 1356; then
  1257.     echo "    ERROR:  \"enable.man\":  expected 1356 chars, got $EXPECT"
  1258.     ERROR=1
  1259. fi
  1260. EXPECT=`wc -c "enable.c" 2>&1 | awk '{print 0 + $1}'`
  1261. if test $EXPECT -ne 16200; then
  1262.     echo "    ERROR:  \"enable.c\":  expected 16200 chars, got $EXPECT"
  1263.     ERROR=1
  1264. fi
  1265. EXPECT=`wc -c "enable42.c" 2>&1 | awk '{print 0 + $1}'`
  1266. if test $EXPECT -ne 11781; then
  1267.     echo "    ERROR:  \"enable42.c\":  expected 11781 chars, got $EXPECT"
  1268.     ERROR=1
  1269. fi
  1270.  
  1271.  
  1272. if test -z "$ERROR"; then echo "    No errors."; fi
  1273.  
  1274. # end of last shar archive out of 1
  1275.  
  1276. exit 0
  1277.  
  1278. -- 
  1279.  
  1280. Rich $alz
  1281. Cronus Project, BBN Labs            rsalz@bbn.com
  1282. Moderator, comp.sources.unix            sources@uunet.uu.net
  1283.