home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume37 / ixobeepr / part01 < prev    next >
Encoding:
Text File  |  1993-05-17  |  53.8 KB  |  1,762 lines

  1. Newsgroups: comp.sources.misc
  2. From: tal@Warren.MENTORG.COM (Tom Limoncelli)
  3. Subject: v37i068:  ixobeeper - Talk to alpha-numeric pager from Unix, Part01/02
  4. Message-ID: <csm-v37i068=ixobeeper.222854@sparky.IMD.Sterling.COM>
  5. X-Md4-Signature: 5c45478c5bc9061ff28a212b33764b8c
  6. Date: Tue, 18 May 1993 03:29:53 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: tal@Warren.MENTORG.COM (Tom Limoncelli)
  10. Posting-number: Volume 37, Issue 68
  11. Archive-name: ixobeeper/part01
  12. Environment: UNIX, Sun, HPUX
  13. Supersedes: ixobeeper: Volume 25, Issue 43
  14.  
  15. Do you have an alpha-numeric pager (beeper)?  Ever wonder about
  16. that little box that you rent to enable you to send messages?
  17. The box is just a keyboard, a modem, and a bit of software that
  18. speaks the IXO protocol.  This program lets you speak the IXO
  19. protocol (manual mode only) and since you can run this program
  20. from shell scripts, Perl, Sun Netmanager, whatever, your computer
  21. can keep you up to date no matter where you are.
  22.  
  23. "tpage" or "Tom's Pager System" is a set of programs that let
  24. you send messages to alpha-numeric pagers using the "IXO" protocol.
  25. It supports a dialing directory, a "who's on duty now" schedule,
  26. and can do special tricks with RFC822-format email.
  27.  
  28. The system has the following features:
  29.  
  30. ...sends pages to any pager system that supports the IXO protocol.
  31.  
  32. ...additional protocols can be added. (I'll write the touch-tone
  33.       protocol soon).
  34.  
  35. ...can parse email messages and extract the interesting info from
  36.    them resulting in shorter messages.
  37.  
  38. ...can copy it's input to stdout and therefore can be used as a "tee".
  39.  
  40. ...maintains a directory of people's phone numbers/PINs.
  41.  
  42. ...can page "the person on duty" (searches a schedule).
  43.  
  44. ...schedule can have slots that are empty, but find someone anyway if
  45.    the message is marked "urgent".
  46.  
  47. ...with programs like procmail, permits you to send certain email
  48.    messages to your pager.
  49.  
  50. ...a list of modems can be given to the daemon.
  51.  
  52. If you aren't using "procmail", you're working too hard.  Check
  53. "archie" for a location near you!
  54.  
  55. ---- Cut Here and unpack ----
  56. #! /bin/sh
  57. # This is a shell archive.  Remove anything before this line, then feed it
  58. # into a shell via "sh file" or similar.  To overwrite existing files,
  59. # type "sh file -c".
  60. # Contents:  README HISTORY ixo.doc ixocico.c tpage.pl tpaged.pl
  61. # Wrapped by kent@sparky on Mon May 17 22:12:19 1993
  62. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  63. echo If this archive is complete, you will see the following message:
  64. echo '          "shar: End of archive 1 (of 2)."'
  65. if test -f 'README' -a "${1}" != "-c" ; then 
  66.   echo shar: Will not clobber existing file \"'README'\"
  67. else
  68.   echo shar: Extracting \"'README'\" \(2321 characters\)
  69.   sed "s/^X//" >'README' <<'END_OF_FILE'
  70. XREADDME -- General information about "tpage"
  71. X  by Tom Limoncelli, tal@warren.mentorg.com 
  72. X  Copyright (c) 1992, Tom Limoncelli
  73. X  The sources can be freely copied for non-commercial use only
  74. X  and only if the source is unmodified.
  75. X
  76. X"tpage" or "Tom's Pager System" is a set of programs that let
  77. Xyou send messages to alpha-numeric pagers using the "IXO" protocol.
  78. XIt supports a dialing directory, a "who's on duty now" schedule,
  79. Xand can do special tricks with RFC822-format email.
  80. X
  81. XThe system has the following features:
  82. X
  83. X...sends pages to any pager system that supports the IXO protocol.
  84. X
  85. X...additional protocols can be added. (I'll write the touch-tone
  86. X      protocol soon).
  87. X
  88. X...can parse email messages and extract the interesting info from
  89. X   them resulting in shorter messages.
  90. X
  91. X...can copy it's input to stdout and therefore can be used as a "tee".
  92. X
  93. X...maintains a directory of people's phone numbers/PINs.
  94. X
  95. X...can page "the person on duty" (searches a schedule).
  96. X
  97. X...schedule can have slots that are empty, but find someone anyway if
  98. X   the message is marked "urgent".
  99. X
  100. X...with programs like procmail, permits you to send certain email
  101. X   messages to your pager.
  102. X
  103. X...a list of modems can be given to the daemon.
  104. X
  105. XHow it works (and how all the programs fit together):
  106. X
  107. Xo  beep2.pl takes command-line and stdin, reads the schedule, looks
  108. X   up people's paging info in the directory, and queues the message.
  109. Xo  tpaged.pl should always be running.  Every 30 seconds it wakes up
  110. X   to check the queue for messages.
  111. Xo  tpaged.pl then sorts and batches the messages.
  112. Xo  tpaged.pl then calls the appropriate program to do the protocol
  113. X   (currently that's ixocico but others can be written) and watches
  114. X   the output for messages:
  115. X   "#MESOK x" which means that message "x" was successful and can be
  116. X       deleted from the queue.
  117. X   "#MESREJECT x" which means that message "x" was rejected and should
  118. X       be deleted from the queue.  Email will be sent to the person
  119. X       that sent the page.
  120. X    Messages that can't be deleted from the queue (due to NFS problems
  121. X       or whatever) are added to a "blacklist" in memory, and are not
  122. X       retried.
  123. X
  124. XFor installation instructions, read INSTALL.
  125. XFor program history, read HISTORY.
  126. X
  127. XIf you aren't using "procmail", you're working too hard.  Check
  128. X"archie" for a location near you!
  129. END_OF_FILE
  130.   if test 2321 -ne `wc -c <'README'`; then
  131.     echo shar: \"'README'\" unpacked with wrong size!
  132.   fi
  133.   # end of 'README'
  134. fi
  135. if test -f 'HISTORY' -a "${1}" != "-c" ; then 
  136.   echo shar: Will not clobber existing file \"'HISTORY'\"
  137. else
  138.   echo shar: Extracting \"'HISTORY'\" \(2174 characters\)
  139.   sed "s/^X//" >'HISTORY' <<'END_OF_FILE'
  140. XHISTORY
  141. X  by Tom Limoncelli, tal@warren.mentorg.com 
  142. X  Copyright (c) 1992, Tom Limoncelli 
  143. X  The sources can be freely copied for non-commercial use only
  144. X  and only if the source is unmodified.
  145. X
  146. XFor installation instructions, read INSTALL.
  147. XFor program history, read HISTORY.
  148. XFor general program information, read README. 
  149. X
  150. X
  151. X(( This program is the successor to "fbeep".
  152. X((
  153. X(( fbeep history:
  154. X((
  155. X((    1.0  Oct 29, 1991 -- First working version.
  156. X((
  157. X((    1.1  Oct 30, 1991 -- Debugging levels implemented (-s -v -vv).
  158. X((
  159. X((    1.2  Oct 30, 1991 -- Cleaned the output, error message on failure.
  160. X((
  161. X((    1.3  Nov 20, 1991 -- Added -m, -M and "null person" options.
  162. X((
  163. X((                         Made -a (abort) replace -w (wait).
  164. X((
  165. X((    1.4  Jan  2, 1992 -- Destination can now be a list of people.
  166. X((
  167. X((     (project ended, replaced by tpage, tpaged, and ixocico)
  168. X
  169. X
  170. Xbeep2.pl:
  171. X    1.0 Jan 31, 1992 -- First release.
  172. X    1.1 Apr 20, 1992 -- Second release.
  173. X    2.0 May  4, 1992 -- Sets umask before creating queue entries.
  174. X
  175. Xtpaged.pl history:
  176. X    1.0 Jan 31, 1992 -- First release.
  177. X    1.1 Apr 20, 1992 -- New "wait before dial" algorithm.
  178. X                        Hunted for "endless-repeat" bug.
  179. X                        Now checks status of unlink.
  180. X    2.0 May  4, 1992 -- Re-wrote data structures of ixo_* routines
  181. X                        to make things more reliable.  No more parallel
  182. X                        arrays, maintains records instead.
  183. X                        Now "blacklists" files that couldn't be deleted.
  184. X2.1 patch 1, May 6, 1992--
  185. X-- &do_protocol_ixo should declare $index as a local variable.
  186. X-- &ixo_listindex should always be called with 1 parameter.
  187. X-- Rejected messages should be deleted from queue directory and from
  188. X   memory.  If they can't be deleted from the directory, blacklist that
  189. X   file.  (previously they were not properly deleted from memory and no
  190. X   attempt was made to remove them from disk)  (LOOPING BUG FIXED)
  191. X-- Debug mode prints a few new messages.
  192. X-- &ixo_mesg_append didn't declare $count as local.
  193. X-- &ixo_mesg_get didn't declare $pin, $error, $mess, $file as local.
  194. X
  195. Xixocico.c
  196. X    1.0 Jan 31, 1992 -- First release.
  197. X    1.1 Feb  4, 1992 -- Tuned some of the time-outs
  198. X    2.0 May  4, 1992 -- Added DJ's 2 patches
  199. X
  200. END_OF_FILE
  201.   if test 2174 -ne `wc -c <'HISTORY'`; then
  202.     echo shar: \"'HISTORY'\" unpacked with wrong size!
  203.   fi
  204.   # end of 'HISTORY'
  205. fi
  206. if test -f 'ixo.doc' -a "${1}" != "-c" ; then 
  207.   echo shar: Will not clobber existing file \"'ixo.doc'\"
  208. else
  209.   echo shar: Extracting \"'ixo.doc'\" \(8737 characters\)
  210.   sed "s/^X//" >'ixo.doc' <<'END_OF_FILE'
  211. XHere is a summary of the IXO protocol.  All typos and
  212. Xmisinterpretations are mine.  This is based on reading the Glenayre
  213. XElectronics book.  I have not yet implemented the automatic protocol,
  214. Xbut I have implemented the manual one.
  215. X
  216. XYou are "remote entry device" and the system that you dial into is
  217. X"paging central".
  218. X
  219. X(1) Remote sends CR at two second intervals until paging central
  220. Xresponds with ID= at correct baud rate or until three transmissions
  221. Xhave been completed.  (This step exists to allow for possible future
  222. Xbaud rate recognition).
  223. X
  224. XSome systems have chosen to send ID= from the central if they do not
  225. Xreceive CR in about two seconds.  This variation appears to be
  226. Xacceptable as the central continues to respond to CR's with ID='s.
  227. X
  228. X(2) Central sends ID=.  Request for ID returned within one second of
  229. Xreceipt of CR.  Paging central may, at its option, send CR or CR LF
  230. Xafter ID=
  231. X
  232. XPaging central may optionally choose to send ID= if it does not
  233. Xreceive CR after appropriate waiting time.
  234. X
  235. XFOR AUTOMATIC REMOTE ENTRY:
  236. XAt this point you can start following 2A or 2M depending on which
  237. Xsub-protocol you want to use.
  238. X
  239. X(2A) Remote sends ESC SST  (typically ESC PG1)
  240. XESC signifies entry device intends to talk in automatic dump mode.
  241. X
  242. XSS is a set of two alphanumeric charactoers signifying a type of
  243. Xservice to be accessed:
  244. X
  245. XFor a paging service where:
  246. XField 1 = Pager ID 
  247. XField 2 = Message
  248. XSS will be sent at "PG".
  249. X
  250. XWhere T is a single alphanumeric character relating to the type of
  251. Xterminal or device attempting to send the message.  T = "1" is a
  252. Xcategory of entry devices using the same protocol.  The PETs and IXO
  253. Xdevices are members of this category.  T = "7" or "8" or "9" are
  254. Xreserved for wild card terminals or devices which may relate to a
  255. Xspecific users' system.
  256. X
  257. X(3A) Remote sends 6-char alphanumeric password then CR.  (Password is
  258. Xoptional and is, in general, reserved for future services.  Password
  259. Xmay be interpreted as either a caller ID or a system entry key.
  260. XLength of password, when used, may be different in some systems.)
  261. X
  262. XWhen an incorrect logon sequence beginning with ESC is received, the
  263. Xpaging central may respond with an ID= if it requires a
  264. Xretransmission.
  265. X
  266. X(4A) Central responds with a message sequence (lines of text, each one
  267. Xfollowed by a CR).  (Typical messages are "Processing - Please Wait",
  268. X"4 pages sent" or "Bad Phone Line Call Again".
  269. X
  270. X(5A) Central then sends one of three sequences:
  271. XCR ACK CR         This means "logon accepted"
  272. XCR NAK CR         This means "requested again"
  273. XCR ESC EOT CR     This means "Forced disconnect"
  274. X
  275. X(6A) Central may then send another message sequence (optional).
  276. X
  277. X(7A) Central sends ESC [p CR
  278. XMessage go ahead is sent when paging central is ready for new
  279. Xinformation.  The "p" is lowercase.
  280. X
  281. X(8A) Remote sends STX field1 CR field2 CR field3 CR ... fieldn CR
  282. XEEE <CHECKSUM> CR
  283. XYou can send as many fields as you wish.  PG terminals send the
  284. Xpager-id in field1 and the message in field2.  (no other fields are
  285. Xdefined).  This entire message can be a max of 250 bytes.  A field may
  286. Xbe any length and where necessary may be continued in following
  287. Xblocks.  A field always ends with a CR.  The CR field delimiter
  288. Xsuggests CR may not be used within a field.  A block always begins
  289. Xwith a STX and ends with a checksum followed by a CR.  The characters
  290. Xpreceding the checksum depends on what, if anything, is continued
  291. Xbeyond the block boundary.
  292. X
  293. XEEE can be an ETX, ETB, or RS.
  294. X
  295. XThe ETX is used as a block termination indicator if a given
  296. Xtransaction (fields 1 through N) ends within the block being
  297. Xtransmitted.
  298. X
  299. XThe ETB is used as a block terminator if the transaction is continued
  300. Xinto the next block, but the last field in the current block is
  301. Xcomplete.
  302. X
  303. XIf the last field within the current block is to be continued in the
  304. Xnext block, no CR is inserted at the end of the first portion of the
  305. Xfield and the US character is used as a block termination character.
  306. XThe CR terminating the broken field is sent at the end of the field in
  307. Xwhatever block the field actually terminates.
  308. X
  309. XNo limit is established within the protocol itself regarding the
  310. Xnumber of transactions, the number of fields or the number of blocks
  311. Xper field; however, a particular user system may have limits on any of
  312. Xthese items.
  313. X
  314. XSome systems may be limited to one block per transaction and one
  315. Xtransaction per telephone connection.
  316. X
  317. XEach checksum is computed by performing the simple arithmetic sum of
  318. X7-bit values of all characters preceding it in that block.  (This
  319. Xmeans that the STX and ETB/ETX are included in the sum).  The checksum
  320. Xis then the least significant 12 bits of this resulting sum.
  321. X
  322. XThe checksum is transmitted as three printable ASCII characters having
  323. Xvalues between HEX 30 and HEX 3F (the characters 0123456789:;<=>?).
  324. XThe most significant four bits of the sum are encoded as the 4 LSB of
  325. Xthe third chacter.  See example.
  326. X
  327. XA normal paging system will have two fields only:
  328. X
  329. XField 1 = Pager ID
  330. X(normally up to eight digits.  May include function and check digit).
  331. X
  332. XField 2 = Message
  333. XField 1 or field 2 may be empty.  For example, when a page is tone only,
  334. Xfield 2 will be empty.  Even when empty, a field is followed by a CR.
  335. XSome systems will reject transactions which have an empty field 2 for
  336. Xa display page or transactions which have an empty field 1.  Other
  337. Xsystems are less restrictive.
  338. X
  339. X(9A) The response to each block is one of four:
  340. XMessage sequence CR ACK CR   = OK, send next block.
  341. XMessage sequence CR NAK CR   = Checksum or transmission error, send last 
  342. X                               block again.
  343. XMessage sequence CR RS CR    = Abandon current transaction and go to next.
  344. X                               RS may occur when the checksum is OK,
  345. X                               but the current transaction violates a
  346. X                               system rule.  At the option of the
  347. X                               system, it may occure in other cases.
  348. XMessage sequence CR ESC EOT CR   = Begin disconnect.
  349. X
  350. XAny of the responses may have an optional message sequence before
  351. Xthem, although the system designer should understand the consequences
  352. Xto the user with all planned entry devices.
  353. X
  354. XIt is expected that many systems will save their message sequence
  355. Xresponses until immediately before disconnect.  For some entry
  356. Xdevices, it may also be desirable that message describing non-checksum
  357. Xerror assocaiated with a particular transaction in a PG service will
  358. Xbegin with the letter ID followed by the contents of field 1 for that
  359. Xtransaction.
  360. X
  361. X(10A) Remote sends EOT CR
  362. XAfter reception of an ACK or RS for the last transaction in a given
  363. Xservice, the entry device sends EOT CR meaning there are no more
  364. Xtransactions remaining in this service.
  365. X
  366. X(11A) Optional: Central sends a message sequence CR.
  367. XAlthough optional, it is highly desirable.
  368. X
  369. X(12A) Central sends RS CR
  370. XAn RS CR should be sent at this point if the paging central finds any
  371. Xdata ACK in previous steps by the system to be unacceptable because of
  372. Xcontent (e.g. invalid pager number or a message field inappropriate
  373. Xfor the type of pages, etc.)
  374. X
  375. X(13A) Central sends ESC EOT CR
  376. Xthen hangs up.
  377. X
  378. X(14A) stop.
  379. X
  380. X(2M) Remote sends M CR (capitol M then CR)
  381. XLack of ESC at beginning of response to ID signifies manual operation
  382. Xwhere applicable.  From here on it's undefined, but most systems ask
  383. X(in English) for the info they need and then return you to step (1).
  384. X
  385. XNOTES:
  386. XBell 103 300bps modem is standard, others may be used.  Protocol is
  387. XASCII with XON, XOFF either direction using 10-bit code (1 start, 7
  388. Xdata, 1 parity, 1 stop) with even parity.
  389. X
  390. XIn the case of delays, the paging central shall wait at least four
  391. Xseconds (eight seconds for (1) (2) (2A)) before disconnecting the
  392. Xremote entry device.  The remote entry device shall wait at least 10
  393. Xseconds for a character from the central before hanging up.
  394. X
  395. XPaging central may, at its option, send CR LF in place of CR at the
  396. Xend of any sequence.
  397. X
  398. XFor inital use of protocol, the paging central shall be equipped to
  399. Xreceive full duplex using a Bell 103 compatible modem at 300 baud.
  400. XOptionally, certain inputs may be capable of receiving 110 baud Bell
  401. X103 full duplex, 300/1200 baud Bell 212 full duplex.  No echo shall be
  402. Xemployed in full duplex mode.  Any attempts at automatic baud rate
  403. Xdetermination shall be within the restraints of the specified
  404. Xprotocol.
  405. X
  406. X
  407. XSAMPLE Checksum Example:
  408. X
  409. XSTX   000 0010
  410. X1     011 0001
  411. X2     011 0010
  412. X3     011 0011
  413. XCR    000 1101
  414. XA     100 0001
  415. XB     100 0010
  416. XC     100 0011
  417. XCR    000 1101
  418. XETX   000 0011
  419. X    10111 1011
  420. X    1   7   ;
  421. X
  422. XSo, the checksum is "17;"
  423. X
  424. XThere is also a really nice flowchart, which I can't reproduce here
  425. Xwithout much too much trouble.  ...and you wouldn't have known it
  426. Xexisted if I hadn't told you. :-)
  427. END_OF_FILE
  428.   if test 8737 -ne `wc -c <'ixo.doc'`; then
  429.     echo shar: \"'ixo.doc'\" unpacked with wrong size!
  430.   fi
  431.   # end of 'ixo.doc'
  432. fi
  433. if test -f 'ixocico.c' -a "${1}" != "-c" ; then 
  434.   echo shar: Will not clobber existing file \"'ixocico.c'\"
  435. else
  436.   echo shar: Extracting \"'ixocico.c'\" \(12894 characters\)
  437.   sed "s/^X//" >'ixocico.c' <<'END_OF_FILE'
  438. X/* ixocico -- IXO protocol call-in call-out.
  439. X**   by Tom Limoncelli, tal@warren.mentorg.com
  440. X**   Copyright (c) 1992, Tom Limoncelli
  441. X**   The sources can be freely copied for non-commercial use only
  442. X**   and only if they are unmodified.
  443. X**
  444. X** Version 2.0 -- See file HISTORY for details.
  445. X**  $Id: ixocico.c,v 1.4 1992/09/22 17:31:01 root Exp $
  446. X
  447. X$Log: ixocico.c,v $
  448. X * Revision 1.4  1992/09/22  17:31:01  root
  449. X * set serial parameters to 300bps, 7e1
  450. X *
  451. X * Revision 1.3  1992/09/21  19:41:31  root
  452. X * made a small change to a comment
  453. X *
  454. X * Revision 1.2  1992/09/21  19:40:22  root
  455. X * changed parity to ODD
  456. X *
  457. X * Revision 1.1  1992/09/21  19:38:02  root
  458. X * Initial revision
  459. X *
  460. X
  461. X*/
  462. X
  463. X/****************************************************************/
  464. X/* USER CONFIGURABLE OPTIONS: */
  465. X
  466. X/* this should be "#define" if you use SunOS, or "#undef" if you
  467. X** use HPUX.  This controls the name of LOCKDIR and if getpriority()
  468. X** is used.  I'm sure more needs to be done, but that's a small start.
  469. X*/
  470. X#define REAL_OS
  471. X
  472. X#ifdef REAL_OS
  473. X#define LOCKDIR    "/var/spool/uucp"
  474. X#else
  475. X#define LOCKDIR "/usr/spool/locks"
  476. X/* That may not be correct */
  477. X#endif
  478. X
  479. X/* not talking to the modem correctly?  Try mucking with
  480. Xthe grabmodem() routine.  */
  481. X
  482. X/* END OF USER CONFIGURABLE OPTIONS */
  483. X/****************************************************************/
  484. X
  485. X#include <stdio.h>
  486. X#include <fcntl.h>
  487. X#include <string.h>
  488. X/* #include <strings.h> */
  489. X#include <ctype.h>
  490. X#include <errno.h>
  491. X#include <sys/termios.h>
  492. X
  493. X#ifdef REAL_OS
  494. X#include <sys/time.h>        /* required for <sys/resource.h> */
  495. X#include <sys/resource.h>    /* required for getpriority() */
  496. X#endif
  497. X
  498. X/* ASCII constants */
  499. X#define STX (2)
  500. X#define EOT (4)
  501. X#define ACK (6)
  502. X#define LF (10)
  503. X#define CR (13)
  504. X#define NAK (21)
  505. X#define ESC (27)
  506. X#define RS (30)
  507. X
  508. X#define MAX_PACKET    (10000)    /* we'll never get a packet this big */
  509. X#define MAXLINE    (1000)
  510. X
  511. X/* only two little global variables, how's that? */
  512. X
  513. Xint modem = 0;
  514. Xchar *lockname = NULL;
  515. X
  516. X/* print a string without worrying about unprintable charactors */
  517. Xvoid 
  518. Xsafeprint(str)
  519. Xchar           *str;
  520. X{
  521. X    while (*str) {
  522. X        if (isgraph(*str))
  523. X            (void) fprintf(stdout, "%c", *str);
  524. X        else {
  525. X            switch (*str) {
  526. X            case LF:
  527. X                (void) fprintf(stdout, "\\n");
  528. X                break;
  529. X            case CR:
  530. X                (void) fprintf(stdout, "\\r");
  531. X                break;
  532. X            case 32:
  533. X                (void) fprintf(stdout, "\\s");
  534. X                break;
  535. X            default:
  536. X                (void) fprintf(stdout, "\\%d", *str);
  537. X                break;
  538. X            }
  539. X        }
  540. X        str++;
  541. X    }
  542. X    fflush(stdout);
  543. X}
  544. X
  545. X/* calculate checksum of a packet */
  546. Xchar *checksum(pk)
  547. Xchar *pk;
  548. X{
  549. X    static char check[10];
  550. X    int sum = 0;
  551. X
  552. X    for (;*pk; pk++) sum += *pk;
  553. X    check[2] = '0' + (sum & 15); sum = sum >> 4;
  554. X    check[1] = '0' + (sum & 15); sum = sum >> 4;
  555. X    check[0] = '0' + (sum & 15);
  556. X    check[3] = 0;
  557. X
  558. Xprintf("CHECKSUM=:"); safeprint(check); printf(":\n");
  559. X    return check;
  560. X}
  561. X
  562. X/* open the modem.  You should have done a lockmodem() first */
  563. Xint grabmodem(dev)
  564. Xchar           *dev;
  565. X{
  566. X    struct termios  ti;
  567. X    int             modem;
  568. X
  569. X    errno = 0;
  570. X    modem = open(dev, O_RDWR, 0);
  571. X    if (errno) {
  572. X        printf("#MODOPEN modem can't be opened\n");
  573. X        return 0;
  574. X    }
  575. X
  576. X    /* set tty params to 300bps, even parity, 7-1-e */
  577. X    errno = 0;
  578. X    ioctl(modem, TCGETS, &ti);
  579. X    if (errno) {
  580. X        close(modem);
  581. X        return 0;
  582. X    }
  583. X    ti.c_iflag |= IGNBRK;   /* ignore breaks */
  584. X    /* ti.c_iflag |= IGNPAR; *//* ignore parity */
  585. X    ti.c_iflag &= ~INPCK;   /* ignore parity errors */
  586. X    ti.c_iflag |= ISTRIP;   /* strip 8th bit */
  587. X    ti.c_iflag &= ~INLCR;   /* don't cr->nl */
  588. X    ti.c_iflag &= ~ICRNL;   /* don't cr->nl */
  589. X    ti.c_iflag &= ~IGNCR;   /* don't ignore cr */
  590. X    /* ti.c_iflag &= ~IXON; *//* don't do xon */
  591. X    /* ti.c_iflag &= ~IXOFF; *//* don't do xoff */
  592. X
  593. X    ti.c_oflag &= ~OPOST;   /* don't post-process */
  594. X
  595. X    ti.c_cflag &= ~CBAUD;
  596. X    ti.c_cflag |= B300;    /* baud=300 */
  597. X    /* ti.c_cflag |= B1200;    /* baud=1200 */
  598. X
  599. X    ti.c_cflag &= ~CSIZE;   /* 7-bit bytes */
  600. X    ti.c_cflag |= CS7;
  601. X/*    ti.c_cflag |= CS8; */
  602. X
  603. X    ti.c_cflag &= ~CSTOPB;  /* one stop bit */
  604. X/*    ti.c_cflag |= CSTOPB;  /* two stop bit */
  605. X
  606. X    ti.c_cflag |= PARENB;      /* parity */
  607. X    ti.c_cflag &= ~PARODD;  /* even parity */
  608. X/*    ti.c_cflag |= PARODD;  /* odd parity */
  609. X
  610. X    ti.c_cflag |= HUPCL;    /* hang up on last close */
  611. X    /* ti.c_cflag |= CRTSCTS; *//* hardware handshaking */
  612. X    ti.c_cc[VMIN] = 0;  /* read() can get as few as 0 bytes */
  613. X    ti.c_cc[VTIME] = 50;    /* time out at 5 seconds no matter what */
  614. X
  615. X    ti.c_lflag &= ~ISIG;    /* disable signals */
  616. X    ti.c_lflag &= ~ICANON;  /* disable signals */
  617. X    ti.c_lflag &= ~ECHO;    /* don't echo */
  618. X
  619. X    errno = 0;
  620. X    ioctl(modem, TCSETS, &ti);
  621. X    if (errno) {
  622. X        close(modem);
  623. X        return 0;
  624. X    }
  625. X    return modem;
  626. X}
  627. X
  628. X/* send data to the modem */
  629. Xvoid send(fd, str)
  630. Xint             fd;
  631. Xchar           *str;
  632. X{
  633. X    printf("Sending: :"); safeprint(str); printf(":\n", str);
  634. X    write(fd, str, strlen(str));
  635. X}
  636. X
  637. X/* wait for a particular string from the modem (err = # of retries permitted ) */
  638. Xint match(mod, str, err) 
  639. X    int mod;
  640. X    char *str;
  641. X    int err;
  642. X{
  643. X    int len;
  644. X    char c;
  645. X
  646. Xprintf("MATCHING on :"); safeprint(str); printf(":\n", str);
  647. X
  648. X    while (1) {
  649. X        c = 0;
  650. X/* printf("waiting for :%c:\n", *str); */
  651. X        len = read(mod, &c, 1);
  652. X        if (len) {
  653. X/* printf("got=%d:%c\n", c,c); */
  654. X            if (c == *str) {
  655. X                str++;
  656. X            }
  657. X            if (!(*str)) break; /* matched all?  Exit loop */
  658. X        }
  659. X        if (!err--) {
  660. X            printf("NOT MATCHED\n");
  661. X            return 1;
  662. X        }
  663. X    }
  664. Xprintf("MATCHED\n");
  665. X    return 0;
  666. X}
  667. X
  668. X/* hang up the modem */
  669. Xvoid 
  670. Xhangup_modem(fd)
  671. Xint             fd;
  672. X{
  673. X    sleep(3);
  674. X    send(fd, "+++");
  675. X    sleep(3);
  676. X    send(fd, "ATH\r");
  677. X    sleep(1);
  678. X}
  679. X
  680. X/* unlock the modem */
  681. Xvoid unlockmodem(name)
  682. Xchar           *name;
  683. X{
  684. X    printf("Unlocking modem.\n");
  685. X    (void)unlink(name);
  686. X    return;
  687. X}
  688. X
  689. X/* clean up and leave this program */
  690. Xvoid bail_out()
  691. X{
  692. X    if (modem) {
  693. X        hangup_modem(modem);
  694. X        close(modem);
  695. X    }
  696. X    if (lockname) unlockmodem(lockname);
  697. X    exit(0);
  698. X}
  699. X
  700. X/* lock the modem OR DIE*/
  701. Xchar *lockmodem(dev)
  702. Xchar           *dev;
  703. X{
  704. X    int lock, pid;
  705. X    int failcnt = 3;
  706. X    char waitcnt = 0;
  707. X    char *lkn, lname[200];
  708. X
  709. X    strcpy(lname, LOCKDIR);
  710. X    strcat(lname, "/LCK..");
  711. X    strcat(lname, 1 + rindex(dev, '/'));
  712. X
  713. Xprintf("Lockfile = %s\n", lname);
  714. X    lkn = strdup(lname);
  715. X    while (failcnt--) {
  716. X        errno = 0;
  717. X        lock = open(lname, O_CREAT | O_WRONLY | O_EXCL, 0777);
  718. X        if (lock == -1) {
  719. X#ifdef REAL_OS
  720. X            printf("Modem is locked, attempting to steal.\n");
  721. X            /* locked, let's read the cookie in the lock */
  722. X            pid = 0;
  723. X            if ((lock = open(lname, O_RDONLY)) != -1) {
  724. X                (void)read(lock, &pid, sizeof(int) );
  725. X                printf("Device is locked by process %d\n", pid);
  726. X                close(lock);
  727. X            }
  728. X            printf("Lock = %d\n", lock);
  729. X            if (pid < 3) {
  730. X                printf("#MODOPEN device is locked by pid < 3\n");
  731. X                bail_out();
  732. X            }
  733. X            /* see if the process still is alive */
  734. X            errno = 0;
  735. X            (void) getpriority(PRIO_PROCESS, pid);
  736. X            if (errno == ESRCH) {   /* lock process dead, let's go! */
  737. X                if (unlink(lname)) {
  738. X                    printf("#MODOPEN Can't steal lock.\n");
  739. X                    bail_out();
  740. X                } else {
  741. X                    printf("Lock is stale, stealing!\n");
  742. X                }
  743. X            } else {
  744. X                printf("#MODOPEN Valid lock in the way.\n");
  745. X                bail_out();
  746. X            }
  747. X#else
  748. X            printf("#MODOPEN it's locked, I'm out of here!\n");
  749. X            bail_out();
  750. X#endif
  751. X        } else {
  752. X            /* lock opened, stuff and go */
  753. X            pid = getpid();
  754. X            write(lock, &pid, sizeof(int));
  755. X            close(lock);
  756. X            break;
  757. X        }
  758. X    }
  759. X    if (failcnt==-1) {
  760. X        printf("#MODOPEN Couldn't lock modem after many tries.\n");
  761. X        bail_out();
  762. X    }
  763. X    return lkn;
  764. X}
  765. X
  766. X/* get a line from stdin OR DIE */
  767. Xchar *getline(line)
  768. Xchar *line;
  769. X{
  770. X    int len;
  771. X    char *r;
  772. X
  773. X    /* get a line, if EOF return 0 */
  774. X    if (!(r = fgets(line, MAXLINE, stdin))) return 0;
  775. X
  776. X    printf("Data in queue=:"); safeprint(line); printf(":\n", line);
  777. X
  778. X    if (!(len = strlen(line))) {
  779. X        printf("#BADQUEUE Blank line in queued data\n");
  780. X        bail_out();
  781. X    }
  782. X
  783. X    if (line[len-1] == '\n') {
  784. X        line[len-1] = 0;
  785. X    } else {
  786. X        /* if fgets didn't return a string ending in \n */
  787. X        printf("#BADQUEUE Data in queue has line too long\n");
  788. X        bail_out();
  789. X    }
  790. X    return r;
  791. X}
  792. X
  793. X/* Loop until you get a valid packet.  If you get a "message sequence" then
  794. Xdisplay it.  If you get an invalid pack DIE */
  795. X
  796. Xvoid getpacket(fd, str)
  797. Xint fd;
  798. Xchar *str;
  799. X{
  800. X    int max;
  801. X    char c;
  802. X    int len;
  803. X    char *buf = str;
  804. X    int err;
  805. X
  806. X    *str = 0;
  807. X    err=50;    /* permit up to 500 message sequences or bad packets */
  808. X    while (err--) {
  809. X        printf("Getting packet\n");
  810. X        max = MAX_PACKET;
  811. X        while (read(fd, &c, 1) == 1) {
  812. X            if (c == LF) continue;    /* skip LF's */
  813. X            if (c == CR) {
  814. X                /* don't actually put CR in the string */
  815. X                break;
  816. X            }
  817. X            *buf++ = c;
  818. X            max--;
  819. X            if (!max) {
  820. X                /* packet was too long */
  821. X                printf("#PACKLEN packet was too long\n");
  822. X                bail_out();
  823. X            }
  824. X        }
  825. X        *buf = 0;
  826. X        len =  buf - str;
  827. X        if (len) {
  828. X            printf("Got packet--length=%d\n", len);
  829. X            if (isgraph(*str)) {
  830. X                printf("#GOTMESSEQ message sequece=:");
  831. X                safeprint(str);
  832. X                printf(":\n");
  833. X                return;        /* ddj 4/30  */
  834. X            } else {
  835. X                /* print packet contents */
  836. X                printf("packet is data=:"); safeprint(str); printf(":013:\n");
  837. X                return;
  838. X            }
  839. X        }
  840. X    }
  841. X    printf("#LONELY It's too quiet in here.  Disconnecting.\n");
  842. X    bail_out();
  843. X}
  844. X
  845. Xint main(argc, argv)
  846. Xint argc;
  847. Xchar *argv[];
  848. X{
  849. X    char dialstring[MAX_PACKET];
  850. X    char *pack = dialstring;    /* use same workspace */
  851. X    char line[MAXLINE+1];
  852. X    char pin[MAXLINE+1];
  853. X    char mesg[MAXLINE+1];
  854. X    int mesnum, failcnt, len;
  855. X    char c;
  856. X
  857. X    /* check arguments */
  858. X    if (argc != 3) {
  859. X        printf("#WRONGARGS wrong number of arguments\n");
  860. X        bail_out();
  861. X    }
  862. X
  863. X    /* lock modem or die */
  864. X    lockname = lockmodem( argv[1] );
  865. X    /* open modem or die */
  866. X    printf("opening modem\n");
  867. X    modem = grabmodem( argv[1] );
  868. X    if (!modem) bail_out();
  869. X
  870. X    /* see if modem is awake or hangup; after 3 tries die */
  871. X    failcnt = 3;
  872. X    while (failcnt--) {
  873. X        send(modem, "AT\r");
  874. X        if (match(modem, "OK", 6)) {
  875. X            printf("No response.  Hang up and try again.\n");
  876. X            hangup_modem(modem);
  877. X            while (read(modem, &c, 1)) { /*DONOTHING*/ };
  878. X        } else break;
  879. X    }
  880. X    if (failcnt==-1) bail_out();
  881. X
  882. Xprintf("dialing\n");
  883. X
  884. X    /* send the "A" of "ATDT" */
  885. X    do {
  886. X        send(modem, "A");
  887. X    } while (match(modem, "A", 2));
  888. X    /* send the rest of the dial string */
  889. X    sprintf( dialstring, "TDT%s\r", argv[2] );
  890. X    send(modem, dialstring);
  891. X    (void) match(modem, argv[2], 10);
  892. X
  893. X    /* wait for the modem to connect */
  894. X    printf("waiting for CONNECT\n");
  895. X    if (match(modem, "CONNECT", 30)) {
  896. X        printf("#NOCONN no connect\n");
  897. X        bail_out();
  898. X    }
  899. X
  900. Xprintf("Waiting for ID=\n");
  901. X
  902. X    /* send a CR ever second until "ID=" comes back */
  903. X    failcnt = 10;
  904. X    /* CRs needed by PacTel   DJ */
  905. X    send(modem, "\r");
  906. X    /* end of DJ */
  907. X    while (failcnt--) {
  908. X        if (match(modem, "ID=", 4)) send(modem, "\r");
  909. X        else break;
  910. X    }
  911. X    if (failcnt==-1) bail_out();
  912. X
  913. Xprintf("Logging in\n");
  914. X
  915. X    failcnt = 3;
  916. X    while (failcnt--) {
  917. X        /* switch to "automatic protocol" */
  918. X        printf("Sending ESC\n");
  919. X        write(modem, "\033", 1);
  920. X        send(modem, "PG1000000\r");
  921. X
  922. X    printf("Waiting for acceptance (aren't we all)\n");
  923. X        getpacket(modem, pack);
  924. X        len = strlen(pack);
  925. X        if ((len==1) && (*pack == ACK)) {
  926. X            printf("login successful\n");
  927. X            break;
  928. X        }
  929. X        if ((len==1) && (*pack == NAK)) {
  930. X            printf("retrying to log in.\n");
  931. X            continue;
  932. X        }
  933. X        if ((len==2) && (*pack == ESC) && (pack[1] == EOT)) {
  934. X            printf("forced disconnect\n");
  935. X            bail_out();
  936. X        }
  937. X        printf("#UNKNOWNPROTO not the response we're looking for.  Disconnecting.\n");
  938. X        bail_out();
  939. X    }
  940. X    if (failcnt==-1) bail_out();
  941. X
  942. Xprintf("waiting for message go ahead\n");
  943. X    failcnt=40;
  944. X    while (failcnt--) {
  945. X        getpacket(modem, pack);
  946. X        if (!strcmp(pack, "\033[p")) break;
  947. X    }
  948. X    if (failcnt==-1) bail_out();
  949. X    printf("got message go-ahead\n");
  950. X
  951. X    for (mesnum=0; getline(pin); ) {
  952. X        getline(mesg);
  953. X        failcnt=100;
  954. X        while (failcnt--) {
  955. X            /* build the packet to be sent */
  956. X            pack[0] = STX;
  957. X            pack[1] = 0;
  958. X            strcat(pack, pin);
  959. X            strcat(pack, "\r");
  960. X            strcat(pack, mesg);
  961. X            strcat(pack, "\r\3");    /* CR then ETX */
  962. X            strcat(pack, checksum(pack));
  963. X            strcat(pack, "\r");
  964. X            send(modem, pack);
  965. X    
  966. X            /* wait for response and deal */
  967. X            printf("waiting for validation\n");
  968. X            getpacket(modem, pack);
  969. X            len = strlen(pack);
  970. X            if ((len==1)&&(*pack==ACK)) {
  971. X                printf("#MESOK %d message xmitted fine\n", mesnum++);
  972. X                break;
  973. X            } else if ((len==1)&&(*pack==NAK)) {
  974. X                printf("re-retrying message %d\n", mesnum);
  975. X            } else if ((len==1)&&(*pack==RS)) {
  976. X                printf("#MESREJECT %d message rejected\n", mesnum++);
  977. X                break;
  978. X            } else if ((len==2)&&(*pack==ESC)&&(*pack==EOT)) {
  979. X                printf("#FORDIS forced disconnect\n");
  980. X                bail_out();
  981. X            }
  982. X        }
  983. X        if (failcnt==-1) {
  984. X            printf("#PROTERR couldn't send packets\n");
  985. X            bail_out();
  986. X        }
  987. X    }
  988. X    printf("#DONE we're done.  Logging out.\n");
  989. X    send(modem, "\4\r");    /* EOT then CR */
  990. X
  991. X    failcnt=3;
  992. X    while (failcnt--) {
  993. X        printf("waiting for system logout\n");
  994. X        getpacket(modem, pack);
  995. X        len=strlen(pack);
  996. X        if ((len==2) && (pack[0] == ESC) && (pack[1] == EOT)) {
  997. X            printf("#BYE asked to hangup\n");
  998. X            bail_out();
  999. X        }
  1000. X        if ((len==1)&&(*pack==RS)) {
  1001. X            printf("#WRONGANY something went wrong. (useless msg)\n");
  1002. X            bail_out();
  1003. X        }
  1004. X/*        if (!stricmp(line, "NO CARRIER")) break; */
  1005. X    }
  1006. X    printf("#BYE we're leaving.\n");
  1007. X    bail_out();
  1008. X}
  1009. X
  1010. X
  1011. END_OF_FILE
  1012.   if test 12894 -ne `wc -c <'ixocico.c'`; then
  1013.     echo shar: \"'ixocico.c'\" unpacked with wrong size!
  1014.   fi
  1015.   # end of 'ixocico.c'
  1016. fi
  1017. if test -f 'tpage.pl' -a "${1}" != "-c" ; then 
  1018.   echo shar: Will not clobber existing file \"'tpage.pl'\"
  1019. else
  1020.   echo shar: Extracting \"'tpage.pl'\" \(10116 characters\)
  1021.   sed "s/^X//" >'tpage.pl' <<'END_OF_FILE'
  1022. X#! /usr/local/bin/perl4.035
  1023. X
  1024. X# tpage.pl -- front-end to tpage system.
  1025. X#   by Tom Limoncelli, tal@warren.mentorg.com
  1026. X#   Copyright (c) 1992, Tom Limoncelli
  1027. X#   The sources can be freely copied for non-commercial use only
  1028. X#   and only if they are unmodified.
  1029. X
  1030. X# $Header: /home/tal/work/beep2/RCS/tpage.pl,v 1.2 1992/09/21 20:11:51 root Exp $
  1031. X
  1032. X# Version 2.0 -- See file HISTORY for details.
  1033. X
  1034. X# $Log: tpage.pl,v $
  1035. X# Revision 1.2  1992/09/21  20:11:51  root
  1036. X# new tr's to remove high bits
  1037. X#
  1038. X# Revision 1.2  1992/09/21  20:11:51  root
  1039. X# new tr's to remove high bits
  1040. X#
  1041. X# Revision 1.1  1992/09/21  20:09:37  root
  1042. X# Initial revision
  1043. X
  1044. X####################################################################
  1045. X#
  1046. X# Parameters that the user can set:
  1047. X#
  1048. X####################################################################
  1049. X
  1050. X$debug = 0;
  1051. X# leave that off
  1052. X
  1053. X$MAX_WINDOW = 16;
  1054. X#This is the number of charactors at a time do you see on your
  1055. X# pager.  This is used when word-wrapping.
  1056. X
  1057. X$MAX_MESSAGE = 110;
  1058. X# How many bytes can one message be.  This must be less than 250
  1059. X# minus the length of your PIN.  This is because each packet in the ixo
  1060. X# protocol must be less than 250 chars.  If you have a pager that can
  1061. X# receive longer messages, you'll have to modify the ixocico.c program
  1062. X# to handle the "packet continuation" feature.  No biggie, just 
  1063. X# something that I didn't feel like implementing since I can't even 
  1064. X# test it with my pager.
  1065. X
  1066. X$DEFAULT_S = '/home/adm/lib/tpage/schedule';
  1067. X# (default: '/home/adm/lib/tpage/schedule')
  1068. X# If you plan on using the schedule feature, this is the file
  1069. X# name where beep2.pl will look for the schedule.  It must be accessable
  1070. X# on the machine that runs tpage.pl, not the machine that runs the
  1071. X# daemon (tpaged.pl).
  1072. X
  1073. X$DEFAULT_T = '/home/adm/lib/tpage/table';
  1074. X# (default: '/home/adm/lib/tpage/table')
  1075. X# If you plan on using the table feature (that is, store a list
  1076. X# of people and their paging info), this is the file name where tpage.pl
  1077. X# will look for the data.  It must be accessable on the machine that
  1078. X# runs tpage.pl, not the machine that runs the daemon (tpaged.pl).
  1079. X
  1080. X$QUEUE_DIR = '/home/adm/lib/tpage/pqueue/';
  1081. X# (default: '/home/adm/lib/tpage/pqueue/'
  1082. X# This is the directory where messages will be queued.  The trailing "/"
  1083. X# is required.
  1084. X
  1085. X####################################################################
  1086. X# some helping functions
  1087. X
  1088. Xrequire("getopts.pl");
  1089. X
  1090. Xsub strip_string {
  1091. X    local($s) = @_;
  1092. Xprint "DEBUG: REMOVE_CONTROLS :", $s, ":\n" if $debug;
  1093. X    $s =~ tr/\200-\377/\000-\177/;    # remove high-bit
  1094. X    $s =~ tr/\000-\037\177//d;    # delete unprintables
  1095. X    $s =~ s/\s+/ /g;            # change groups of white space into " "
  1096. X    $s =~ s/^ //;                # remove spaces from the front
  1097. X    $s =~ s/ $//;                # remove spaces from the end
  1098. X    
  1099. Xprint "DEBUG: REMOVE_DONE :", $s, ":\n" if $debug;
  1100. X    return $s;
  1101. X}
  1102. X
  1103. X####################################################################
  1104. X# Here's the actual program
  1105. X
  1106. X####################################################################
  1107. X# Get the command line options.
  1108. X
  1109. X# set the defaults
  1110. X
  1111. Xprint "\n";
  1112. X
  1113. X# -S  schedule file
  1114. X$opt_S = $DEFAULT_S;
  1115. X# -T  pager table
  1116. X$opt_T = $DEFAULT_T;
  1117. X# -U  use urgent schedule if no one is scheduled for that time.
  1118. X$opt_U = 0;
  1119. X# -d  number to dial. (first name in list only)
  1120. X$opt_d = "";
  1121. X# -p  pager id to use. (first name in list only)
  1122. X$opt_p = "";
  1123. X# -t  tee all stdin into stdout.
  1124. X$opt_t = 0;
  1125. X# -v  verbose mode.
  1126. X$opt_v = 0;
  1127. X# -m  input will be in RFC822, skip boring stuff.
  1128. X$opt_m = 0;
  1129. X# -M  like -m but also skip >-quoted text.
  1130. X$opt_M = 0;
  1131. X# -e  if it errors, send email to this person.
  1132. X$opt_e = "";
  1133. X
  1134. X$line_from = "";
  1135. X$line_subj = "";
  1136. X$line_prio = "";
  1137. X
  1138. Xdo Getopts('S:T:Ud:p:tvmMe:');
  1139. X
  1140. X# get the wholist
  1141. X$opt_wholist = shift (@ARGV);
  1142. X$opt_wholist = "special" if $opt_d && $opt_p;
  1143. X
  1144. X####################################################################
  1145. X# Get the message (either on the command line or stdin; handle -t -m -M
  1146. X
  1147. X# bunch up all the rest
  1148. X$opt_message = join(' ', @ARGV);
  1149. Xprint "opt_message = :$opt_message:\n" if $debug;
  1150. X$opt_message = &strip_string( $opt_message ) if $opt_message;
  1151. Xprint "opt_message = :$opt_message:\n" if $debug;
  1152. Xdie "$0: No message.  Cat got your tongue?" if ( $opt_message eq "" );
  1153. X
  1154. Xdie "$0: Can't use -m/-M and have message on the command line"
  1155. X        if ($opt_m || $opt_M) && $opt_message ne '-';
  1156. X
  1157. X# maybe get message from stdin, echoing to stdout if $opt_t;
  1158. Xif ($opt_message eq '-') {
  1159. X    $opt_message = '';
  1160. X    # handle -m headers first
  1161. X    if ($opt_m) {
  1162. X        print "DEBUG: Doing -m work\n" if $debug;
  1163. X        local($line) = "";
  1164. X        while (<>) {
  1165. X            if ( /^\S/ || /^$/ ) {    # start of new header, do previous one
  1166. X                $line_from = substr($line, 6) if $line =~ /^From/;
  1167. X                $line_subj = substr($line, 9) if $line =~ /^Subject: /;
  1168. X                $line_prio = substr($line, 10) if $line =~ /^Priority: /;
  1169. X                $line = $_;
  1170. X            } else {
  1171. X                $line .= $_;
  1172. X            }
  1173. X            last if /^$/;            # end of headers, start processing
  1174. X        }
  1175. X    }
  1176. X    $line_from = &strip_string( $line_from ) if $line_from;
  1177. X    $line_subj = &strip_string( $line_subj ) if $line_subj;
  1178. X    $line_prio = &strip_string( $line_prio ) if $line_prio;
  1179. X
  1180. X    while (<>) {
  1181. X# -M means skip if the line is news quoted email.
  1182. X# a line is news quoted if it begins with one of the following:
  1183. X#      [white] [word] quote
  1184. X# where "white" is any amount of whitespace (zero or one times)
  1185. X# where word is any letters/numbers (userid) (zero or one times)
  1186. X# where quote is any of >, <, }, or {.
  1187. X        next if $opt_M && /^\s*\S*[\>\}\<\{]/;
  1188. X        print if $opt_t;
  1189. X        $_ = &strip_string( $_ );
  1190. X        $opt_message .= $_;
  1191. X        $opt_message .= " ";
  1192. X        # once we've got quite a bunch, screw the rest.
  1193. X        if ( length($opt_message) > ($MAX_MESSAGE * 8)) {
  1194. X             while (<>) { print if $opt_t; }
  1195. X        }
  1196. X    }
  1197. X}
  1198. X
  1199. X####################################################################
  1200. X# massage the message
  1201. X
  1202. Xif ($debug) {
  1203. X    print "DEBUG: pre-processed messages\n";
  1204. X    print "FROM=:$line_from:\n";
  1205. X    print "PRIO=:$line_prio:\n";
  1206. X    print "SUBJ=:$line_subj:\n";
  1207. X    print "MESS=:$opt_message:\n";
  1208. X}
  1209. X
  1210. X$line_from = substr( "F: " . $line_from . ' ' x $MAX_WINDOW,
  1211. X        0, $MAX_WINDOW) if $line_from;        # pad to display size
  1212. X
  1213. X$line_prio = substr( "P: " . $line_prio . ' ' x $MAX_WINDOW,
  1214. X        0, $MAX_WINDOW) if $line_prio;        # pad to display size
  1215. X
  1216. X$l = $MAX_WINDOW * int ((length($line_subj)+$MAX_WINDOW+2) / $MAX_WINDOW);
  1217. X$line_subj = substr( "S: " . $line_subj . ' ' x $MAX_WINDOW,
  1218. X        0, $l) if $line_subj;        # pad to display size
  1219. X
  1220. X$opt_message = &strip_string( $opt_message );
  1221. X# put it all together
  1222. X$the_message = substr( $line_prio . $line_from . $line_subj . $opt_message, 0, $MAX_MESSAGE - 1);
  1223. X
  1224. Xif ($debug) {
  1225. X    print "DEBUG: post-processed messages\n";
  1226. X    print "FROM=:$line_from:\n";
  1227. X    print "PRIO=:$line_prio:\n";
  1228. X    print "SUBJ=:$line_subj:\n";
  1229. X    print "MESS=:$opt_message:\n";
  1230. X    print "COMPLETE=:$the_message:\n";
  1231. X}
  1232. X
  1233. X####################################################################
  1234. X# At this point we can do some more of the sanity checking.
  1235. X
  1236. X#die "$0: Conflicting verbosity levels" if ($opt_s && ($opt_v || $opt_V));
  1237. Xdie "$0: Schedule file $opt_S can't be read/found"
  1238. X        unless ( ($opt_wholist eq '-') || (-r $opt_S && -r $opt_T) );
  1239. Xdie "$0: Pager table $opt_T can't be read"
  1240. X        unless ($opt_d && $opt_p) || ( -r $opt_T );
  1241. X
  1242. X####################################################################
  1243. X# use the schedule to fill in "who" if we need.
  1244. X
  1245. Xif ($opt_wholist eq '-') {
  1246. X    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  1247. X    local($l) = $wday;
  1248. X    local($h) = $hour * 2 + int ($hour / 30) + 1;
  1249. X    local($w,$found1) = 0;
  1250. X
  1251. Xprint "L = $l\n" if $debug;
  1252. Xprint "H = $h\n" if $debug;
  1253. Xprint "U = $opt_U\n" if $debug;
  1254. X
  1255. X    # Read from schedule until you hit a line beginning with $l.
  1256. X    # At that point, get the char $h bytes in.  If that byte is "-",
  1257. X    # and $opt_U, keep going.
  1258. X    print "\nChecking schedule:\n\n";
  1259. X    open(SCHED, "<$opt_S") || die "Can't open $opt_S: $!";
  1260. X    while (1) {
  1261. X        $w = '';
  1262. X        while (<SCHED>) {
  1263. X            last if /^${l}/;
  1264. X        }
  1265. X        $w = substr($_, $h, 1);
  1266. X        $found1 = 1 if $w;                # we found one!
  1267. X        next if $opt_U && $w eq '-';
  1268. X        last;
  1269. X    }
  1270. X
  1271. X    die "$0: Schedule doesn't have a line for this day of the week.\n" unless $found1;
  1272. X    die "$0: No one is assigned to be on duty at this time.\n" if $w eq '-';
  1273. X
  1274. X    # Now search until a line begins with $w= and assign line to wholist
  1275. X    $opt_wholist = '';
  1276. X    while (<SCHED>) {
  1277. X        next unless /^${w}\=/;
  1278. X        chop( $opt_wholist = substr($_, 2) );
  1279. X    }
  1280. X    die "$0: Schedule error: No people assigned to '" . $w . "'\n" unless $opt_wholist;
  1281. X    close SCHED;
  1282. X}
  1283. X
  1284. X####################################################################
  1285. X# we we still don't know who to call, bail out.
  1286. X
  1287. Xdie "$0: The schedule didn't specify anyone to call!"
  1288. X        unless ($opt_wholist) || ($opt_d && $opt_p);
  1289. Xdie "$0: There isn't anyone scheduled for this time of day."
  1290. X        if $opt_wholist eq '-';
  1291. X
  1292. X####################################################################
  1293. X# rotate through "$opt_wholist" and queue each person
  1294. X
  1295. X$cnt = 0;
  1296. Xforeach $who ( split(',', $opt_wholist) ) {
  1297. X    $cnt++;
  1298. X
  1299. X    # look up "who"'s information
  1300. X    open(TABL, "<$opt_T") || die "Can't open $opt_T: $!";
  1301. X    while (<TABL>) {
  1302. X        next if /^#/;
  1303. X        chop;
  1304. X        local($name,$phonen,$phonea,$pin) = split;
  1305. X        if ($name eq $who) {
  1306. X            $opt_d = $phonea unless $opt_d;    # might have it from ARGV
  1307. X            $opt_p = $pin unless $opt_p;    # might have it from ARGV
  1308. X            print "Got $who is :$opt_d:$opt_p:\n" if $debug;
  1309. X            last;
  1310. X        }
  1311. X    }
  1312. X    close TABL;
  1313. X
  1314. X    die "$0: We were not able to find a phone number for $who.\n" unless $opt_d;
  1315. X    die "$0: We were not able to find a PIN for $who.\n" unless $opt_p;
  1316. X
  1317. X    # write into the queue the proper information.
  1318. X    chop( $thishost = `hostname` );
  1319. X    $qname = $QUEUE_DIR . "P" . $thishost . time . $cnt;
  1320. X    print "QUEUE=$qname\n" if $debug;
  1321. X    local($um) = umask 2;
  1322. X    open(QU, ">$qname" ) || die "Can't open $qname for writing: $!";
  1323. X    umask $um;
  1324. X    print QU "A\n";
  1325. X    print QU $opt_d, "\n";
  1326. X    print QU $opt_p, "\n";
  1327. X    if ($opt_e eq '-') {     # '-' means send errors to $who,
  1328. X        print QU $who, "\n";
  1329. X    } else {
  1330. X        print QU $opt_e, "\n";
  1331. X    }
  1332. X    print QU $the_message, "\n";
  1333. X    print QU "X\n";
  1334. X    close QU;
  1335. X    print "Message queued for $who: $the_message\n";
  1336. X    
  1337. X    # zap the phone# and PIN so that ARGV's info only effects us once.
  1338. X    $opt_d = "";
  1339. X    $opt_p = "";
  1340. X}
  1341. X
  1342. Xprint "\n";
  1343. END_OF_FILE
  1344.   if test 10116 -ne `wc -c <'tpage.pl'`; then
  1345.     echo shar: \"'tpage.pl'\" unpacked with wrong size!
  1346.   fi
  1347.   chmod +x 'tpage.pl'
  1348.   # end of 'tpage.pl'
  1349. fi
  1350. if test -f 'tpaged.pl' -a "${1}" != "-c" ; then 
  1351.   echo shar: Will not clobber existing file \"'tpaged.pl'\"
  1352. else
  1353.   echo shar: Extracting \"'tpaged.pl'\" \(12085 characters\)
  1354.   sed "s/^X//" >'tpaged.pl' <<'END_OF_FILE'
  1355. X#! /usr/local/bin/perl4.019
  1356. X
  1357. X# tpaged -- back-end to tpage system.
  1358. X#   by Tom Limoncelli, tal@warren.mentorg.com
  1359. X#   Copyright (c) 1992, Tom Limoncelli
  1360. X#   The sources can be freely copied for non-commercial use only
  1361. X#   and only if they are unmodified.
  1362. X
  1363. X# Version 2.0 -- See file HISTORY for details. 
  1364. X
  1365. X####################################################################
  1366. X#
  1367. X# Parameters that the user can set:
  1368. X#
  1369. X####################################################################
  1370. X
  1371. X$debug = 0;
  1372. X# $| = 1; open( STDOUT, ">/home/adm/lib/tpage/log.txt" ) if $debug; $| = 1;
  1373. X$QUEUE_DIR = '/home/adm/lib/tpage/pqueue/';        # same as in tpage.pl
  1374. X#$IXOCICO = '/home/tal/work/beep2/ixocico';        # where is ixocico?
  1375. X$IXOCICO  = '/home/adm/lib/tpage/ixocico';        # where is ixocico?
  1376. X$MAIL = '/usr/ucb/mail';                        # which mail to use?
  1377. X     # Recommended mailers:  SunOS & BSD's:  /usr/ucb/mail, AT&T Unix's xmail
  1378. X     # Not recommended mailers:  /bin/mail
  1379. X
  1380. X# list of devices to rotate through.
  1381. X@DEVICES = ( "/dev/ttyz4" );    # currently they are all spoken
  1382. X# to at the same speed and same parameters.  Some day I'll set up
  1383. X# a modemtab system, but I don't think more than one modem is
  1384. X# really needed for this system.
  1385. X
  1386. X# amount of time to sleep between scans of the queue
  1387. X$SLEEP_TIME =  150;    # 2.5 minutes
  1388. X$SLEEP_TIME =  10 if $debug;    # smaller when I'm debugging
  1389. X# Small amount of time to wait between finding anything in the queue
  1390. X# and doing a real scan of the queue.
  1391. X$MULT_SLEEP_TIME =  10;
  1392. X
  1393. X####################################################################
  1394. X# QUEUE FILES FORMAT:
  1395. X#
  1396. X# Files in the queue have the name of the format in the
  1397. X# first line.  Currently there is only one format and it
  1398. X# is named "A".  The first line marks it as the "A" format.
  1399. X# a subroutine called read_format_A reads this format.  Other
  1400. X# formats can be written (see comments by read_format_A)
  1401. X#
  1402. X# The "A" format:
  1403. X# line  contents
  1404. X#    1: A\n
  1405. X#    2: number to dial\n
  1406. X#    3: pin\n
  1407. X#    4: entire message\n
  1408. X#    5: X\n
  1409. X
  1410. X# read_format_*  -- modules that read various data formats.
  1411. X#                   Currently implemented: The "A" format.
  1412. X# do_proto_*     -- modules that do various beeper protocols.
  1413. X#                   Currently implmented: the ixo protocol.
  1414. X#                   Future protocols:     numeric-only pagers.
  1415. X
  1416. X####################################################################
  1417. X# Here's the actual program
  1418. X
  1419. X# define some globals
  1420. X
  1421. Xlocal(%protocols);
  1422. X
  1423. Xwhile (1) {
  1424. X    local ($first, @allfiles, @anyfiles);
  1425. X
  1426. X    # We could scoop up all the files and process them, but chances
  1427. X    # are if one file is found, more are on the way.  So, instead
  1428. X    # we scoop, if any are found we sleep 5 seconds and re-scoop.
  1429. X
  1430. X    # wait for any files to appear.
  1431. X    while (1) {
  1432. X        @anyfiles = &scan_queue;
  1433. X        print "DEBUG: anyfiles= ", join(' ', @anyfiles), "\n" if $debug;
  1434. X
  1435. X        if ($#anyfiles!=-1) {    # files?  take a rest and then process.
  1436. X            sleep $MULT_SLEEP_TIME unless $debug;
  1437. X            last;
  1438. X        } else {            # no files?  hibernate.
  1439. X            sleep $SLEEP_TIME;
  1440. X            next;
  1441. X        }
  1442. X    }
  1443. X
  1444. X    # re-get the files in the queue
  1445. X    @allfiles = &scan_queue;
  1446. X    print "DEBUG: allfiles= ", join(' ', @allfiles), "\n" if $debug;
  1447. X
  1448. X    # get all the data out of the queue'd files.
  1449. X    foreach $file (@allfiles) {
  1450. X        print "DEBUG: Doing $file\n" if $debug;
  1451. X        open(DATA, "<" . $QUEUE_DIR . $file) || print "Can't open $file: $!";
  1452. X        chop( $first = <DATA> );
  1453. Xprint "DEBUG: first=$first\n" if $debug;
  1454. X        eval "do read_format_$first()";
  1455. X    }
  1456. X
  1457. X    # process all the extracted data (do_protocol_* should delete the files)
  1458. X    foreach $proto (keys %protocols) {
  1459. X        eval "do do_protocol_$proto()";
  1460. X        delete $protocols{ $proto };
  1461. X        sleep $SLEEP_TIME;
  1462. X    }
  1463. X}
  1464. X
  1465. X# scan the queue for entries (avoid "blacklisted" files)
  1466. Xsub scan_queue {
  1467. X    local(@files);
  1468. X    # scan the directory for "P files (pager files)
  1469. X    opendir(QDIR, $QUEUE_DIR) || die "$0: Can't open $QUEUE_DIR: $!";
  1470. X    @files = grep( /^P/, readdir(QDIR) );
  1471. X    closedir(QDIR);
  1472. X    print "DEBUG: filescan= ", join(' ', @files), "\n" if $debug;
  1473. X    # remove the blacklisted files
  1474. X    @files = grep( ! defined $blacklist_data{ $_ }, @files);
  1475. X    print "DEBUG: goodfiles= ", join(' ', @files), "\n" if $debug;
  1476. X    # return the files
  1477. X    @files;
  1478. X}
  1479. X
  1480. X# blacklist a file in the queue (couldn't delete it for some reason
  1481. X# and we don't want to repeat it)
  1482. Xsub blacklist {
  1483. X    local($file) = @_;
  1484. X    $blacklist_data{ $file } = 1;
  1485. X}
  1486. X
  1487. X# Each read_format_ must:
  1488. X#  read from <DATA> and then close(DATA).
  1489. X#  %protocols{ protocol name } = 1 (for the protocol to use)
  1490. X#  and stuff the right data into the right variables for that protocol
  1491. X#  to use.
  1492. X
  1493. Xsub read_format_A
  1494. X{
  1495. X    local($dial,$pin,$error,$mess,$X);    # $file is by sideeffect
  1496. X    print "DEBUG: reading format A\n" if $debug;
  1497. X    chop( $dial = <DATA> );
  1498. X    chop( $pin = <DATA> ); 
  1499. X    chop( $error = <DATA> );
  1500. X    chop( $mess = <DATA> );
  1501. X    chop( $X = <DATA> );
  1502. X
  1503. X    return if $X ne "X";  # file isn't in correct format or isn't done.
  1504. X    return if $dial eq "";
  1505. X    return if $pin eq "";
  1506. X    return if $mess eq "";
  1507. X
  1508. X    $protocols{ 'ixo' } = 1;
  1509. X    &ixo_mesg_append( $dial, $pin, $error, $mess, $file );
  1510. X}
  1511. X
  1512. X# Each do_protocol_ must:
  1513. X#  delete files out of the queue that are successful.
  1514. X#  delete files out of the queue that are aged.
  1515. X#  clean up so that the routine can be called again.
  1516. X
  1517. Xsub do_protocol_ixo {
  1518. X    print "DEBUG: doing protocol IXO\n" if $debug;
  1519. X    local($pin, $error, $mess, $file, $cmd, $status, $index);
  1520. X    local($general_reject, $general_error_message);
  1521. X    # build the temp file and the command line
  1522. X    local($tmpfile) = "/tmp/tpaged.$$";
  1523. X    foreach $dial ( &ixo_listphones ) {
  1524. X        print "DEBUG: Number to dial is $dial\n" if $debug;
  1525. X
  1526. X        # fill the data file
  1527. X        open(IX, ">$tmpfile" ) || die "$0: Can't create $tmpfile: $!";
  1528. X        foreach $index ( &ixo_listindexes( $dial ) ) {
  1529. X            ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index );
  1530. X            # put it in the file for ixocico to use
  1531. X            print IX "$pin\n$mess\n";
  1532. X        }
  1533. X        close IX;
  1534. X
  1535. X        print "DEBUG: messages to send", &ixo_listindexes( $dial ), "\n" if $debug;
  1536. X
  1537. X        $general_reject = 1;    # when done, 1=cancel remaining; 0=retry remaining
  1538. X        $general_error_message = "SHOULD NOT HAPPEN";    # if all messages are cancelled
  1539. X
  1540. X        $cmd = $IXOCICO . " <" . $tmpfile . " "
  1541. X            . push(@DEVICES, shift @DEVICES) . " " . $dial;
  1542. X        print "DEBUG: About to execute: $cmd\n" if $debug;
  1543. X        open(IX, $cmd . "|") || die "$0: Can't execute ixocico: $!";
  1544. X
  1545. X        while (<IX>) {
  1546. X            print if $debug;
  1547. X            next unless /^#/;
  1548. X
  1549. X            print unless $debug;
  1550. X
  1551. X            /^#WRONGARGS / &&
  1552. X                die("$0: Major program bug: $!");
  1553. X            /^#NOCONN / && do {
  1554. X                printf("$0: Nobody answered the phone!\n") if $debug;
  1555. X                $general_reject = 0;
  1556. X                last;
  1557. X            };
  1558. X            /^#UNKNOWNPROTO / && do {
  1559. X                print "$0: Uhhh, are you sure that's a pager service?\n" if $debug;
  1560. X                $general_reject = 1;
  1561. X                $general_error_message = "other end using different protocol";
  1562. X                last;
  1563. X            };
  1564. X            /^\#MESOK (\d) / && do {
  1565. X                $index = $1;
  1566. X                print "DEBUG: message $index done.\n" if $debug;
  1567. X
  1568. X                ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index );
  1569. X                print "DEBUG: ERROR=$error; FILE=$file\n" if $debug;
  1570. X
  1571. X                print "DEBUG: unlinking " . $QUEUE_DIR . $file . "\n" if $debug;
  1572. X                $status = unlink $QUEUE_DIR . $file;
  1573. X                print "DEBUG: unlink status=$status; $!\n" if $debug;
  1574. X                &blacklist( $file) unless $status;
  1575. X
  1576. X                # remove from queue
  1577. X                &ixo_mesg_delete( $dial, $index );
  1578. X            };
  1579. X            /^#MESREJECT (\d) / && do {        # very similar to #MESOK
  1580. X                $index = $1;
  1581. X                print "DEBUG: message $index rejected.\n" if $debug;
  1582. X
  1583. X                ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index );
  1584. X                print "DEBUG: ERROR=$error; FILE=$file\n" if $debug;
  1585. X
  1586. X                # notify anyone that wants to know about failures
  1587. X                if ($error + 0) {
  1588. X                     $cmd = "$MAIL <"
  1589. X                     . $QUEUE_DIR . $file
  1590. X                     . " -s 'TPAGE_MESSAGE: request rejected by service' "
  1591. X                     . $error;
  1592. X                    print "DEBUG: About to execute $cmd\n" if $debug;
  1593. X                    system $cmd;
  1594. X                }
  1595. X
  1596. X                print "DEBUG: unlinking " . $QUEUE_DIR . $file . "\n" if $debug;
  1597. X                $status = unlink $QUEUE_DIR . $file;
  1598. X                print "DEBUG: unlink status=$status; $!\n" if $debug;
  1599. X                &blacklist( $file) unless $status;
  1600. X
  1601. X                # remove from queue
  1602. X                &ixo_mesg_delete( $dial, $index );
  1603. X            };
  1604. X            /^#FORDIS / && do {
  1605. X                print "Forced disconnect from server.\n" if $debug;
  1606. X                $general_reject = 1;
  1607. X                $general_error_message = "other end requesting disconnect";
  1608. X                last;
  1609. X            };
  1610. X            /^#PROTERR / && do {
  1611. X                print "Server not following protocol.\n" if $debug;
  1612. X                $general_reject = 1;
  1613. X                $general_error_message = "other end having a protocol error";
  1614. X                last;
  1615. X            };
  1616. X            ( /^#DONE / || /#BYE / ) && do {
  1617. X                print "Done with sending batch.  Waiting BYE.\n" if $debug;
  1618. X                $general_reject = 0;
  1619. X                $general_error_message = "been told we're done but weren't".
  1620. X                next;
  1621. X            };
  1622. X            /^#WRONGANY / && do {
  1623. X                print "We've been notified that one of the batch may have been not xmited.\n(great protocol, eh?)\n" if $debug;
  1624. X                next;
  1625. X            };
  1626. X            /^#BADQUEUE / && do {
  1627. X                die "$0: Programmer error.  Data in queue is bad: $_\n";
  1628. X            };
  1629. X            /^#MODOPEN / && do {
  1630. X                print "Modem can't be opened\n" if $debug;
  1631. X                $general_reject = 0;
  1632. X                last;
  1633. X            };
  1634. X            /^#PACKLEN / && do {
  1635. X                die "$0: Protocol error.  Should never happen: $_\n";
  1636. X            };
  1637. X            /^#GOTMESSEQ / && do {
  1638. X                print "MESSAGE: $_\n" if $debug;
  1639. X            };
  1640. X            /^#LONELY / && do {
  1641. X                print "Hello?  Hello?  Either I'm getting the silent treatment or the server is dead." if $debug;
  1642. X                $general_reject = 0;
  1643. X                last;
  1644. X            };
  1645. X        }
  1646. X        close IX;
  1647. X        unlink $tmpfile;
  1648. X
  1649. X        print "DEBUG: rejecting remaining messages\n" if $debug;
  1650. X        # now reject remaining messages
  1651. X        foreach $index ( &ixo_listindexes( $dial) ) {
  1652. X            # if general_reject then we have work to do
  1653. X            if ($general_reject) {
  1654. X                print "DEBUG: removing $dial:$index\n" if $debug;
  1655. X                ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index );
  1656. X                ###### mail a warning
  1657. X                if ($error + 0) {
  1658. X                     $cmd = "$MAIL <"
  1659. X                     . $QUEUE_DIR . $file
  1660. X                     . " -s 'TPAGE_MESSAGE: unprocessed message deleted due to "
  1661. X                     . $general_error_message . "' "
  1662. X                     . $error;
  1663. X                    print "DEBUG: About to execute $cmd\n" if $debug;
  1664. X                    system $cmd;
  1665. X                }
  1666. X                ###### make sure it gets deleted
  1667. X                print "DEBUG: unlinking (leftover) " . $QUEUE_DIR . $file . "\n" if $debug;
  1668. X                $status = unlink $QUEUE_DIR . $file;
  1669. X                print "DEBUG: unlink status=$status; $!\n" if $debug;
  1670. X                &blacklist( $file) unless $status;
  1671. X            }
  1672. X            print "DEBUG: deleting from memory $dial:$index\n" if $debug;
  1673. X            # delete it from the ixo list
  1674. X            &ixo_mesg_delete( $dial, $index );
  1675. X        }
  1676. X        # at this point %ixo_data should be empty
  1677. X        &ixo_end_asserts;
  1678. X
  1679. X
  1680. X    # now do the next phone number
  1681. X    }
  1682. X}
  1683. X
  1684. Xsub ixo_end_asserts {
  1685. X    # test a couple assertions
  1686. X    print "DEBUG: testing assertions\n" if $debug;
  1687. X
  1688. X    # $ixo_count{ $dial } should be zero
  1689. X    die "$0: bug1\n" if $ixo_count{ $dial };
  1690. X
  1691. X    # %ixo_data should be empty at this point
  1692. X    die "$0: bug2\n" if grep(1,keys %ixo_data);    # fast key counter
  1693. X}
  1694. X
  1695. Xsub ixo_mesg_append {
  1696. X    local($dial, $pin, $error, $mess, $file, $count) = @_;
  1697. X    print "APPEND: dial=$dial pin=$pin error=$error file=$file mess=$mess\n" if $debug;
  1698. X    $count = 0 + $ixo_count{ $dial }++;
  1699. X    $ixo_data{ "$dial:$count" } = "$pin\n$error\n$mess\n$file";
  1700. X    print "APPEND: data=", $ixo_data{ "$dial:$count" }, "\n" if $debug;
  1701. X}
  1702. X
  1703. Xsub ixo_mesg_get {
  1704. X    local($dial, $index) = @_;
  1705. X    local($pin, $error, $mess, $file, @list);
  1706. X    print "GET: dial=$dial index=$index\n" if $debug;
  1707. X    @list = split( '\n', $ixo_data{ "$dial:$index" } );
  1708. X    ($pin, $error, $mess, $file) = @list;
  1709. X    print "GET: pin=$pin error=$error file=$file mess=$mess\n" if $debug;
  1710. X    @list;
  1711. X}
  1712. X
  1713. Xsub ixo_mesg_delete {
  1714. X    local($dial, $index) = @_;
  1715. X    print "DELETE: dial=$dial, index=$index\n" if $debug;
  1716. X    delete $ixo_data{ "$dial:$index" };
  1717. X    $ixo_count{ $dial }--;
  1718. X}
  1719. X
  1720. Xsub ixo_listindexes {
  1721. X    local($dial) = @_;
  1722. X
  1723. X    # gather and sort the second field
  1724. X    sort grep( s/^$dial:(.+)/$1/, keys %ixo_data );
  1725. X}
  1726. X
  1727. Xsub ixo_listphones {
  1728. X    local(@list);
  1729. X    local($l) = undef;
  1730. X
  1731. X    # gather and sort the first field.
  1732. X    @list = sort grep( s/^(.+):.+$/$1/, keys %ixo_data );
  1733. X    # uniq them
  1734. X    @list = grep (!($_ eq $l || ($l = $_, 0)), @list );
  1735. X    # return them
  1736. X    @list;
  1737. X}
  1738. END_OF_FILE
  1739.   if test 12085 -ne `wc -c <'tpaged.pl'`; then
  1740.     echo shar: \"'tpaged.pl'\" unpacked with wrong size!
  1741.   fi
  1742.   chmod +x 'tpaged.pl'
  1743.   # end of 'tpaged.pl'
  1744. fi
  1745. echo shar: End of archive 1 \(of 2\).
  1746. cp /dev/null ark1isdone
  1747. MISSING=""
  1748. for I in 1 2 ; do
  1749.     if test ! -f ark${I}isdone ; then
  1750.     MISSING="${MISSING} ${I}"
  1751.     fi
  1752. done
  1753. if test "${MISSING}" = "" ; then
  1754.     echo You have unpacked both archives.
  1755.     rm -f ark[1-9]isdone
  1756. else
  1757.     echo You still must unpack the following archives:
  1758.     echo "        " ${MISSING}
  1759. fi
  1760. exit 0
  1761. exit 0 # Just in case...
  1762.