home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume32 / shlm / part01 < prev    next >
Encoding:
Text File  |  1992-09-19  |  47.9 KB  |  1,734 lines

  1. Newsgroups: comp.sources.misc
  2. From: root@candle.UUCP (Bruce Momjian)
  3. Subject:  v32i087:  shlm - shell layer manager, console dump/restore, Part01/01
  4. Message-ID: <1992Sep20.234931.4255@sparky.imd.sterling.com>
  5. X-Md4-Signature: 6a04d7c41c128193529bdc76f253b7e8
  6. Date: Sun, 20 Sep 1992 23:49:31 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: root@candle.UUCP (Bruce Momjian)
  10. Posting-number: Volume 32, Issue 87
  11. Archive-name: shlm/part01
  12. Environment: sxt, SYSV
  13.  
  14. Designed for Unix Systems, this source code produces one executable that
  15. does three things:
  16.  
  17.     shm        - a shell layer manager, like shl(1), but better
  18.     condump    - outputs the contents of the console screen
  19.     conrestore - restores the console screen from a previous condump
  20.  
  21. The shell layer manager requires sxt devices, found on many System V
  22. machines.  Condump/restore requires that the video memory of your
  23. console display adaptor be addressable from the kernel.  All programs
  24. must be installed set-uid root.
  25.  
  26. ---- cut here ----
  27. #! /bin/sh
  28. # This is a shell archive.  Remove anything before this line, then feed it
  29. # into a shell via "sh file" or similar.  To overwrite existing files,
  30. # type "sh file -c".
  31. # Contents:  README MANIFEST Make.con_only Makefile condump.c
  32. #   condump.doc config.h getkey.c halt.c patchlevel.h shm.c shm.doc
  33. # Wrapped by kent@sparky on Sun Sep 20 18:43:31 1992
  34. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  35. echo If this archive is complete, you will see the following message:
  36. echo '          "shar: End of archive 1 (of 1)."'
  37. if test -f 'README' -a "${1}" != "-c" ; then 
  38.   echo shar: Will not clobber existing file \"'README'\"
  39. else
  40.   echo shar: Extracting \"'README'\" \(1978 characters\)
  41.   sed "s/^X//" >'README' <<'END_OF_FILE'
  42. Xshm - shell layer manager, version 1.1                      Sept 13, 1992
  43. Xcondump/restore - dump/restore console screen contents
  44. X
  45. XINTRODUCTION  
  46. XShm allows a user at any terminal to create multiple shell sessions and
  47. Xto switch between them.  It is designed as an improved 'shl'. Read shm.doc
  48. Xfor more information.
  49. X
  50. XCondump/restore allows you to pipe the contents of the console screen
  51. Xinto a file, and print or restore it later.
  52. X
  53. XPLATFORMS
  54. XAny system that has sxt devices, like System V and Xenix, should be able
  55. Xto run this program.  It does not work under some SVr4 because of an sxt
  56. Xbug. If your 'shl' command allows you to create shells and do 'ls'
  57. Xcommands, this program should work for you too.
  58. X
  59. XINSTALLATION  
  60. XEdit the Makefile and config.h, type 'make', then  'make install'.  
  61. X
  62. XIf you desire to have shm without condump or restore, merely edit
  63. Xconfig.h to undefine USE_CONDUMP, and remove the links to condump and
  64. Xconrestore after installation.
  65. X
  66. XIf you desire condump and conrestore without shm, copy Make.con_only to
  67. XMakefile and tdo your 'make' and 'make install'.
  68. X
  69. XDEBUGGING
  70. XI think you can possibly have three types of problems.  Here they are,
  71. Xand here's how to deal with them.
  72. X
  73. X- shm hangs - 
  74. Xcompile with DEBUG defined and look in a file called 'shm_debug.log' in
  75. Xthe current directory to see the last debug line it reached.  Add more
  76. X'DB;' calls to the code and try again until you have found the line it
  77. Xis getting hung on.  Then, if you can't find the problem, e-mail me with
  78. Xthe source code line and I will try to figure it out.  Telling me the
  79. Xline number probably wouldn't help because after you add all those DB's,
  80. Xthe line numbering is going to change.
  81. X
  82. X- you exit shm but processes are still running on your sxt devices -
  83. XLet me know the circumstances of this.
  84. X
  85. X- program works on console but not on dumb terminals -
  86. X- multi-screens broke -
  87. X- compile problems -
  88. XLet me know about these.
  89. X
  90. XBUGS, COMMENTS
  91. Xsend to Bruce Momjian, root%candle.uucp@bts.com 
  92. X
  93. END_OF_FILE
  94.   if test 1978 -ne `wc -c <'README'`; then
  95.     echo shar: \"'README'\" unpacked with wrong size!
  96.   fi
  97.   # end of 'README'
  98. fi
  99. if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  100.   echo shar: Will not clobber existing file \"'MANIFEST'\"
  101. else
  102.   echo shar: Extracting \"'MANIFEST'\" \(770 characters\)
  103.   sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
  104. Xtotal 90
  105. X-rw-r--r--   1 root     other          0 Aug 21 01:36 MANIFEST
  106. X-rw-r--r--   1 root     other        592 Aug 21 01:34 Make.con_only
  107. X-rw-r--r--   1 root     other        628 Aug 21 01:34 Makefile
  108. X-rw-r--r--   1 root     other       1977 Aug 21 01:24 README
  109. X-rw-r--r--   1 root     other       7536 Aug 10 19:25 condump.c
  110. X-rw-r--r--   1 root     other       2414 Aug 17 14:08 condump.doc
  111. X-rw-r--r--   1 root     other       3360 Aug 13 13:54 config.h
  112. X-rw-r--r--   2 root     other        839 Aug 15 00:23 getkey.c
  113. X-rw-r--r--   2 root     other       1253 Aug 10 19:04 halt.c
  114. X-rw-r--r--   1 root     other         23 Aug 13 13:54 patchlevel.h
  115. X-rw-r--r--   1 root     other      15882 Aug 16 01:24 shm.c
  116. X-rw-r--r--   1 root     other       5734 Aug 13 14:00 shm.doc
  117. END_OF_FILE
  118.   if test 770 -ne `wc -c <'MANIFEST'`; then
  119.     echo shar: \"'MANIFEST'\" unpacked with wrong size!
  120.   fi
  121.   # end of 'MANIFEST'
  122. fi
  123. if test -f 'Make.con_only' -a "${1}" != "-c" ; then 
  124.   echo shar: Will not clobber existing file \"'Make.con_only'\"
  125. else
  126.   echo shar: Extracting \"'Make.con_only'\" \(610 characters\)
  127.   sed "s/^X//" >'Make.con_only' <<'END_OF_FILE'
  128. X#
  129. X# Makefile
  130. X#
  131. X#
  132. X
  133. X# add -DDEBUG for debug output to shm_debug.log
  134. XCFLAGS = -O
  135. X
  136. X# librarys
  137. XLIBS = -ltermlib
  138. X
  139. X# install dir
  140. XBINDIR = /usr/lbin
  141. X
  142. Xcondump   : condump.o halt.o
  143. X    $(CC) -o condump $(CFLAGS) condump.o halt.o $(LIBS)
  144. X    rm -f conrestore
  145. X    ln condump conrestore
  146. X
  147. Xcondump.o : condump.c
  148. X    $(CC) -DMAIN -c $(CFLAGS) condump.c
  149. X    
  150. Xhalt.o : halt.c
  151. X    $(CC) -c $(CFLAGS) halt.c
  152. X
  153. Xcondump.o halt.o : config.h
  154. X
  155. Xclean:
  156. X    rm -f *.o log core condump
  157. X
  158. Xinstall:
  159. X    strip condump
  160. X    cp condump $(BINDIR)
  161. X    chown root $(BINDIR)/condump
  162. X    chmod u+s $(BINDIR)/condump
  163. X    rm $(BINDIR)/conrestore
  164. X    ln $(BINDIR)/condump $(BINDIR)/conrestore
  165. X
  166. END_OF_FILE
  167.   if test 610 -ne `wc -c <'Make.con_only'`; then
  168.     echo shar: \"'Make.con_only'\" unpacked with wrong size!
  169.   fi
  170.   # end of 'Make.con_only'
  171. fi
  172. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  173.   echo shar: Will not clobber existing file \"'Makefile'\"
  174. else
  175.   echo shar: Extracting \"'Makefile'\" \(646 characters\)
  176.   sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  177. X#
  178. X# Makefile
  179. X#
  180. X#
  181. X
  182. X# add -DDEBUG for debug output to shm_debug.log
  183. XCFLAGS = -O
  184. X
  185. X# librarys
  186. XLIBS = -ltermlib
  187. X
  188. X# install dir
  189. XBINDIR = /usr/lbin
  190. X
  191. Xshm   : shm.o condump.o halt.o
  192. X    $(CC) -o shm $(CFLAGS) shm.o condump.o halt.o $(LIBS)
  193. X
  194. Xshm.o : shm.c
  195. X    $(CC) -c $(CFLAGS) shm.c
  196. X
  197. Xcondump.o : condump.c
  198. X    $(CC) -c $(CFLAGS) condump.c
  199. X    
  200. Xhalt.o : halt.c
  201. X    $(CC) -c $(CFLAGS) halt.c
  202. X
  203. Xshm.o condump.o halt.o : config.h
  204. X
  205. Xclean:
  206. X    rm -f *.o shm log core
  207. X
  208. Xinstall:
  209. X    strip shm
  210. X    cp shm $(BINDIR)
  211. X    chown root $(BINDIR)/shm
  212. X    chmod u+s $(BINDIR)/shm
  213. X    rm -f $(BINDIR)/condump $(BINDIR)/conrestore
  214. X    ln $(BINDIR)/shm $(BINDIR)/condump
  215. X    ln $(BINDIR)/shm $(BINDIR)/conrestore
  216. X
  217. END_OF_FILE
  218.   if test 646 -ne `wc -c <'Makefile'`; then
  219.     echo shar: \"'Makefile'\" unpacked with wrong size!
  220.   fi
  221.   # end of 'Makefile'
  222. fi
  223. if test -f 'condump.c' -a "${1}" != "-c" ; then 
  224.   echo shar: Will not clobber existing file \"'condump.c'\"
  225. else
  226.   echo shar: Extracting \"'condump.c'\" \(7541 characters\)
  227.   sed "s/^X//" >'condump.c' <<'END_OF_FILE'
  228. X/*
  229. X
  230. X    condump / conrestore
  231. X
  232. X*/
  233. X
  234. X/* tabs = 4 */
  235. X
  236. X/*------------------------------------------------------------------------
  237. X**
  238. X**    includes
  239. X**
  240. X**-----------------------------------------------------------------------*/
  241. X
  242. X#include "config.h"
  243. X#if defined(MAIN) || defined(USE_CONDUMP)
  244. X    
  245. X#include <fcntl.h>
  246. X#include <unistd.h>
  247. X#include <termio.h>
  248. X#include <stdio.h>
  249. X#include <string.h>
  250. X
  251. Xvoid condump(), conrestore();
  252. Xint  put_mark();
  253. X
  254. Xextern char *optarg;
  255. Xextern int optind;
  256. Xstatic int mem_fd = -1, put_mark_fd;
  257. X
  258. X#ifdef MAIN
  259. X
  260. X/*-----------------------------------------------------------------------
  261. X**
  262. X**    main()
  263. X**
  264. X**----------------------------------------------------------------------*/
  265. Xint main(argc, argv)
  266. Xint argc;
  267. Xchar **argv;
  268. X{
  269. X    return con_run(argc, argv);
  270. X}
  271. X
  272. X#endif /* MAIN */
  273. X
  274. X/*-----------------------------------------------------------------------
  275. X**
  276. X**    con_run()
  277. X**
  278. X**----------------------------------------------------------------------*/
  279. Xint con_run(argc, argv)
  280. Xint argc;
  281. Xchar **argv;
  282. X{
  283. X    int     ch, iosize, cursor_fd;
  284. X    char     screen[SCREEN_SIZE + SCREEN_LINES + 2];
  285. X    int     verbose = 0,
  286. X            newline = 0,
  287. X            cursor  = 0;
  288. X    char    *ttyname(),
  289. X            *tty_ptr = NULL;
  290. X    
  291. X    while ((ch = getopt(argc, argv, "cnv")) != -1)
  292. X        switch (ch)
  293. X        {
  294. X            case 'c' : cursor = 1;    break;
  295. X            case 'n' : newline = 1;    break;
  296. X            case 'v' : verbose = 1; break;
  297. X            case '?' :
  298. X                halt("USAGE: %s [ -vnc ]\n-v verbose, -n newlines, -c cursor\n",
  299. X                    argv[0]);
  300. X        }
  301. X     if (optind < argc)
  302. X        tty_ptr = argv[optind];
  303. X
  304. X    open_mem_fd();
  305. X    
  306. X    iosize = SCREEN_LINES * SCREEN_COLS;
  307. X    if (verbose)    iosize *= SCREEN_CHAR_SPACING;
  308. X    if (newline)    iosize += SCREEN_LINES;
  309. X    if (cursor)        iosize += 2;
  310. X
  311. X     if (strcmp(argv[0], "conrestore") != 0 &&
  312. X        strcmp(strrchr(argv[0],'/'), "/conrestore" ) != 0)
  313. X    {
  314. X        if (cursor)
  315. X        {
  316. X            if ( tty_ptr == NULL &&
  317. X                (tty_ptr = ttyname(0)) == NULL)
  318. X                halt("Cannot find tty name.\n");
  319. X            if ( (cursor_fd=open(tty_ptr, O_WRONLY)) == -1)
  320. X                halt("PERROR : Can not open console for writing.\n");
  321. X            if ( (cursor_fd=open(tty_ptr, O_WRONLY)) == -1)
  322. X                halt("PERROR : Can not open console for writing.\n");
  323. X        }
  324. X        condump(screen, cursor, newline, verbose, cursor_fd);
  325. X        if (fwrite(screen, 1, iosize, stdout) != iosize)
  326. X            halt("PERROR : Could not write required number of bytes.\n");
  327. X    }
  328. X    else
  329. X    {
  330. X        if (cursor)
  331. X        {
  332. X            if (tty_ptr == NULL)
  333. X                cursor_fd = 1;
  334. X            else if ( (cursor_fd=open(tty_ptr, O_WRONLY)) == -1)
  335. X                    halt("PERROR : Can not open console for writing.\n");
  336. X        }
  337. X        if (fread(screen, 1, iosize, stdin) != iosize)
  338. X            halt("PERROR : Could not read required number of bytes.\n");
  339. X        conrestore(screen, cursor, newline, verbose, cursor_fd);
  340. X    }
  341. X    return 0;
  342. X}
  343. X
  344. X/*-----------------------------------------------------------------------
  345. X**
  346. X**    condump()
  347. X**
  348. X**----------------------------------------------------------------------*/
  349. Xvoid condump(screen, cursor, newline, verbose, cursor_fd)
  350. Xchar *screen;
  351. Xint cursor, newline, verbose, cursor_fd;
  352. X{
  353. X    static char    *left_ptr = NULL;
  354. X    char    screen2[SCREEN_SIZE];
  355. X    int     i, errret;
  356. X
  357. X    if (lseek(mem_fd, SCREEN_START, SEEK_SET) == -1)
  358. X        halt( "PERROR : Can not seek in memory device.\n");
  359. X
  360. X    if (read(mem_fd, screen2, SCREEN_SIZE) == -1)
  361. X        halt("PERROR : Can not read from memory device.\n");
  362. X
  363. X    if (verbose && !newline)
  364. X    {
  365. X        memcpy(screen, screen2, SCREEN_SIZE);
  366. X        screen += SCREEN_SIZE;
  367. X    }
  368. X    else
  369. X        for (i=0 ; i < SCREEN_SIZE; i++)
  370. X        {
  371. X            if (newline &&
  372. X                    i % (SCREEN_COLS * SCREEN_CHAR_SPACING) == 0 &&
  373. X                    i != 0)
  374. X                *(screen++) = '\n';
  375. X            *(screen++) = screen2[i];
  376. X            if (!verbose)
  377. X                i += SCREEN_CHAR_SPACING - 1;
  378. X        }
  379. X        
  380. X    if (cursor)
  381. X    {
  382. X        if (lseek(mem_fd, SCREEN_START, SEEK_SET) == -1)
  383. X            halt( "PERROR :Can not seek in memory device.\n");
  384. X        put_mark_fd = cursor_fd;
  385. X        put_mark(CURSOR_MARK);
  386. X        if (read(mem_fd, screen2, SCREEN_SIZE) == -1)
  387. X            halt("PERROR : Can not read from memory device.\n");
  388. X        for (i=0; i < SCREEN_SIZE; i += SCREEN_CHAR_SPACING)
  389. X            if (screen2[i] == CURSOR_MARK)
  390. X                break;
  391. X        if (i < SCREEN_SIZE)
  392. X        {
  393. X            *(screen++) = (i/SCREEN_CHAR_SPACING) / SCREEN_COLS;
  394. X            *(screen++) = (i/SCREEN_CHAR_SPACING) % SCREEN_COLS;
  395. X
  396. X#ifdef TERMINFO
  397. X        if (left_ptr == NULL &&
  398. X            (setupterm(CONSOLE_TERM_TYPE, 2, &errret) != 0 ||  /* 0==OK in curses.h */
  399. X             (left_ptr = (char *)tigetstr("cub1")) == NULL ||
  400. X                 left_ptr == (char *) -1 ) )
  401. X#else
  402. X        if (left_ptr == NULL &&
  403. X            (tgetent(left_ptr, CONSOLE_TERM_TYPE) == -1 ||
  404. X               (left_ptr = (char *)tgetstr("le", NULL)) == NULL) )
  405. X#endif
  406. X            halt("Can not get termcap/info for left cursor movement\n");
  407. X
  408. X            tputs(left_ptr, 1, put_mark);
  409. X            put_mark(screen2[i]);
  410. X            tputs(left_ptr, 1, put_mark);
  411. X        }
  412. X        else
  413. X        {
  414. X            fprintf(stderr,"Can not get cursor position.\n");
  415. X            sleep(2);
  416. X        }
  417. X    }
  418. X}
  419. X
  420. X/*-----------------------------------------------------------------------
  421. X**
  422. X**    conrestore()
  423. X**
  424. X**----------------------------------------------------------------------*/
  425. Xvoid conrestore(screen, cursor, newline, verbose, cursor_fd)
  426. Xchar *screen;
  427. Xint cursor, newline, verbose;
  428. Xint cursor_fd;
  429. X{
  430. X    int i, j, errret;    
  431. X    static  char     *curs_ptr  = NULL,
  432. X                    *curs_ptr2 = NULL;
  433. X    char    *tparm();
  434. X    char    screen2[SCREEN_SIZE];
  435. X                    
  436. X    if (verbose && !newline)
  437. X    {
  438. X        memcpy(screen2, screen, SCREEN_SIZE);
  439. X        screen += SCREEN_SIZE;
  440. X    }
  441. X    else
  442. X    {
  443. X        if (lseek(mem_fd, SCREEN_START, SEEK_SET) == -1)
  444. X            halt( "PERROR : Can not seek in memory device.\n");    
  445. X
  446. X        if (read(mem_fd, screen2, SCREEN_SIZE) == -1)
  447. X            halt("PERROR : Can not read from memory device.\n");
  448. X
  449. X        for (i=0 ; i < SCREEN_SIZE; i++)
  450. X        {
  451. X            if (!newline ||
  452. X                    i % (SCREEN_COLS * SCREEN_CHAR_SPACING) != 0 ||  
  453. X                    i == 0)
  454. X            screen2[i] = *(screen++);
  455. X
  456. X            if (!verbose)
  457. X                i += SCREEN_CHAR_SPACING - 1;
  458. X        }
  459. X    }
  460. X    
  461. X    if (lseek(mem_fd, SCREEN_START, SEEK_SET) == -1)
  462. X        halt( "PERROR :Can not seek in memory device.\n");
  463. X    if (write(mem_fd, screen2, SCREEN_SIZE) == -1)
  464. X        halt("PERROR : Can not write to memory device.\n");
  465. X    if (cursor)
  466. X    {
  467. X        i  = *(screen++);
  468. X        j  = *(screen++);
  469. X#ifdef TERMINFO
  470. X        if (curs_ptr == NULL &&                            /*0==OK in curses.h */
  471. X            (setupterm(CONSOLE_TERM_TYPE, 2, &errret) != 0 ||
  472. X             (curs_ptr = (char *)tigetstr("cup")) == NULL ||
  473. X                 curs_ptr == (char *) -1 ) )
  474. X            halt("Can not get termcap/info for address cursor.\n");
  475. X        curs_ptr2 = tparm(curs_ptr, i, j);
  476. X#else
  477. X        if (curs_ptr == NULL &&
  478. X            (tgetent(curs_ptr, CONSOLE_TERM_TYPE) == -1 ||
  479. X               (curs_ptr = (char *)tgetstr("cm", NULL)) == NULL) )
  480. X             halt("Can not get termcap/info for address cursor.\n");
  481. X        curs_ptr2 = tgoto(curs_ptr, i, j);
  482. X#endif
  483. X        put_mark_fd = cursor_fd;
  484. X        tputs(curs_ptr2, 1, put_mark);
  485. X    }
  486. X}
  487. X
  488. X/*-----------------------------------------------------------------------
  489. X**
  490. X**    open_mem_fd()
  491. X**
  492. X**----------------------------------------------------------------------*/
  493. Xint open_mem_fd()
  494. X{
  495. X    if (mem_fd == -1)
  496. X    {
  497. X        if ( access(CONSOLE_DEVICE, R_OK) == -1)
  498. X         halt("PERROR : Can not read from console.  Security check failed.\n");
  499. X        if ( (mem_fd=open(MEMORY_DEVICE, O_RDWR)) == -1)
  500. X            halt("PERROR : Can not open memory device.\n");
  501. X    }
  502. X    return mem_fd;
  503. X}
  504. X
  505. X/*-----------------------------------------------------------------------
  506. X**
  507. X**    close_mem_fd()
  508. X**
  509. X**----------------------------------------------------------------------*/
  510. Xint close_mem_fd()
  511. X{
  512. X
  513. X    close(mem_fd);    
  514. X    return mem_fd;
  515. X}
  516. X
  517. X/*-----------------------------------------------------------------------
  518. X**
  519. X**    put_mark()
  520. X**
  521. X**----------------------------------------------------------------------*/
  522. Xstatic int put_mark(ch)
  523. Xchar ch;
  524. X{
  525. X    if (write(put_mark_fd, &ch, 1) == -1)
  526. X         halt("PERROR : Can not write to terminal.\n");
  527. X     return 0;
  528. X}
  529. X
  530. X#endif /* MAIN || USE_CONDUMP */
  531. END_OF_FILE
  532.   if test 7541 -ne `wc -c <'condump.c'`; then
  533.     echo shar: \"'condump.c'\" unpacked with wrong size!
  534.   fi
  535.   # end of 'condump.c'
  536. fi
  537. if test -f 'condump.doc' -a "${1}" != "-c" ; then 
  538.   echo shar: Will not clobber existing file \"'condump.doc'\"
  539. else
  540.   echo shar: Extracting \"'condump.doc'\" \(2414 characters\)
  541.   sed "s/^X//" >'condump.doc' <<'END_OF_FILE'
  542. XCONDUMP(1)                 Unix Programmer's Manual                 CONDUMP(1)
  543. X
  544. XNAME
  545. X    condump    - dump console screen
  546. X    conrestore - restore console screen
  547. X
  548. XSYNOPSYS
  549. X    condump [ -cnv ] [ console_device ]
  550. X    conrestore [ -cnv ] [ console_device ]
  551. X
  552. XDESCRIPTION
  553. X    condump copies the contents of the console screen to the
  554. X    standard output.  conrestore replaces the contents of the
  555. X    console screen with standard input.
  556. X
  557. X    Normally, only the ASCII characters on the screen are 
  558. X    handled.  The -v flag causes attribute bytes stored in 
  559. X    the video screen memory area to be handled as well. 
  560. X    The -n flag causes newlines to be placed at the end of 
  561. X    each line.  The -c flag causes additional information to
  562. X    be processed representing the current position of the cursor
  563. X    on the screen.  When using the previous output of condump
  564. X    with conrestore, the conrestore flags used should be the same
  565. X    used in comdump.
  566. X
  567. X    If condump(restore) is invoked from a terminal other than the
  568. X    console, the console device must be supplied as standard input
  569. X    (output), or on the command line.
  570. XNOTES
  571. X    To use either program, you must have write permission on the
  572. X    console device.  This prevents non-root users from seeing the
  573. X    the contents of another user's console screen.
  574. X
  575. X    Here's a sample screen-saver.  It displays a user-supplied
  576. X    program's output on to the screen after 15 minutes of console
  577. X    inactivity.  Note the use of the /etc/inittab comment field 
  578. X    by 'who' with 'grep' to find the console device even under shm. 
  579. X    See details in the shm documentation.
  580. X
  581. X        # ssaver - console screen saver
  582. X        umask 077    # so one else can read it
  583. X        TERM=AT386-M    # my console TERM type
  584. X        export TERM
  585. X        PATH=/bin:/usr/bin:/usr/lbin    # path to executables
  586. X        while :
  587. X        do
  588. X            sleep 900         # 15 minutes
  589. X            set `(who -u;who -l) | grep console`
  590. X            DELAY=`echo $6 | cut -d: -f2`
  591. X            if [ "$DELAY" -ge "15" ]
  592. X            then    screendump -vc > /tmp/$$ </dev/$2
  593. X                clear > /dev/$2
  594. X                # put your screen saver display program here
  595. X                nice /usr/games/fireworks </dev/$2 >/dev/$2 &
  596. X                # this just hangs until a single 
  597. X                # character is entered, see getkey.c
  598. X                getkey </dev/$2 >/dev/null
  599. X                kill -1 $!
  600. X                screenload -vc </tmp/$$ >/dev/$2
  601. X                rm /tmp/$$
  602. X            fi
  603. X        done    
  604. X
  605. X
  606. X    and add this line to /etc/inittab with a path to the above script:
  607. X
  608. X        ss:2:respawn:/bin/sh -c 'exec /usr/lbin/ssaver'
  609. X
  610. XAUTHOR
  611. X    Bruce Momjian, root%candle.uucp@bts.com      Aug 12, 1992
  612. X    condump version 1.1
  613. X
  614. END_OF_FILE
  615.   if test 2414 -ne `wc -c <'condump.doc'`; then
  616.     echo shar: \"'condump.doc'\" unpacked with wrong size!
  617.   fi
  618.   # end of 'condump.doc'
  619. fi
  620. if test -f 'config.h' -a "${1}" != "-c" ; then 
  621.   echo shar: Will not clobber existing file \"'config.h'\"
  622. else
  623.   echo shar: Extracting \"'config.h'\" \(3588 characters\)
  624.   sed "s/^X//" >'config.h' <<'END_OF_FILE'
  625. X/*
  626. X**
  627. X**    config.h for shm and condump/restore
  628. X**
  629. X*/
  630. X
  631. X/* tabs = 4 */
  632. X
  633. X/*---------------------------------------------------------------------
  634. X      Comment this out if you don't have curses/terminfo, and want to use
  635. X      termcap */
  636. X
  637. X#define TERMINFO        
  638. X
  639. X/*---------------------------------------------------------------------
  640. X    Uncomment either line if you don't have void. */
  641. X
  642. X/*#define void int        /**/
  643. X/*typedef int void         /**/
  644. X
  645. X/*---------------------------------------------------------------------
  646. X      Comment this out if you don't want .profile executed at each
  647. X    shell startup. If uncommented, your profile should check for $LAYER
  648. X    and set an appropriate prompt. */
  649. X
  650. X#define SHELL_STARTUP    
  651. X                           
  652. X/*----------------------------------------------------------------------
  653. X    Used to change the utmp entry to the proper tty name for use by 'who',
  654. X    etc. */
  655. X
  656. X#define USE_GETUT
  657. X
  658. X/*---------------------------------------------------------------------
  659. X    Define this to be more layer groups than your kernel supports,
  660. X    which should equal (Number_of_sxt_devices / MAXPCHAN) + 1 */
  661. X
  662. X#define    MAX_SXT_GROUPS 10
  663. X
  664. X/*---------------------------------------------------------------------
  665. X    Where are your sxt devices?  If your sxt devices are not in the /dev
  666. X    directory, but only in the /dev/sxt/ directory, you need to add a slash
  667. X    at the end of this string, and you may need add the slash to the scripts
  668. X    contained in the shm.doc file */
  669. X    
  670. X#define SXT_DEVICE "/dev/sxt"    
  671. X
  672. X/*---------------------------------------------------------------------
  673. X    Uncomment this and enter a value if your sxt devices are not continuous.
  674. X    If so, define this to equal the step increment needed to get from the
  675. X    first member of one sxt grouping to the next. */
  676. X
  677. X/*#define SXT_STEP    10 */
  678. X
  679. X/*---------------------------------------------------------------------
  680. X    Comment this if you don't want console screens saved during
  681. X    layer switches */
  682. X
  683. X#define USE_CONDUMP            
  684. X
  685. X/*---------------------------------------------------------------------*/
  686. X/* THESE ARE ALL CONSOLE SCREEN PARAMETERS, USED BY CONDUMP/RESTORE
  687. X   IF YOU ARE NOT USING THESE OR USE_CONDUMP, YOU MAY STOP HERE        */
  688. X
  689. X/*---------------------------------------------------------------------*/
  690. X
  691. X
  692. X#define SCREEN_LINES        25            /* console screen rows */
  693. X#define SCREEN_COLS         80            /* console screen columns */
  694. X
  695. X/*---------------------------------------------------------------------
  696. X     On PC text screens, for each console screen character, there is
  697. X     one ASCII byte followed by one attribute byes, hence a value of 2 */
  698. X
  699. X#define SCREEN_CHAR_SPACING 2    
  700. X
  701. X/*---------------------------------------------------------------------
  702. X     if you're not on a PC, try looking in /usr/include/sys/kd.h for
  703. X     MONO_BASE or COLOR_BASE */
  704. X
  705. X#define SCREEN_START         0xB0000        /* Mono  - MDA,Mono VGA */
  706. X/*#define SCREEN_START         0xB8000        /* Color - CGA,EGA,VGA,SVGA */
  707. X
  708. X/*---------------------------------------------------------------------
  709. X     This should be a visable character that you rarely see on the screen.
  710. X    On my screen, it is a trianglular character */
  711. X
  712. X#define CURSOR_MARK         '\177'    
  713. X
  714. X/*---------------------------------------------------------------------*/
  715. X
  716. X#define CONSOLE_TERM_TYPE    "AT386-M"    
  717. X#define CONSOLE_DEVICE         "/dev/console"
  718. X#define MEMORY_DEVICE        "/dev/mem"        /* real, not virtual memory */
  719. X
  720. X/*---------------------------------------------------------------------*/
  721. X                    /* DO NOT EDIT PAST THIS LINE */
  722. X/*---------------------------------------------------------------------*/
  723. X/* computed */
  724. X#define SCREEN_SIZE (SCREEN_LINES * SCREEN_COLS * SCREEN_CHAR_SPACING)
  725. X
  726. X
  727. END_OF_FILE
  728.   if test 3588 -ne `wc -c <'config.h'`; then
  729.     echo shar: \"'config.h'\" unpacked with wrong size!
  730.   fi
  731.   # end of 'config.h'
  732. fi
  733. if test -f 'getkey.c' -a "${1}" != "-c" ; then 
  734.   echo shar: Will not clobber existing file \"'getkey.c'\"
  735. else
  736.   echo shar: Extracting \"'getkey.c'\" \(839 characters\)
  737.   sed "s/^X//" >'getkey.c' <<'END_OF_FILE'
  738. X/*
  739. X**
  740. X**    getkey.c
  741. X**
  742. X**     read a single character from the keyboard and return the ASCII
  743. X**    value as a return code
  744. X**
  745. X**  to compile, type
  746. X**
  747. X**        cc -O -c getkey.c
  748. X**        cc -O -c halt.c
  749. X**        cc -o getkey getkey.o halt.o  # add any shared libs here
  750. X**
  751. X*/
  752. X
  753. X#include <fcntl.h>
  754. X#include <termio.h>
  755. X
  756. Xint main()
  757. X{
  758. X    int fd, ch;
  759. X    struct termio savedterm, myterm;
  760. X    
  761. X    if (ioctl( 0, TCGETA, &savedterm ) == -1)
  762. X        halt("PERROR getkey:  Can not get terminal driver settings.\n");
  763. X
  764. X    myterm = savedterm;
  765. X    myterm.c_lflag &= ~(ICANON|ECHO|ISIG);
  766. X    myterm.c_cc[VMIN] = 1;
  767. X    myterm.c_cc[VTIME] = 0;
  768. X
  769. X    if (ioctl( 0, TCSETA, &myterm ) == -1)
  770. X        halt("PERROR getkey: Can not set terminal.\n");
  771. X
  772. X    if (read(0, &ch, 1) == -1)
  773. X            ch = -1;
  774. X    else    write(1, &ch, 1);
  775. X
  776. X    if (ioctl( 0, TCSETA, &savedterm ) == -1)
  777. X        halt("PERROR getkey: Can not set terminal.\n");
  778. X    return ch;    
  779. X}
  780. END_OF_FILE
  781.   if test 839 -ne `wc -c <'getkey.c'`; then
  782.     echo shar: \"'getkey.c'\" unpacked with wrong size!
  783.   fi
  784.   # end of 'getkey.c'
  785. fi
  786. if test -f 'halt.c' -a "${1}" != "-c" ; then 
  787.   echo shar: Will not clobber existing file \"'halt.c'\"
  788. else
  789.   echo shar: Extracting \"'halt.c'\" \(1253 characters\)
  790.   sed "s/^X//" >'halt.c' <<'END_OF_FILE'
  791. X/*
  792. X**
  793. X**    halt.c
  794. X**
  795. X**    This is used to print out error messages and exit
  796. X*/
  797. X
  798. X#include <varargs.h>
  799. X#include <signal.h>
  800. X#include <stdio.h>
  801. X#include <errno.h>
  802. X
  803. X
  804. X/*-------------------------------------------------------------------------
  805. X**
  806. X**    halt - print error message, and call clean up routine or exit
  807. X**
  808. X**------------------------------------------------------------------------*/
  809. X
  810. X/*VARARGS*/
  811. Xvoid halt(va_alist)
  812. Xva_dcl
  813. X{
  814. X    va_list arg_ptr;
  815. X    char    *format, *pstr;
  816. X    void (*sig_func)();
  817. X
  818. X    va_start(arg_ptr);
  819. X    format = va_arg(arg_ptr,char *);
  820. X    if (strncmp(format,"PERROR", 6) != 0)
  821. X        vfprintf(stderr,format,arg_ptr);
  822. X    else
  823. X    {
  824. X        for (pstr=format+6; *pstr == ' '; pstr++)
  825. X            ;
  826. X        vfprintf(stderr,pstr,arg_ptr);
  827. X        perror("");
  828. X    }            
  829. X    va_end(arg_ptr);
  830. X    fflush(stderr);
  831. X
  832. X        /* call one clean up function if defined */
  833. X    if ( (sig_func = signal(SIGTERM, SIG_DFL)) != SIG_DFL &&
  834. X          sig_func != SIG_IGN)
  835. X        (*sig_func)(0);
  836. X    else if ( (sig_func = signal(SIGHUP, SIG_DFL)) != SIG_DFL &&
  837. X                  sig_func != SIG_IGN)
  838. X        (*sig_func)(0);
  839. X    else if ( (sig_func = signal(SIGINT, SIG_DFL)) != SIG_DFL &&
  840. X                  sig_func != SIG_IGN)
  841. X        (*sig_func)(0);
  842. X    else if ( (sig_func = signal(SIGQUIT, SIG_DFL)) != SIG_DFL &&
  843. X                  sig_func != SIG_IGN)
  844. X        (*sig_func)(0);
  845. X    exit(1);
  846. X}
  847. END_OF_FILE
  848.   if test 1253 -ne `wc -c <'halt.c'`; then
  849.     echo shar: \"'halt.c'\" unpacked with wrong size!
  850.   fi
  851.   # end of 'halt.c'
  852. fi
  853. if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  854.   echo shar: Will not clobber existing file \"'patchlevel.h'\"
  855. else
  856.   echo shar: Extracting \"'patchlevel.h'\" \(23 characters\)
  857.   sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
  858. X#define PATCHLEVEL 1.1
  859. END_OF_FILE
  860.   if test 23 -ne `wc -c <'patchlevel.h'`; then
  861.     echo shar: \"'patchlevel.h'\" unpacked with wrong size!
  862.   fi
  863.   # end of 'patchlevel.h'
  864. fi
  865. if test -f 'shm.c' -a "${1}" != "-c" ; then 
  866.   echo shar: Will not clobber existing file \"'shm.c'\"
  867. else
  868.   echo shar: Extracting \"'shm.c'\" \(15879 characters\)
  869.   sed "s/^X//" >'shm.c' <<'END_OF_FILE'
  870. X/*
  871. X**
  872. X**    shm.c
  873. X**  version 1.1
  874. X**  Author - root%candle.uucp@bts.com
  875. X**
  876. X**    shell layers with sxt devices
  877. X**
  878. X*/
  879. X
  880. X/* tabs = 4 */
  881. X
  882. X#include "config.h"
  883. X
  884. X#include <fcntl.h>
  885. X#include <ctype.h>
  886. X#include <signal.h>
  887. X#include <errno.h>
  888. X#include <stdio.h>
  889. X#include <string.h>
  890. X#include <varargs.h>
  891. X#include <sys/types.h>
  892. X#include <sys/tty.h>
  893. X#include <sys/sxt.h>
  894. X#include <termio.h>
  895. X#include <malloc.h>
  896. X#ifdef USE_GETUT
  897. X#    include <utmp.h>
  898. X#endif
  899. X
  900. X#ifdef DEBUG
  901. XFILE *db_file;
  902. X#define DB            fprintf(db_file,"shm eached line:  %d\n",__LINE__)
  903. X#else
  904. X#define DB
  905. X#endif
  906. X
  907. X
  908. X#ifndef SXT_STEP
  909. X#define SXT_STEP MAXPCHAN
  910. X#endif
  911. X
  912. X#define USED        1
  913. X#define FREE        0
  914. X
  915. X#define NEXT        1
  916. X#define PREV        (-1)
  917. X
  918. X#define CLRSCR        1    /* define this to be 0 if you don't like your
  919. X                           screen being cleared when changing layers */
  920. X#define NOCLRSCR     0
  921. X
  922. X#define NOT_FOUND    (-1)
  923. X
  924. X#define PRINT_OPTIONS    fprintf(stderr,"\n\
  925. XAll commands start with %c%c, then:\n\
  926. XC,Z                    create\n\
  927. XD                      delete\n\
  928. XS                      show layers\n\
  929. X^Z,N,^N,L,^L,Swt,Ret   next layers\n\
  930. XP,^P,Bs                previous layer\n\
  931. X1-%1d                    activate layer #\n\
  932. XB                      turn off blocking\n\
  933. XR,Sp                   resume\n\
  934. XT                      toggle between layers\n\
  935. XH,?                    help\n\
  936. XQ,^D                   quit\n",\
  937. X(iscntrl(layerterm.c_cc[VSWTCH]) ? '^' : ' '),\
  938. Xlayerterm.c_cc[VSWTCH] + (iscntrl(layerterm.c_cc[VSWTCH]) ? '@' : 0),\
  939. XMAXPCHAN-1);
  940. X
  941. X#define CONTROL(x)    ((x) & 0x1f)
  942. X
  943. Xstruct {
  944. X    int     status;
  945. X    int        pid;
  946. X    char     *screen;
  947. X}    layer[MAXPCHAN];
  948. X
  949. Xstruct termio savedterm, layerterm, cntrlterm;
  950. X
  951. X
  952. Xint sxt_group;            /* active sxt group made up of MAXPCHAN devices */
  953. Xint cur_slot;            /* active sxt slot within group */
  954. Xchar clear_str[20];        /* string to clear screen */
  955. Xint console = 0;
  956. X
  957. Xvoid exit_layers();
  958. Xvoid manage_layer();
  959. Xvoid layer_died();
  960. Xvoid halt();
  961. Xvoid exit();
  962. X
  963. X#ifdef USE_GETUT
  964. Xchar orig_tty[15];        /* original tty in utmp */
  965. Xstruct utmp my_utmp;
  966. X#endif 
  967. X
  968. X            
  969. X/*-------------------------------------------------------------------------- 
  970. X**
  971. X**    main
  972. X**
  973. X**-------------------------------------------------------------------------*/
  974. X
  975. Xmain(argc, argv)
  976. Xint argc;
  977. Xchar **argv;
  978. X{
  979. X    int     errret, args = 1, i;
  980. X    char     *clear_ptr,
  981. X             *tty_ptr,
  982. X            *getenv(),
  983. X            *ttyname();
  984. X    char    sxt_file[40];
  985. X    int     slot;
  986. X#ifdef USE_GETUT
  987. X    struct     utmp *utmp_ptr,
  988. X                 *getutline();    
  989. X#endif
  990. X
  991. X#ifdef DEBUG
  992. Xdb_file = fopen("shm_debug.log","w");
  993. X#endif
  994. X
  995. X#ifdef USE_CONDUMP
  996. X     if (strcmp(argv[0], "condump") == 0 ||
  997. X        strcmp(strrchr(argv[0],'/'), "/condump" ) == 0 ||
  998. X         strcmp(argv[0], "conrestore") == 0 ||
  999. X        strcmp(strrchr(argv[0],'/'), "/conrestore" ) == 0)
  1000. X        return con_run(argc, argv);
  1001. X
  1002. X    if ( (tty_ptr = ttyname(0)) == NULL)
  1003. X        halt("shm:  Cannot find tty name.\n");
  1004. X    if (strcmp(tty_ptr, CONSOLE_DEVICE) == 0)
  1005. X        console = 1;
  1006. X    if (argc >= 2 && strcmp(argv[1], "-c") == 0)
  1007. X    {
  1008. X        console = 1;
  1009. X        args++;
  1010. X    }
  1011. X    if (argc >= 2 && strcmp(argv[1], "+c") == 0)
  1012. X    {
  1013. X        console = 0;
  1014. X        args++;
  1015. X    }
  1016. X    if (console)
  1017. X        open_mem_fd();
  1018. X#endif
  1019. X    DB;
  1020. X
  1021. X                    /* GET UTMP ENTRY */
  1022. X#ifdef USE_GETUT
  1023. X    if ( (tty_ptr = ttyname(0)) == NULL)
  1024. X        halt("shm:  Cannot find tty name.\n");
  1025. X    strcpy(my_utmp.ut_line, strrchr(tty_ptr, '/') + 1);
  1026. X    strcpy(orig_tty, strrchr(tty_ptr, '/') + 1);
  1027. X    if ( (utmp_ptr = getutline(&my_utmp)) == NULL)
  1028. X        halt("shm:  Cannot find utmp entry.\n");
  1029. X    memcpy(&my_utmp, utmp_ptr, sizeof(struct utmp));
  1030. X#endif
  1031. X
  1032. X                /* SET UP TERMINAL CHARACTERISTICS */
  1033. X
  1034. X    if (ioctl( 0, TCGETA, &savedterm ) == -1)
  1035. X        halt("PERROR shm:  Can not get terminal driver settings.\n");
  1036. X
  1037. X    signal(SIGHUP, exit_layers);
  1038. X    signal(SIGINT, SIG_IGN);
  1039. X    signal(SIGQUIT, SIG_IGN);
  1040. X    signal(SIGTERM, exit_layers);
  1041. X    signal(SIGCLD, SIG_DFL);    /* hold dead children until we are ready */
  1042. X    DB;
  1043. X    
  1044. X    cntrlterm = layerterm = savedterm;
  1045. X    if ( layerterm.c_cc[VSWTCH] == 0 )     /* stty shows this as ^` */
  1046. X        layerterm.c_cc[VSWTCH] = CONTROL('z');
  1047. X    layerterm.c_cflag |= LOBLK;    /* start layer with blocking on */
  1048. X
  1049. X    /* this allows control process to sleep on read and return immediately
  1050. X       when one character is entered */
  1051. X    cntrlterm.c_lflag &= ~(ICANON|ECHO|ISIG);
  1052. X    cntrlterm.c_cc[VMIN] = 1;
  1053. X    cntrlterm.c_cc[VTIME] = 0;
  1054. X    cntrlterm.c_cflag &= ~LOBLK;    /* no output blocking */
  1055. X    DB;
  1056. X
  1057. X                    /* GET CLEAR SCREEN STRING */
  1058. X
  1059. X#ifdef TERMINFO
  1060. X    if (setupterm(NULL, 2, &errret) == 0 &&  /* 0==OK in curses.h */
  1061. X       (clear_ptr = (char *)tigetstr("clear")) != NULL &&
  1062. X        clear_ptr != (char *) -1 )
  1063. X#else
  1064. X    if (tgetent(clear_ptr, getenv("TERM")) != -1 &&
  1065. X       (clear_ptr = (char *)tgetstr("cl", clear_str)) != NULL )
  1066. X#endif
  1067. X            strcpy(clear_str,clear_ptr);
  1068. X    else
  1069. X            strcpy(clear_str,"");
  1070. X    DB;
  1071. X
  1072. X                /* SET UP INPUT/OUTPUT IN CONTROL LAYER */
  1073. X    
  1074. X    fclose( stdin );
  1075. X    for (sxt_group = 0; sxt_group < MAX_SXT_GROUPS; sxt_group++)
  1076. X        if (open_sxt_device(0) != NOT_FOUND)
  1077. X            break;
  1078. X    if (sxt_group == MAX_SXT_GROUPS)
  1079. X        halt("No available ttys.\n");
  1080. X
  1081. X      if ( fdopen( 0, "r" ) == NULL )
  1082. X        halt("PERROR child fopen failed\n");
  1083. X
  1084. X    fclose( stdout );
  1085. X    dup( 0 );
  1086. X    if ( fdopen( 1, "w" ) == NULL )
  1087. X        halt("PERROR child fopen stdout\n" );
  1088. X    DB;
  1089. X                        /* SET UP LAYERS */
  1090. X        
  1091. X     if (ioctl(0, SXTIOCLINK, MAXPCHAN) == -1)
  1092. X        halt(
  1093. X    "PERROR shm: Can not allocate layers, perhaps layers already active.\n");
  1094. X
  1095. X                        /* get ownership of sxt group files */
  1096. X    for (slot = 0; slot < MAXPCHAN; slot++)    
  1097. X    {
  1098. X        sprintf(sxt_file, "%s%03d", SXT_DEVICE, slot + sxt_group * SXT_STEP );
  1099. X        if ( chown(sxt_file, getuid(), getgid()) == -1)
  1100. X            halt("PERROR shm:  Can not set ownership of sxt files.\n");
  1101. X    }
  1102. X
  1103. X                        /* ROOT PRIV ENDS HERE */    
  1104. X
  1105. X    /* needed root priv to do SXTIOCLINK, restore user privs */
  1106. X    setuid(getuid());
  1107. X    DB;
  1108. X    
  1109. X    if ( ioctl(0, SXTIOCSWTCH, 0) == -1)
  1110. X        halt("PERROR shm: Can not switch to controlling layer.\n");
  1111. X    
  1112. X    if (ioctl( 0, TCSETA, &cntrlterm ) == -1)
  1113. X        halt("PERROR shm: Can not set terminal.\n");
  1114. X
  1115. X    fclose( stderr );    /* all errors are done, move stderr */
  1116. X    dup( 0 );
  1117. X    if ( fdopen( 2, "w" ) == NULL )
  1118. X        halt("PERROR child fopen stderr\n" );
  1119. X    setvbuf(stderr, NULL, _IONBF, NULL);    /* stderr not buffered */
  1120. X    DB;
  1121. X    
  1122. X    for (i = 1; args < argc; args++, i++)
  1123. X        (void)create_layer(i, argv[args], NOCLRSCR);
  1124. X        
  1125. X    manage_layer();
  1126. X    /*NOTREACHED*/
  1127. X}
  1128. X
  1129. X/*-------------------------------------------------------------------------
  1130. X**
  1131. X**     manage_layer    - loop that waits for control activation and does commands
  1132. X**
  1133. X**-------------------------------------------------------------------------*/
  1134. X
  1135. Xvoid manage_layer()
  1136. X{
  1137. X    int        new_slot, tog_slot, i;
  1138. X    char    option;
  1139. X
  1140. X    
  1141. X    if (layer[1].status == FREE)
  1142. X        cur_slot = create_layer(1, NULL, NOCLRSCR);
  1143. X    else
  1144. X        cur_slot = activate_layer(1, 1, NEXT);
  1145. X
  1146. X    if (cur_slot == NOT_FOUND)
  1147. X        halt("shm:  Can not start any layer.\n");
  1148. X
  1149. X    DB;
  1150. X    while (1)
  1151. X    {
  1152. X        signal(SIGCLD, layer_died);
  1153. X        while (read(0,&option,1) <= 0)
  1154. X            ;
  1155. X        signal(SIGCLD, SIG_IGN);
  1156. X
  1157. X        new_slot = cur_slot;     /* layer_died may have changed cur_slot */
  1158. X
  1159. X        if ( layerterm.c_cc[VERASE] == option )    /* they hit back space */
  1160. X            option = 'p';
  1161. X            
  1162. X        if ( layerterm.c_cc[VSWTCH] == option )    /* they hit switch char */
  1163. X            option = 'n';
  1164. X            
  1165. X        switch (tolower(option))
  1166. X        {
  1167. X            case 'z':
  1168. X            case 'c':
  1169. X                        new_slot = create_layer(cur_slot, NULL, CLRSCR);
  1170. X                        break;
  1171. X            case 'd':
  1172. X                        signal(SIGCLD, SIG_DFL);
  1173. X                        if (layer[cur_slot].status == USED)
  1174. X                            kill(-layer[cur_slot].pid, SIGHUP);
  1175. X                        break;
  1176. X            case CONTROL('z'):
  1177. X            case 'n':
  1178. X            case CONTROL('n'):
  1179. X            case 'l':
  1180. X            case CONTROL('l'):
  1181. X            case CONTROL('j'):
  1182. X            case CONTROL('m'):
  1183. X                        i = (cur_slot < MAXPCHAN-1 ? cur_slot + 1 : 1);
  1184. X                        new_slot = activate_layer(i, cur_slot, NEXT);
  1185. X                        break;
  1186. X            case 'p':
  1187. X            case CONTROL('p'):
  1188. X                        i = (cur_slot > 1 ? cur_slot - 1 : MAXPCHAN-1);
  1189. X                        new_slot = activate_layer(i, cur_slot, PREV);
  1190. X                        break;
  1191. X            case 's':
  1192. X                        for (i = 1; i < MAXPCHAN; i++)
  1193. X                            if (layer[i].status == USED)
  1194. X                                fprintf(stderr,"%d ", i);
  1195. X                        putc('\n', stderr);
  1196. X                        new_slot = activate_layer(cur_slot, cur_slot, NEXT);
  1197. X                        break;
  1198. X            case '?':
  1199. X            case 'h':    PRINT_OPTIONS;
  1200. X                        new_slot = activate_layer(cur_slot, cur_slot, NEXT);
  1201. X                        break;
  1202. X            case 'b':
  1203. X                        if ( ioctl(0, SXTIOCUBLK, cur_slot) == -1)
  1204. X                            fprintf(stderr, "shm: Can not turn off blocking.\n");
  1205. X                        new_slot = activate_layer(cur_slot, cur_slot, NEXT);
  1206. X                        break;
  1207. X            case 'r':
  1208. X            case ' ':
  1209. X                        new_slot = activate_layer(cur_slot, cur_slot, NEXT);
  1210. X                        break;
  1211. X            case 't':    
  1212. X                        new_slot = activate_layer(tog_slot, cur_slot, NEXT);
  1213. X                        break;
  1214. X            case CONTROL('d'):
  1215. X            case 'q':
  1216. X                        exit_layers(0);
  1217. X                        break;
  1218. X            default :
  1219. X                        i = option - '0';
  1220. X                        if (i >= 1 && i < MAXPCHAN)
  1221. X                        {
  1222. X                            if (layer[i].status == USED)
  1223. X                                new_slot = activate_layer(i, cur_slot, NEXT);
  1224. X                            else
  1225. X                                new_slot = create_layer(i, NULL, CLRSCR);
  1226. X                        }
  1227. X                        else
  1228. X                        {
  1229. X                            if (isprint(option))
  1230. X                                fprintf(stderr,
  1231. X                                    "shm: '%c' unknown command\n",option);
  1232. X                            else
  1233. X                                fprintf(stderr, "shm: unknown command\n");
  1234. X                            new_slot = activate_layer(cur_slot, cur_slot, NEXT);
  1235. X                        }
  1236. X                        break;
  1237. X        }
  1238. X        if (new_slot != NOT_FOUND && new_slot != cur_slot)
  1239. X        {
  1240. X            tog_slot = cur_slot;
  1241. X            cur_slot = new_slot;
  1242. X        }
  1243. X    DB;
  1244. X    }
  1245. X}
  1246. X        
  1247. X/*---------------------------------------------------------------------------
  1248. X**
  1249. X**    activate_layer - find next used slot, test for validity
  1250. X**
  1251. X**--------------------------------------------------------------------------*/
  1252. X
  1253. Xint activate_layer(slot, old_slot, direct)
  1254. Xint slot,        /* start searching at this slot */
  1255. X    old_slot,    /* slot that was last active */
  1256. X    direct;        /* direction of search NEXT, PREV */
  1257. X{
  1258. X    
  1259. X        /* SCAN USED SLOTS FOR ACTIVE PROCESSES, REMOVE DEAD SLOTS */
  1260. X    while ( (slot = get_slot(slot, USED, direct)) != NOT_FOUND &&
  1261. X            ( ioctl(0, SXTIOCSWTCH, slot) == -1  ||
  1262. X              ( kill(layer[slot].pid, 0) == -1 && errno == ESRCH) ) )
  1263. X    {
  1264. X        kill(-layer[slot].pid, SIGHUP);
  1265. X        layer[slot].status = FREE;
  1266. X    }
  1267. X
  1268. X    if (slot != NOT_FOUND)
  1269. X    {
  1270. X#ifdef USE_GETUT
  1271. X        sprintf(my_utmp.ut_line,"%s%03d", strrchr(SXT_DEVICE, '/') + 1,
  1272. X                                            slot + sxt_group * SXT_STEP);
  1273. X        pututline(&my_utmp);
  1274. X#endif
  1275. X         if (slot != old_slot)
  1276. X        {
  1277. X#ifdef USE_CONDUMP
  1278. X            if (console)
  1279. X            {
  1280. X                if (layer[old_slot].status == USED)
  1281. X                    condump(layer[old_slot].screen, 1, 0, 1, 1);
  1282. X                conrestore(layer[slot].screen, 1, 0, 1, 1);
  1283. X            }
  1284. X            else
  1285. X#endif
  1286. X            {
  1287. X                putp(clear_str );
  1288. X                fflush(stdout);
  1289. X                fprintf(stderr,"[%d] ", slot);
  1290. X                fflush(stderr);
  1291. X            }
  1292. X        }
  1293. X    }
  1294. X    return slot;
  1295. X}
  1296. X
  1297. X/*----------------------------------------------------------------------------
  1298. X**
  1299. X**    create_layer - create new layer using fork()
  1300. X**
  1301. X**--------------------------------------------------------------------------*/
  1302. X
  1303. Xint create_layer( slot, exfile, clrscr )
  1304. Xint slot,        /* slot to start searching for FREE slot */
  1305. X    clrscr;        /* should screen be cleared on activation */
  1306. Xchar *exfile;    /* startup executable, or NULL for shell */
  1307. X{
  1308. X    int pid;
  1309. X    char layerstr[15], execstr[1024], execarg0[25];
  1310. X    int control_fd;
  1311. X    char *getenv();
  1312. X#ifndef SHELL_STARTUP
  1313. X    char ps1str[15];
  1314. X#endif
  1315. X
  1316. X    if ((slot = get_slot(slot, FREE, NEXT)) == NOT_FOUND)
  1317. X    {
  1318. X        fprintf(stderr, "shm:  No more ttys.\n");
  1319. X        return NOT_FOUND;
  1320. X    }
  1321. X    
  1322. X#ifdef USE_CONDUMP
  1323. X    if (console)
  1324. X        if (layer[cur_slot].status == USED)
  1325. X            condump(layer[cur_slot].screen, 1, 0, 1, 1);
  1326. X#endif
  1327. X
  1328. X    if ( (pid=fork()) == 0)
  1329. X    {
  1330. X        signal( SIGINT,  SIG_DFL );
  1331. X        signal( SIGQUIT, SIG_DFL );
  1332. X        signal( SIGHUP,  SIG_DFL );
  1333. X        signal( SIGTERM, SIG_DFL );
  1334. X        signal( SIGCLD,  SIG_DFL );
  1335. X        setpgrp();
  1336. X        
  1337. X#ifdef DEBUG
  1338. Xfclose(db_file);
  1339. X#endif
  1340. X        
  1341. X        control_fd = dup(0);
  1342. X        fclose( stdin );
  1343. X        if (open_sxt_device(slot) == NOT_FOUND)
  1344. X            halt("PERROR shm:  Can not open tty.\n");
  1345. X
  1346. X        if ( exfile == NULL && ioctl(control_fd, SXTIOCSWTCH, slot) == -1)
  1347. X            halt( "PERROR Can not activate slot %d.\n", slot);
  1348. X                
  1349. X        if ( exfile != NULL && ioctl(control_fd, SXTIOCWF, slot) == -1)
  1350. X            halt( "PERROR shm: Can not wait for activation.\n");
  1351. X
  1352. X
  1353. X        if ( ioctl( 0, TCSETA, &layerterm ) == -1)
  1354. X            halt("PERROR shm:  Can not set terminal driver characteristics.\n");
  1355. X
  1356. X        if (clrscr == CLRSCR)
  1357. X            putp(clear_str);
  1358. X
  1359. X        fclose(stdout); dup(0);
  1360. X        fclose(stderr); dup(0);
  1361. X        close(control_fd);
  1362. X#ifdef USE_CONDUMP
  1363. X        if (console)    close_mem_fd();
  1364. X#endif
  1365. X        
  1366. X#ifdef USE_GETUT
  1367. X        sprintf(my_utmp.ut_line,"%s%03d", strrchr(SXT_DEVICE, '/') + 1,
  1368. X                                            slot + sxt_group * SXT_STEP );
  1369. X        pututline(&my_utmp);
  1370. X        endutent();
  1371. X#endif
  1372. X        setpgrp();
  1373. X        sprintf(layerstr,"LAYER=%d",slot);
  1374. X        putenv(layerstr);
  1375. X        
  1376. X        if (exfile == NULL || *exfile == '\0')
  1377. X        {
  1378. X            if (getenv("ISHELL") != NULL)        /* interactive shell */
  1379. X                strcpy(execstr, getenv("ISHELL"));
  1380. X            else if (getenv("SHELL") != NULL)
  1381. X                strcpy(execstr, getenv("SHELL"));
  1382. X            else strcpy(execstr, "/bin/sh");
  1383. X
  1384. X#ifdef SHELL_STARTUP
  1385. X                /* put dash before command name to signal startup */
  1386. X            strcpy(execarg0,strrchr(execstr, '/'));
  1387. X            execarg0[0] = '-';
  1388. X#else
  1389. X            strcpy(execarg0,strrchr(execstr, '/') + 1);
  1390. X            /* if they don't run their .profile, make a prompt */
  1391. X            sprintf(ps1str,"PS1=(%d) ", slot);
  1392. X            putenv(ps1str);
  1393. X#endif
  1394. X            execl(execstr, execarg0, (char *)0 );
  1395. X            halt("PERROR shm: execl failed.\n");
  1396. X        }
  1397. X        else
  1398. X        {
  1399. X            sprintf(execstr, "exec %s", exfile);
  1400. X            execl("/bin/sh", "sh", "-c", execstr, (char *)0 );
  1401. X            halt("PERROR shm: execl failed.\n");
  1402. X        }
  1403. X        /*NOTREACHED*/
  1404. X    }
  1405. X
  1406. X    if (pid == -1)
  1407. X    {
  1408. X        fprintf(stderr, "shm: fork failed.\n");
  1409. X        return(NOT_FOUND);
  1410. X    }
  1411. X
  1412. X    layer[slot].pid = pid;
  1413. X    layer[slot].status = USED;
  1414. X#ifdef USE_CONDUMP
  1415. X    if (console)
  1416. X        if (layer[slot].screen == 0)
  1417. X            if ( (layer[slot].screen = malloc(SCREEN_SIZE+2)) == NULL)
  1418. X                halt("PERROR shm : can not get more memory.\n");
  1419. X#endif
  1420. X    return slot;
  1421. X}
  1422. X    
  1423. X    
  1424. X/*----------------------------------------------------------------------------
  1425. X**
  1426. X**    get_slot - scan slots, return desired slot number
  1427. X**
  1428. X**--------------------------------------------------------------------------*/
  1429. X
  1430. Xint get_slot( slot, status, direct )
  1431. Xint slot,        /* start searching at this slot */
  1432. X    status,        /* for this type of slot, USED or FREE */
  1433. X    direct;        /* search in this direction, NEXT or PREV */
  1434. X{
  1435. X    int loops = 0;
  1436. X
  1437. X    while (layer[slot].status != status && ++loops < MAXPCHAN)
  1438. X    {
  1439. X        slot += direct;
  1440. X        if (slot == MAXPCHAN)
  1441. X            slot = 1;
  1442. X        if (slot == 0)
  1443. X            slot = MAXPCHAN-1;
  1444. X    }
  1445. X
  1446. X    return (layer[slot].status == status) ? slot : NOT_FOUND;
  1447. X}
  1448. X
  1449. X/*----------------------------------------------------------------------------
  1450. X**
  1451. X**    open_sxt_device - open sxt device
  1452. X**
  1453. X**--------------------------------------------------------------------------*/
  1454. X
  1455. Xint open_sxt_device( slot )
  1456. Xint slot;
  1457. X{
  1458. X    char    sxt_file[40];
  1459. X
  1460. X    sprintf(sxt_file, "%s%03d", SXT_DEVICE, slot + sxt_group * SXT_STEP );
  1461. X    return open(sxt_file, O_RDWR | ((slot == 0) ? O_EXCL : 0) );
  1462. X}
  1463. X
  1464. X/*----------------------------------------------------------------------------
  1465. X**
  1466. X**    layer_died - layer died, change status
  1467. X**
  1468. X**--------------------------------------------------------------------------*/
  1469. X
  1470. Xvoid layer_died(sig)
  1471. Xint sig;
  1472. X{
  1473. X    int new_slot;
  1474. X
  1475. X    signal(SIGCLD, SIG_IGN);    /* discard child */
  1476. X    new_slot = activate_layer(cur_slot, cur_slot, NEXT);
  1477. X    if (new_slot != NOT_FOUND)
  1478. X        cur_slot = new_slot;
  1479. X    else
  1480. X        exit_layers(0);
  1481. X    signal(SIGCLD, layer_died);
  1482. X
  1483. X}
  1484. X    
  1485. X/*----------------------------------------------------------------------------
  1486. X**
  1487. X**    exit_layers - this and halt are the only exit points
  1488. X**
  1489. X**--------------------------------------------------------------------------*/
  1490. X
  1491. Xvoid exit_layers(sig)
  1492. Xint sig;
  1493. X{
  1494. X    int slot;
  1495. X
  1496. X    if (sig != SIGHUP)
  1497. X    {
  1498. X        ioctl(0, SXTIOCSWTCH, 0);
  1499. X        (void)ioctl( 0, TCSETA, &savedterm );
  1500. X        putchar('\n');
  1501. X    }
  1502. X        
  1503. X    signal(SIGHUP,  SIG_IGN );
  1504. X    signal(SIGINT,  SIG_IGN );
  1505. X    signal(SIGQUIT, SIG_IGN );
  1506. X    signal(SIGTERM, SIG_IGN );
  1507. X    signal(SIGCLD,  SIG_IGN );
  1508. X
  1509. X#ifdef USE_GETUT
  1510. X    strcpy(my_utmp.ut_line, orig_tty);
  1511. X    pututline(&my_utmp);
  1512. X    endutent();
  1513. X#endif
  1514. X
  1515. X    for (slot = 1; slot < MAXPCHAN; slot++)
  1516. X        if (layer[slot].status == USED )
  1517. X        {
  1518. X            kill(-layer[slot].pid, (sig != 0) ? sig : SIGHUP );
  1519. X            ioctl(0, SXTIOCSWTCH, slot);    /* activate layer and/or */
  1520. X            ioctl(0, SXTIOCUBLK,  slot);    /* unblock layer */
  1521. X        }
  1522. X
  1523. X    exit(sig);
  1524. X}
  1525. END_OF_FILE
  1526.   if test 15879 -ne `wc -c <'shm.c'`; then
  1527.     echo shar: \"'shm.c'\" unpacked with wrong size!
  1528.   fi
  1529.   # end of 'shm.c'
  1530. fi
  1531. if test -f 'shm.doc' -a "${1}" != "-c" ; then 
  1532.   echo shar: Will not clobber existing file \"'shm.doc'\"
  1533. else
  1534.   echo shar: Extracting \"'shm.doc'\" \(5740 characters\)
  1535.   sed "s/^X//" >'shm.doc' <<'END_OF_FILE'
  1536. XSHM(1)                     Unix Programmer's Manual                     SHM(1)
  1537. X
  1538. XNAME
  1539. X    shm - shell layer manager
  1540. X
  1541. XSYNOPSYS
  1542. X    shm [ -c | +c ] [ program ... ]
  1543. X
  1544. XDESCRIPTION
  1545. X    shm is a clone of shl which allows a user to interact with
  1546. X    more than one shell from a single terminal.  The user controls
  1547. X    these shells, known as layers, using the key strokes
  1548. X    described below.  shm automatically sets saves the screen 
  1549. X        contents when it is started from the console device.  The -c 
  1550. X        flag forces console saving, the +c flag disables it.
  1551. X
  1552. X        shm differs from shl in five important respects.
  1553. X    
  1554. X           1) shm allows quicker switching and layer creation, using
  1555. X              simple key sequence. Each sequence starts with your
  1556. X              swtch(switch) character, which is usually ^Z, but may 
  1557. X              be redefined using stty.
  1558. X
  1559. X               2) when run from the console, shm saves the screen
  1560. X                  contents of each layer between switches
  1561. X
  1562. X           3) shm allows the specification of one or more programs
  1563. X              to be placed in separate layers upon startup.
  1564. X
  1565. X           4) shm starts up in a layer, not at an shm prompt.
  1566. X              In fact, there is no shm prompt, and all commands 
  1567. X              conclude by re-activating another layer.
  1568. X
  1569. X           5) shm clears the screen when switching layers, and
  1570. X              automatically exits when all layers have exited.
  1571. X
  1572. X
  1573. X    The current layer is the layer that can receive input from
  1574. X    the keyboard.  Other layers attempting to read from the
  1575. X    keyboard are blocked. Output from dormant layers causes
  1576. X       the layer to go to sleep, waiting to become active before
  1577. X    resuming.  This default behavior can be changed by using
  1578. X    stty -loblk in that layer or using an shm command described 
  1579. X    below.
  1580. X
  1581. XCOMMANDS
  1582. X    All shm commands start with your switch character, which we
  1583. X    will assume is ^Z.  The next character may be:
  1584. X
  1585. X            C, Z                  create a new layer
  1586. X
  1587. X            D                     delete the current layer
  1588. X
  1589. X            S                     show existing layers
  1590. X
  1591. X            ^Z, N, ^N, L, ^L
  1592. X            Switch, or Return     next layer
  1593. X
  1594. X            P, ^P, Backspace      previous layer
  1595. X
  1596. X            1, 2,..max_layers     create/activate layer number 
  1597. X
  1598. X            R, Space              resume current layer
  1599. X
  1600. X            B                     turn off output blocking for that layer
  1601. X
  1602. X            T                     toggle between layers
  1603. X
  1604. X            H, ?                  help
  1605. X
  1606. X            Q, ^D                 close all layers and quit shm
  1607. X
  1608. X
  1609. X    Upper/lower case is insignificant, and ^ designates a control 
  1610. X    character.  There are many alternatives for several commands
  1611. X    to suit individual tastes.
  1612. X
  1613. X    Some of my very popular key sequences are:
  1614. X
  1615. X            ^Z, Z           create new layer
  1616. X
  1617. X            ^Z, ^Z           go to next layer
  1618. X
  1619. X            ^Z, ^L, ^L      go to next layer and redisplay
  1620. X                             application screen
  1621. X            ^Z, Return,
  1622. X            Return          go to next layer and display prompt
  1623. X
  1624. X            ^Z, 1           go to layer 1
  1625. X
  1626. X    shm will automatically terminate when all its layers have been
  1627. X    exited, so if you have three shells active, ^D, ^D, ^D will cause
  1628. X    shm to exit, and return you to your normal single-layer shell.
  1629. X
  1630. XENVIRONMENT
  1631. X    shm reads several environment variables.
  1632. X
  1633. X      ISHELL        Users default interactive shell
  1634. X
  1635. X      SHELL         Shell to invoke if ISHELL is not set
  1636. X
  1637. X      TERM          Used to get string that clears the screen
  1638. X
  1639. X
  1640. X    shm sets one or two variables.
  1641. X
  1642. X      LAYER         Equals the layer number
  1643. X
  1644. X      PS1           Set only if shm is compiled with SHELL_STARTUP
  1645. X                    undefined, this equals $LAYER surrounded by 
  1646. X                    parentheses
  1647. X
  1648. XNOTES
  1649. X    I have found that using 'ps' from within a shell layer displays 
  1650. X    a header but nothing else.  I suppose 'ps' get confused by the
  1651. X    sxt devices.  Try using this as a substitute for 'ps'.  It should
  1652. X    be aliased to 'ps' or placed in your path before the real 'ps'.
  1653. X
  1654. X        :
  1655. X        # ps for shm
  1656. X        # usage ps [-z & usual ps args]
  1657. X        MAXPCHAN=8
  1658. X        if [ $# -eq 0 ] 
  1659. X        then     SXT_NUM="`expr \`tty\` : '/dev/sxt\(.*\)'`"
  1660. X            exec /bin/ps -t "sxt/$SXT_NUM"
  1661. X        elif [ "`expr \"$1\" : '-.*z.*$'`" -ne 0 ]
  1662. X        then
  1663. X            [ "$1" != "-z" ] &&
  1664. X                ZOPT="`echo $1 | sed 's/\(-.*\)z\(.*\)$/\1\2/'`"
  1665. X            shift
  1666. X            SXT_NUM="`expr \`tty\` : '/dev/sxt\(.*\)'`"
  1667. X            SXT_TERMS="`echo $SXT_NUM $MAXPCHAN | \
  1668. X                awk '{ for (i=0; i<$2; i++) \
  1669. X                    printf \"sxt/%03d,\",\
  1670. X                    int($1/$2)*$2+i; }'`"
  1671. X            exec /bin/ps $ZOPT $@ -t $SXT_TERMS
  1672. X        else    exec /bin/ps $*
  1673. X        fi
  1674. X        
  1675. X
  1676. X    It is also difficult to find out the name of the tty you logged
  1677. X    in on when you are in a shell layer.  'tty' reports the layer
  1678. X    device, i.e. /dev/sxt001 not /dev/console.  One way to get this 
  1679. X    information is to add a comment to the end of each (uu)getty 
  1680. X    line in /etc/inittab, like this:
  1681. X
  1682. X        co:12345:respawn:/etc/getty console console #console
  1683. X        I00:23:respawn:/etc/uugetty -rt180 tty00 2400 #tty00
  1684. X        I01:23:respawn:/etc/getty tty01 19200 #tty01
  1685. X
  1686. X    You can then use a script like this one to find the login tty:
  1687. X
  1688. X        :
  1689. X        # login tty for shm
  1690. X        TTY_DEV="`expr \`tty\` : '/dev/\(.*\)'`"
  1691. X        set `/bin/who -u | grep "$TTY_DEV"` ; echo $8
  1692. X        
  1693. X    The login tty is often needed in profiles to test for terminal 
  1694. X    types.
  1695. X
  1696. X    When placing programs in certain layers upon invocation of
  1697. X    shm, the programs do not actually start running until the 
  1698. X    layer is activated.  Use the null string('') to start up a
  1699. X    shell in a certain layer, i.e. shm and shm '' do the same
  1700. X    thing, and shm '' '' starts two shells.
  1701. X
  1702. X    I have found that when running shm from the console, if a 
  1703. X    warning message is sent to the console from the kernel, the
  1704. X    message is lost or the /dev/console becomes the active tty, 
  1705. X    causing shm to hang.
  1706. X
  1707. XAUTHOR
  1708. X    Bruce Momjian, root%candle.uucp@bts.com      Sept 13, 1992
  1709. X    shm version 1.1
  1710. END_OF_FILE
  1711.   if test 5740 -ne `wc -c <'shm.doc'`; then
  1712.     echo shar: \"'shm.doc'\" unpacked with wrong size!
  1713.   fi
  1714.   # end of 'shm.doc'
  1715. fi
  1716. echo shar: End of archive 1 \(of 1\).
  1717. cp /dev/null ark1isdone
  1718. MISSING=""
  1719. for I in 1 ; do
  1720.     if test ! -f ark${I}isdone ; then
  1721.     MISSING="${MISSING} ${I}"
  1722.     fi
  1723. done
  1724. if test "${MISSING}" = "" ; then
  1725.     echo You have the archive.
  1726.     rm -f ark[1-9]isdone
  1727. else
  1728.     echo You still must unpack the following archives:
  1729.     echo "        " ${MISSING}
  1730. fi
  1731. exit 0
  1732. exit 0 # Just in case...
  1733.