home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / unix_c / utils / bbs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-21  |  35.0 KB  |  1,488 lines

  1. Article 6 of alt.sources:
  2. Path: mit-eddie!husc6!labrea!decwrl!mejac!hoptoad!ncoast!devon!stb!michael
  3. From: michael@stb.UUCP (Michael)
  4. Newsgroups: alt.sources
  5. Subject: Bulletin board program
  6. Message-ID: <1644@stb.UUCP>
  7. Date: 15 Jul 87 06:42:23 GMT
  8. Reply-To: michael@stb.UUCP (Michael)
  9. Distribution: alt
  10. Organization: STB BBS, La, Ca, USA, 90402, (213) 459-7231
  11. Lines: 1473
  12.  
  13. This is a bulletin board system, a program designed to let people read and leave
  14. messages and electronic mail. It is not as powerful as news nor /bin/mail;
  15. it was originally written for an 8 bit micro (in basic), and roughly translated
  16. over time. It is relatively bug free (however, the chat command was never
  17. implemented), will work with multiple users at once, and is "completely secure"
  18. -- after losing directories when I ran it as root (unlink ("") did wonders in
  19. V7) I made sure it was secure enough to be a login "shell" that does not need
  20. a password, and still usable by regular users. If you want to see it in action,
  21. give me a call (# is in header, PC-pursuit will work, 3/12/2400)
  22.  
  23. I have it use the directory /bbs (not /usr/lib/bbs) for personal reasons; feel
  24. free to change it.
  25.  
  26. (Oh yes-- the code comments make reference to a tree structured message base.
  27. It never got implemented.)
  28. This is not a shar; there are several lines with CUT in them.
  29. PAcking list: /bbs/help userlog.h fixbbs.c logsort.c bbs.c
  30. --- Lights, camera, help file --- (/bbs/help)
  31. Rough draft of help file
  32. B: read bulletin
  33. C: Chat (not working)
  34. F: Leave feedback
  35. G: goodbye, hangup
  36. H: This file
  37. I: System information
  38. L: Leave message
  39. M: Mail user
  40. O: Other systems
  41. Q: Quit (hangup, don't update auto-reads)
  42. R: Read
  43. U: Userlog
  44. X: eXpert mode (this call only)
  45. Y: Return to system
  46. Z: Logon again
  47. ZS: same as Y
  48. ?: Same as H
  49.  
  50. Note: if a second word is given on L, M, and F commands, the system will prompt
  51. before each line (for automated uploads)
  52. --- He hacked him to bits with his CUTlass --- (userlog.h)
  53.  
  54. /* sys/types.h and sys/dir.h MUST be included already */
  55.  
  56. struct userlog {
  57.     char    name[DIRSIZ+1];
  58.     char    password[9];
  59.     long    lastread[6];        /* allow enough for 5 boards */
  60.     char    extra[7];        /* size=64 bytes after alignment */
  61.     time_t    lastcall;
  62.     short    numcalls;
  63.     char    lastbyte;
  64.     };
  65.  
  66. typedef    struct userlog ulog;
  67. --- CUT through the trees over there --- (fixbbs.c)
  68. /* @(#)bbsfix.c    1.2 7/15/87 06:19:41 ( 10/5/86 15:26:56 ) */
  69. # include <stdio.h>
  70. extern int errno;
  71.  
  72. /* 1.2 reduced the amount of output (no longer copy board to output) */
  73. /* Fix an index file if it gets clobbered. Only 200 messages (unless you
  74. change the constants */
  75.  
  76. main(argc, argv)
  77. char *argv[];
  78. {
  79.     int fidx, msg;
  80.     FILE *fdat;
  81.     char sidx[200], sdat[200];
  82.     if (argc != 3) {printf("Useage: fixbbs <boardname> <lowmsg>. Only one board fixed per call\n "); exit(1); }
  83.     umask (0002);        /* Only turn off world write */
  84.     sprintf (sidx, "/bbs/idx/%s", argv[1]);
  85.     sprintf (sdat, "/bbs/dat/%s", argv[1]);
  86.     msg=0; sscanf (argv[2], "%d", &msg);
  87.     if (msg==0) die ("Can't read message number");
  88.     fidx = creat (sidx, 0666);
  89.     if (fidx == -1) die ("Index file?!?!?!?");
  90.     fdat = fopen (sdat, "r");
  91.     if (fdat == NULL) die ("Data file ?!?!?!?!!");
  92.     fixit (fdat, fidx, msg);
  93. }
  94.  
  95. die(s)
  96. char *s;
  97. {
  98.     printf ("Death: %s %d\n", s, errno);
  99.     abort();
  100. }
  101.  
  102. mywrite (idx, addr)
  103. long addr;
  104. {
  105.     write (idx, &addr, sizeof addr);
  106. }
  107.  
  108. findmsg (data)
  109. FILE *data;
  110. {
  111.     int x;
  112.     while ((x=getc(data)) != '\001' && x != EOF) ;
  113.     if ((x=ungetc(getc(data), data)) != EOF) return;
  114.     printf("EOF Found\n");
  115. }
  116.  
  117. fixit (data, idx, lowmsg)
  118. FILE * data;
  119. {
  120.     int cnt=lowmsg;
  121.     mywrite (idx, lowmsg);
  122.     if (empty(idx)) return;
  123.     mywrite (idx, 0L);        /* first message is at 0 */
  124.     while (!feof(data))
  125.     {    printf("Message %d found at %d\n", cnt++, ftell(data) );
  126.         findmsg(data);
  127.         if (!feof(data)) mywrite (idx, ftell(data));
  128.         else printf("Final message, terminating \n");
  129.     };
  130. }
  131.  
  132. empty(fd)
  133. {
  134.     return (! lseek(fd, 0L, 2)); /* lseek=0 if empty */
  135. }
  136. --- "Enough of the CUTe cut lines" --- (logsort.c)
  137. /* @(#)logsort.c    1.1 7/15/87 06:23:32 ( 10/2/86 18:51:59 ) */
  138. /* Log file sort routine for stb. */
  139.  
  140. # define NUMLOGS    250        /* Maximun # of userlog entries */
  141. # include <stdio.h>
  142. # include <sys/types.h>
  143. # include <sys/dir.h>
  144. # include <local/userlog.h>
  145. # include <sys/locking.h>
  146.  
  147. ulog *logs;
  148. int number, fd;
  149. FILE *fp;
  150.  
  151. main()
  152. {
  153.     readit();
  154.     sortit();
  155.     writeit();
  156. }
  157.  
  158. comp_log(p1, p2)
  159. ulog *p1, *p2;
  160. {
  161.     return (p2->lastcall - p1->lastcall);    /* if = calls, sort by time */
  162.     /* NOTREACHED */            /* old code to sort by calls */
  163.     if (p1->numcalls < p2->numcalls)    /* return larger calls */
  164.         return 1;
  165.     if (p1->numcalls > p2->numcalls)
  166.         return -1;
  167. }
  168.     
  169. sortit()
  170. {
  171.     qsort(logs, number, sizeof (*logs), comp_log);
  172. }
  173.  
  174. readit()
  175. {
  176.     fd=open("/bbs/userlog",2);
  177.     fp=fdopen(fd,"r+");
  178.     if (fp==NULL)
  179.     {
  180.         perror("log_sort: /bbs/userlog");
  181.         abort();
  182.     }
  183.     locking (fileno(fp), LK_LOCK, 0);
  184.     logs=(ulog *) calloc (NUMLOGS, sizeof (*logs));
  185.     number=fread(logs, sizeof (*logs), NUMLOGS, fp);
  186. }
  187.  
  188. writeit()
  189. {
  190.     rewind(fp);
  191.     fwrite(logs, sizeof (*logs), number, fp);
  192. }
  193. --- "CUT!" yelled the director. --- (bbs.c)
  194.  
  195. /* @(#)stb.c    3.25 7/15/87 06:13:39 ( 2/1/87 20:34:27 ) */
  196.  
  197. #define VERSION 3
  198. #define FIX 25
  199. #define STB_UID 13
  200. #define STB_GID 13
  201. #define OUTBUF 20        /* output buffering quantity, tunable */
  202. #define crypt(string, salt)    string  /* No encryption */
  203. #define alarm(time)        /* 1.1 No more hangup detection */
  204. /* Serial Tree Board, ver 4.0 (basic algorythm), 0.3 (C conversion) */
  205. /* similarities in algorthm between C and basic are rapidly disapearing, and
  206.     the only similarity left seems to be the basic structure of the
  207.     program. */
  208. /* Changes:
  209.     he_left now updates the userlog at every logon (not after final hangup)
  210.     hangup signal is not trapped; o/s kills the job resulting in effective
  211.         quit command.
  212. */
  213.  
  214. /* Version 3. Minor fixes, improvements. Speed up output,
  215.     anything else I think of */
  216.  
  217. /* Version 2 Get the editor working correctly */
  218.  
  219. /* 1.6 strchr/strrchr for index/rindex */
  220. /* 1.1 Message read is now by message number. */
  221.  
  222. /* .8 Final (?) ctrl-c/k fix:
  223.     cbreak/cooked maintain a count of state;
  224.     cbreak also disables echo, cooked re-enables.
  225.  
  226.     Where to put a <cr> out for ctrl-c/k? FNStop?
  227. */
  228.  
  229. /* .7 fix ctrl-c/k that .6 destroyed.
  230.     Fixed by switching between cbreak and cooked. */
  231. /* .6 changes: Will remove ALL calls to signal (except for sighup) */
  232.  
  233. /* .3 changes: Will use better signal operations (actually turn them
  234.     on/off) and other things. Goal: Make it look done. This includes
  235.     the bbs answering the phone, with getty never seeing the line
  236.     (minor change to /etc/default/newgetty).
  237.  
  238.     New command: Y, myexits program without hanging up (re-spawn getty)
  239.     EOF in message now means end of message, not hang up.
  240.  
  241.     Tree might get implemented one of these days.
  242.  */
  243.  
  244. /* Tree implementation details:
  245. /bin/mkdir is used to create a directory whose name is the title, which has
  246. .text    text of message
  247. dir1    child message 1
  248. dir2    child message 2
  249. etc. The root is in /bbs/tree/root, and should be on a mountable file system
  250.     so it doesn't gobble up root space. /bbs/idx/tree has the following:
  251. msg # [7]
  252. parent # [7]
  253. left # [7]
  254. right # [7]
  255. child # [7]
  256. title [15]
  257. date [11]
  258. total record space: 61. Fields are padded with spaces on the right on disk,
  259. with the line terminated by a newline. Msg titles need not be distinct, but
  260. only the first of duplicates will be found.  Numbers are in ascii. All the data
  261. is on one line. Number first, parent second, title third, date forth.
  262. Read/written with scanf/printf, not read/write.
  263. */
  264.  
  265. /* Serial format: Index files are arrays of longs. [0]=lowest number message,
  266.     sequentially up from there. */
  267.  
  268. # include <stdio.h>
  269. # include <errno.h>
  270. # include <sgtty.h>
  271. # include <fcntl.h>
  272. # include <signal.h>
  273. # include <assert.h>
  274. # include <ctype.h>
  275. # include <sys/types.h>
  276. # include <sys/locking.h>
  277. # include <sys/stat.h>
  278. # include <sys/dir.h>
  279. # include <local/userlog.h>
  280.  
  281. #if defined(LK_UNLCK) && !defined(LK_UNLK)    /* sys 3 spelling change */
  282. #  define LK_UNLK LK_UNLCK
  283. #else
  284. #  define LK_UNLCK LK_UNLK
  285. #endif
  286.  
  287. #if defined(SYS3) || defined (M_SYS3) || defined (ATT) || defined (SYS5)
  288. #   define index strchr
  289. #   define rindex strrchr
  290. #endif
  291.  
  292. # define    FALSE        0
  293.  
  294. #ifdef lint
  295. # define    TRUE        1
  296. #else
  297. # define    TRUE        !FALSE
  298. #endif
  299.  
  300. # define    CTRL_K        1
  301. # define    CTRL_C        2
  302. # define    UPDATE        TRUE
  303. # define    NUPDATE        FALSE
  304. # define    LINESIZE    257    /* most that can be typed +1 */
  305. # define    WAIT_TIME    10*60
  306.                     /* hangup assumed after 10 minutes */
  307.  
  308. struct lines {
  309.     struct lines    *next;        /* pointer to next line */
  310.     char    text[LINESIZE];    /* define line for input text */
  311.     };
  312.  
  313. # define    NUMSIZE        7
  314. # define    DATESIZE    11
  315.  
  316. struct treejunk {
  317.     char    t_msgnum[NUMSIZE];
  318.     char    t_parent[NUMSIZE];
  319.     char    t_left[NUMSIZE];
  320.     char    t_right[NUMSIZE];
  321.     char    t_child[NUMSIZE];
  322.     char    t_date[DATESIZE];
  323.     char    t_title[DIRSIZ+1];
  324. };
  325.  
  326. # define    TREESIZE    NUMSIZE*5+DATESIZE+DIRSIZE+1
  327.                     /* Sum of all the field sizes in
  328.                     treejunk, != sizeof(treejunk). Size of
  329.                     TREEIDX records */
  330.  
  331. struct treestuff {
  332.     int    msgnum;
  333.     int    parent;
  334.     int    left;
  335.     int    right;
  336.     int    child;
  337.     time_t    date;
  338.     char    title[DIRSIZ+1];
  339. };
  340.  
  341. typedef    struct treestuf treemem;
  342. typedef struct treejunk treedisk;
  343. typedef struct lines line;
  344. typedef int func();
  345. typedef func *fp;
  346.  
  347. #ifndef __STRING
  348. typedef char *STRING;
  349. #define __STRING
  350. #endif
  351.  
  352. ulog a_log;
  353. extern errno;
  354. extern char **environ;
  355. extern time_t time ();
  356. extern long lseek ();
  357. extern long tell ();
  358. long filesize();
  359. extern char *malloc();
  360. extern char *realloc();
  361. extern char *calloc();
  362. extern STRING strcat();
  363. extern STRING strncat();
  364. extern STRING strcpy();
  365. extern STRING strncpy();
  366. extern char *index();
  367. extern char *rindex();
  368. extern int read();
  369. extern int write();
  370. treemem go_left();
  371. treemem go_right();
  372. treemem go_up();
  373. treemem go_down();
  374. treemem go_x();
  375. treemem disk_to_mem();
  376. treedisk mem_to_disk();
  377. /* char *crypt();     /* no encryption now 0.3 */
  378. char *ctime();
  379. char *getpass();
  380. int line_lost();
  381. long msg_write(), input_message();
  382.  
  383. # define    prints(string)    { printf(string); free(string); }
  384. # define    len(s1)        strlen(s1)
  385. # define    yes(string)    (string[0]=='Y' || string[0]=='y')
  386. # define    no(string)    (string[0]=='N' || string[0]=='n')
  387. # define    instr(s, c)    ((_temp = index((s),(c))-(s)) >=0 ? _temp :-2)
  388. # define    msg_quit()    ((*(get_command("Delete message?")))=='y')
  389. # define    CTRL(c)        ('c'-'a'+1)
  390. # define    readline(buf)    { if (fgets (buf, BUFSIZ, stdin)==0 && \
  391.                     errno!=EINTR) fortune(2,NUPDATE); \
  392.                 }
  393.     /* .8 */
  394. # define    FNStop(x)    (\
  395.             (_temp=(x))==CTRL(c) ||  (_temp)==CTRL(k) ?\
  396.             (putchar('\n'), TRUE) : FALSE\
  397.         )
  398.     /* 1.2 */
  399. # define    NOGOOD(name)    (name[0] == '.' || name[0] == '\0'\
  400.         || name[0] == ' ' || name[0] == '/')
  401.  
  402. int _temp;
  403.  
  404. struct sgttyb termp, origp;
  405. struct tchars termc, origc;
  406. fp oldsig[NSIG];
  407.  
  408. STRING    goof="/bbs/goofmesg";
  409. STRING    bulletin="/bbs/bulletin";
  410. STRING    mhelp="/bbs/help";
  411. STRING    other="/bbs/other";
  412. STRING    sysinfo="/bbs/info";
  413. STRING    ehelp="/bbs/entryhelp";
  414. STRING    notice="/bbs/manycalls";
  415. STRING    complaints="/bbs/mail/sysop";
  416. STRING    errorfile="/bbs/errorlog";
  417. STRING    command="b,c,f,g,h,i,l,m,o,q,r,u,x,y,z,?,a";
  418. STRING    ecommand="c,e,h,l,n,q,s,?";
  419. STRING    tcommand="luqdruq";
  420. STRING    CmdA, CmdB;        /* first word, rest of command */
  421. STRING    currentuser;
  422. char    buffer[BUFSIZ];        /* character buffer for whatever */
  423. char    log_entry[8000];    /* big enough to hold all commands you could
  424.                     possibly enter */
  425.  
  426. /* These defines aren't actually used (except MAINB) */
  427. # define    MAINB        0
  428. # define    WEIRD        1
  429. # define    ODDBALL        2
  430. # define    DandD        3
  431. # define    newboard    4
  432. # define    AMIGAB        5
  433. # define    BOARDS        6
  434. # define    DAT        0
  435. # define    IDX        1
  436. # define    BTYPES        2
  437.  
  438. # define    USERLOG        0
  439. # define    LOG        1
  440. # define    MAIL        2
  441. # define    TREEIDX        3
  442. # define    OTHERS        4
  443.  
  444. /* display is fopened, and does not use a file descriptor. Mail is re-opened
  445.     each time it is used, and may not always have the same descriptor.
  446.     Tree index is fopened/fclosed EACH time it is written to or read from.
  447. */
  448.  
  449. int files[OTHERS], board[BOARDS][BTYPES], lastmsg[BOARDS], justread[BOARDS];
  450.  
  451. /* Next arrays must be in the same order as the #define statements */
  452. STRING    bnames [] [BTYPES] = {
  453.     {"/bbs/dat/board0",    "/bbs/idx/board0"},
  454.     {"/bbs/dat/board1",    "/bbs/idx/board1"},
  455.     {"/bbs/dat/board2",    "/bbs/idx/board2"},
  456.     {"/bbs/dat/board3",    "/bbs/idx/board3"},
  457.     {"/bbs/dat/board4",    "/bbs/idx/board4"},
  458.     {"/bbs/dat/board5",    "/bbs/idx/board5"},
  459. };
  460. STRING    onames [] [2] = {    /* [2] to prevent compiler error */
  461.     {"/bbs/userlog",""},
  462.     {"/bbs/logfile",""},    /* log of who called and what they did */
  463.     {"/bbs/idx/tree",""},
  464.     {"",""},        /* mail is reopened each time */
  465. };
  466.  
  467. int
  468.     beginner=TRUE,
  469.     cboard=MAINB,
  470.     new_caller=FALSE,
  471.     breakcnt=0,
  472.     __;        /* end of globals */
  473.  
  474. # define    CBOARD        (board[cboard][DAT])
  475. # define    CIDX        (board[cboard][IDX])
  476.  
  477. int argc;
  478. char **argv;
  479.  
  480. main (c,v)
  481. char **v;
  482. {
  483.     if (sizeof (ulog)!=64) {
  484.         printf("Userlog size not 64. Current size=%d\n",sizeof(ulog));
  485.         myexit(1);
  486.     }
  487.     umask (0002);
  488.     setgid (STB_GID); setuid (STB_UID);
  489.         /* Don't run as root on V7 systems (and dump core correctly) */
  490.     argc=c; argv=v;        /* keep track of who we are for z command */
  491.     initialize();        /* set rnd # generator, signals, files */
  492. /*    skip_line();        /* wait for call (DELETED) */
  493.     printf("Can't turn off linefeeds, sorry\n");
  494.     printf("Welcome to the serial tree board (C %d.%d).\n", VERSION, FIX);
  495.     do ; while (!get_password());
  496.     log_user();
  497.     read_bulletin();
  498.     if (read_mail()==-1)
  499.         printf("Sorry, your mailbox is empty\n");
  500.     do_commands();
  501. }
  502.  
  503. hangup()    /* come here if they hang up */
  504. {
  505.     add_log("\n");
  506.     add_log("HANGUP RECEIVED");
  507.     fortune(0,NUPDATE);        /* Die normally */
  508. }
  509.  
  510. initialize ()
  511. {
  512.     int t,b,f;
  513.     chdir ("/bbs");    /* make sure we can core dump if there is a problem */
  514.     srand ((int) time(0));
  515.     defopen("/etc/default/stb");
  516.     for (b=0; b<BOARDS; b++)
  517.         for (t=0; t<BTYPES; t++)
  518.             board[b][t]=ropen(bnames[b][t],O_RDWR);
  519.     for (f=0; f<OTHERS; f++)
  520.         files[f]=ropen(onames[f][0],O_RDWR);
  521.     (void) signal (SIGALRM, hangup);
  522.     (void) signal (SIGHUP, hangup);
  523. /* set terminal to disable ctrl-c/k, ctrl-x=kill */
  524.     ioctl (0, TIOCGETP, &origp);   /* 0.3 Save the originals */
  525.     ioctl (0, TIOCGETC, &origc);    /* & restore later. */
  526.     ioctl(0,TIOCGETP,&termp);
  527.     termp.sg_kill=CTRL(x);
  528.     ioctl(0,TIOCSETP,&termp);
  529.     ioctl(0,TIOCGETC,&termc);
  530.     termc.t_quitc= -1;
  531.     termc.t_intrc= -1;
  532.     ioctl(0, TIOCSETC, &termc);
  533. /* need to read index files for the boards */
  534. /* Make sure the index files are not empty (if they are, they get lowmsg=1,
  535. address=0) */
  536.     for (b=0; b<BOARDS; b++)
  537.         if (filesize(board[b][IDX]) == 0)
  538.         {    long x;        /* File position */
  539.             x=1; write (board[b][IDX], &x, sizeof x);
  540.         }
  541. }
  542.  
  543. nodie()
  544. {
  545.     int x;
  546.     for (x=0; x<NSIG; x++)
  547.         oldsig[x] == signal (x, SIG_IGN);
  548. }
  549.  
  550. yesdie()
  551. {
  552.     int x;
  553.     for (x=0; x<NSIG; x++)
  554.         signal (x, oldsig[x]);
  555. }
  556.  
  557. ropen (name, flag)        /* open/creat file, whichever is needed */
  558. STRING name;
  559. {
  560.     int fd;
  561.     if (*name == 0) return -1;
  562.     if ((fd=open(name,flag))!=-1)
  563.         return fd;
  564.     if (errno==ENOENT)    /* if it doesn't exist, try creating it */
  565.         if ((fd=creat(name,0664))!=-1)
  566.             return close(fd), ropen(name, flag);
  567.     fprintf(stderr, "stb: error in file %s ",name);
  568.     perror("");
  569.     errno=0;
  570.     return -1;
  571. }
  572.  
  573. STRING makestring(n)
  574. {
  575.     char *temp;
  576.     temp=calloc((unsigned)n+1,(unsigned)1);
  577.     if (temp==NULL)
  578.         syscrash ("No Memory");
  579.     return temp;
  580. }
  581.  
  582. STRING stvcat(s1, s2)    /* Variable string allocator/catinator. Adds string 2
  583.                 to the end of string 1, but first free's space
  584.                 previously used by string 1. */
  585. STRING *s1, s2;
  586. {
  587.     STRING temp;
  588.     temp=makestring(len(*s1)+len(s2));
  589.     strcpy (temp, *s1);
  590.     strcat (temp, s2);
  591.     free(*s1);
  592.     *s1=temp;
  593.     return (*s1);
  594. }
  595.  
  596. STRING stvadd(s1, s2)    /* Variable string allocator/catinator. Adds string 2
  597.                 to the START of string 1, but first free's space
  598.                 previously used by string 1. */
  599. STRING *s1, s2;
  600. {
  601.     STRING temp;
  602.     temp=makestring(len(*s1)+len(s2));
  603.     strcpy (temp, s2);
  604.     strcat (temp, *s1);
  605.     free(*s1);
  606.     *s1=temp;
  607.     return (*s1);
  608. }
  609.  
  610. STRING mkmail(s1)
  611. STRING s1;
  612. {
  613.     return stvadd(&s1,"/bbs/mail/");
  614. }
  615.  
  616. STRING strsave(s)        /* save a string in memory. Taken from K&R */
  617. STRING s;
  618. {
  619.     STRING p;
  620.     if ((p=malloc((unsigned)strlen(s)+1)) != NULL)
  621.         strcpy (p,s);
  622.     return p;
  623. }
  624.  
  625. STRING first(s1)     /* s1 is <returned><space><ignored>. Returns same string
  626.             if there is no space, different string if there is */
  627. STRING s1;
  628. {
  629.     STRING s2;
  630.     char *temp;
  631.     temp=index(s1,' ');
  632.     if (temp==NULL)
  633.         return strsave(s1);
  634.     s2=strncpy (makestring( (int)(temp-s1) ),s1,temp-s1);
  635.     s2[temp-s1]='\0';
  636.     return s2;
  637. }
  638.  
  639. STRING rest(s1)    /* s1 is <ignored><space><returned>. Null string if no space */
  640. STRING s1;
  641. {
  642.     if (index(s1,' ')==0)
  643.         return makestring(0);
  644.     return strsave(index(s1,' ')+1);
  645. }
  646.  
  647. STRING strleft(s1,n)
  648. STRING s1;
  649. {
  650.     STRING s2;
  651.     s2=strncpy(makestring(n),s1,n);
  652.     s2[n]='\0';        /* strncpy doesn't guarentee null terminator */
  653.     return s2;
  654. }
  655.  
  656. STRING strright(s1,n)
  657. STRING s1;
  658. {
  659.     if (n>strlen(s1))
  660.         return strsave(s1);
  661.     return strsave(s1+n-strlen(s1));
  662. }
  663.  
  664. STRING lower_convert(string)    /* converts a string to lowercase */
  665. STRING string;
  666. {
  667.     STRING s2;
  668.     for (s2=string; *string!=0; string++)
  669.         if (isascii(*string) && isupper(*string))
  670.             *string=tolower(*string);
  671.     return s2;    /* return the converted string. */
  672. }
  673.  
  674. skip_line()        /* skip rest of line (until newline) */
  675. {
  676.     int c;
  677.     while ((c=getchar())!=EOF && c!='\n')
  678.     ;
  679. }
  680.  
  681. STRING getline(s, lim)    /* input a string up to lim-1 characters, NO \n */
  682. STRING s;
  683. {
  684.     int temp;
  685.     if (fgets (s,lim,stdin)==NULL)
  686.         fortune (2,NUPDATE);
  687.     temp=len(s)-1;
  688.     if (s[temp]!='\n')
  689.         skip_line();
  690.     else    s[temp]='\0';        /* remove newline */
  691.     return s;
  692. }
  693.  
  694. STRING get_command(string)        /* put the command in CmdA, CmdB */
  695. STRING string;
  696. {
  697.     char buffer[100];
  698.     printf(string);
  699.     getline(buffer, 100);
  700.     if (CmdA) {
  701.         free(CmdA); CmdA=NULL;
  702.     }
  703.     if (CmdB) {
  704.         free(CmdB); CmdB=NULL;
  705.     }
  706.     CmdA=first(buffer);
  707.     CmdB=rest(buffer);
  708.     lower_convert(CmdA);
  709.     lower_convert(CmdB);
  710.     add_log(CmdA);
  711.     add_log(" ");
  712.     return CmdA;
  713. }
  714.  
  715. STRING strsqueeze (s1)        /* remove blanks in string. Note that there
  716.                   will be an extra 0 for each space removed. */
  717. STRING s1;
  718. {
  719.     char *cp;
  720.     for (cp=s1; *cp!=0; cp++)
  721.         if (*cp==' ')
  722.             strcpy(cp,cp+1), --cp;
  723.     return s1;
  724. }
  725.  
  726. cbreak()        /* .8 */
  727. {
  728.     if (breakcnt++ != 0) return;
  729.     termp.sg_flags |= CBREAK;
  730.     termp.sg_flags &= ~ECHO;
  731.     ioctl (0, TIOCSETP, &termp);
  732. }
  733.  
  734. cooked()        /* .8 */
  735. {
  736.     if (--breakcnt !=0) return;
  737.     termp.sg_flags &= ~CBREAK;
  738.     termp.sg_flags |= ECHO;
  739.     ioctl (0, TIOCSETP, &termp);
  740. }
  741.  
  742. input (prompt)        /* 2.1 */
  743. STRING prompt;
  744. {
  745.     int x;
  746.     printf (prompt);
  747.     getline (buffer, BUFSIZ);
  748.     if (sscanf (buffer, "%d", &x) == 1)
  749.         return x;
  750.     else return -1;
  751. }
  752.  
  753. do_commands()            /* .6 */
  754. {
  755.     for (;;) {    /* Exit is in goodbye() */
  756.         alarm (WAIT_TIME);    /* 10 minutes of inactivity = hangup */
  757.         if (beginner) printf("Command list: %s\n",command);
  758.         get_command("Command? ");
  759.         switch (instr(command, *CmdA)/2) {
  760.         case 0: chboard(); break;
  761.         case 1: chat(); break;
  762.         case 2: feedback(); break;
  763.         case 3: goodbye(); myexit (-1); /* exit should be done from
  764.                     goodbye; if error, then return -1 */
  765.         case 4: help(); break;
  766.         case 5: info(); break;
  767.         case 6: leave_message(); break;
  768.         case 7: mail(); break;
  769.         case 8: other_systems(); break;
  770.         case 9: log_quit(); myexit(-1);
  771.                     /* logoff w/out updating auto-read */
  772.         case 10: msg_read(); break;
  773.         case 11: userlog(); break;
  774.         case 12: xpert(); break;
  775.         case 13: yagain(); break;
  776.                     /* it should myexit w/out hang up */
  777.         case 14: relog(); break;
  778.         case 15: help(); break;
  779.         case 16: attach(CmdB); break;
  780.         default: printf("No such command\n"); break;
  781.         }
  782.     }
  783. }
  784.  
  785. attach (parent)
  786. STRING parent;
  787. {
  788.     treemem tree;
  789.     if (parent==NULL)
  790.     {
  791.         printf("Please specify a parent\n");
  792.         return;
  793.     }
  794.     not_ready_yet();
  795. }
  796.  
  797. chboard()
  798. {
  799.     int x;
  800.     printf("Enter board number (0-%d): ",BOARDS-1);
  801.     readline (buffer);
  802.     sscanf(buffer, "%d",&x);
  803.     if (x<BOARDS)            /* .6 invalid board means stay here */
  804.         cboard=x;
  805.     else printf("Invalid board--staying on board %d\n", cboard);
  806. }
  807.  
  808. chat()
  809. {
  810.     printf("Can't chat. Please leave feedback.\n");
  811.     feedback ();
  812. }
  813.  
  814. feedback()
  815. {
  816.     printf("Enter feedback now\n");
  817.     close(files[MAIL]);
  818.     files[MAIL]=ropen(complaints,O_WRONLY);
  819.     input_message(files[MAIL], "Complaints ");
  820.     close(files[MAIL]);
  821. }
  822.  
  823. goodbye()
  824. {
  825.     if (new_caller) make_account();
  826.     fortune(0,UPDATE);
  827. }
  828.  
  829. help ()
  830. {
  831.     displ_file (mhelp);
  832. }
  833.  
  834. info ()
  835. {
  836.     displ_file (sysinfo);
  837. }
  838.  
  839. leave_message ()
  840. {
  841.     long where;
  842.     char header[50];
  843.     sprintf (header, "Message %d ", maxmsg(CIDX) +1 ) ;
  844.     printf("Enter your message now.\n");
  845.     where=input_message(CBOARD, header);
  846.  
  847.     if (where==-1) return;
  848.     locking (CIDX, LK_RLCK, 0L);
  849.     lseek (CIDX, 0L, 2);
  850.     if (write (CIDX, &where, sizeof (where)) != sizeof (where))
  851.         syscrash ("Can't update message pointers");
  852.     locking (CIDX, LK_UNLK, 0L);
  853. }
  854.  
  855. line *in_message();
  856.  
  857. mail ()        /* 3.10 */
  858. {
  859.     STRING s1;
  860.     line *mess;
  861.     printf("Enter mail now\n");
  862.     mess = in_message();
  863.     if (mess==NULL) return;
  864.     for (;;) {
  865.         printf("Who shall it go to? ");
  866.         getline(buffer, BUFSIZ);
  867.         if (*buffer == '\0') break;
  868.         if (NOGOOD(buffer)) {printf("Bad name\n"); continue;}
  869.         if (*buffer != '!')
  870.         {    /* Normal--not rmail */
  871.             lower_convert(strsqueeze(buffer));
  872.             s1=strsave(buffer);
  873.             s1=mkmail(s1);        /* Memory loss */
  874.             files[MAIL]=ropen(s1, O_WRONLY);
  875.         } else { /* uucp mail */
  876.             s1=strsave(&(buffer[0])); /* Memory loss--never freed */
  877.             sprintf (buffer, "rmail %s", s1);
  878.             files[MAIL] = pwrite (buffer);
  879.         }
  880.         msg_write (mess, files[MAIL], "Mail ");
  881.         close (files[MAIL]);
  882.     }
  883. }
  884.  
  885. pwrite (s)
  886. STRING s;
  887. /* This is not accurate, but mearly sufficient. Don't call more than 10 or
  888. so times, ok? */
  889. {
  890.     FILE *f, *popen();
  891.     f=popen(s, "w");
  892.     if (f==NULL) perror("stb:");
  893.     else return fileno(f);
  894.     return -1;
  895. }
  896.  
  897. other_systems ()
  898. {
  899.     displ_file (other);
  900. }
  901.  
  902. log_quit ()
  903. {
  904.     fortune(0,NUPDATE);
  905. }
  906.  
  907. minmsg(fdboard)        /* 1.1 */
  908. {
  909.     long li;        /* pointers into message base may be >32K */
  910.                 /* (Unsigned ints are no better (64 K)) */
  911.     lseek (fdboard, 0L, 0);
  912.     return( read (fdboard, &li, sizeof (li) ) == sizeof li ? li
  913.             : syscrash ("Can't read low message number")
  914.         );
  915. }
  916.  
  917. maxmsg(fdboard)        /* 1.1 */
  918. {
  919.     return (
  920.     (lseek(fdboard, 0L, 2) / sizeof (long)) -2 + minmsg(fdboard)
  921.     ) ;
  922.     /*        (Size of file)     # of messages in file    +lowest message
  923.                 == highest message */
  924.     /* -2 corrects for first message being #1 */
  925.     /* (for one item file: 8/4 (=2) -2 (=0) +minmsg (1) = 1 */
  926. }
  927.  
  928. long wheremsg (fdboard, msgnum)
  929. {
  930.     long li;
  931.     lseek (fdboard, (long) ((msgnum-minmsg(fdboard)+1) * sizeof (long)), 0);
  932.     return( read (fdboard, &li, sizeof li) == sizeof li ? li
  933.             : syscrash ("Can't find message pointer in read")
  934.         );
  935. }
  936.  
  937. msg_read ()                /* .6 */
  938.         /* Major changes in 1.1:
  939.         A. Index files are now used as arrays, with known seek offsets
  940.         B. Read is now by message number, not byte number. Message 0
  941.            is NOT valid, but instead holds the lowest message number.
  942.         */
  943. {
  944.     long start, current;
  945.     printf("Starting message number (%d-%d, %d suggested)? ",
  946.             minmsg(CIDX),    maxmsg(CIDX), justread[cboard]);
  947.     readline(buffer);
  948.     start=0;
  949.     if (sscanf(buffer, "%D", &start)==0) start=0;
  950.     if (start==0) start=justread[cboard];
  951.     if (start <minmsg(CIDX) || start > maxmsg(CIDX))
  952.     {    printf("No such message\n"); return;
  953.     }
  954.     lseek (CBOARD, wheremsg(CIDX, start), 0);
  955.     cbreak();
  956.     current = start;
  957.     do
  958.     {    justread[cboard]=current++; /* assign old value, then inc */
  959.     } while (!empty(CBOARD) && displ_message(CBOARD));
  960.     cooked();
  961. }
  962.  
  963. userlog ()                /* .7 */
  964. {
  965.     ulog log;
  966.     lseek(files[USERLOG], 0L, 0);
  967.     printf("Name          Calls    Last called\n");
  968.     cbreak();
  969.     while (read (files[USERLOG], &log, sizeof log)!=NULL
  970.                 && !FNStop(finkey(stdin)))
  971.         printf ("%-14s %4d %s", log.name, log.numcalls,
  972.                             ctime(&log.lastcall));
  973.     cooked();
  974. }
  975.  
  976. xpert ()
  977. {
  978.     beginner= !beginner;
  979. }
  980.  
  981. yagain()
  982. {
  983.     if (new_caller) make_account();
  984.     fortune(2, UPDATE); myexit (-1);
  985. }
  986.  
  987. relog ()
  988. {
  989.     int temp=0;
  990.     temp = ( strcmp(CmdA,"zs")==0 ? 1: 0);
  991.     if (new_caller) make_account();
  992.     if (temp==1)
  993.         fortune(2,UPDATE);    /* Return to where we came from */
  994.     else fortune(1,UPDATE);    /* DON'T hang up the phone in either case */
  995. }
  996.  
  997. not_ready_yet ()
  998. {
  999.     printf("Not finished yet. Please try next week\n");
  1000. }
  1001.  
  1002. syscrash(s1)
  1003. STRING s1;
  1004. {
  1005.     int x;
  1006.     perror("");
  1007.     printf("Fatal error (%s) has occured. System crashing\n", s1);
  1008.     fflush(stdout);
  1009.     freopen(errorfile,"a", stderr); /* use freopen to make sure that
  1010.                      a file descriptor is available */
  1011.     fprintf(stderr, "%s %d", s1, errno);
  1012.     x=time(0);
  1013.     fputs(" ",stderr);
  1014.     fputs(ctime(&x),stderr);
  1015.     fflush(stderr);
  1016.     fclose (stderr);
  1017.     fclose(fopen("/bbs/core", "w")); /* Must be empty for core to dump */
  1018.     restoretty();
  1019.     abort();
  1020. }
  1021.  
  1022. long filesize(file)        /* Wasn't used til 1.1. Hmm...*/
  1023. {
  1024.     return lseek (file, 0L, 2);
  1025. }
  1026.  
  1027. int display(fd)        /* 3.0 */
  1028. {
  1029.     char c;
  1030.     int x, count;
  1031.     setbuf (stdout, buffer);
  1032.     while (read(fd, &c, 1)==1 && c!='\001')
  1033.     {    putchar(c);
  1034.         if (count++ >= OUTBUF)
  1035.         {    fflush(stdout);
  1036.             count = 0;
  1037.             if FNStop(x=finkey(stdin))
  1038.                 break;
  1039.         }
  1040.     }
  1041.     fflush(stdout); setbuf (stdout, NULL);
  1042.     return x;
  1043. }
  1044.  
  1045. displ_file (file)
  1046. STRING file;            /* 3.0 */
  1047. {
  1048.     int fd;
  1049.     int c;
  1050.     fd = open(file, O_RDONLY);
  1051.     if (fd == -1) return -1;    /* no file, then return */
  1052.     cbreak();        /* .7 */
  1053.     display(fd);        /* 3.0 */
  1054.     cooked();        /* .7 */
  1055.     close(fd);
  1056.     return 0;
  1057. }
  1058.  
  1059. displ_message (file)        /* display's the file until a chr$(1) or EOF */
  1060.                 /* .6 Returns false if ctrl_c, true otherwise */
  1061.                 /* .7 Runs in cbreak */
  1062.                 /* .8 Now accepts ctrl-c/k at ? prompt */
  1063.                 /* 1.1 Puts a blank line after return hit */
  1064.                 /* 1.2 Puts 2 blanks */
  1065.                 /* 3.0 one blank before, one after */
  1066. {
  1067.     char c;
  1068.     int x;
  1069.     cbreak();
  1070.     x=display (file);
  1071.     cooked();
  1072.     if (x==CTRL(c)) return FALSE;
  1073.     if (x==CTRL(k)) { skip_message(file); return TRUE; } /* .7 */
  1074.     if (empty(file)) return TRUE;
  1075.     cbreak();
  1076.     putchar('\n');
  1077.     putchar('?');
  1078.     alarm (WAIT_TIME);        /* hangup detection */
  1079.     x=getchar();            /* .8 */
  1080.         if (x==EOF)
  1081.             fortune(2,NUPDATE);
  1082.     cooked();            /* No need to worry about exit throu
  1083.                     fortune; that clears cbreak */
  1084.     puts("\n\n");
  1085.     return (x == CTRL(c) ? FALSE:TRUE);
  1086. }
  1087.  
  1088. skip_message(file)        /* move to next ctrl-a */
  1089. {
  1090.     char c;
  1091.     while (read(file, &c, 1)==1 && c!=CTRL(a))
  1092.     ;
  1093. }
  1094.  
  1095. get_password()
  1096. {
  1097.     char *pass;
  1098. loop:
  1099.     printf("What is your name (or handle)? ");
  1100.     getline(buffer, BUFSIZ);
  1101.     strcpy(currentuser=makestring(BUFSIZ), lower_convert(buffer));
  1102.     if (NOGOOD(currentuser))
  1103.     {    printf ("Bad name, try again. \n");
  1104.         goto loop;
  1105.         /* NOTREACHED */
  1106.     }
  1107.     if (!get_log(currentuser))
  1108.     {    printf("Are you a new user? ");        /* 1.1 */
  1109.         getline (buffer, BUFSIZ);
  1110.         if (*buffer != 'y' && *buffer != 'Y') goto loop;
  1111.         new_caller=TRUE;
  1112.         return TRUE;
  1113.     };
  1114.     pass=lower_convert(getpass("What is your password?"));
  1115.     if (strcmp(a_log.password, pass)==0)
  1116.     {
  1117.         int x;
  1118.         char *temp;
  1119.         printf((temp=defread("GREET="))
  1120.             ? temp
  1121.             : "Welcome back. Last call was %s\nTotal calls: %d\n",
  1122.           ctime(&a_log.lastcall),  a_log.numcalls);
  1123.                             
  1124.         for (x=0; x<BOARDS; x++)
  1125.             justread[x]=a_log.lastread[x];
  1126.         return TRUE;
  1127.     }
  1128.     else return FALSE;
  1129. }
  1130.  
  1131. read_bulletin()
  1132. {
  1133.     displ_file (bulletin);
  1134. }
  1135. ;
  1136.  
  1137. read_mail()            /* .6 version */
  1138. {
  1139.     int file;
  1140.     STRING s1;
  1141.     file=open(s1=strsave(mkmail(strsqueeze(strsave(currentuser))))
  1142.                                 ,O_RDONLY);
  1143.     if (file==-1) return -1;
  1144.     while (!empty(file) && displ_message (file))
  1145.     ;            /* displ_message returns false if ctrl_c */
  1146.     close (file);
  1147.     get_command("Delete this? ");
  1148.     if (*CmdA=='y')
  1149.         unlink (s1);
  1150.     return 0;
  1151. }
  1152.  
  1153. log_user()
  1154. {
  1155.     long x;
  1156.     if (add_log(currentuser)==-1)
  1157.         syscrash("Can't log you in, sorry");
  1158.     add_log(" ");
  1159.     x=time(0);
  1160.     if (add_log(ctime(&x))==-1)
  1161.         syscrash("Can't log you in, sorry");
  1162. }
  1163.  
  1164. add_log(s1)
  1165. STRING s1;
  1166. {
  1167.     sprintf(log_entry,"%s%s", log_entry, s1);
  1168.     return 0;
  1169. }
  1170.  
  1171. get_log(name)        /* get the log entry for name into a_log */
  1172. char *name;        /* returns 0 for not found, non-zero otherwise */
  1173. {
  1174.     int x;
  1175.     char *temp;
  1176.     lseek (files[USERLOG], 0L, 0);
  1177.     temp=strleft(strsqueeze(strsave(name)),DIRSIZ);
  1178.     while ((x=read (files[USERLOG], &a_log, sizeof a_log)) == sizeof a_log)
  1179.         if (strcmp(a_log.name, temp)==0)
  1180.             break;
  1181.     if (!(x==sizeof a_log || x==0))
  1182.         syscrash("Error in userlog");
  1183.     return x;
  1184. }
  1185.  
  1186. he_left(arg)        /* update calls, last call date, last message read */
  1187. {
  1188.     int x;
  1189.     lseek (files[LOG], 0L, 2);
  1190.     nodie();
  1191.     write(files[LOG], log_entry, strlen(log_entry));
  1192.     write(files[LOG],"\n",1);
  1193.     yesdie();
  1194.     if (new_caller) return;
  1195. /* search for record (assume it exists) */
  1196.     get_log(currentuser);
  1197. /* update it (always do time, the rest only if he didn't quit or hang up)*/
  1198.     if (arg==UPDATE)
  1199.     {
  1200.         for (x=0; x<BOARDS; x++)
  1201.             a_log.lastread[x]=justread[x];
  1202.         a_log.numcalls++;
  1203.     }
  1204.     a_log.lastcall=time(0);
  1205.     lseek(files[USERLOG],(long) -sizeof a_log, 1);
  1206.     nodie();
  1207.     write (files[USERLOG], &a_log, sizeof a_log);
  1208.     yesdie();
  1209. }
  1210.  
  1211. zeromem (addr, bytes)
  1212. char *addr;
  1213. {
  1214.     while (bytes--) *addr++ = ' ';
  1215. }
  1216.  
  1217. make_account()        /* Called when a new user logs off */
  1218. {
  1219.     get_command("Will you be coming back? ");
  1220.     if (*lower_convert(CmdA)=='y')
  1221.     {
  1222.         STRING pass1, pass2;
  1223.         zeromem (&a_log, sizeof a_log);
  1224.         strcpy(a_log.name,strleft(strsqueeze(currentuser),DIRSIZ));
  1225.         do
  1226.         {
  1227.             pass1=strsave(lower_convert(getpass(
  1228.                     "Please enter your password: ")));
  1229.             pass2=strsave(lower_convert(getpass("Retype it: ")));
  1230.         } while (strcmp(pass1,pass2)!=0);
  1231.         if (*pass1 == '\0') return; /* if no password then return */
  1232.         strcpy(a_log.password, pass1 );
  1233.         a_log.numcalls=0;
  1234.         a_log.lastbyte='\n';
  1235.         lseek (files[USERLOG], 0L, 2);
  1236.         if (write(files[USERLOG], &a_log, sizeof a_log)!=sizeof a_log)
  1237.             syscrash ("Write error on userlog--userlog clobbered");
  1238.         new_caller=FALSE;
  1239.     }
  1240. }
  1241.  
  1242. fortune (arg, update)
  1243.         /* print a fortune, hang up phone, and reset system for next
  1244.         caller. If arg=1, phone is NOT hung up, but the program is re-
  1245.         executed (assumed to be in either /bin or /usr/bin) If arg=2,
  1246.         eof is assumed (myexit code 0) */
  1247. {    int temp=0;
  1248.     alarm (0);
  1249.     signal (SIGHUP, SIG_IGN);
  1250.     temp=fork();
  1251.     if (temp==0)
  1252.     /* in child */
  1253.     {    execl("/usr/games/fortune","fortune",NULL);
  1254.         printf("No cookies available--sorry\n");
  1255.         kill(getpid(), SIGEMT);    /* any unused signal that core dumps */
  1256.     }
  1257.     /* in parent */
  1258.     he_left(update);
  1259.     if (arg==1) {
  1260.         int x;
  1261.         for (x=3; x<20; x++)        /* keep terminal channels */
  1262.             close(x);        /* open, close the rest */
  1263.         wait (0);        /* let the fortune print out */
  1264.         execlp ("bbs","bbs",0);
  1265.         syscrash ("YOW! Can't find myself!");
  1266.     }
  1267.     if (arg==2)
  1268.     {
  1269. /* SYS 3 SYS3 SYS3 SYS3 SYS3 SYS 3 */
  1270. /* The following is to prevent hanging up after the last close, which is
  1271. impossible under V7 (where once set, always set) 
  1272. /* */
  1273. #ifdef SYS3
  1274. #include <termio.h>
  1275. #include <sys/ioctl.h>
  1276.         struct termio t;
  1277.         ioctl (0, TCGETA, &t);
  1278.         t.c_cflag &= ~HUPCL;    /* Don't hang up */
  1279.         ioctl (0, TCSETA, &t);
  1280. #endif
  1281. /* End sys 3 */
  1282.         wait (0);        /* let the fortune print out */
  1283.         myexit (0);
  1284.     }
  1285. /*    termp.sg_flags ^= ECHO;        /* no echo */
  1286. /*    stty(1,&termp);
  1287. /*    wait (0);            /* let the fortune print out */
  1288. /*    printf("\r");
  1289. /*    sleep (3);            /* wait a bit */
  1290. /*    printf("+++");            /* hang up phone */
  1291. /*    sleep (3);
  1292. /*    printf("ath\r");
  1293. /*    termp.sg_flags ^= ECHO;        /* turn echo on for local use */
  1294. /*    sleep (1);            /* just to be safe */
  1295. /*    stty(1,&termp);
  1296. /* Lines deleted since the system now hangs up properly. */
  1297.     wait(0);
  1298.     myexit (0);            /* exit program */
  1299. }
  1300.  
  1301. myexit(arg)        /* Restore terminal and exit */
  1302. {
  1303.     restoretty();
  1304.     exit (arg);
  1305. }
  1306.  
  1307. restoretty()
  1308. {
  1309.     ioctl (0, TIOCSETP, &origp);
  1310.     ioctl (0, TIOCSETC, &origc);
  1311. }
  1312.  
  1313. empty(file)    /* Checks to see if a file has at least 2 characters (ctrl-a
  1314.                         and newline usually) */
  1315. {
  1316.     char c;
  1317.     if (read (file,&c,2)==2) {        /* if a character was read, */
  1318.         lseek (file, -2L, 1);        /* go back to it */
  1319.         return FALSE;
  1320.     }
  1321.     return TRUE;            /* nothing left in file */
  1322. }
  1323.  
  1324. line *findline (message, l)        /* 2.1 */
  1325. line *message;
  1326. {
  1327.     while (message != NULL && (--l) > 0)
  1328.         message = message->next;
  1329.     return message;
  1330. }
  1331.  
  1332. line *cont (message)
  1333. line *message;
  1334. {
  1335.     line *buffer, *last;
  1336. /* find end of message */
  1337.     for (last=message; last!=NULL && last->next!=NULL; last=last->next)
  1338.     ;
  1339.     printf("Input text now\n");
  1340.     do {
  1341.         alarm (WAIT_TIME);        /* hangup detection */
  1342.         if ((buffer=(line *)calloc((unsigned)1,
  1343.                         (unsigned)sizeof(line)))==NULL)
  1344.             syscrash("No memory");
  1345.         printf("%s",CmdB);
  1346.         /* Do not use getline (EOF) */
  1347.         fgets (buffer->text, LINESIZE, stdin);
  1348.             /* if EOF then break loop */
  1349.         if (*buffer->text=='\n' || *buffer->text == '\0') break;
  1350.         else
  1351.         /* adjust pointers */
  1352.         if (last==NULL) message=buffer;
  1353.         else last->next=buffer;
  1354.         last=buffer;
  1355.         buffer->next=NULL;
  1356.     } while (*buffer->text!='\n');    /* continue until blank line */
  1357.     return message;
  1358. }
  1359.  
  1360. line *edit (message)    /* 2.1 */
  1361. line *message;
  1362. {
  1363.     int x;
  1364.     line *edline;
  1365.     x = input ("Line number to change? ");
  1366.     edline=findline(message, x); if (edline == NULL) return message;
  1367.     printf ("Old line:\n");
  1368.     printf (edline->text);
  1369.     printf ("Enter new line\n");
  1370.     getline (buffer, BUFSIZ);    /* 2.4 */
  1371.     if (*buffer == '\0' || *buffer == '\n')
  1372.         return message;
  1373.     else sprintf (edline->text, "%s\n", buffer);    /* 2.7 */
  1374.     return message;
  1375. }
  1376.  
  1377. list_message (message)        /* 2.1 now stops on ctrl-c/k */
  1378. line *message;
  1379. {
  1380.     line *cline;
  1381.     int line_no=1;
  1382.     for (cline=message; cline!=NULL && !FNStop (finkey(stdin));
  1383.                             cline=cline->next)
  1384.         printf ("%d: %s", line_no++, cline->text);
  1385. }
  1386.  
  1387. line *delete (message)
  1388. line *message;
  1389. {
  1390.     return NULL;    /* temp. version; doesn't actually delete anything */
  1391. }
  1392.  
  1393. line *new (message)
  1394. line *message;
  1395. {
  1396.     if (msg_quit()) {
  1397.     message=delete (message);
  1398.     return cont(message);
  1399.     }
  1400.     return message;
  1401. }
  1402.  
  1403. long msg_write (message,file,header)
  1404.             /* Actual header is header+"from xx date yyy" */
  1405. line *message;
  1406. STRING header;
  1407. {
  1408.     time_t tim;
  1409.     long retval;
  1410.     nodie();
  1411.     locking (file, LK_RLCK, 0L);    /* don't let anyone else write; wait if
  1412.                         someone is writting */
  1413.     retval=lseek (file, 0L, 2);        /* EOF */
  1414.     tim=time(0);
  1415.     sprintf (buffer, "%sfrom %s date %s", header, currentuser,
  1416.             ctime(&tim));
  1417.     write (file, buffer, len(buffer));
  1418.     for (; message != NULL; message=message->next)
  1419.         if (write (file, message->text, len(message->text))
  1420.                 !=len(message->text))
  1421.             syscrash("write error in message entry");
  1422.     delete(message);    /* recover memory used by message */
  1423.     write (file, "\001", 1);    /* ctrl-a terminates each message */
  1424.     locking (file, LK_UNLK, 0L);    /* let others write to it */
  1425.     yesdie();
  1426.     return retval;
  1427. }
  1428.  
  1429. line *in_message ()        /* Input a message at the end of file. */
  1430.                 /* Message terminated by ctrl-a */
  1431. {
  1432.     line    *message=NULL;
  1433.             /* message points to the first line in the message */
  1434.     message=cont(message);
  1435.     for (;;) {
  1436.         if (beginner) printf("Command list: %s\n",ecommand);
  1437.         get_command("Message entry command? ");
  1438.         switch (instr(ecommand, *CmdA)/2) {
  1439.         case 0: message=cont(message); break;
  1440.         case 1: message=edit(message); break;
  1441.         case 2: displ_file(ehelp); break;
  1442.         case 3: list_message(message); break;
  1443.         case 4: message=new(message); break;
  1444.         case 5: if (msg_quit()) return NULL; else break;
  1445.         case 6: return message;
  1446.         case 7: displ_file(ehelp); break;
  1447.         default: printf("No such command\n"); break;
  1448.         }
  1449.     }
  1450. }
  1451.  
  1452. long input_message (file, header)    /* 3.10 */
  1453. STRING header;
  1454. {
  1455.     line *message = in_message ();
  1456.     if (message == 0) return -1;
  1457.     return msg_write (message, file, header);
  1458. }
  1459.  
  1460. /* Tree routines follow. Routines that return a treestuff structure (treemem)
  1461. go_left(treemem)
  1462. go_right(treemem)
  1463. go_up(treemem)
  1464. go_down(treemem)
  1465. go_x(msgnum)
  1466. disk_to_mem(treedisk)
  1467. make_down(treemem)    (makes a child and returns its treemem)
  1468.  
  1469. All the above change the current directory to whatever the message is.
  1470. Other routines: returns STRING
  1471. message_name(treemem)
  1472.  
  1473. List of children is obtained by system(ls), or childfile=popen("ls","r")
  1474.  
  1475. mem_to_disk(treedisk) returns a treejunk structure.
  1476.  
  1477. Remember, treejunk is used for TREEIDX file, treestuff is used for internal
  1478. computation. TREEIDX is NOT memory resident; it is read a record at a time
  1479. when needed. Note: The message number in treejunk/treestuff MUST be the record
  1480. number in TREEIDX; if messages are deleted, they must be renumbered.
  1481. Future improvment number one: Get around this restriction.
  1482. */
  1483. -- 
  1484. : Michael Gersten        seismo!scgvaxd!stb!michael
  1485. : Copy protection? Just say Pirate! (if its worth pirating)
  1486.  
  1487.  
  1488.