Newsgroups: alt.sources
From: ken@bitsko.slc.ut.us (Ken MacLeod)
Subject: Unidel 1.0, a Citadel-style news/mail reader and BBS, part 05 of 09
Message-ID: <1992May3.013001.18821@bitsko.slc.ut.us>
Date: Sun, 3 May 1992 01:30:01 GMT

Submitted-by: ken@bitsko.slc.ut.us
Archive-name: unidel-1.0/part05

---- Cut Here and unpack ----
#!/bin/sh
# This is part 05 of unidel-1.0
if touch 2>&1 | fgrep 'amc' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
# ============= unidel/ReadMesg.c ==============
echo "x - extracting unidel/ReadMesg.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > unidel/ReadMesg.c &&
X/*
X * NAME
X *     ReadMesg.c -- Message reading commands and functions
X *
X * AUTHOR
X *     Ken MacLeod
X */
X
X#ifndef lint
Xstatic char sccsId [] = "@(#) ReadMesg.c  1.9 02 May 1992 10:03:26\n\t";
X#endif
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <string.h>
X#include <malloc.h>
X#include <fgetmfs.h>
X#include <time.h>
X#include <ctype.h>
X#include "protos.h"
X#include "Log.h"
X#include "defs.h"
X#include "Unidel.h"
X#include "Room.h"
X#include "User.h"
X#include "Message.h"
X
Xextern char *hdr[], *hdrFieldNames[];
Xstatic char *Who ();
Xstatic int BetterNewsgroup ();
Xstatic void PrintRealShortHeader ();
Xstatic void BackErase ();
Xstatic void PrintHeader ();
Xstatic void PrintLine ();
Xstatic int PrintFileLine ();
X
Xvoid StrCat ();
X
Xstatic int position;
X#define _atHeader 0
X#define _atTop 1
X#define _inMiddle 2
X#define _atBottom 3
X
Xextern int linesLeft;		/* number of lines left */
Xextern int numLines;		/* number of lines to a screen */
X
X/* XXX These should be in a structure and each instance local to */
X/* PrintArticle  */
X/* XXX Put 'hdr' here with these */
Xstatic FILE *articleFile;
Xstatic char *articleFileName;
Xstatic int articleNum;		/* Relative article number,
X				   < 0 if not an article, -1 if has header */
Xstatic long bytes;
Xstatic long length;
Xstatic long startOfBody;	/* File position after ReadHeader */
Xstatic int done;		/* _true to end reading */
Xstatic int reading;		/* _false to end reading this message */
Xstatic int whoa;		/* _true to do display "--More--" */
Xstatic int backFlag;		/* _true if we want to go back an article */
Xstatic int backErase;		/* Characters to erase from prompt */
X
Xstatic int MorePageDown (), MorePageUp (), MoreLineDown (), MoreLineUp ();
Xstatic int MoreArtNext (), MoreArtPrev ();
Xstatic int MoreDownload (), MoreFollowup (), MoreReply (), MoreDisplayHeader ();
Xstatic int MoreStop (), MoreCancel (), MoreHelp ();
X#ifdef REMIND
Xstatic int MoreUpdate ();
X#endif
X
Xstatic Menu moreMenu[] = {
X  "?", "=", NULL, NULL, NULL, 0l, MoreHelp,
X  " ", "=", NULL, NULL, NULL, 0l, MorePageDown,
X  "\n", "=", NULL, NULL, NULL, 0l, MoreLineDown,
X  "B", "=", NULL, NULL, NULL, 0l, MoreArtPrev,
X  "C", "=", NULL, NULL, NULL, 0l, MoreCancel,
X  "D", "=", NULL, NULL, NULL, 0l, MoreDownload,
X  "F", "=", NULL, NULL, NULL, 0l, MoreFollowup,
X  "H", "=", NULL, NULL, NULL, 0l, MoreDisplayHeader,
X  "L", "=", NULL, NULL, NULL, 0l, MoreLineUp,
X  "N", "=", NULL, NULL, NULL, 0l, MoreArtNext,
X  "R", "=", NULL, NULL, NULL, 0l, MoreReply,
X  "S", "=", NULL, NULL, NULL, 0l, MoreStop,
X  "Q", "=", NULL, NULL, NULL, 0l, MoreStop,
X  "U", "=", NULL, NULL, NULL, 0l, MorePageUp,
X
X  /* End of list */
X  "", NULL, NULL, NULL, NULL, 0l, NULL,
X};
X
Xstatic Menu headerMenu[] = {
X  " ", "=", NULL, NULL, NULL, 0l, MorePageDown,
X  "\n", "=", NULL, NULL, NULL, 0l, MoreLineDown,
X  "S", "=", NULL, NULL, NULL, 0l, MoreStop,
X  "Q", "=", NULL, NULL, NULL, 0l, MoreStop,
X
X  /* End of list */
X  "", NULL, NULL, NULL, NULL, 0l, NULL,
X};
X
Xint
XPrintMessage (flags, fileName, article, back)
Xlong flags;
Xchar *fileName;		/* article file name */
Xint article;		/* article relative number,
X			   < 0 if not an article, -1 if has header */
Xint back;		/* We've come back to this article, no skipping */
X{
X  void ClearHeader (), FreeHeader ();
X  char moreBuf[_bufSize];
X
X  if (flags & _readMoreDefault)
X    more = moreDefault;
X  else if (flags & _readMore)
X    more = 1;
X  else
X    more = 0;
X  articleNum = article;
X  articleFileName = fileName;
X  done = _false;
X  if ((articleFile = fopen (fileName, "r")) == NULL) {
X    (void) printf ("%7d (Article no longer on-line)\n", articleNum + 1);
X    return (-1);
X  }
X  if (article >= -1) {
X    ClearHeader (hdr, hdrFieldNames);
X    /* XXX Status from ReadHeader */
X    (void) ReadHeader (articleFile, hdr, hdrFieldNames);
X    if (!back && !(flags & _readAll) && hdr[_hdrNewsgroups]
X	&& BetterNewsgroup (hdr[_hdrNewsgroups])) {
X      if (!(flags & _readHeaders))
X	PrintRealShortHeader (hdr, article, flags);
X      goto outtaHere;
X    }
X    if (flags & _readHeaders) {
X      PrintRealShortHeader (hdr, article, flags);
X      goto outtaHere;
X    }
X  }
X  startOfBody = ftell (articleFile);
X  (void)fseek (articleFile, 0l, 2);
X  length = ftell (articleFile) - startOfBody;
X  (void)fseek (articleFile, startOfBody, 0);
X  if (length <=0)
X    length = 1;
X  bytes = 1;
X
X  numLines = lines;
X
X  position = _atHeader;
X  reading = _true;
X  whoa = _false;
X  backFlag = _false;
X  do {
X    if (article >= -1 && position == _atHeader) {
X      if (more >= 2)
X	linesLeft = lines;
X      PrintShortHeader (hdr, article);
X      if (hdr[_hdrUploadFile] != NULL) {
X	position = _atBottom;
X	(void)fseek (articleFile, 0l, 2);
X      } else {
X	position = _atTop;
X	if (more >= 2)
X	  whoa = _true;
X      }
X    }
X    if (whoa) {
X      whoa = _false;
X      (void) sprintf (moreBuf, "--More--(%d%%)", (int) (bytes*100/length));
X      backErase = strlen (moreBuf);
X      (void) printf ("%s", moreBuf);
X      (void) DoMenu (flags, moreMenu);
X    } else if (PrintFileLine (articleFile, length, flags, moreMenu) == EOF) {
X      /* XXX stop at bottom?  External pager? */
X      position = _atBottom;
X      break;
X    }
X  } while (reading && !done);
X
X outtaHere:
X  (void) fclose (articleFile);
X  if (article >= -1)
X    FreeHeader (hdr, hdrFieldNames);
X  if (done)
X    return (2);
X  else
X    return (backFlag);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreHelp (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  (void) printf ("Help\n");
X  Help ("more");
X  whoa = _true;
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreDownload (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  (void) printf ("Download\n");
X  if (hdr[_hdrUploadFile])
X    ReadOneFile (hdr[_hdrUploadFile]);
X  else
X    ReadOneFile (articleFileName);
X  whoa = _true;
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreFollowup (dummy)
Xlong dummy;
X{
X  int followupToPoster;
X
X  BackErase (backErase);
X  (void) printf ("Followup\n");
X  if (articleNum < 0) {
X    /* XXX How 'bout sysop? */
X    (void) printf ("Not a message, can't followup.\n");
X  } else {
X    followupToPoster = hdr[_hdrFollowupTo]
X      && (strcmp (hdr[_hdrFollowupTo], "poster") == 0);
X    if (followupToPoster)
X      (void) printf ("Followups have been directed to be emailed to the author.\n");
X    if (EnterMessage (0l, articleFileName, followupToPoster))
X      (void) printf ("Article not %s.\n",
X		     followupToPoster ? "mailed" : "posted");
X  }
X  whoa = _true;
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreReply (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  (void) printf ("Reply to author\n");
X  if (articleNum < -1) {
X    (void) printf ("Not a message, can't reply.\n");
X  } else if (EnterMessage (0l, articleFileName, _true))
X    (void) printf ("Mail not sent.\n");
X  whoa = _true;
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreDisplayHeader (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  if (articleNum < -1) {
X    (void) printf ("Not a message, no header.\n");
X    whoa = _true;
X  } else {
X    PrintHeader (articleFile);
X    (void)fseek (articleFile, startOfBody, 0);
X    position = _atTop;
X  }
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreStop (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  done = _true;
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreCancel (dummy)
Xlong dummy;
X{
X  _FuncName ("MoreCancel");
X  FILE *articlePipe;
X  char commandString[_bufSize];
X  char *copyOfFrom, *author, *fullname, *sitename;
X  extern int errno;
X
X  BackErase (backErase);
X  if (articleNum < 0) {
X    (void) printf ("Not a message, can't cancel.\n");
X  } else {
X    (void) printf ("Cancel\n");
X    copyOfFrom = NULL;
X    if (hdr[_hdrFrom]) {
X      AllocString (&copyOfFrom, hdr[_hdrFrom]);
X      BreakFrom (copyOfFrom, &author, &fullname, &sitename);
X    }
X    /* XXX aide privs */
X    if (rooms[currentRoom].type == _mailType) {
X      if (AnswerYesNo ("Delete this message (Y/[N])? ", _defaultAnswer)
X	  && unlink (articleFileName) != 0) {
X	Log (funcName, __LINE__, LOG_ERROR,
X	     "Can't unlink mail file %s, errno %d.", articleFileName, errno);
X	reading = _false;
X      }
X    } else if ((hdr[_hdrFrom] && author
X	 && (strcmp (author, logname) == 0)
X	 && sitename && (IsIn (_siteName, sitename)))) {
X      if (AnswerYesNo ("Cancel this message (Y/[N])? ", _defaultAnswer)) {
X	(void) sprintf (commandString,
X			"%s -t \"cmsg cancel %s\" -n %s -f unidel",
X			_newsDelivery, hdr[_hdrMessageID], rooms[currentRoom].name);
X	articlePipe = popen (commandString, "w");
X	if (articlePipe == NULL) {
X	  Log (funcName, __LINE__, LOG_ERROR,
X	       "Unable to open pipe to generate cancle article.");
X	  return 0;
X	}
X	(void) fprintf (articlePipe,
X			"Control: cancel %s\n", hdr[_hdrMessageID]);
X	(void) fprintf (articlePipe, "\n");
X	(void) fprintf (articlePipe,
X		"Cancelled using an alpha version of Unidel.\n");
X	(void) fprintf (articlePipe,
X		"RFC violations to unidel@bitsko.slc.ut.us.\n");
X	(void) pclose (articlePipe);
X	reading = _false;
X      }
X    } else
X      (void) printf ("You're not the author!\n");
X    if (copyOfFrom)
X      free ((malloc_t)copyOfFrom);
X  }
X  whoa = _true;
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMorePageDown (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  position = _inMiddle;
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMorePageUp (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  /* XXX go figure */
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreLineDown (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  position = _inMiddle;
X  linesLeft = 1;
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreLineUp (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  /* XXX Go figure */
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreArtNext (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  reading = _false;
X  return (0);
X}
X
X/* ARGSUSED */
Xstatic int
XMoreArtPrev (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  if (position == _atTop) {
X    reading = _false;
X    backFlag = _true;
X  } else {
X    (void) fseek (articleFile, startOfBody, 0);
X    position = _atHeader;
X    bytes = 1;
X  }
X  return (0);
X}
X
X#ifdef REMIND
Xstatic int
XMoreUpdate (dummy)
Xlong dummy;
X{
X  BackErase (backErase);
X  (void) printf ("Update\n");
X  if (articleNum < 0) {
X    (void) printf ("Not a message, can't update.\n");
X  } else {
X    /* XXX Well?  Where's the remind? */
X    /* To anyone who reads this, this is/was supposed to be part of a */
X    /* calendar/reminder/todo/tracking function */
X  }
X  whoa = _true;
X  return (0);
X}
X#endif
X
Xstatic int
XPrintFileLine (file, fileLength, flags, menu)
XFILE *file;
Xlong fileLength, flags;
XMenu *menu;
X{
X  int eof;
X  char *fileLine;
X
X  eof = _true;
X  if ((fileLine = fgetms (file)) != NULL) {
X    bytes += strlen (fileLine);
X    PrintLine (fileLine, (int) (bytes*100/fileLength), flags, menu);
X    eof = !showSig && (strcmp (fileLine, "-- \n") == 0);
X    free ((malloc_t)fileLine);
X  }
X  return (eof ? EOF : 0);
X}
X
Xstatic void
XPrintLine (string, percent, flags, menu)
Xchar *string;
Xint percent;
Xlong flags;
XMenu *menu;
X{
X  char moreBuf[_bufSize];
X  char lineBuf[_bufSize];
X  int i;
X
X  /* XXX implement term width */
X  i = 0;
X  while (*string) {
X    lineBuf[i] = *string;
X    if (++i == 80 || *string == '\n') {
X      lineBuf[i] = '\0';
X      (void) fputs (lineBuf, dumpFile);
X      linesLeft --;
X      if (linesLeft <= 1) {
X	linesLeft = 1;	/* If we don't have a menu, stop next time we do */
X	if (menu && more >= 1) {
X	  linesLeft = lines;
X	  if (percent >= 0)
X	    (void) sprintf (moreBuf, "--More--(%d%%)", percent);
X	  else
X	    (void) strcpy (moreBuf, "--More--");
X	  backErase = strlen (moreBuf);
X	  (void) printf ("%s", moreBuf);
X	  (void) DoMenu (flags, menu);
X	}
X      }
X      i = 0;
X    }
X    string ++;
X  }
X}
X
Xstatic void
XPrintRealShortHeader (hdr, article, flags)
Xchar *hdr[];
Xint article;
Xlong flags;
X{
X  time_t messageTime, parsedate ();
X  struct tm *tm;
X  char *who;
X  char msgDate[128], *copyOfTitle;
X  char articleNumFlags[20], articleNum[20];
X  char string[_bufSize];
X  int i;
X
X  who = Who (hdr[_hdrFrom], hdr[_hdrOrganization], _false);
X  if (who && (strlen (who) > (size_t) _whoLength))
X    who[_whoLength] = '\0';
X
X  if (hdr[_hdrDate]) {
X    messageTime = parsedate (hdr[_hdrDate], NULL);
X    tm = localtime (&messageTime);
X    if (messageTime < (time ((long *) 0) - 7 * 24 * 60 * 60))
X      (void) sprintf (msgDate, "%s %2d", months[tm -> tm_mon],
X		      tm -> tm_mday);
X    else
X      (void) sprintf (msgDate, "%s %2d:%2.2d", days[tm -> tm_wday],
X		      tm -> tm_hour, tm -> tm_min);
X  } else {
X    (void) strcpy (msgDate, "(no date)");
X  }
X
X  copyOfTitle = NULL;
X  if (hdr[_hdrSubject]) {
X    AllocString (&copyOfTitle, hdr[_hdrSubject]);
X    if (columns > (22 + _whoLength)) {
X      if (strlen (copyOfTitle) > (size_t) (columns - (22 + _whoLength)))
X	/* truncate title so we don't wrap */
X	copyOfTitle[columns - (22 + _whoLength)] = '\0';
X    }
X  }
X
X  i = 0;
X  if (articles[article].read)
X    articleNumFlags[i++] = 'O';
X  if (articles[article].checked)
X    articleNumFlags[i++] = 'X';
X  if (hdr[_hdrPriority] && (hdr[_hdrPriority][0] >= '0') && (hdr[_hdrPriority][0] <= '9') && (atoi (hdr[_hdrPriority]) < 10))
X    articleNumFlags[i++] = hdr[_hdrPriority][0];
X  else if (hdr[_hdrPriority])
X    articleNumFlags[i++] = 'P';
X  if (hdr[_hdrNewsgroups]) {
X    if (BetterNewsgroup (hdr[_hdrNewsgroups]))
X      articleNumFlags[i++] = '<';
X    else if (strchr (hdr[_hdrNewsgroups], ',') != NULL)
X      articleNumFlags[i++] = '>';
X  }
X  for (; i < 7; i ++)
X    articleNumFlags[i] = ' ';
X  (void) sprintf (articleNum, "%7d", article + 1);
X  /* This copies the terminator first, then the num */
X  for (i = 7; (i >= 0) && (articleNum[i] != ' '); i --)
X    articleNumFlags[i] = articleNum[i];
X
X  (void) sprintf (string, "%7s %-18s  %-9s  %s\n", articleNumFlags,
X		 who ? who : "Unknown",
X		 msgDate, copyOfTitle ? copyOfTitle : "");
X  PrintLine (string, -1, flags, headerMenu);
X
X  if (copyOfTitle)
X    free ((malloc_t)copyOfTitle);
X  if (who)
X    free ((malloc_t)who);
X}
X
Xvoid
XPrintShortHeader (hdr, article)
Xchar *hdr[];
Xint article;
X{
X  char tempBuffer[_bufSize * 4], lineBuffer[_bufSize * 4];
X  char *who, *copyOfSubject;
X  time_t messageTime, parsedate ();
X  struct tm *tm;
X  int ii;
X
X  /* XXX wrapped 'from' lines (i.e. with organization), should be left
X                                                  then right justified. */
X
X  if (dumpFile != stdout)
X    /* A marker that can be used to separate messages */
X    (void) fprintf (dumpFile, " ----- \n");
X  if ((article > 0) && rooms[currentRoom].displayNumber
X      || floors[currentFloor].displayNumber) {
X    if (dumpFile == stdout)
X      (void) sprintf (tempBuffer, "%d/%d -- ", article + 1, numArticles);
X    else
X      (void) sprintf (tempBuffer, "%s -- ", hdr[_hdrMessageID]);
X    StrCat (lineBuffer, tempBuffer);
X  } else {
X    StrCat (lineBuffer, seperMsg ? "-- " : "   ");
X  }
X  if (rooms[currentRoom].anonymous || floors[currentFloor].anonymous)
X    StrCat ((char *) NULL, "*****");
X  else {
X    if (hdr[_hdrDate]) {
X      messageTime = parsedate (hdr[_hdrDate], NULL);
X      tm = localtime (&messageTime);
X      (void) sprintf (tempBuffer, "%2d%s%2.2d %d:%2.2d %cm",
X		      tm -> tm_year, months[tm -> tm_mon], tm -> tm_mday,
X		      tm -> tm_hour % 12, tm -> tm_min,
X		      (tm -> tm_hour / 12) == 0 ? 'a' : 'p');
X      StrCat ((char *) NULL, tempBuffer);
X    }
X    who = Who (hdr[_hdrFrom], hdr[_hdrOrganization], _true);
X    if (who) {
X      StrCat ((char *) NULL, " from ");
X      StrCat ((char *) NULL, who);
X      free ((malloc_t)who);
X    }
X  }
X  ii = strlen (lineBuffer);
X  if (seperMsg) {
X    if (ii < 78)
X      lineBuffer[ii++] = ' ';
X    for (; ii < 78; ii ++)
X      lineBuffer[ii] = '-';
X  }
X  lineBuffer[ii++] = '\n';
X  lineBuffer[ii++] = '\0';
X  PrintLine (lineBuffer, 0, 0l, (Menu *) NULL);
X
X  if (hdr[_hdrSubject]) {
X    copyOfSubject = NULL;
X    AllocString (&copyOfSubject, hdr[_hdrSubject]);
X    (void)strcpy (tempBuffer, hdr[_hdrSubject]);
X    for (ii = 0; tempBuffer[ii] != '\0'; ii ++) {
X      if (isupper (tempBuffer[ii]))
X	tempBuffer[ii] = tolower (tempBuffer[ii]);
X    }
X    if ((strcmp (tempBuffer, "(none)") != 0)
X	&& (strcmp (tempBuffer, "re: (none)") != 0)) {
X      (void) sprintf (tempBuffer, "   Subject: %s\n", hdr[_hdrSubject]);
X      PrintLine (tempBuffer, 0, 0l, (Menu *) NULL);
X    }
X    free ((malloc_t)copyOfSubject);
X  }
X  if (hdr[_hdrSummary]) {
X    (void) sprintf (tempBuffer, "   Summary: %s\n", hdr[_hdrSummary]);
X    PrintLine (tempBuffer, 0, 0l, (Menu *) NULL);
X  }
X  if (hdr[_hdrKeywords]) {
X    (void) sprintf (tempBuffer, "   Keywords: %s\n", hdr[_hdrKeywords]);
X    PrintLine (tempBuffer, 0, 0l, (Menu *) NULL);
X  }
X}
X
Xstatic void
XPrintHeader (article)
XFILE *article;
X{
X  char *headerLine;
X  char sigBuffer[_bufSize * 4]; /* XXX Aaack! an arbitrary size! */
X  char *fileLine;
X  int i, aChar;
X
X  (void)fseek (article, 0l, 0);
X  /* Print header of article */
X  while ((headerLine = fgetms (article)) != NULL) {
X    if (headerLine[0] == '\n') {
X      free ((malloc_t)headerLine);
X      break;
X    }
X    PrintLine (headerLine, 0, 0l, (Menu *) NULL);
X    free ((malloc_t)headerLine);
X  }
X
X  /* Print signature of article */
X  while ((fileLine = fgetms (article)) != NULL) {
X    if (strcmp (fileLine, "-- \n") == 0)
X      break;
X    free ((malloc_t)fileLine);
X  }
X  if (fileLine != NULL) {
X    free ((malloc_t)fileLine);
X    PrintLine (signature, 0, 0l, (Menu *) NULL);
X    i = 0;
X    while ((aChar = getc (article)) != EOF) {
X      sigBuffer[i++] = (char) aChar;
X      if (aChar == '\n') {
X	sigBuffer[i] = '\0';
X	PrintLine (sigBuffer, 0, 0l, (Menu *) NULL);
X	i = 0;
X      }
X    }
X  } else {
X    PrintLine ("\n", 0, 0l, (Menu *) NULL);
X  }
X}
X
Xstatic void
XBackErase (characters)
Xint characters;
X{
X  char lineBuf[_bufSize*3], *ptr;
X
X  ptr = lineBuf;
X  while (characters--) {
X    *ptr++ = '\b';
X    *ptr++ = ' ';
X    *ptr++ = '\b';
X  }
X  *ptr = '\0';
X  (void) fputs (lineBuf, stdout);
X}
X
Xstatic int
XBetterNewsgroup (string)
X/* returns _true if there is a more appropriate newsgroup for this */
X/* article than the current one  */
Xchar *string;
X{
X  char *copyOfString, *ptr;
X  int i;
X
X  copyOfString = NULL;
X  AllocString (&copyOfString, string);
X  for (ptr = strtok (copyOfString, ",");
X       ptr != NULL;
X       ptr = strtok ((char *) NULL, ",")) {
X    if (strcmp (rooms[currentRoom].name, ptr) == 0) {
X      free ((malloc_t)copyOfString);
X      return (_false);
X    } else {
X      for (i = 0; rooms[i].name != NULL; i ++) {
X	if (!floors[rooms[i].floor].forgotten && !rooms[i].forgotten
X	    && (strcmp (rooms[i].name, ptr) == 0)) {
X	  free ((malloc_t)copyOfString);
X	  return (_true);
X	}
X      }
X    }
X  }
X  free ((malloc_t)copyOfString);
X  return (_false);
X}
X
Xvoid
XBreakFrom (line, author, fullname, sitename)
Xregister char *line;
Xchar **author, **fullname, **sitename;
X{
X  char *ptr;
X
X  *author = line;
X  *fullname = *sitename = NULL;
X  /*
X    Parse to '@' if "user@site" form, or to '<' if
X    "full name <address>" form, or to '!' if it's
X    siteA!siteB!user
X    */
X  while (*line && *line != '@' && *line != '<' && *line != '!')
X    line ++;
X
X  switch (*line) {
X  case '@':	/* It's "user@site" */
X    *line++ = '\0';
X    *sitename = line;
X    while (*line && !(*line == ' ' || *line == '\t'))
X      line ++;
X    if (*line) {	/* Wow! there might be a "(full name)" */
X      *line++ = '\0';
X      while (*line && (*line == ' ' || *line == '\t'))
X	line ++;
X      if (*line++ == '(') {	/* Yep! */
X	*fullname = line;
X	while (*line)		/* We'll go straight to the end */
X	  line ++;
X	if (*(--line) == ')')	/* and work back one */
X	  *line = '\0';
X      }
X    }
X    break;
X  case '<':	/* It's "full name <address>" */
X    *fullname = *author;
X    ptr = line - 1;
X    /* clear any trailing spaces on full name */
X    while (ptr > *fullname && (*ptr == ' ' || *ptr == '\t'))
X      *ptr-- = '\0';
X    line++;
X    *author = line;
X    while (*line && *line != '@' && *line != '!')
X      line ++;
X    if (*line == '@') {	/* 'address' is "user@site" */
X      *line ++ = '\0';
X      *sitename = line;
X      while (*line && *line != '>')
X	line ++;
X    } else if (*line == '!') {	/* 'address' is "siteA!siteB!user" */
X      *sitename = *author;
X      *author = line + 1;
X      *line ++ = '\0';
X      while (*line && *line != '>') {
X	if (*line == '!') {
X	  *sitename = *author;
X	  *author = line + 1;
X	  *line = '\0';
X	}
X	line ++;
X      }
X    }
X    *line = '\0';
X    break;
X  case '!':	/* It's "siteA!siteB!user" */
X    *sitename = *author;
X    *author = line + 1;
X    *line ++ = '\0';
X    while (*line && *line != ' ' && *line != '\t') {
X      if (*line == '!') {
X	*sitename = *author;
X	*author = line + 1;
X	*line = '\0';
X      }
X      line ++;
X    }
X    if (*line) {	/* Wow! there might be a "(full name)" */
X      *line++ = '\0';
X      while (*line && (*line == ' ' || *line == '\t'))
X	line ++;
X      if (*line++ == '(') {	/* Yep! */
X	*fullname = line;
X	while (*line)		/* We'll go straight to the end */
X	  line ++;
X	if (*(--line) == ')')	/* and work back one */
X	  *line = '\0';
X      }
X    }
X    break;
X  }
X}
X
Xstatic char *
XWho (from, org, withOrg)
Xchar *from, *org;
Xint withOrg;
X{
X  _FuncName ("Who");
X  char *who, *copyOfFrom, *author, *fullname, *sitename;
X  int i;
X
X  copyOfFrom = NULL;
X  if (from) {
X    AllocString (&copyOfFrom, from);
X    BreakFrom (copyOfFrom, &author, &fullname, &sitename);
X  } else
X    author = fullname = sitename = NULL;
X
X  if (fullname)
X    author = fullname;
X
X  if (org) {
X    if (strcmp (org, _organization) == 0)
X      sitename = NULL;	/* Don't use org if it's ours */
X    else
X      sitename = org;
X  }
X
X  if (sitename && !author)
X    author = "Unknown";
X
X  if (author) {
X    i = strlen (author);
X    who = (char *)malloc ((size_t) (i + 1 + (sitename
X					     ? strlen (sitename) + 1 : 0)));
X    if (!who) {
X      Log (funcName, __LINE__, LOG_ERROR, _outOfMemory);
X      exit (1);
X    }
X    (void)strcpy (who, author);
X    if (withOrg && sitename) {
X      who[i] = '@';
X      (void)strcpy (&who[i+1], sitename);
X    }
X  } else
X    who = NULL;
X
X  if (copyOfFrom)
X    free ((malloc_t)copyOfFrom);
X
X  return (who);
X}
SHAR_EOF
$TOUCH -am 0502135492 unidel/ReadMesg.c &&
chmod 0444 unidel/ReadMesg.c ||
echo "restore of unidel/ReadMesg.c failed"
set `wc -c unidel/ReadMesg.c`;Wc_c=$1
if test "$Wc_c" != "21909"; then
	echo original size 21909, current size $Wc_c
fi
# ============= unidel/Room.c ==============
echo "x - extracting unidel/Room.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > unidel/Room.c &&
X/*
X * NAME
X *     Room.c -- Room handling functions
X *
X * AUTHOR
X *     Ken MacLeod
X */
X
X#ifndef lint
Xstatic char sccsId [] = "@(#) Room.c  1.5 02 May 1992 10:03:51\n\t";
X#endif
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <dirent.h>
X#include <ctype.h>
X#include <string.h>
X#include <malloc.h>
X#include "protos.h"
X#include "Log.h"
X#include "defs.h"
X#include "Unidel.h"
X#include "User.h"
X#include "Message.h"
X#include "Room.h"
X
XArticle *articles = NULL;
Xint numArticles;
Xstatic void GotoNew ();
Xstatic void LoadArticles ();
Xstatic void MarkBits ();
X
X/* Enter, i.e. create, a new room */
X/* ARGSUSED */
Xint
XEnterRoom (dummy)
Xlong dummy;
X{
X  _FuncName ("EnterRoom");
X  FILE *mailPipe;
X  char roomDescription[_bufSize], roomName[_bufSize], commandString[_bufSize];
X  int private, fileRoom;
X
X  /* XXX can we create new rooms in this floor or below this room? */
X  (void) printf ("What is the new room's name? ");
X  GetString (roomName, _lowerCase);
X  (void) printf ("Give a short description of the room:\n: ");
X  GetString (roomDescription, _normal);
X  private = AnswerYesNo ("Is this a private room (Y/[N])? ", _defaultAnswer);
X  if (private == -1)
X    return 0;
X  fileRoom = AnswerYesNo ("Is this a file room (Y/[N])? ", _defaultAnswer);
X  if (fileRoom == -1)
X    return 0;
X  (void) printf ("\nDue to a small problem, new rooms cannot be automatically created,\n");
X  (void) printf ("the sysop will be told about the new room and will mail you when it has\n");
X  (void) printf ("been created.\n");
X
X  (void) sprintf (commandString, "/bin/mail sysop");
X  mailPipe = popen (commandString, "w");
X  if (mailPipe == NULL) {
X    (void) printf ("*** ERROR ***  Unable to mail to sysop, new room message NOT sent.");
X    Log (funcName, __LINE__, LOG_ERROR, "Cannot open pipe to 'mail'");
X    return 0;
X  }
X  (void) fprintf (mailPipe, "To: sysop@%s\n", _siteName);
X  (void) fprintf (mailPipe, "From: %s@%s\n", logname, _siteName);
X  (void) fprintf (mailPipe, "Sender: unidel@%s\n", _siteName);
X  (void) fprintf (mailPipe, "Subject: Request for room creation.\n");
X  (void) fprintf (mailPipe, "\n");
X  (void) fprintf (mailPipe, "#ROOM %s\n", roomName);
X  (void) fprintf (mailPipe, "#DESC %s\n", roomDescription);
X  (void) fprintf (mailPipe, "#PRIV %s\n", private ? "Yes" : "No");
X  (void) fprintf (mailPipe, "#FILE %s\n", fileRoom ? "Yes" : "No");
X  (void) pclose (mailPipe);
X
X  return 0;
X}
X
Xchar *
XRoomNameString (room)
Xunsigned int room;
X{
X  static char roomName[_bufSize];	/* XXX Can we allocate this? */
X
X  if (rooms[room].description
X      && (strlen (rooms[room].description) < (size_t) _aliasLength))
X    (void)strcpy (roomName, rooms[room].description);
X  else
X    (void)strcpy (roomName, rooms[room].name);
X
X  (void)strcat (roomName, (rooms[room].fileDir != NULL) ? "]" : ">");
X	
X  return (roomName);
X}
X
X/* ARGSUSED */
Xint
XPrintForgottenRooms (dummy)
Xlong dummy;
X{
X  unsigned int room, floor;
X
X  (void) printf ("*** Forgotten public rooms:\n");
X  Columnize ((char *) NULL);
X  for (room = 0; rooms[room].name != NULL; room ++) {
X    if ((rooms[room].forgotten && !rooms[room].private)
X	&& (!floorMode || rooms[room].floor == currentFloor)
X	&& (rooms[room].inviteOnly == rooms[room].invited)) {
X      Columnize (RoomNameString (room));
X    }
X  }
X  (void) printf ("\n");
X  if (floorMode) {
X    (void) printf ("*** Forgotten public floors:\n");
X    Columnize ((char *) NULL);
X    for (floor = 0; floors[floor].name != NULL; floor ++) {
X      if (floors[floor].forgotten && !floors[floor].private
X	  && (floors[floor].inviteOnly == floors[floor].invited)) {
X	if (floors[floor].description)
X	  Columnize (floors[floor].description);
X	else
X	  Columnize (floors[floor].name);
X      }
X    }
X    (void) printf ("\n");
X  }
X
X  return 0;
X}
X
Xint
XKnownRooms ()
X{
X  unsigned int i;
X
X  PrintRoomsWithUnreadMessages ();
X  (void) printf ("You have read all messages in these rooms:\n");
X  Columnize ((char *) NULL);
X  for (i = 0; rooms[i].name != NULL; i ++) {
X    if (!rooms[i].forgotten && !floors[rooms[i].floor].forgotten
X	&& !rooms[i].newArticles
X	&& (!floorMode || rooms[i].floor == currentFloor)
X	&& (rooms[i].inviteOnly == rooms[i].invited)) {
X      Columnize (RoomNameString (i));
X    }
X  }
X  (void) printf ("\n");
X
X  return 0;
X}
X
Xvoid
XPrintRoomsWithUnreadMessages ()
X{
X  unsigned int room, floor;
X
X  (void) printf ("There are unread messages in these rooms:\n");
X  Columnize ((char *) NULL);
X  for (room = 0; rooms[room].name != NULL; room ++) {
X    if (!rooms[room].forgotten && !floors[rooms[room].floor].forgotten
X	&& rooms[room].newArticles
X	&& (!floorMode || rooms[room].floor == currentFloor)
X	&& (rooms[room].inviteOnly == rooms[room].invited)) {
X      Columnize (RoomNameString (room));
X    }
X  }
X  (void) printf ("\n");
X  if (floorMode) {
X    (void) printf ("There are unread messages on these floors:\n");
X    Columnize ((char *) NULL);
X    for (floor = 0; floors[floor].name != NULL; floor ++) {
X      if (!floors[floor].forgotten && floor != currentFloor) {
X	for (room = 0; rooms[room].name != NULL; room ++) {
X	  if (!rooms[room].forgotten && rooms[room].newArticles
X	      && rooms[room].floor == floor
X	      && (rooms[room].inviteOnly == rooms[room].invited)) {
X	    if (floors[floor].description != NULL)
X	      Columnize (floors[floor].description);
X	    else
X	      Columnize (floors[floor].name);
X	    break;
X	  }
X	}
X      }
X    }
X  }
X  (void) printf ("\n");
X}
X
Xstatic void
XMarkAllRead ()
X{
X  int i;
X  char articlesRead[512];
X
X  for (i = 0; i < numArticles; i ++)
X    articles[i].read = 1;
X  (void) sprintf (articlesRead, "1-%ld",
X		  i ? articles[i-1].number : rooms[currentRoom].highest);
X  rooms[currentRoom].newArticles = 0;
X  AllocString (&rooms[currentRoom].articlesRead, articlesRead);
X}
X
Xvoid
XMarkRead ()
X{
X  /* XXX individual articles should be noted as being read */
X}
X
Xint
XFindRoomCommand (type)
Xlong type;
X{
X  char roomName[_bufSize];
X  unsigned int room;
X
X  GetString (roomName, _lowerCase);
X  if (roomName[0]) {
X    if ((room = FindRoom (type, roomName)) == -1)
X      (void) printf ("No room matching '%s'.\n", roomName);
X    else
X      (void) printf ("%s\n", RoomNameString (room));
X  }
X
X  return 0;
X}
X
Xint
XFindRoom (type, roomName)
Xlong type;
Xchar *roomName;
X{
X  register int room;
X  char copyOfRoomName[_bufSize], copyOfRoomsName[_bufSize];
X  char *ptr;
X  int roomNameLength, i;
X
X  if (rooms[0].name == NULL)	/* Dirty, lazy fix for ReadNewsFile */
X    return (-1);
X
X  /* convert room name to lower case */
X  for (i = 0; roomName[i]; i ++) {
X    if (isupper (roomName[i]))
X      roomName[i] = tolower (roomName[i]);
X  }
X
X  /* search for exact match first */
X  room = currentRoom;
X  do {
X    room ++;
X    if (rooms[room].name == NULL)
X      room = 0;
X    if (strcmp (rooms[room].name, roomName) == 0)
X      return (room);
X  } while (room != currentRoom);
X  if (type == _findExact)
X    return (-1);
X
X  /* then search for a partial match if we didn't find an exact match */
X  (void)strcpy (copyOfRoomName, roomName);	/* convert to lower case */
X  for (i = 0; copyOfRoomName[i]; i ++) {
X    if (isupper (copyOfRoomName[i]))
X      copyOfRoomName[i] = tolower (copyOfRoomName[i]);
X  }
X  roomNameLength = strlen (roomName);
X  room = currentRoom;
X  do {
X    room ++;
X    if (rooms[room].name == NULL)
X      room = 0;
X    if (rooms[room].description
X	&& (strlen (rooms[room].description) < (size_t) _aliasLength)) {
X      /* convert to lower case */
X      (void)strcpy (copyOfRoomsName, rooms[room].description);
X      for (i = 0; copyOfRoomsName[i]; i ++) {
X	if (isupper (copyOfRoomsName[i]))
X	  copyOfRoomsName[i] = tolower (copyOfRoomsName[i]);
X      }
X      /* we prime this for the 'strchr' call */
X      ptr = copyOfRoomsName - 1;
X      while ((ptr = strchr (ptr + 1, copyOfRoomName[0])) != NULL) {
X	if (strncmp (ptr, copyOfRoomName, roomNameLength) == 0)
X	  return (room);
X      }
X    }
X    /* convert to lower case */
X    (void)strcpy (copyOfRoomsName, rooms[room].name);
X    for (i = 0; copyOfRoomsName[i]; i ++) {
X      if (isupper (copyOfRoomsName[i]))
X	copyOfRoomsName[i] = tolower (copyOfRoomsName[i]);
X    }
X    ptr = copyOfRoomsName - 1;	/* we prime this for the 'strchr' call */
X    while ((ptr = strchr (ptr + 1, copyOfRoomName[0])) != NULL) {
X      if (strncmp (ptr, copyOfRoomName, roomNameLength) == 0)
X	return (room);
X    }
X  } while (room != currentRoom);
X  return (-1);
X}
X
Xint
XGotoCommand (type)
Xlong type;
X{
X  char roomName[_bufSize];
X  int room;
X
X  switch ((int) type) {
X  case 0:	/* Yawn, mark everything read an let's get out of here */
X    MarkAllRead ();
X    GotoNew ();
X    break;
X  case 1:	/* Mark only what we have read */
X    MarkRead ();
X    GotoNew ();
X    break;
X  case 2:	/* Don't mark anything read, as if we never came here */
X    GotoNew ();
X    break;
X  case 3:	/* Forget this room, mark everything read on the way out */
X    rooms[currentRoom].forgotten = _true;
X    MarkAllRead ();
X    GotoNew ();
X    break;
X  case 4:	/* Skip this room, go directly to 'room' */
X  case 5:	/* Ungoto this room, go directly to 'room' */
X  case 6:	/* Go directly to room */
X    if (type != 6l)
X      (void) printf (" %s goto ", RoomNameString (currentRoom));
X    GetString (roomName, _lowerCase);
X    room = FindRoom (_findPartial, roomName);
X    if (room == -1) {
X      (void) printf ("No room matching '%s'.\n", roomName);
X    } else {
X      if (rooms[room].inviteOnly != rooms[room].invited)
X	(void) printf ("No room matching '%s'.\n", roomName);
X      else {
X	if (type != 5l)
X	  MarkRead ();
X	Goto (room);
X      }
X    }
X    break;
X  }
X
X  return 0;
X}
X
Xstatic void
XGotoNew ()
X{
X  int floor, room, done;
X
X  floor = currentFloor;
X  done = _false;
X  do {
X    room = currentRoom;
X    do {
X      /* if floor and room room are not forgotten, and there are new
X       * articles, and we're on the right floor (or not in floor mode)
X       * then we're done
X       */
X      if ((!rooms[room].forgotten && !floors[rooms[room].floor].forgotten)
X	  && rooms[room].newArticles
X	  && (!floorMode || rooms[room].floor == floor)
X	  && (rooms[room].inviteOnly == rooms[room].invited))
X	done = _true;
X      else
X	room ++;
X      if (rooms[room].name == NULL)
X	room = 0;
X    } while (!done && room != currentRoom);
X    if (!done && floorMode) {
X      do {
X	floor ++;
X	if (floors[floor].name == NULL)
X	  floor = 0;
X      } while (floors[floor].forgotten);
X    }
X  } while (!done && floorMode && floor != currentFloor);
X  if (room == currentRoom)
X    room = 0;
X  Goto (room);
X}
X
Xvoid
XGoto (room)
Xint room;
X{
X  int newArticles, i;
X  int previousFloor;
X
X  if (rooms[room].inviteOnly != rooms[room].invited)
X    return;
X
X  previousRoom = currentRoom;
X  previousFloor = currentFloor;
X  currentRoom = room;
X  currentFloor = rooms[currentRoom].floor;
X  rooms[currentRoom].forgotten = _false;
X  floors[currentFloor].forgotten = _false;
X  LoadArticles ();
X  MarkBits (rooms[currentRoom].articlesRead);
X  (void) printf ("%s", RoomNameString (currentRoom));
X  if (rooms[currentRoom].description
X      && !(strlen (rooms[currentRoom].description) < (size_t) _aliasLength))
X    (void) printf ("\t%s", rooms[currentRoom].description);
X  (void) printf ("\n");
X  if (floorMode && (previousFloor != currentFloor)) {
X#if 0
X    (void) printf ("[%s", floors[currentFloor].name);
X    if (floors[currentFloor].description) {
X      (void) printf ("%s", floors[currentFloor].description);
X    }
X    (void) printf ("]\n");
X#endif
X    if (floors[currentFloor].description)
X      (void) printf ("[%s]\n", floors[currentFloor].description);
X    else
X      (void) printf ("[%s]\n", floors[currentFloor].name);
X  }
X  (void) printf ("  %d messages\n", numArticles);
X
X  for (newArticles = i = 0; i < numArticles; i ++)
X    if (!articles[i].read)
X      newArticles ++;
X  if (newArticles != 0) {
X    (void) printf ("  %d new\n", newArticles);
X  }
X}
X
Xstatic int IntCompare ();
X
Xstatic void
XLoadArticles ()
X/* load article numbers into articles, and count in numArticles. */
X{
X  _FuncName ("LoadArticles");
X  void qsort ();
X  long atol ();
X  static int maxArticles = 0;
X  DIR *tempDir;
X  struct dirent *dirEnt;
X  int n;
X
X  numArticles = 0;
X  if ((tempDir = opendir (RoomPath (rooms[currentRoom].name))) == NULL) {
X    return;
X  }
X  /* slow way to count number of articles in dir */
X  for (n = 0; (dirEnt = readdir (tempDir)) != NULL;) {
X    if ((dirEnt -> d_name[0] >= '0') && (dirEnt -> d_name[0] <= '9')) {
X      if (n == maxArticles) {
X	/* XXX Test numArticles overflow */
X	maxArticles += 50;
X	if (articles == NULL)
X	  articles = (Article *) malloc ((size_t) maxArticles
X					 * sizeof (Article));
X	else
X	  articles = (Article *) realloc ((malloc_t) articles,
X					  (size_t) maxArticles
X					  * sizeof (Article));
X	if (articles == NULL) {
X	  Log (funcName, __LINE__, LOG_ERROR, 
X	       "Out of memory (too many articles, %d, in %s).",
X	       maxArticles, rooms[currentRoom].name);
X	  exit (1);
X	}
X      }
X      articles[n].number = atol (dirEnt -> d_name);
X      articles[n].checked = 0;
X      articles[n++].read = 0;
X    }
X  }
X  (void)closedir (tempDir);
X  numArticles = n;
X  if (n == 0)
X    return;
X  qsort ((void *) articles, (size_t) n, sizeof (*articles), IntCompare);
X  return;
X}
X
Xstatic int
XIntCompare (item1, item2)
Xlong *item1, *item2;
X{
X  /* XXX Better way to return an int indicator for the two longs? */
X  long result;
X
X  result = *item1 - *item2;
X  if (result != 0l)
X    return (result < 0l ? -1 : 1);
X  else
X    return (0);
X}
X
Xchar *
XRoomPath (string)
Xchar *string;
X{
X  /* XXX Can we allocate this? */
X  static char returnString[_bufSize];
X  register char *ptr;
X
X  if (rooms[currentRoom].type == _mailType) {
X    (void) sprintf (returnString, "%s/mail/%s", homeDir, string);
X  } else {
X    (void) sprintf (returnString, "%s/%s", _spoolDir, string);
X  }
X  for (ptr = returnString; *ptr; ptr ++) {
X    if (*ptr == '.')
X      *ptr = '/';
X  }
X  return (returnString);
X}
X
X/* articles on-line are already marked, now we'll
X   clear the ones we've read, leaving unread articles marked */
Xstatic void
XMarkBits (string)
Xchar *string;
X{
X  register int i;
X  long next, atol ();
X  int dashed;
X
X  i = 0;
X  dashed = 0;
X  while (*string && i < numArticles) {
X    while (*string && !isdigit (*string))
X      string++;
X    if (!*string)
X      break;
X    next = atol (string);
X    while (i < numArticles && articles[i].number < next)
X      articles[i++].read = dashed;
X    if (i < numArticles && articles[i].number == next)
X      articles[i++].read = 1;
X    while (!ispunct (*string) && *string)
X      string++;
X    dashed = *string == '-';
X  }
X}
SHAR_EOF
$TOUCH -am 0502135492 unidel/Room.c &&
chmod 0444 unidel/Room.c ||
echo "restore of unidel/Room.c failed"
set `wc -c unidel/Room.c`;Wc_c=$1
if test "$Wc_c" != "14441"; then
	echo original size 14441, current size $Wc_c
fi
# ============= unidel/Room.h ==============
echo "x - extracting unidel/Room.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > unidel/Room.h &&
X/*
X * NAME
X *     Room.h -- Room definitions
X *
X * AUTHOR
X *     Ken MacLeod
X *
X * SCCS
X *     @(#) Room.h  1.3 25 Nov 1991 23:23:20 
X */
X
Xtypedef struct {
X	char *name;			/* From '.newsrc' or 'active' */
X	char *articlesRead;		/* ascii bitmap of articles read, "1-50,60-70,80,90-1000", from '.newsrc' */
X	char *originalArticlesRead;	/* original bitmap, for ungoto */
X	char *fileDir;			/* file directory if there is one, from 'fileDirs' */
X	char *description;		/* description, if there is one, from 'newsgroups' */
X	short floor;			/* floor this room is on */
X	char type;				/* Index into type table */
X	unsigned int forgotten:1;		/* room is forgotten, from '.newsrc' or 'F' in 'active' */
X	unsigned int readOnly:1;		/* Read only room, from 'R' in 'active' */
X	unsigned int private:1;			/* Private room, from 'P' in 'active' */
X	unsigned int inviteOnly:1;		/* By invite only, from 'I' in 'active' */
X	unsigned int anonymous:1;		/* Anonymous room, from 'A' in 'active' */
X	unsigned int moderated:1;		/* Room is moderated, from 'M' in 'active' */
X	unsigned int localOnly:1;		/* Local posting only, from 'L' in 'active' */
X	unsigned int displayNumber:1;		/* Display article number, from 'D' in 'active' */
X	unsigned int displaySubject:1;		/* Display/require message subject, from 'S' in 'active' */
X	unsigned int invited:1;			/* Has been invited to an invite only room, from 'Invited: ' in '.unidelrc' */
X	unsigned int newArticles:1;		/* room has new articles, the union of !articlesRead and lowest-highest */
X	unsigned int lowest, highest;	/* lowest and highest articles in the room, from 'active' */
X} Room;
X
X#define _deadType 0
X#define _newsType 1
X#define _mailType 2
X#define _numRoomTypes 3
X
Xtypedef struct {
X	char *name;			/* from 'Floors' */
X	short nameLength;		/* length of name, used for strncmp when comparing room names */
X	char *description;		/* Verbose floor name, from 'Floors' */
X	unsigned int forgotten:1;		/* Floor is forgotten, set from 'F' in 'Floors' cleared by 'KnownFloors:' in '.unidelrc' */
X	unsigned int readOnly:1;		/* Read only floor, from 'R' in 'Floors' */
X	unsigned int allowNewRooms:1;		/* New rooms allowed to be created in this floor, from 'N' in 'Floors' */
X	unsigned int inviteOnly:1;		/* By invite only, from 'I' in 'Floors' */
X	unsigned int anonymous:1;		/* Anonymous floor, from 'A' in 'Floors' */
X	unsigned int moderated:1;		/* Moderated floor, from 'M' in 'Floors' */
X	unsigned int localOnly:1;		/* Local posting only, from 'L' in 'Floors' */
X	unsigned int private:1;			/* Private room, from 'P' in 'Floors' */
X	unsigned int displayNumber:1;		/* Display article number, from 'D' in 'Floors' */
X	unsigned int displaySubject:1;		/* Display/require message subject, from 'S' in 'Floors' */
X	unsigned int invited:1;			/* Has been invited to an invite only floor, from 'Invited: ' in '.unidelrc' */
X} Floor;
X
Xchar *RoomPath (), *RoomNameString ();
X
Xextern Room *rooms;
Xextern Floor *floors;
X
X/* Room by room information */
Xextern unsigned int currentFloor;
Xextern unsigned int currentRoom, previousRoom;
SHAR_EOF
$TOUCH -am 0502135492 unidel/Room.h &&
chmod 0444 unidel/Room.h ||
echo "restore of unidel/Room.h failed"
set `wc -c unidel/Room.h`;Wc_c=$1
if test "$Wc_c" != "3032"; then
	echo original size 3032, current size $Wc_c
fi
# ============= unidel/Strings.c ==============
echo "x - extracting unidel/Strings.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > unidel/Strings.c &&
X/*
X * NAME
X *     Strings.c -- string manipulation and storage functions
X *
X * AUTHOR
X *     Ken MacLeod
X */
X
X#ifndef lint
Xstatic char sccsId [] = "@(#) Strings.c  1.7 02 May 1992 10:05:31\n\t";
X#endif
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <malloc.h>
X#include <ctype.h>
X#include <string.h>
X#include <varargs.h>
X#include "defs.h"
X#include "Unidel.h"
X#include "Log.h"
X
Xchar *
XKeepString (string, length)
Xchar *string;
Xint length;
X{
X  _FuncName ("KeepString");
X  static char *ptr = NULL;
X  static int whatsLeft;
X  char *ptr1;
X
X  length ++;
X  if (ptr == NULL || (whatsLeft - length) < 0) {
X    if (length >= _stringBlockAllocation) {
X      ptr1 = (char *)malloc ((size_t) length);
X      if (ptr1 == NULL) {
X	Log (funcName, __LINE__, LOG_ERROR, _outOfMemory);
X	exit (1);
X      }
X      return strcpy (ptr1, string);
X    }
X    ptr = (char *)malloc ((size_t)_stringBlockAllocation);
X    if (ptr == NULL) {
X      Log (funcName, __LINE__, LOG_ERROR, _outOfMemory);
X      exit (1);
X    }
X    whatsLeft = _stringBlockAllocation;
X  }
X  ptr1 = strcpy (ptr, string);
X  ptr += length;
X  whatsLeft -= length;
X  return ptr1;
X}
X
Xvoid
XAllocString (destination, source)
Xchar *source, **destination;
X{
X  _FuncName ("AllocString");
X  if (*destination == NULL)
X    *destination = (char *)malloc ((size_t) (strlen (source) + 1));
X  else
X    *destination = (char *)realloc ((malloc_t)*destination,
X				    (size_t) (strlen (source) + 1));
X  if (*destination == NULL) {
X    Log (funcName, __LINE__, LOG_ERROR, _outOfMemory);
X    exit (1);
X  }
X  (void) strcpy (*destination, source);
X}
X
Xchar *
XToLower (string)
Xregister char *string;
X{
X  char *oldString;
X
X  oldString = string;
X
X  while (*string) {
X    if (isupper (*string))
X      *string = tolower (*string);
X    string ++;
X  }
X
X  return (oldString);
X}
X
Xint
XIsIn (string1, string2)
Xchar *string1, *string2;
X{
X  char copyOfString2[_bufSize], *ptr2;
X  int lengthOfString1;
X
X  if (*string1 == '\0')
X    return (_true);
X
X  lengthOfString1 = strlen (string1);
X  (void) strcpy (copyOfString2, string2);
X  ptr2 = ToLower (copyOfString2);
X  while ((ptr2 = strchr (ptr2, *string1)) != NULL) {
X    if (strncmp (ptr2, string1, lengthOfString1) == 0)
X      return (_true);
X    ptr2 ++;
X  }
X  return (_false);
X}
X
Xvoid
XStrCat (string1, string2)
Xchar *string1;
Xregister char *string2;
X{
X  static char *ptr = NULL;
X  register char *ptr1;
X
X  if (string1 != NULL)
X    ptr = string1;
X
X  ptr1 = ptr;	/* get it into a register */
X  while (*ptr1++ = *string2++)
X    ;
X  ptr = --ptr1;
X}
X
Xint
Xsplit (string, array, num, delim)
Xchar *string, *delim, **array;
Xint num;
X{
X  register char *ss, *dd;
X  register int nn;
X  int n2;
X
X  ss = string;
X  nn = 0;
X  while (*ss != '\0') {
X    if (nn++ < num)
X      *array++ = ss;
X    /*
X     * Scan for the next delimiter in string (ss)
X     */
X    for (; *ss != '\0'; ss ++) {
X      /* Scan for "this" character (*ss) in delim (dd) */
X      dd = delim;
X      while ((*dd != '\0') && (*ss != *dd))
X	dd ++;
X
X      if (*ss == *dd) {
X	*ss = '\0';
X	ss ++;
X	if (*ss == '\0')	/* Case where delim is last char */
X	  nn++;
X	break;
X      }
X    }
X  }
X  /*
X   * Last substring delimited by '\0'
X   */
X  if (nn < num)
X    *array++ = ss;
X
X  /*
X   * Put NULL in remaining array positions.
X   */
X  for (n2 = nn; n2 < num; n2 ++)
X    *array++ = (char *) 0;
X
X  return (nn);
X}
X
Xint
X/*splitl (string, delim, arg0, ..., argn, (char *) 0)*/
Xsplitl (va_alist)
Xva_dcl
X{
X  register char *ss, *dd, *delim;
X  register int nn, pastArgn;
X  char **arg;
X  va_list pvar;
X
X
X  va_start (pvar);
X
X  /*LINTED va_arg */
X  ss = va_arg (pvar, char *);
X  /*LINTED va_arg */
X  delim = va_arg (pvar, char *);
X
X  pastArgn = 0;
X  nn = 0;
X  while (*ss != '\0') {
X    /*LINTED va_arg */
X    if (!pastArgn && ((arg = va_arg (pvar, char **)) != (char **) 0))
X      *arg = ss;
X    pastArgn = arg == (char **)0;
X    nn ++;
X    
X    /*
X     * Scan for the next delimiter in string (ss)
X     */
X    for (; *ss != '\0'; ss ++) {
X      /* Scan for "this" character (*ss) in delim (dd) */
X      dd = delim;
X      while ((*dd != '\0') && (*ss != *dd))
X	dd ++;
X
X      if (*ss == *dd) {
X	*ss = '\0';
X	ss ++;
X	if (*ss == '\0')	/* Case where delim is last char */
X	  nn++;
X	break;
X      }
X    }
X  }
X  /*
X   * Last substring delimited by '\0'
X   */
X  /*LINTED va_arg */
X  if (!pastArgn && ((arg = va_arg (pvar, char **)) != (char **) 0))
X    *arg = ss;
X  pastArgn = arg == (char **)0;
X
X  /*
X   * Put NULL in remaining arg positions.
X   */
X  if (!pastArgn)
X    /*LINTED va_arg */
X    while ((arg = va_arg (pvar, char **)) != (char **) 0)
X      *arg = (char *) 0;
X
X  va_end (pvar);
X  
X  return (nn);
X}
SHAR_EOF
$TOUCH -am 0502135492 unidel/Strings.c &&
chmod 0444 unidel/Strings.c ||
echo "restore of unidel/Strings.c failed"
set `wc -c unidel/Strings.c`;Wc_c=$1
if test "$Wc_c" != "4588"; then
	echo original size 4588, current size $Wc_c
fi
echo "End of part 5, continue with part 6"
exit 0
