home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / ispin / part05 < prev    next >
Encoding:
Text File  |  1992-02-01  |  57.7 KB  |  1,678 lines

  1. Newsgroups: comp.sources.unix
  2. From: sir-alan!ispin!lbartz@iuvax.cs.indiana.edu (Larry Bartz)
  3. Subject: v25i116: Indianapolis Standard Printer Interface for Networked printers, Part05/15
  4. Sender: sources-moderator@pa.dec.com
  5. Approved: vixie@pa.dec.com
  6.  
  7. Submitted-By: sir-alan!ispin!lbartz@iuvax.cs.indiana.edu (Larry Bartz)
  8. Posting-Number: Volume 25, Issue 116
  9. Archive-Name: ispin/part05
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 5 (of 15)."
  18. # Contents:  ISPIN/src/IQUEUER.c.aa ISPIN/src/ISPIN.c.af
  19. #   ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_NOSHT
  20. #   ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_SHT
  21. #   ISPIN/install/lib_rtab/CPU_PTR/QUME_SHT
  22. #   ISPIN/install/lib_rtab/CPU_PTR/QUME_NOSHT
  23. #   ISPIN/install/lib_rtab/README ISPIN/doc/OLD-DOCS/bio
  24. # Wrapped by socrates@indy6 on Tue Jan 28 15:26:39 1992
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f 'ISPIN/src/IQUEUER.c.aa' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'ISPIN/src/IQUEUER.c.aa'\"
  28. else
  29. echo shar: Extracting \"'ISPIN/src/IQUEUER.c.aa'\" \(34486 characters\)
  30. sed "s/^X//" >'ISPIN/src/IQUEUER.c.aa' <<'END_OF_FILE'
  31. X/****************************************************************************/
  32. X/*                                                                          */
  33. X/* IQUEUER - ISPIN's queuer                                                 */
  34. X/*         - the secondary queueing daemon for:                             */
  35. X/*                                                                          */
  36. X/* ISPIN                                                                    */
  37. X/*                                                                          */
  38. X/* Indianapolis Standard Printer Interface (for Network printers)           */
  39. X/****************************************************************************/
  40. X/*                                                                          */
  41. X/*  Copyright (C) 1991                                                      */
  42. X/*  Larry Bartz                                                             */
  43. X/*  Internal Revenue Service                                                */
  44. X/*  Indianapolis District Office                                            */
  45. X/*                                                                          */
  46. X/*  This program is free software; you can redistribute it and/or modify    */
  47. X/*  it under the terms of the GNU General Public License as published by    */
  48. X/*  the Free Software Foundation, version 1.                                */
  49. X/*                                                                          */
  50. X/*  This program is distributed in the hope that it will be useful,         */
  51. X/*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
  52. X/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           */
  53. X/*  GNU General Public License for more details.                            */
  54. X/*                                                                          */
  55. X/*  You should have received a copy of the GNU General Public License       */
  56. X/*  along with this program; if not, write to the Free Software             */
  57. X/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               */
  58. X/*                                                                          */
  59. X/****************************************************************************/
  60. X/*                                                                          */
  61. X/* COMMENTS                                                                 */
  62. X/*                                                                          */
  63. X/* Read comments in common.h for the BIG PICTURE.                           */
  64. X/*                                                                          */
  65. X/*                                                                          */
  66. X/****************************************************************************/
  67. X/*                                                                          */
  68. X/* DEFINES                                                                  */
  69. X/*                                                                          */
  70. X/* Refer to common.h and iqueuer.h                                          */
  71. X/*                                                                          */
  72. X/* #define DEBUG                                                            */
  73. X/*                                                                          */
  74. X/****************************************************************************/
  75. X/*                                                                          */
  76. X/* INCLUDES                                                                 */
  77. X/*                                                                          */
  78. X/* Refer to common.h and iqueuer.h                                          */
  79. X/*                                                                          */
  80. X#include "../h/iqueuer.h"
  81. X/*                                                                          */
  82. X/*                                                                          */
  83. X/****************************************************************************/
  84. X/*                                                                          */
  85. X/* DATA TYPES, VARIABLES, ETC                                               */
  86. X/*                                                                          */
  87. X/* Refer to common.h and iqueuer.h                                          */
  88. X/*                                                                          */
  89. X/*                                                                          */
  90. X/*                                                                          */
  91. X/****************************************************************************/
  92. X/*                                                                          */
  93. X/* MODIFICATIONS                                                            */
  94. X/*                                                                          */
  95. X/* 10/13/89 Kevin Fannin - Modified method by which waiting jobs are queued.*/
  96. X/*                         Formerly, waiting print jobs were assigned to    */
  97. X/*                         a device (port) and remained in queue for that   */
  98. X/*                         device, even if another device which the job     */
  99. X/*                         could use became available. Now, all waiting     */
  100. X/*                         jobs are put in one queue and are not assigned   */
  101. X/*                         a device until they are actually ready to be     */
  102. X/*                         printed. To indicate that waiting jobs do not    */
  103. X/*                         have an assigned device, the device inode is     */
  104. X/*                         set to -1. This change also affected iqueuer.h   */
  105. X/*                         and IQ.c.                                        */
  106. X/****************************************************************************/
  107. X
  108. X
  109. Xmain(argc,argv)
  110. Xint argc;
  111. Xchar *argv[];
  112. X{
  113. X
  114. X  int timeout();
  115. X
  116. X
  117. X            /* format startup message */
  118. X            time(&tloc);
  119. X            nowtime = (struct tm *)localtime(&tloc);
  120. X            time_str = asctime(nowtime);
  121. X            strcpy(errmsg,"IQUEUER: startup time:\n");
  122. X            strcat(errmsg,"                            ");
  123. X            strcat(errmsg,time_str);
  124. X            strcat(errmsg,"\n");
  125. X         if((logfile = fopen(LOGFILE,"w")) != NULL)
  126. X         {
  127. X            fprintf(logfile,errmsg);
  128. X            fclose(logfile);
  129. X        chmod(LOGFILE,00666);
  130. X         }
  131. X
  132. X  /* The first thing to do is make sure that another invocation of iqueuer */
  133. X  /* is not already running. T'would be disasterous!                       */
  134. X  /* We'll use our status FIFO for this purpose. A running IQUEUER will be */
  135. X  /* holding this named pipe open for writing. If we open this pipe for    */
  136. X  /* reading without O_NDELAY set, and the open succeeds, that means that  */
  137. X  /* a viable writer process exists. That writer process could ONLY be     */
  138. X  /* another current IQUEUER. If the open fails, no other IQUEUER exists,  */
  139. X  /* so we may proceed.                                                    */
  140. X
  141. X  iq_pid = 31999;        /* An impossible pid, just to get us started. */
  142. X                         /* see write_status().                        */
  143. X
  144. X  /* allocate storage for our file status buffer */
  145. X  stat_buf = (struct stat *) calloc(1,sizeof(struct stat));
  146. X
  147. X  /* what is the name of the status FIFO ? */
  148. X  strcpy(statFIFO,FIFOdir);
  149. X  strcat(statFIFO,"STATUS");
  150. X  /* does it exist? */
  151. X  if(stat(statFIFO,stat_buf))
  152. X  {
  153. X    /* if not,      */
  154. X    /* create it if we can */
  155. X    if(mknod(statFIFO,0010666,1) != SUCCESS)
  156. X    {
  157. X      /* It didn't exist and we couldn't create it either. */
  158. X      /* All hope is lost.                                 */
  159. X      /* need to send out an error msg */
  160. X          /* format an error message */
  161. X          time(&tloc);
  162. X          nowtime = (struct tm *)localtime(&tloc);
  163. X          time_str = asctime(nowtime);
  164. X          sprintf(errmsg,"IQUEUER: mknod statFIFO: %s. time:\n",sys_errlist[errno]);
  165. X          strcat(errmsg,"                            ");
  166. X          strcat(errmsg,time_str);
  167. X          strcat(errmsg,"\n");
  168. X
  169. X          /* We can still write to stderr at this point. */
  170. X          fprintf(stderr,errmsg);
  171. X
  172. X         /* call the error routine, never come back */
  173. X         my_error(NONOTIFY);
  174. X
  175. X    }
  176. X  }
  177. X
  178. X
  179. X  /* An open for reading will fail unless a process (IQUEUER) has the file    */
  180. X  /* open for writing. Failure is what we want.                               */
  181. X
  182. X  signal(SIGALRM, timeout);     /* set up timeout for file open */
  183. X  time_out = 0;         /* assume no timeout */
  184. X  alarm(3);             /* wait a few seconds for file open      */
  185. X
  186. X  if((statropn = open(statFIFO,O_RDONLY)) >= 0)
  187. X  {
  188. X    if(time_out != 1)
  189. X    {
  190. X    /* if the open does not fail */
  191. X    /* need to send out an error msg */
  192. X          /* format an error message */
  193. X          time(&tloc);
  194. X          nowtime = (struct tm *)localtime(&tloc);
  195. X          time_str = asctime(nowtime);
  196. X          sprintf(errmsg,"IQUEUER: IQUEUER already exists. time:\n");
  197. X          strcat(errmsg,"                            ");
  198. X          strcat(errmsg,time_str);
  199. X          strcat(errmsg,"\n");
  200. X
  201. X          /* We can still write to stderr at this point. */
  202. X          fprintf(stderr,errmsg);
  203. X
  204. X         /* call the error routine, never come back */
  205. X         my_error(NONOTIFY);
  206. X    }
  207. X
  208. X  }
  209. X  alarm(0);      /* turn off the alarm clock */
  210. X  signal(SIGALRM, SIG_IGN);     /* ignore alarm again */
  211. X
  212. X  free(stat_buf);
  213. X
  214. X  /* Now that we know we're alone, proceed. */
  215. X  /* Close all files we'll not be using.   */
  216. X  fclose(stdin);
  217. X  fclose(stdout);
  218. X  fclose(stderr);
  219. X
  220. X  /* Fork child to run as the daemon. Parent dies */
  221. X  if(fork() != 0)
  222. X  {
  223. X    exit(0);
  224. X  }
  225. X  /* child daemon starts here */
  226. X
  227. X  /* divorce ourself from the current process group and its control terminal */
  228. X  setpgrp();
  229. X
  230. X  /* allocate storage for our file status buffer */
  231. X  stat_buf = (struct stat *) calloc(1,sizeof(struct stat));
  232. X
  233. X  /* open up our communication FIFOs */
  234. X
  235. X  /* status FIFO: we'll be the writer */
  236. X
  237. X  /* this open just tricks the FIFO into staying open for our write           */
  238. X  if((statropn = open(statFIFO,O_RDWR | O_NDELAY)) == -1)
  239. X  {
  240. X    /* need to send out an error msg */
  241. X          /* format an error message */
  242. X          time(&tloc);
  243. X          nowtime = (struct tm *)localtime(&tloc);
  244. X          time_str = asctime(nowtime);
  245. X          sprintf(errmsg,"IQUEUER: open statFIFO: %s. time:\n",sys_errlist[errno]);
  246. X          strcat(errmsg,"                            ");
  247. X          strcat(errmsg,time_str);
  248. X          strcat(errmsg,"\n");
  249. X
  250. X         /* call the error routine, never come back */
  251. X         my_error(NONOTIFY);
  252. X
  253. X  }
  254. X
  255. X  /* this open of the FIFO is the one we'll use for our write           */
  256. X  if((statfifo = open(statFIFO,O_WRONLY)) == -1)
  257. X  {
  258. X    /* need to send out an error msg */
  259. X          /* format an error message */
  260. X          time(&tloc);
  261. X          nowtime = (struct tm *)localtime(&tloc);
  262. X          time_str = asctime(nowtime);
  263. X          sprintf(errmsg,"IQUEUER: open statFIFO: %s. time:\n",sys_errlist[errno]);
  264. X          strcat(errmsg,"                            ");
  265. X          strcat(errmsg,time_str);
  266. X          strcat(errmsg,"\n");
  267. X
  268. X         /* call the error routine, never come back */
  269. X         my_error(NONOTIFY);
  270. X
  271. X  }
  272. X
  273. X
  274. X  /* IQUEUER's FIFO: we'll be the reader */
  275. X
  276. X  /* what is the name of the FIFO I listen through? */
  277. X  strcpy(inFIFO,FIFOdir);
  278. X  strcat(inFIFO,"IQUEUER");
  279. X  /* does it exist? */
  280. X  if(stat(inFIFO,stat_buf))
  281. X  {
  282. X    /* if not,      */
  283. X    /* create it if we can */
  284. X    if(mknod(inFIFO,0010666,1) != SUCCESS)
  285. X    {
  286. X      /* It didn't exist and we couldn't create it either. */
  287. X      /* All hope is lost.                                 */
  288. X      /* need to send out an error msg */
  289. X          /* format an error message */
  290. X          time(&tloc);
  291. X          nowtime = (struct tm *)localtime(&tloc);
  292. X          time_str = asctime(nowtime);
  293. X          sprintf(errmsg,"IQUEUER: mknod inFIFO: %s. time:\n",sys_errlist[errno]);
  294. X          strcat(errmsg,"                            ");
  295. X          strcat(errmsg,time_str);
  296. X          strcat(errmsg,"\n");
  297. X
  298. X         /* call the error routine, never come back */
  299. X         my_error(SYSERR);
  300. X
  301. X    }
  302. X  }
  303. X
  304. X  /* take care of the signals */
  305. X        signal(SIGINT, SIG_IGN);    /* ignore "interrupt" signal <DEL> */
  306. X        signal(SIGHUP, SIG_IGN);    /* ignore "hang up" signal */
  307. X        signal(SIGQUIT, SIG_IGN);   /* ignore "quit" signal */
  308. X        signal(SIGALRM, SIG_IGN);   /* ignore "alarm" signal */
  309. X        signal(SIGTERM, my_error);   /* trap "term" signal */
  310. X        /* Maybe on termination, we should write all waits to the FIFO */
  311. X        /* so they'll be waiting there for the next IQUEUER to handle. */
  312. X#ifdef NQ
  313. X        signal(SIGRES, SIG_IGN);     /* ignore "restart" signal */
  314. X        signal(SIGBACK, SIG_IGN);   /* ignore "back-up" signal */
  315. X        signal(SIGSTOP, SIG_IGN);   /* ignore "stop" signal */
  316. X#endif
  317. X
  318. X  /* set up our linked lists */
  319. X      /* First, set up the heads of our lists.              */
  320. X
  321. X      head_wait = (struct wait_queue *) calloc(1,sizeof(struct wait_queue));
  322. X      head_wait->prev = NULL;
  323. X      head_wait->next = NULL;
  324. X      curr_wait = list_wait = hold_wait = head_wait;
  325. X
  326. X      head_dsptch = (struct go_list *) calloc(1,sizeof(struct go_list));
  327. X      head_dsptch->prev = NULL;
  328. X      head_dsptch->next = NULL;
  329. X      curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  330. X
  331. X
  332. X  /* finally, do a "blocking" open for reading for   */
  333. X  /* an opportunity to do "blocking" reads.          */
  334. X  if((infifo = open(inFIFO,O_RDONLY)) == -1)
  335. X  {
  336. X    /* need to send out an error msg */
  337. X          /* format an error message */
  338. X          time(&tloc);
  339. X          nowtime = (struct tm *)localtime(&tloc);
  340. X          time_str = asctime(nowtime);
  341. X          sprintf(errmsg,"IQUEUER: open inFIFO: %s. time:\n",sys_errlist[errno]);
  342. X          strcat(errmsg,"                            ");
  343. X          strcat(errmsg,time_str);
  344. X          strcat(errmsg,"\n");
  345. X
  346. X         /* call the error routine, never come back */
  347. X         my_error(SYSERR);
  348. X
  349. X  }
  350. X
  351. X  /* All set? You bet! */
  352. X  while(1)
  353. X  {
  354. X      /* Now wait for a message, in the form of a work order. */
  355. X      /* A blocking read will allow the kernel to put us to sleep until... */
  356. X
  357. X      count1 = count2 = 0;
  358. X
  359. X      chars_got = read(infifo,msg_buf,to_iqrsiz);
  360. X      if(chars_got < 1)
  361. X      {
  362. X        close(infifo);
  363. X        if((infifo = open(inFIFO,O_RDONLY)) == -1)
  364. X        {
  365. X          /* need to send out an error msg */
  366. X                /* format an error message */
  367. X                time(&tloc);
  368. X                nowtime = (struct tm *)localtime(&tloc);
  369. X                time_str = asctime(nowtime);
  370. X                sprintf(errmsg,"IQUEUER: open inFIFO: %s. time:\n",sys_errlist[errno]);
  371. X                strcat(errmsg,"                            ");
  372. X                strcat(errmsg,time_str);
  373. X                strcat(errmsg,"\n");
  374. X
  375. X               /* call the error routine, never come back */
  376. X               my_error(SYSERR);
  377. X
  378. X        }
  379. X        chars_got = read(infifo,msg_buf,to_iqrsiz);
  380. X      }
  381. X
  382. X      /* fill the structure of unions from the msg_buf */
  383. X      while(count1 < intsize)
  384. X      {
  385. X        incoming.typ.chr[count1++] = msg_buf[count2++];
  386. X      }
  387. X      count1 = 0;
  388. X
  389. X      while(count1 < intsize)
  390. X      {
  391. X        incoming.my_pid.chr[count1++] = msg_buf[count2++];
  392. X      }
  393. X      count1 = 0;
  394. X
  395. X      while(count1 < intsize)
  396. X      {
  397. X        incoming.my_fifo.chr[count1++] = msg_buf[count2++];
  398. X      }
  399. X      count1 = 0;
  400. X
  401. X      while(count1 < intsize)
  402. X      {
  403. X        incoming.dev1_inod.chr[count1++] = msg_buf[count2++];
  404. X      }
  405. X      count1 = 0;
  406. X
  407. X      while(count1 < intsize)
  408. X      {
  409. X        incoming.dev2_inod.chr[count1++] = msg_buf[count2++];
  410. X      }
  411. X      count1 = 0;
  412. X
  413. X      while(count1 < intsize)
  414. X      {
  415. X        incoming.dev3_inod.chr[count1++] = msg_buf[count2++];
  416. X      }
  417. X      count1 = 0;
  418. X
  419. X      while(count1 < intsize)
  420. X      {
  421. X        incoming.dev4_inod.chr[count1++] = msg_buf[count2++];
  422. X      }
  423. X      count1 = 0;
  424. X
  425. X      while(count1 < intsize)
  426. X      {
  427. X        incoming.dev5_inod.chr[count1++] = msg_buf[count2++];
  428. X      }
  429. X      count1 = 0;
  430. X
  431. X      while(count1 < intsize)
  432. X      {
  433. X        incoming.dev6_inod.chr[count1++] = msg_buf[count2++];
  434. X      }
  435. X      count1 = 0;
  436. X
  437. X      while(count1 < intsize)
  438. X      {
  439. X        incoming.dev7_inod.chr[count1++] = msg_buf[count2++];
  440. X      }
  441. X      count1 = 0;
  442. X
  443. X      while(count1 < intsize)
  444. X      {
  445. X        incoming.dev8_inod.chr[count1++] = msg_buf[count2++];
  446. X      }
  447. X      count1 = 0;
  448. X
  449. X      while(count1 < intsize)
  450. X      {
  451. X        incoming.dev9_inod.chr[count1++] = msg_buf[count2++];
  452. X      }
  453. X      count1 = 0;
  454. X
  455. X      while(count1 < intsize)
  456. X      {
  457. X        incoming.dev10_inod.chr[count1++] = msg_buf[count2++];
  458. X      }
  459. X      count1 = 0;
  460. X
  461. X      while(count1 < intsize)
  462. X      {
  463. X        incoming.dev11_inod.chr[count1++] = msg_buf[count2++];
  464. X      }
  465. X      count1 = 0;
  466. X
  467. X      while(count1 < intsize)
  468. X      {
  469. X        incoming.uid.chr[count1++] = msg_buf[count2++];
  470. X      }
  471. X      count1 = 0;
  472. X
  473. X      while(count1 < intsize)
  474. X      {
  475. X        incoming.loop.chr[count1++] = msg_buf[count2++];
  476. X      }
  477. X      count1 = 0;
  478. X      count2 = 0;
  479. X
  480. X      /* what kind of request is this? */
  481. X
  482. X
  483. X#ifdef DEBUG
  484. X            /* format DEBUG message */
  485. X            time(&tloc);
  486. X            nowtime = (struct tm *)localtime(&tloc);
  487. X            time_str = asctime(nowtime);
  488. X            sprintf(errmsg,"IQUEUER: received message type = %d. time:\n",incoming.typ.intgr);
  489. X            strcat(errmsg,"                            ");
  490. X            strcat(errmsg,time_str);
  491. X            strcat(errmsg,"\n");
  492. X         if((logfile = fopen(LOGFILE,"a+")) != NULL)
  493. X         {
  494. X            fprintf(logfile,errmsg);
  495. X            fclose(logfile);
  496. X         }
  497. X#endif
  498. X
  499. X      if(kill(incoming.my_pid.intgr, 0) != -1)
  500. X      {
  501. X       /* if the message sender didn't die before we could help him */
  502. X      switch(incoming.typ.intgr)
  503. X      {
  504. X        case HERE:
  505. X          /* Always look for, and expunge any ISPINs which have croaked. */
  506. X          /* Find_dead() will call clean_out() with pid of dead ISPINs.  */
  507. X          find_dead();
  508. X
  509. X           /* reset the list pointers to aim at the head */
  510. X           curr_wait = list_wait = hold_wait = head_wait;
  511. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  512. X
  513. X           time(&tloc);                   /* what time did we get request ? */
  514. X
  515. X           /* let those who have been waiting go first */
  516. X           check_queue();    /* re-org the lists, issue go orders if appropos */
  517. X
  518. X           /* reset the list pointers to aim at the head */
  519. X           curr_wait = list_wait = hold_wait = head_wait;
  520. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  521. X
  522. X           go_or_queue();                 /* go now or wait in queue? */
  523. X        break;
  524. X
  525. X        case BUSY:
  526. X          /* Always look for, and expunge any ISPINs which have croaked. */
  527. X          /* Find_dead() will call clean_out() with pid of dead ISPINs.  */
  528. X
  529. X           /* reset the list pointers to aim at the head */
  530. X           curr_wait = list_wait = hold_wait = head_wait;
  531. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  532. X
  533. X          find_dead();
  534. X
  535. X           /* reset the list pointers to aim at the head */
  536. X           curr_wait = list_wait = hold_wait = head_wait;
  537. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  538. X
  539. X           /* save the original time_in */
  540. X           ret_val = firstime(incoming.my_pid.intgr);
  541. X
  542. X           /* remove our previous list member for this job */
  543. X           ret_val = clean_out(incoming.my_pid.intgr);
  544. X
  545. X           /* reset the list pointers to aim at the head */
  546. X           curr_wait = list_wait = hold_wait = head_wait;
  547. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  548. X
  549. X           /* let those who have been waiting go first */
  550. X           check_queue();    /* re-org the lists, issue go orders if appropos */
  551. X
  552. X           /* reset the list pointers to aim at the head */
  553. X           curr_wait = list_wait = hold_wait = head_wait;
  554. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  555. X
  556. X           /* set the global time variable to the original time_in */
  557. X           tloc = oldtime;
  558. X           go_or_queue();                 /* go now or wait in queue? */
  559. X        break;
  560. X
  561. X        case DONE:
  562. X        case TROUBLE:
  563. X           /* reset the list pointers to aim at the head */
  564. X           curr_wait = list_wait = hold_wait = head_wait;
  565. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  566. X
  567. X           ret_val = clean_out(incoming.my_pid.intgr);  /* remove job from whichever list */
  568. X
  569. X           /* reset the list pointers to aim at the head */
  570. X           curr_wait = list_wait = hold_wait = head_wait;
  571. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  572. X
  573. X          /* Always look for, and expunge any ISPINs which have croaked. */
  574. X          /* Find_dead() will call clean_out() with pid of dead ISPINs.  */
  575. X          find_dead();
  576. X
  577. X
  578. X           /* reset the list pointers to aim at the head */
  579. X           curr_wait = list_wait = hold_wait = head_wait;
  580. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  581. X
  582. X           check_queue();    /* re-org the lists, issue go orders if appropos */
  583. X        break;
  584. X
  585. X        case STARTUP:
  586. X        case CONNECTING:
  587. X        case PRINTING:
  588. X        case LOOPING:
  589. X        case WAITING:
  590. X        case DISCONNECTING:
  591. X        case QUITTING:
  592. X
  593. X           /* reset the list pointers to aim at the head */
  594. X           curr_wait = list_wait = hold_wait = head_wait;
  595. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  596. X
  597. X           ret_val = newstate(incoming.my_pid.intgr,incoming.typ.intgr);
  598. X
  599. X           /* reset the list pointers to aim at the head */
  600. X           curr_wait = list_wait = hold_wait = head_wait;
  601. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  602. X
  603. X        break;
  604. X
  605. X        case STATUS:
  606. X          /* Always look for, and expunge any ISPINs which have croaked. */
  607. X          /* Find_dead() will call clean_out() with pid of dead ISPINs.  */
  608. X
  609. X           /* reset the list pointers to aim at the head */
  610. X           curr_wait = list_wait = hold_wait = head_wait;
  611. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  612. X
  613. X          find_dead();
  614. X
  615. X           /* reset the list pointers to aim at the head */
  616. X           curr_wait = list_wait = hold_wait = head_wait;
  617. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  618. X
  619. X           check_queue();    /* re-org the lists, issue go orders if appropos */
  620. X
  621. X           /* reset the list pointers to aim at the head */
  622. X           curr_wait = list_wait = hold_wait = head_wait;
  623. X           curr_dsptch = list_dsptch = hold_dsptch = head_dsptch;
  624. X
  625. X           ret_val = write_status();   /* read both lists, write messages */
  626. X        break;
  627. X      }
  628. X      }
  629. X  }
  630. X
  631. X}
  632. X
  633. X/******************************************************************************/
  634. X
  635. X
  636. Xfind_dead()
  637. X{
  638. X  /* check both of our lists, if any of the jobs have died, delete from list */
  639. X
  640. X  /* check the go_list first */
  641. X
  642. X  /* walk the list */
  643. X
  644. X  while(curr_dsptch->next != NULL)
  645. X  {
  646. X    curr_dsptch = curr_dsptch->next;
  647. X    if((kill(curr_dsptch->job_id.my_pid.intgr, 0)) == -1)
  648. X    {
  649. X      ret_val = clean_out(curr_dsptch->job_id.my_pid.intgr);
  650. X    }
  651. X  }
  652. X
  653. X  /* check the wait_queue next */
  654. X
  655. X  /* walk the list */
  656. X
  657. X  while(curr_wait->next != NULL)
  658. X  {
  659. X    curr_wait = curr_wait->next;
  660. X
  661. X    if((kill(curr_wait->job_id.my_pid.intgr, 0)) == -1)
  662. X      {
  663. X        ret_val = clean_out(curr_wait->job_id.my_pid.intgr);
  664. X      }
  665. X  }
  666. X}
  667. X
  668. X
  669. Xclean_out(pid)
  670. Xint pid;
  671. X{
  672. X  /* we are given the pid of a job to be deleted from whichever list */
  673. X
  674. X  /* some strictly local pointers which mimic the global ones */
  675. X
  676. X  struct wait_queue *c_head_wait;
  677. X  struct wait_queue *c_curr_wait;
  678. X  struct wait_queue *c_list_wait;
  679. X  struct wait_queue *c_hold_wait;
  680. X
  681. X  struct go_list *c_head_dsptch;
  682. X  struct go_list *c_curr_dsptch;
  683. X  struct go_list *c_list_dsptch;
  684. X  struct go_list *c_hold_dsptch;
  685. X
  686. X  /* set the local pointers to aim at the true heads of the lists */
  687. X  c_head_wait = c_curr_wait = c_list_wait = c_hold_wait = head_wait;
  688. X  c_head_dsptch = c_curr_dsptch = c_list_dsptch = c_hold_dsptch = head_dsptch;
  689. X
  690. X  /* check the go_list first */
  691. X
  692. X  /* walk the list */
  693. X
  694. X  while(c_curr_dsptch->next != NULL)
  695. X  {
  696. X    c_curr_dsptch = c_curr_dsptch->next;
  697. X
  698. X    if(c_curr_dsptch->job_id.my_pid.intgr == pid)
  699. X    {
  700. X
  701. X      /* find the tty name */
  702. X      strcpy(lil_buf,myncheck(c_curr_dsptch->devinod,"/dev"));
  703. X    
  704. X      /* create the lock file name */
  705. X      strcpy(tty_lock,LCK_DIR);
  706. X      strcat(tty_lock,"LCK..");
  707. X      strcat(tty_lock,lil_buf);
  708. X
  709. X      /* get rid of the lockfile */
  710. X      /* We know this is our own lockfile. This job could not have */
  711. X      /* become a member of the go_list unless we were previously  */
  712. X      /* able to successfully create our own lockfile.             */
  713. X      ret_val = unlink(tty_lock);
  714. X      if(ret_val != 0)
  715. X      {
  716. X        /* need to send out an error msg */
  717. X        /* format an error message */
  718. X        time(&tloc);
  719. X        nowtime = (struct tm *)localtime(&tloc);
  720. X        time_str = asctime(nowtime);
  721. X        sprintf(errmsg,"IQUEUER: unlink %s: %s. time:\n",tty_lock,sys_errlist[errno]);
  722. X        strcat(errmsg,"                            ");
  723. X        strcat(errmsg,time_str);
  724. X        strcat(errmsg,"\n");
  725. X
  726. X        /* call the error routine, don't exit */
  727. X        ret_val = my_error(NO_EXIT);
  728. X      }
  729. X
  730. X      /* take the job out of the list */
  731. X      /* cut our guy out from between his neighbors */
  732. X
  733. X      if(c_curr_dsptch->next != NULL)
  734. X      {
  735. X        /* if we're not at the end of the list */
  736. X        c_curr_dsptch->prev->next = c_curr_dsptch->next;
  737. X        c_curr_dsptch->next->prev = c_curr_dsptch->prev;
  738. X      }
  739. X      else
  740. X      {
  741. X        /* if we are at the end of the list */
  742. X        c_curr_dsptch->prev->next = NULL;
  743. X      }
  744. X
  745. X      /* hold onto the memory location we're about to free */
  746. X
  747. X      c_hold_dsptch = c_curr_dsptch;
  748. X
  749. X      /* Shortly, the global curr_dsptch will be pointing at thin air. */
  750. X      /* Aim it at a valid memory location; the local one too. */
  751. X
  752. X      curr_dsptch = c_curr_dsptch->prev;
  753. X      c_curr_dsptch = c_curr_dsptch->prev;
  754. X
  755. X      /* if you love someone, set them free */
  756. X
  757. X      free(c_hold_dsptch);
  758. X      return(0);
  759. X    }
  760. X  }
  761. X
  762. X  /* check the wait_queue next */
  763. X
  764. X  /* walk the list */
  765. X
  766. X  while(c_curr_wait->next != NULL)
  767. X  { 
  768. X    c_curr_wait = c_curr_wait->next;
  769. X
  770. X    /* walk the list */
  771. X
  772. X    if(c_curr_wait->job_id.my_pid.intgr == pid)
  773. X      {
  774. X        /* take it out of the list */
  775. X        /* cut our guy out from between his neighbors */
  776. X
  777. X        if(c_curr_wait->next != NULL)
  778. X        {
  779. X          /* if we're not at the end of the list */
  780. X          c_curr_wait->prev->next = c_curr_wait->next;
  781. X          c_curr_wait->next->prev = c_curr_wait->prev;
  782. X        }
  783. X        else
  784. X        {
  785. X          /* if we are at the end of the list */
  786. X          c_curr_wait->prev->next = NULL;
  787. X        }
  788. X
  789. X        /* hold onto the memory location we're about to free */
  790. X
  791. X        c_hold_wait = c_curr_wait;
  792. X
  793. X        /* Shortly, the global curr_wait will be pointing at thin air. */
  794. X        /* Aim it at a valid memory location; the local one too. */
  795. X
  796. X        curr_wait = c_curr_wait->prev;
  797. X        c_curr_wait = c_curr_wait->prev;
  798. X
  799. X        /* if you love someone, set them free */
  800. X
  801. X        free(c_hold_wait);
  802. X        return(0);
  803. X      }
  804. X  }
  805. X}
  806. X
  807. X
  808. Xgo_or_queue()
  809. X{
  810. X  int in_use = 0;
  811. X  int louper = 0;
  812. X
  813. X  /* some strictly local pointers which mimic the global ones */
  814. X
  815. X  struct go_list *c_head_dsptch;
  816. X  struct go_list *c_curr_dsptch;
  817. X  struct go_list *c_list_dsptch;
  818. X  struct go_list *c_hold_dsptch;
  819. X
  820. X
  821. X  /* A work order has come in. May it be dispatched now, or must it wait? */
  822. X
  823. X  /* If any of the devices requested are not now members of the go_list,    */
  824. X  /* the job may be dispatched now. If all devices are in use, queue the    */
  825. X  /* job in the wait list. Remember, not all requests will ask for multiple */
  826. X  /* devices.                                                               */
  827. X
  828. X  /* set the local pointers to aim at the true heads of the lists */
  829. X  c_head_dsptch = c_curr_dsptch = c_list_dsptch = c_hold_dsptch = head_dsptch;
  830. X
  831. X  /* initialize the array of structures from incoming info */
  832. X
  833. X  dev_ray[0].inode = incoming.dev1_inod.intgr;
  834. X  dev_ray[1].inode = incoming.dev2_inod.intgr;
  835. X  dev_ray[2].inode = incoming.dev3_inod.intgr;
  836. X  dev_ray[3].inode = incoming.dev4_inod.intgr;
  837. X  dev_ray[4].inode = incoming.dev5_inod.intgr;
  838. X  dev_ray[5].inode = incoming.dev6_inod.intgr;
  839. X  dev_ray[6].inode = incoming.dev7_inod.intgr;
  840. X  dev_ray[7].inode = incoming.dev8_inod.intgr;
  841. X  dev_ray[8].inode = incoming.dev9_inod.intgr;
  842. X  dev_ray[9].inode = incoming.dev10_inod.intgr;
  843. X  dev_ray[10].inode = incoming.dev11_inod.intgr;
  844. X
  845. X  for(louper = 0; louper <= 10; ++louper)
  846. X  {
  847. X    dev_ray[louper].so_many = 0;
  848. X  }
  849. X
  850. X  /* check the go_list */
  851. X
  852. X  /* walk the list */
  853. X
  854. X  while(c_curr_dsptch->next != NULL)
  855. X  {
  856. X    c_curr_dsptch = c_curr_dsptch->next;
  857. X
  858. X
  859. X    for(louper = 0; louper <= 10; ++louper)
  860. X    {
  861. X      if(c_curr_dsptch->devinod == dev_ray[louper].inode)
  862. X      {
  863. X        dev_ray[louper].so_many = 1;
  864. X      }
  865. X    }
  866. X
  867. X   }
  868. X
  869. X/*************************************************************************/
  870. X  for(louper = 0; louper <= 10; ++louper)
  871. X  {
  872. X    if((dev_ray[louper].inode > 0)&&(dev_ray[louper].so_many == 0))
  873. X    {
  874. X      strcpy(lil_buf,myncheck(dev_ray[louper].inode,"/dev"));
  875. X      /* we'll try to create a lockfile for the tty we want */
  876. X      in_use = lockit(lil_buf);
  877. X      if(in_use == 0)
  878. X      {
  879. X        go_for_it(&incoming,dev_ray[louper].inode,tloc);
  880. X        return;
  881. X      }
  882. X    }
  883. X  }
  884. X  queue_it();
  885. X  return;
  886. X/*************************************************************************/
  887. X}
  888. X
  889. X
  890. Xgo_for_it(go_job,go_inod,in_time)
  891. Xstruct to_iqueuer *go_job;
  892. Xint go_inod;
  893. Xlong in_time;
  894. X{
  895. X  /* We have decided this job may proceed, using a certain device.   */
  896. X  /* "in_time" is the time the job request was submitted to IQUEUER. */
  897. X  /* "in_time" is determined on the fly if go_for_it() is called by  */
  898. X  /* go_or_queue(). "in_time" comes from wait_queue->time_in if      */
  899. X  /* go_for_it() is called by check_queue().                         */
  900. X
  901. X  /* char *myncheck(); */
  902. X
  903. X  /* some strictly local pointers which mimic the global ones */
  904. X
  905. X  struct go_list *c_head_dsptch;
  906. X  struct go_list *c_curr_dsptch;
  907. X  struct go_list *c_list_dsptch;
  908. X  struct go_list *c_hold_dsptch;
  909. X
  910. X  /* set the local pointers to aim at the true heads of the lists */
  911. X  c_head_dsptch = c_curr_dsptch = c_list_dsptch = c_hold_dsptch = head_dsptch;
  912. X
  913. X
  914. X  /* add it to the go_list */
  915. X  /* If we're called by check_que, it'll be his responsibility to */
  916. X  /* tidy-up the wait list after we return.                       */
  917. X
  918. X  /* walk the list */
  919. X
  920. X  while(c_curr_dsptch->next != NULL)
  921. X  {
  922. X    c_curr_dsptch = c_curr_dsptch->next;
  923. X  }
  924. X
  925. X  /* allocate memory for the new member of the list */
  926. X  c_curr_dsptch->next = (struct go_list *) calloc(1,sizeof(struct go_list));
  927. X
  928. X  /* point the new one back at the current one */
  929. X  c_curr_dsptch->next->prev = c_curr_dsptch;
  930. X
  931. X  /* change our notion of "current" to the new one */
  932. X  c_curr_dsptch = c_curr_dsptch->next;
  933. X
  934. X  /* terminate the list */
  935. X  c_curr_dsptch->next = NULL;
  936. X
  937. X  /* fill the new structure */
  938. X
  939. X  c_curr_dsptch->devinod                 =  go_inod;
  940. X  c_curr_dsptch->job_id.typ.intgr        =  STARTUP;
  941. X  c_curr_dsptch->job_id.my_pid.intgr     =  go_job->my_pid.intgr;
  942. X  c_curr_dsptch->job_id.my_fifo.intgr    =  go_job->my_fifo.intgr;
  943. X  c_curr_dsptch->job_id.dev1_inod.intgr  =  go_job->dev1_inod.intgr;
  944. X  c_curr_dsptch->job_id.dev2_inod.intgr  =  go_job->dev2_inod.intgr;
  945. X  c_curr_dsptch->job_id.dev3_inod.intgr  =  go_job->dev3_inod.intgr;
  946. X  c_curr_dsptch->job_id.dev4_inod.intgr  =  go_job->dev4_inod.intgr;
  947. X  c_curr_dsptch->job_id.dev5_inod.intgr  =  go_job->dev5_inod.intgr;
  948. X  c_curr_dsptch->job_id.dev6_inod.intgr  =  go_job->dev6_inod.intgr;
  949. X  c_curr_dsptch->job_id.dev7_inod.intgr  =  go_job->dev7_inod.intgr;
  950. X  c_curr_dsptch->job_id.dev8_inod.intgr  =  go_job->dev8_inod.intgr;
  951. X  c_curr_dsptch->job_id.dev9_inod.intgr  =  go_job->dev9_inod.intgr;
  952. X  c_curr_dsptch->job_id.dev10_inod.intgr  =  go_job->dev10_inod.intgr;
  953. X  c_curr_dsptch->job_id.dev11_inod.intgr  =  go_job->dev11_inod.intgr;
  954. X  c_curr_dsptch->job_id.uid.intgr        =  go_job->uid.intgr;
  955. X  c_curr_dsptch->job_id.loop.intgr       =  go_job->loop.intgr;
  956. X  c_curr_dsptch->time_in                 =  in_time;
  957. X  time(&tloc);
  958. X  c_curr_dsptch->time_out          =  tloc;
  959. X
  960. X  /* now tell the ISPIN he may proceed */
  961. X
  962. X  /* where's his FIFO? */
  963. X
  964. X  strcpy(lil_buf,myncheck(c_curr_dsptch->job_id.my_fifo.intgr,FIFOdir));
  965. X
  966. X  if(strlen(lil_buf) == 0)
  967. X  {
  968. X    /* need to send out an error msg */
  969. X    /* format an error message */
  970. X    time(&tloc);
  971. X    nowtime = (struct tm *)localtime(&tloc);
  972. X    time_str = asctime(nowtime);
  973. X    sprintf(errmsg,"IQUEUER: can't find outFIFO to ISPIN pid = %d.  time:\n",incoming.my_pid.intgr);
  974. X    strcat(errmsg,"                            ");
  975. X    strcat(errmsg,time_str);
  976. X    strcat(errmsg,"\n");
  977. X
  978. X   /* call the error routine, don't exit */
  979. X   ret_val = my_error(NO_EXIT);
  980. X   ret_val = clean_out(c_curr_dsptch->job_id.my_pid.intgr);
  981. X   return(0);
  982. X }
  983. X
  984. X strcpy(outFIFO, FIFOdir);
  985. X strcat(outFIFO, lil_buf);
  986. X
  987. X  /* this open of the FIFO is the one we'll use for our write           */
  988. X  if((outfifo = open(outFIFO,O_WRONLY)) == -1)
  989. X  {
  990. X    /* need to send out an error msg */
  991. X    /* format an error message */
  992. X    time(&tloc);
  993. X    nowtime = (struct tm *)localtime(&tloc);
  994. X    time_str = asctime(nowtime);
  995. X    sprintf(errmsg,"IQUEUER: open %s: %s. time:\n",outFIFO,sys_errlist[errno]);
  996. X    strcat(errmsg,"                            ");
  997. X    strcat(errmsg,time_str);
  998. X    strcat(errmsg,"\n");
  999. X
  1000. X   /* call the error routine, don't exit */
  1001. X   ret_val = my_error(NO_EXIT);
  1002. X   ret_val = clean_out(c_curr_dsptch->job_id.my_pid.intgr);
  1003. X   return(0);
  1004. X  }
  1005. X
  1006. X  /* construct the message */
  1007. X  count1 = count2 = 0;
  1008. X
  1009. X  out_msg.orders.intgr      = GO;
  1010. X  while(count1 < intsize)
  1011. X  {
  1012. X    msg_buf[count2++] = out_msg.orders.chr[count1++];
  1013. X  }
  1014. X  count1 = 0;
  1015. X
  1016. X  out_msg.iq_pid.intgr      = getpid();
  1017. X  while(count1 < intsize)
  1018. X  {
  1019. X    msg_buf[count2++] = out_msg.iq_pid.chr[count1++];
  1020. X  }
  1021. X  count1 = 0;
  1022. X
  1023. X  out_msg.dev_use_ino.intgr = go_inod;
  1024. X  while(count1 < intsize)
  1025. X  {
  1026. X    msg_buf[count2++] = out_msg.dev_use_ino.chr[count1++];
  1027. X  }
  1028. X  count1 = 0;
  1029. X
  1030. X  out_msg.pad1.intgr        = 0;
  1031. END_OF_FILE
  1032. if test 34486 -ne `wc -c <'ISPIN/src/IQUEUER.c.aa'`; then
  1033.     echo shar: \"'ISPIN/src/IQUEUER.c.aa'\" unpacked with wrong size!
  1034. fi
  1035. # end of 'ISPIN/src/IQUEUER.c.aa'
  1036. fi
  1037. if test -f 'ISPIN/src/ISPIN.c.af' -a "${1}" != "-c" ; then 
  1038.   echo shar: Will not clobber existing file \"'ISPIN/src/ISPIN.c.af'\"
  1039. else
  1040. echo shar: Extracting \"'ISPIN/src/ISPIN.c.af'\" \(13239 characters\)
  1041. sed "s/^X//" >'ISPIN/src/ISPIN.c.af' <<'END_OF_FILE'
  1042. X                                /* assume no timeout */
  1043. X                                time_out = 0; 
  1044. X
  1045. X                                alarm(NETWAIT);
  1046. X
  1047. X              wr_count += write(out_file,usr_strng,strlen(usr_strng));
  1048. X
  1049. X                                /* turn off the alarm clock */
  1050. X                                alarm(0);
  1051. X
  1052. X                                /* ignore alarm */
  1053. X        signal(SIGALRM, SIG_IGN);
  1054. X
  1055. X                                if(time_out == 1)
  1056. X                                {
  1057. X                                  /* We timed out on the write.*/
  1058. X                                  /* Behave as though we were  */
  1059. X                                  /* disconnected.             */
  1060. X
  1061. X                                  my_error(NET_TIME);
  1062. X                                }
  1063. X
  1064. X              break;       
  1065. X            case '0':
  1066. X            case '1':
  1067. X            case '2':
  1068. X            case '3':
  1069. X            case '4':
  1070. X            case '5':
  1071. X            case '6':
  1072. X            case '7':
  1073. X              /* octal representation of an ascii character */
  1074. X              oct_cnt = sscanf(&msg[count - 1],"%3o",&octal);
  1075. X              if(octal <= 0177)
  1076. X              {
  1077. X                sprintf(octal_char,"%c",octal);
  1078. X
  1079. X                                /* set up timeout for write */
  1080. X        signal(SIGALRM, my_error);
  1081. X
  1082. X                                /* assume no timeout */
  1083. X                                time_out = 0; 
  1084. X
  1085. X                                alarm(NETWAIT);
  1086. X
  1087. X                wr_count += write(out_file,&octal_char[0],1);
  1088. X
  1089. X                                /* turn off the alarm clock */
  1090. X                                alarm(0);
  1091. X
  1092. X                                /* ignore alarm */
  1093. X        signal(SIGALRM, SIG_IGN);
  1094. X
  1095. X                                if(time_out == 1)
  1096. X                                {
  1097. X                                  /* We timed out on the write.*/
  1098. X                                  /* Behave as though we were  */
  1099. X                                  /* disconnected.             */
  1100. X
  1101. X                                  my_error(NET_TIME);
  1102. X                                }
  1103. X
  1104. X              }
  1105. X              /* Move our pointer along to account for possibly processing */
  1106. X              /* more than one char via the sscanf.                        */
  1107. X              /* count += (oct_cnt - 1); */
  1108. X              count += (oct_cnt + 1);
  1109. X              break;       
  1110. X            default:
  1111. X              /* send the character */
  1112. X
  1113. X                                /* set up timeout for write */
  1114. X        signal(SIGALRM, my_error);
  1115. X
  1116. X                                /* assume no timeout */
  1117. X                                time_out = 0; 
  1118. X
  1119. X                                alarm(NETWAIT);
  1120. X
  1121. X              wr_count += write(out_file,&msg[count - 1],1);
  1122. X
  1123. X                                /* turn off the alarm clock */
  1124. X                                alarm(0);
  1125. X
  1126. X                                /* ignore alarm */
  1127. X        signal(SIGALRM, SIG_IGN);
  1128. X
  1129. X                                if(time_out == 1)
  1130. X                                {
  1131. X                                  /* We timed out on the write.*/
  1132. X                                  /* Behave as though we were  */
  1133. X                                  /* disconnected.             */
  1134. X
  1135. X                                  my_error(NET_TIME);
  1136. X                                }
  1137. X
  1138. X              break;       
  1139. X          }
  1140. X        }
  1141. X        break;       
  1142. X      default:
  1143. X        /* send the character */
  1144. X
  1145. X                                /* set up timeout for write */
  1146. X        signal(SIGALRM, my_error);
  1147. X
  1148. X                                /* assume no timeout */
  1149. X                                time_out = 0; 
  1150. X
  1151. X                                alarm(NETWAIT);
  1152. X
  1153. X        wr_count += write(out_file,&msg[count - 1],1);
  1154. X
  1155. X                                /* turn off the alarm clock */
  1156. X                                alarm(0);
  1157. X
  1158. X                                /* ignore alarm */
  1159. X        signal(SIGALRM, SIG_IGN);
  1160. X
  1161. X                                if(time_out == 1)
  1162. X                                {
  1163. X                                  /* We timed out on the write.*/
  1164. X                                  /* Behave as though we were  */
  1165. X                                  /* disconnected.             */
  1166. X
  1167. X                                  my_error(NET_TIME);
  1168. X                                }
  1169. X
  1170. X        break;       
  1171. X    }
  1172. X    if(msg[count - 1] != '\0')
  1173. X    {
  1174. X      checkit = msg[count++];
  1175. X    }
  1176. X  }
  1177. X  return(wr_count);
  1178. X}
  1179. X
  1180. X
  1181. Xquit_net()
  1182. X{
  1183. X  int answer = 0;
  1184. X  if(stayt == QUITTING)
  1185. X  {
  1186. X    SETJMP(kwit);
  1187. X    ++quit_once;
  1188. X    if(quit_once == 3)
  1189. X    {
  1190. X      kill(getpid(), SIGTERM);
  1191. X    }
  1192. X  }
  1193. X  /************************************************************************/
  1194. X  /* Reset traps on all signals!                                          */
  1195. X  /* We don't want to be interrupted when we already know we're           */
  1196. X  /* on our way out!                                                      */
  1197. X  /************************************************************************/
  1198. X
  1199. X  /* advise IQUEUER of our current state */
  1200. X  notify(stayt);
  1201. X
  1202. X#ifdef DEBUG
  1203. X          /* format an error message */
  1204. X          time(&tloc);
  1205. X          nowtime = (struct tm *)localtime(&tloc);
  1206. X          time_str = asctime(nowtime);
  1207. X          strcpy(errmsg,"ISPIN: quit_net 1  time:\n");
  1208. X          strcat(errmsg,"                            ");
  1209. X          strcat(errmsg,time_str);
  1210. X          strcat(errmsg,"\n");
  1211. X         if((logfile = fopen(LOGFILE,"a+")) != NULL)
  1212. X         {
  1213. X            fprintf(logfile,errmsg);
  1214. X            fclose(logfile);
  1215. X         }
  1216. X#endif
  1217. X
  1218. X
  1219. X
  1220. X/* take care of the signals */
  1221. X        signal(SIGINT, SIG_IGN);    /* ignore "interrupt" signal <DEL> */
  1222. X        signal(SIGHUP, SIG_IGN);    /* ignore "hang up" signal */
  1223. X        signal(SIGQUIT, SIG_IGN);   /* ignore "quit" signal */
  1224. X        signal(SIGALRM, SIG_IGN);
  1225. X        signal(SIGTERM, my_error);
  1226. X#ifdef NQ
  1227. X        signal(SIGRES, SIG_IGN);     /* ignore "restart" signal */
  1228. X        signal(SIGBACK, SIG_IGN);   /* ignore "back-up" signal */
  1229. X        signal(SIGSTOP, my_error);   /* trap "stop" signal */
  1230. X#endif
  1231. X
  1232. X#ifdef DEBUG
  1233. X          /* format an error message */
  1234. X          time(&tloc);
  1235. X          nowtime = (struct tm *)localtime(&tloc);
  1236. X          time_str = asctime(nowtime);
  1237. X          strcpy(errmsg,"ISPIN: quit_net 2  time:\n");
  1238. X          strcat(errmsg,"                            ");
  1239. X          strcat(errmsg,time_str);
  1240. X          strcat(errmsg,"\n");
  1241. X         if((logfile = fopen(LOGFILE,"a+")) != NULL)
  1242. X         {
  1243. X            fprintf(logfile,errmsg);
  1244. X            fclose(logfile);
  1245. X         }
  1246. X#endif
  1247. X
  1248. X
  1249. X  /* The interaction between this module and others poses significant */
  1250. X  /* potential for loopiness in the extremus. If we have been here    */
  1251. X  /* once already in this iteration of ISPIN, we obviously weren't    */
  1252. X  /* able to quit successfully. This could happen in the case where   */
  1253. X  /* the device (network or printer) is refusing to accept characters.*/
  1254. X  /* The write of our "quit" strings times out, so we try to quit,    */
  1255. X  /* and so on, ad nauseum. Fuggit.                                   */
  1256. X
  1257. X  /* 06/25/90: At least some of the SysV lpscheds send two software   */
  1258. X  /* termination signals to jobs which are being cancelled. The second*/
  1259. X  /* signal   will interrupt a valid quit sequence. So we give it two */
  1260. X  /* chances to back out normally.                              LSB   */
  1261. X
  1262. X  if(quit_once <= 3)
  1263. X  {
  1264. X
  1265. X#ifdef DEBUG
  1266. X          /* format an error message */
  1267. X          time(&tloc);
  1268. X          nowtime = (struct tm *)localtime(&tloc);
  1269. X          time_str = asctime(nowtime);
  1270. X          strcpy(errmsg,"ISPIN: quit_net 3  time:\n");
  1271. X          strcat(errmsg,"                            ");
  1272. X          strcat(errmsg,time_str);
  1273. X          strcat(errmsg,"\n");
  1274. X         if((logfile = fopen(LOGFILE,"a+")) != NULL)
  1275. X         {
  1276. X            fprintf(logfile,errmsg);
  1277. X            fclose(logfile);
  1278. X         }
  1279. X#endif
  1280. X
  1281. X
  1282. X    /* start out with clean buffers */
  1283. X    ioctl(out_file, TCFLSH, 2);
  1284. X
  1285. X
  1286. X#ifdef DEBUG
  1287. X          /* format an error message */
  1288. X          time(&tloc);
  1289. X          nowtime = (struct tm *)localtime(&tloc);
  1290. X          time_str = asctime(nowtime);
  1291. X          strcpy(errmsg,"ISPIN: quit_net 4  time:\n");
  1292. X          strcat(errmsg,"                            ");
  1293. X          strcat(errmsg,time_str);
  1294. X          strcat(errmsg,"\n");
  1295. X         if((logfile = fopen(LOGFILE,"a+")) != NULL)
  1296. X         {
  1297. X            fprintf(logfile,errmsg);
  1298. X            fclose(logfile);
  1299. X         }
  1300. X#endif
  1301. X
  1302. X    /* set our list pointers to point at head of the list */
  1303. X    quit_list = quit_curr = quit_head;
  1304. X
  1305. X#ifdef DEBUG
  1306. X          /* format an error message */
  1307. X          time(&tloc);
  1308. X          nowtime = (struct tm *)localtime(&tloc);
  1309. X          time_str = asctime(nowtime);
  1310. X          strcpy(errmsg,"ISPIN: quit_net 5  time:\n");
  1311. X          strcat(errmsg,"                            ");
  1312. X          strcat(errmsg,time_str);
  1313. X          strcat(errmsg,"\n");
  1314. X         if((logfile = fopen(LOGFILE,"a+")) != NULL)
  1315. X         {
  1316. X            fprintf(logfile,errmsg);
  1317. X            fclose(logfile);
  1318. X         }
  1319. X#endif
  1320. X
  1321. X
  1322. X    while(quit_curr->next != NULL)
  1323. X    {
  1324. X
  1325. X
  1326. X      quit_curr = quit_curr->next;
  1327. X
  1328. X
  1329. X      answer = write_net(quit_curr->quit_strg);
  1330. X
  1331. X#ifdef DEBUG
  1332. X          /* format an error message */
  1333. X          time(&tloc);
  1334. X          nowtime = (struct tm *)localtime(&tloc);
  1335. X          time_str = asctime(nowtime);
  1336. X          strcpy(errmsg,"ISPIN: quit_net 6  time:\n");
  1337. X          strcat(errmsg,"                            ");
  1338. X          strcat(errmsg,time_str);
  1339. X          strcat(errmsg,"\n");
  1340. X         if((logfile = fopen(LOGFILE,"a+")) != NULL)
  1341. X         {
  1342. X            fprintf(logfile,errmsg);
  1343. X            fclose(logfile);
  1344. X         }
  1345. X#endif
  1346. X
  1347. X    }
  1348. X
  1349. X#ifdef DEBUG
  1350. X          /* format an error message */
  1351. X          time(&tloc);
  1352. X          nowtime = (struct tm *)localtime(&tloc);
  1353. X          time_str = asctime(nowtime);
  1354. X          strcpy(errmsg,"ISPIN: quit_net 7  time:\n");
  1355. X          strcat(errmsg,"                            ");
  1356. X          strcat(errmsg,time_str);
  1357. X          strcat(errmsg,"\n");
  1358. X         if((logfile = fopen(LOGFILE,"a+")) != NULL)
  1359. X         {
  1360. X            fprintf(logfile,errmsg);
  1361. X            fclose(logfile);
  1362. X         }
  1363. X#endif
  1364. X
  1365. X
  1366. X    alarm(0);
  1367. X
  1368. X  }
  1369. X  if(stayt == QUITTING)
  1370. X  {
  1371. X    ioctl(out_file, TCSETA, &Tsav);
  1372. X    close(out_file);
  1373. X    port_open = 0;
  1374. X    my_exit(0);
  1375. X  }
  1376. X  else
  1377. X  {
  1378. X    return(0);
  1379. X  }
  1380. X}
  1381. X
  1382. X
  1383. Xagain()
  1384. X{
  1385. X
  1386. X        ++netloop;
  1387. X        ++req_msg.loop.intgr;
  1388. X        /* free the allocated memory */
  1389. X        free(expt1);
  1390. X        free(c_send);
  1391. X        free(expt2);
  1392. X        stayt = DISCONNECTING;
  1393. X        ret_val = quit_net();
  1394. X        ioctl(out_file, TCSETA, &Tsav);
  1395. X        quit_once = 0;
  1396. X
  1397. X#ifndef NQ
  1398. X        if(strcmp(ctrl_tty,GO_dev) != 0)
  1399. X        {
  1400. X          /* never close the control terminal */
  1401. X          /* Pyramid puts us in la-la land if we do */
  1402. X          close(out_file);
  1403. X          out_file = ctrltty;
  1404. X        }
  1405. X        numfiles = num_ofile;
  1406. X#else
  1407. X        /* just because of a couple of old Zilogs in Fargo */
  1408. X        close(out_file);
  1409. X        port_open = 0;
  1410. X#endif
  1411. X        notify(LOOPING);
  1412. X        alarm(0);
  1413. X        sleep(NETSLEEP);
  1414. X        notify(BUSY);
  1415. X        /* jump back to main(), just before setuptty() call   07/27/90  LSB */
  1416. X        LONGJMP(agayn,netloop);
  1417. X}
  1418. X
  1419. X
  1420. Xisit_gone()
  1421. X{
  1422. X  /* called whenever the do_it function has reason to     */
  1423. X  /* believe the printer has been disconnected or powered */
  1424. X  /* down. The user (whoever configured the rtab entry    */
  1425. X  /* for this printer) has specified specific string(s)   */
  1426. X  /* which we expect the network may send us to indicate  */
  1427. X  /* the disconnected condition. Here is where we compare */
  1428. X  /* what we have received to what we expect. If we       */
  1429. X  /* determine we have been disconnected, try again.      */
  1430. X
  1431. X
  1432. X    if(netloop <= MAXNET_TRY)
  1433. X    {
  1434. X      if(disconn()== 1)
  1435. X      {
  1436. X        /* advise IQUEUER of our current state */
  1437. X        notify(DISCONNECTING);
  1438. X
  1439. X        my_error(DISCONNECTING);
  1440. X      }
  1441. X     }
  1442. X     else
  1443. X     {
  1444. X       /* need to send out an error msg */
  1445. X       /* format an error message */
  1446. X       time(&tloc);
  1447. X       nowtime = (struct tm *)localtime(&tloc);
  1448. X       time_str = asctime(nowtime);
  1449. X  
  1450. X       sprintf(errmsg,"ISPIN: printer %s: %d LOOPS, so quitting.\n",dest,netloop);
  1451. X          sprintf(errmsg2,"    USER: %s\n",from);
  1452. X          strcat(errmsg,errmsg2);
  1453. X          if(usr_addr)
  1454. X          {
  1455. X            sprintf(errmsg2,"    ADDR: %s\n",usr_strng);
  1456. X            strcat(errmsg,errmsg2);
  1457. X          }
  1458. X          if(port_open == 1)
  1459. X          {
  1460. X            sprintf(errmsg2,"     DEV: %s\n",GO_dev);
  1461. X            strcat(errmsg,errmsg2);
  1462. X          }
  1463. X#ifdef NQ
  1464. X          sprintf(errmsg2,"    FILE: %s\n",fyle);
  1465. X#else
  1466. X          sprintf(errmsg2,"    FILE: %s\n",fyles[0]);
  1467. X#endif
  1468. X          strcat(errmsg,errmsg2);
  1469. X          sprintf(errmsg2,"    TIME: %s\n",time_str);
  1470. X          strcat(errmsg,errmsg2);
  1471. X
  1472. X       /* call the error routine, never come back */
  1473. X       my_error(MYERR);
  1474. X     }
  1475. X}
  1476. X
  1477. X
  1478. Xdisconn()
  1479. X{
  1480. X  ushort answer;
  1481. X
  1482. X  /* set our list pointers to point at head of the list */
  1483. X  disc_list = disc_curr = disc_head;
  1484. X
  1485. X  while(disc_curr->next != NULL)
  1486. X  {
  1487. X    disc_curr = disc_curr->next;
  1488. X
  1489. X
  1490. X
  1491. X
  1492. X    answer = matcher(in_buf,disc_curr->disc_strg);
  1493. X
  1494. X    if(answer == 0)
  1495. X    {
  1496. X      return(1);
  1497. X    }
  1498. X  }
  1499. X  return(0);
  1500. X}
  1501. END_OF_FILE
  1502. if test 13239 -ne `wc -c <'ISPIN/src/ISPIN.c.af'`; then
  1503.     echo shar: \"'ISPIN/src/ISPIN.c.af'\" unpacked with wrong size!
  1504. fi
  1505. # end of 'ISPIN/src/ISPIN.c.af'
  1506. fi
  1507. if test -f 'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_NOSHT' -a "${1}" != "-c" ; then 
  1508.   echo shar: Will not clobber existing file \"'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_NOSHT'\"
  1509. else
  1510. echo shar: Extracting \"'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_NOSHT'\" \(599 characters\)
  1511. sed "s/^X//" >'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_NOSHT' <<'END_OF_FILE'
  1512. X# The address entered by the user must be the last 8 (eight) digits of the
  1513. X# fourteen digit X.25 address of the printer.
  1514. X# If you use the ISPI application as the frontend for your users, the comment
  1515. X# line following this entry will be used in ISPI's menu.
  1516. X#
  1517. X#
  1518. X#
  1519. Xextqume;/dev/ttyi07,/dev/ttyi31,/dev/ttyi15;9600;-L;-Bcongestion;-Bremote dte;-Q\L\L\032\dclr\r\d;-DConnection cleared;-Dpad>;ad>-\p\K\pclr\r\d-ad>;\r\p;pad>-\p\K\pclr\r\d-pad>;set recall=26\r\p;pad>-\r\d-pad>;\pconnect\s000000\U\r\d;pened-\p\K\pclr\r\dset recall=26\r\p\pconnect\s000000\U\r\d-pened;\d\033F66\033C00\0339\d;;;
  1520. X#Qume
  1521. X#
  1522. X#
  1523. END_OF_FILE
  1524. if test 599 -ne `wc -c <'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_NOSHT'`; then
  1525.     echo shar: \"'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_NOSHT'\" unpacked with wrong size!
  1526. fi
  1527. # end of 'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_NOSHT'
  1528. fi
  1529. if test -f 'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_SHT' -a "${1}" != "-c" ; then 
  1530.   echo shar: Will not clobber existing file \"'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_SHT'\"
  1531. else
  1532. echo shar: Extracting \"'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_SHT'\" \(615 characters\)
  1533. sed "s/^X//" >'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_SHT' <<'END_OF_FILE'
  1534. X# The address entered by the user must be the last 8 (eight) digits of the
  1535. X# fourteen digit X.25 address of the printer.
  1536. X# If you use the ISPI application as the frontend for your users, the comment
  1537. X# line following this entry will be used in ISPI's menu.
  1538. X#
  1539. X#
  1540. X#
  1541. Xextsqume;/dev/ttyi07,/dev/ttyi31,/dev/ttyi15;9600;-L;-Bcongestion;-Bremote dte;-Q\L\L\032\dclr\r\d;-DConnection cleared;-Dpad>;ad>-\p\K\pclr\r\d-ad>;\r\p;pad>-\p\K\pclr\r\d-pad>;set recall=26\r\p;pad>-\r\d-pad>;\pconnect\s000000\U\r\d;pened-\p\K\pclr\r\dset recall=26\r\p\pconnect\s000000\U\r\d-pened;\d\033F90\033C28\0339\d;;;
  1542. X#Qume - Sheet Feeder
  1543. X#
  1544. X#
  1545. END_OF_FILE
  1546. if test 615 -ne `wc -c <'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_SHT'`; then
  1547.     echo shar: \"'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_SHT'\" unpacked with wrong size!
  1548. fi
  1549. # end of 'ISPIN/install/lib_rtab/CPU_CDN_PTR/USER_ADDR/QUME_SHT'
  1550. fi
  1551. if test -f 'ISPIN/install/lib_rtab/CPU_PTR/QUME_SHT' -a "${1}" != "-c" ; then 
  1552.   echo shar: Will not clobber existing file \"'ISPIN/install/lib_rtab/CPU_PTR/QUME_SHT'\"
  1553. else
  1554. echo shar: Extracting \"'ISPIN/install/lib_rtab/CPU_PTR/QUME_SHT'\" \(70 characters\)
  1555. sed "s/^X//" >'ISPIN/install/lib_rtab/CPU_PTR/QUME_SHT' <<'END_OF_FILE'
  1556. X#
  1557. Xnetind1;/dev/ttyi07;9600;-L;;\033S\033F90\033C28\0339\015\033T;;;
  1558. X#
  1559. END_OF_FILE
  1560. if test 70 -ne `wc -c <'ISPIN/install/lib_rtab/CPU_PTR/QUME_SHT'`; then
  1561.     echo shar: \"'ISPIN/install/lib_rtab/CPU_PTR/QUME_SHT'\" unpacked with wrong size!
  1562. fi
  1563. # end of 'ISPIN/install/lib_rtab/CPU_PTR/QUME_SHT'
  1564. fi
  1565. if test -f 'ISPIN/install/lib_rtab/CPU_PTR/QUME_NOSHT' -a "${1}" != "-c" ; then 
  1566.   echo shar: Will not clobber existing file \"'ISPIN/install/lib_rtab/CPU_PTR/QUME_NOSHT'\"
  1567. else
  1568. echo shar: Extracting \"'ISPIN/install/lib_rtab/CPU_PTR/QUME_NOSHT'\" \(70 characters\)
  1569. sed "s/^X//" >'ISPIN/install/lib_rtab/CPU_PTR/QUME_NOSHT' <<'END_OF_FILE'
  1570. X#
  1571. Xnetind1;/dev/ttyi07;9600;-L;;\033S\033F66\033C00\0339\015\033T;;;
  1572. X#
  1573. END_OF_FILE
  1574. if test 70 -ne `wc -c <'ISPIN/install/lib_rtab/CPU_PTR/QUME_NOSHT'`; then
  1575.     echo shar: \"'ISPIN/install/lib_rtab/CPU_PTR/QUME_NOSHT'\" unpacked with wrong size!
  1576. fi
  1577. # end of 'ISPIN/install/lib_rtab/CPU_PTR/QUME_NOSHT'
  1578. fi
  1579. if test -f 'ISPIN/install/lib_rtab/README' -a "${1}" != "-c" ; then 
  1580.   echo shar: Will not clobber existing file \"'ISPIN/install/lib_rtab/README'\"
  1581. else
  1582. echo shar: Extracting \"'ISPIN/install/lib_rtab/README'\" \(2325 characters\)
  1583. sed "s/^X//" >'ISPIN/install/lib_rtab/README' <<'END_OF_FILE'
  1584. X
  1585. X
  1586. X
  1587. X
  1588. X
  1589. X                               LIB_RTAB
  1590. X
  1591. XThis directory contains subdirectories of valid, (mostly) tested rtab entries
  1592. Xfor various networking situations. These rtab entries may be added
  1593. Xdirectly to the top of your rtab. Adjust them to suit your needs, especially
  1594. Xin the areas of printer name, device(s)to be used, destination addresses,
  1595. Xlogin heralds and info messages from local switching hardware, and the like.
  1596. X
  1597. XSee ISPIN/doc/rtab. These sample rtab entries are pure vanilla, plain-jane
  1598. X(excuse me, Jane) examples. They do not demonstrate the full flexibility
  1599. Xand capability of this component of ISPIN. The rtab is intended as your entry
  1600. Xpoint for creative implementation of ISPIN. You will not be able to take 
  1601. Xadvantage of this feature unless you study ISPIN/doc/rtab carefully.
  1602. X
  1603. XThe major subdirectories at this time include:
  1604. X
  1605. XCPU_CDN_PTR - for connecting from the cpu, through PACNET/CDN, to the printer
  1606. X            - both the cpu and the printer are directly connected to CDN
  1607. X            - (PACNET/CDN is IRS's current (05/15/89) X.25 network, with
  1608. X              asynchronous pads at both ends.)
  1609. X
  1610. XCPU_SW_PTR - for connecting from the cpu, through a local switch (the examples
  1611. X             given are for Tellabs), to the printer
  1612. X           - both the cpu and the printer are connected to the switch
  1613. X
  1614. XCPU_SW_CDN_PTR - for connecting from the cpu, through a local switch (example
  1615. X                 is Tellabs), then through CDN to the printer
  1616. X               - the cpu is connected to the local switch, the switch is
  1617. X                 connected to CDN, the printer is connected to CDN
  1618. X
  1619. XCPU_PTR - for printers hardwired to the cpu
  1620. X
  1621. X
  1622. XEach of the above-mentioned directories contains two subdirectories, as follow:
  1623. X
  1624. XADDR_SPEC - for rtab entries in which the destination address is explicitly
  1625. X            specified
  1626. X
  1627. XUSER_ADDR - for rtab entries which allow the user to specify the destination
  1628. X            address of the printer
  1629. X
  1630. X
  1631. XFinally, beneath those directories lie the example rtab entries. One of those
  1632. Xentries is called PLAIN, and will suffice for most printers. Other entries
  1633. Xwill be named similarly to their make and model. The make and model-specific
  1634. Xrtab entries may contain special printer command/control sequences and/or
  1635. Xspecific variations on the pauses, delays, or waits upon connection and
  1636. Xdisconnection.
  1637. X
  1638. END_OF_FILE
  1639. if test 2325 -ne `wc -c <'ISPIN/install/lib_rtab/README'`; then
  1640.     echo shar: \"'ISPIN/install/lib_rtab/README'\" unpacked with wrong size!
  1641. fi
  1642. # end of 'ISPIN/install/lib_rtab/README'
  1643. fi
  1644. if test -f 'ISPIN/doc/OLD-DOCS/bio' -a "${1}" != "-c" ; then 
  1645.   echo shar: Will not clobber existing file \"'ISPIN/doc/OLD-DOCS/bio'\"
  1646. else
  1647. echo shar: Extracting \"'ISPIN/doc/OLD-DOCS/bio'\" \(228 characters\)
  1648. sed "s/^X//" >'ISPIN/doc/OLD-DOCS/bio' <<'END_OF_FILE'
  1649. XAbout the author:
  1650. X
  1651. XLarry Bartz chose to enter the field of Data Processing in 1983 after
  1652. Xhis wife convinced him that hog farming was not the only attractive 
  1653. Xalternative to continuing his career with IRS's Examination Division.
  1654. END_OF_FILE
  1655. if test 228 -ne `wc -c <'ISPIN/doc/OLD-DOCS/bio'`; then
  1656.     echo shar: \"'ISPIN/doc/OLD-DOCS/bio'\" unpacked with wrong size!
  1657. fi
  1658. # end of 'ISPIN/doc/OLD-DOCS/bio'
  1659. fi
  1660. echo shar: End of archive 5 \(of 15\).
  1661. cp /dev/null ark5isdone
  1662. MISSING=""
  1663. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ; do
  1664.     if test ! -f ark${I}isdone ; then
  1665.     MISSING="${MISSING} ${I}"
  1666.     fi
  1667. done
  1668. if test "${MISSING}" = "" ; then
  1669.     echo You have unpacked all 15 archives.
  1670.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1671. else
  1672.     echo You still need to unpack the following archives:
  1673.     echo "        " ${MISSING}
  1674. fi
  1675. ##  End of shell archive.
  1676. exit 0
  1677.  
  1678.