/****************************************************************************/
/*                                                                          */
/*    Misc.c      :Miscellaneous routines for MaxMail                       */
/*                                                                          */
/****************************************************************************/

#include "MaxMail.h"
#include <sys\timeb.h>

struct user_cfg uscfg;
int do_reset;
int CurColor;
int Hollercount=0;
int Bells;

extern int newuser;
extern int FirstArc;

void _pascal signon()
{
   char *p;
   struct stat fbuf;

   setcolor(TextAttr[HILITE_TEXT]);

   clearscreen();

   sprintf(temp,"Begin, v%2.2f",Version);
   logit(temp,'+');
   if (!newuser) 
      sprintf(temp,"%s logging on",LastUser.name);
   else sprintf(temp,"New User: %s logging on",LastUser.name);
   logit(temp,':');

   strout("MaxMail - a MAXIMUS mail/message archiver utility by Craig Derouen\r\n");
   sprintf(temp,"\t *** Version %2.2f ***\r\n",Version);
   strout(temp);

   setcolor(TextAttr[STD_TEXT]);
   strcpy(temp1,LastUser.name);
   p = strtok(temp1," ");		/* Fetch 1st name only, shows how friendly we are */
   if (USERCFG.calls) {
      sprintf(temp,"\r\nWelcome back to MaxMail, %s.\r\n",p);
      strout(temp);
      sprintf(temp,"You have had %d previous successful downloads. Good luck!\r\n",USERCFG.packcount);
      strout(temp);
   }
   else {
      sprintf(temp,"\r\nWelcome to MaxMail, %s.\r\n",p);
      strout(temp);
  }

/* Now test filedate on Bulletin file. If it is newer than last logon
   of user, display it. */

   if(stat(BullFile,&fbuf) == 0 && !newuser ) {		/* File does exist */
      if ((fbuf.st_mtime >= USERCFG.lasttime ))	{	/* See if changed since last logon */
         logit("User is viewing bulletin file",'#');
         strout("\r\nBulletins:\r\n");
         show_file(BullFile,TRUE);
      }
   }
}

void _pascal menu(void)
{
   int choice;
   int good,x;
   unsigned msgs;
   struct msgupd_st *msgupd;
   time_t testtime;
   time_t thistime;

   choice = TRUE;
   PackDone = FALSE;
   while (choice) {
      if (USERCFG.msgfrmt == 1)
         qwkinit();
      msgs = get_msgcount();
      if (msgs) {
         setcolor(TextAttr[HILITE_TEXT]);
         if (PackDone){
            msgs = msgs < MaxMsgs ? msgs : MaxMsgs;
            sprintf(temp,"\r\nYou have %u messages in %s. Select [T]ransmit to download\r\n",msgs,ArcFile);
            strout(temp);
            strout("or select [R]eset,[A]dd or [D]elete to scan more messages/areas\r\n");
            setcolor(TextAttr[STD_TEXT]);
            strout("\r\nSelect a choice\r\n");
         }
         else {
            setcolor(TextAttr[HILITE_TEXT]);
            sprintf(temp,"\r\nThere are %u new messages in areas:\r\n",msgs);
            strout(temp);
            setcolor(TextAttr[STD_TEXT]);
            show_areas(TRUE);
            if (msgs > MaxMsgs) {
               setcolor(TextAttr[ATTN_TEXT]);
               sprintf(temp,"\r\nSorry, but only %u new messages can be packed up during this run.\r\n",MaxMsgs);
               strout(temp);
               sprintf(temp,"MaxMail will pack up the first %u messages it finds.\r\n",MaxMsgs);
               strout(temp);
            }
            setcolor(TextAttr[HILITE_TEXT]);
            strout("\r\nPress ENTER to capture new messages, or select a choice\r\n");
         }
      }
      else {
         setcolor(TextAttr[HILITE_TEXT]);
         strout("\r\n Sorry, but you have NO new messages to capture\r\n");
         strout(" in the current message areas you have selected.\r\n");
         setcolor(TextAttr[STD_TEXT]);
         strout("\r\nSelect a choice\r\n");
      }
      setcolor(TextAttr[STD_TEXT]);
      strout("-------------------------------------------------------\r\n");
      setcolor(TextAttr[MENU_KEY]);
      strout("[C]hange configuration   [L]ist configuration\r\n");
      strout("[A]dd message areas      [D]elete message areas\r\n");
      strout("[R]eset message pointers [S]can for new messages\r\n");
      if (YellOn) {
         strout("[V]ersion                [Y]ell for sysop\r\n");
         if (!IsLocal) {
            strout("[Q]uit back to Maximus");
            if (Hangup)
               strout("   [G]oodbye, hang up\r\n");
            else strout("\r\n");
         }
         else strout("[Q]uit to DOS\r\n");
      }
      else {
         if (!IsLocal) {
            strout("[V]ersion                [Q]uit back to Maximus\r\n");
            if (Hangup)
               strout("[G]oodbye, hang up\r\n");
         }
         else strout("[V]ersion                [Q]uit to DOS\r\n");
      }
      if (HelpFile[0] != 0) {
         setcolor(TextAttr[HILITE_TEXT]);
         strout("[H]elp\r\n");
      }
      if (USERCFG.msgfrmt == 1) {
         setcolor(TextAttr[HILITE_TEXT]);
         strout("[U]pload REP packet\r\n");
      }
      if (PackDone) {
         setcolor(TextAttr[HILITE_TEXT]);
         strout("[T]ransmit archive\r\n");
      }
      if (LastUser.priv >= ASSTSYSOP) {
         setcolor(TextAttr[STD_TEXT]);
         strout("[Z]Sysop functions\r\n");
      }
      strout("\r\n");
      good = FALSE;
      while (!good) {
         timeremain();
         setcolor(TextAttr[PROMPT_TEXT]);
         strout("Choice --> ");
         ChatOk = TRUE;
         x = chrin();
         if (isalpha(x))
            x &= 0x00df;		/* Force upper case, toupper() has side effects */
         ChatOk = FALSE;
         if(x == '\r')
            x = 'S';
         good = TRUE;
         chrout((char) x);
         strout("\r\n");
         setcolor(TextAttr[STD_TEXT]);
         switch (x) {
            case 'A':
               add_msgareas();
               break;

            case 'C':
               configmenu();
               break;

            case 'D':
               del_msgareas();
               break;

            case 'G':
               if (Hangup) {
                  choice = FALSE;
                  Hangup = 1;
               }
               break;

            case 'H':
            case '?':
               if (HelpFile[0]) {
                  if (show_file(HelpFile,TRUE)) {
                     setcolor(TextAttr[ATTN_TEXT]);
                     strout("\r\nSorry, the help file is missing. Tell the sysop.\r\n");
                     sprintf(temp,"User Helpfile %s is missing",HelpFile);
                     logit(temp,'!');
                     HelpFile[0] = 0;		/* Prevent it from being called again */
                  }
               }
               break;

            case 'L':
               list_config();
               break;

            case 'Q':
               choice = FALSE;
               Hangup = 0;
               if (!packer_del && PackDone) /* Kill any built archives */
                  erase_arc();
               break;

            case 'R':
               edit_msgptrs();
               break;

            case 'S':
               if (msgs) {
               /* Check last called date. Compare it to now and 24hour count */
                  time(&thistime);
                  testtime = thistime - USERCFG.lasttime;		/* How many seconds since last call? */
                  if (testtime < (time_t) (3600 * 24) ) {		/* Seconds in 24 hours */
                     if (!IsLocal && MaxDaily && LastUser.priv < ASSTSYSOP &&
                        (USERCFG.dailycount >= (byte) MaxDaily)) { /* User can not scan */
                        setcolor(TextAttr[ATTN_TEXT]);
                        sprintf(temp,"\r\nSorry, %s, but you have already run MaxMail %d times today.\r\n",
                           USERCFG.name,USERCFG.dailycount);
                        strout(temp);
                        sprintf(temp,"The sysop here only allows %d message runs from MaxMail daily.\r\n",MaxDaily);
                        strout(temp);
                        strout("Try running MaxMail again in 24 hours from now.\r\n");
                     }
                     else {
                        if (scan_msgs() && totmsgs) {
                           if(Packit()) {
                              if (sendit()) {
                                 if(packer_del)
                                    PackDone = FALSE;
                                 choice = FALSE;
                              }
                           }
                        }
                     }
                  }
                  else {
                     USERCFG.dailycount = 0;		/* Reset it then */
                     if (scan_msgs() && totmsgs) {
                        if(Packit()) {
                           if (sendit()) {
                              PackDone = FALSE;
                              choice = FALSE;
                           }
                        }
                     }
                  }
               }
               else good = FALSE;
               break;

            case 'T':
               if (PackDone) {
                  if (sendit()) {
                     PackDone = FALSE;
                     choice = FALSE;
                  }
               }
               else good = FALSE;
               break;

            case 'U':
               if (USERCFG.msgfrmt == 1) 
                  getrep();
               break;

            case 'V':
               ShowVers();
               break;

            case 'Y':
               if (YellOn)
                  Holler();
               break;

            case 'Z':
               if (LastUser.priv >= ASSTSYSOP) 
                  Sys_menu();		/* We won't return from Sysop menu */
               else good = FALSE;
               break;

            case '!':		/* Redraw the full menu */
               good = TRUE;
               break;

            default:
               good = FALSE;
               break;
         }
      }
   }
   if (PackDone && packer_del) {		/* Pack file aborted */
      if (LogMode == VERBOSE) logit("User aborted message pack",'~');
      erase_arc();
      msgupd = MSGUPD_1;
      while (msgupd) {
         msgupd->update = FALSE;
         msgupd = msgupd->next;
      }
   }
   return;
}

void _pascal display_packers(void)
{
   int x,col;
   struct packer_st *pack1;

   pack1 = PACKER_1;

   col = 0;
   for (x = 0; x < totpackers; x++) {
      sprintf(temp,"[%d]%s",x +1,pack1->packname);
      while (strlen(temp) < 32) 
         strcat(temp," ");
      strout(temp);
      if (col++) {
         strout("\r\n");
         col = 0;
      }
      pack1 = pack1->next;
   }
   if (col)
      strout("\r\n");
   return;
}

void _pascal display_protos(void)
{
   int x,col;
   struct proto_st *proto1;

   proto1 = PROTO_1;

   col = 0;
   for (x = 0; x < totprotocols; x++) {
      sprintf(temp,"[%d]%s",x +1,proto1->protoname);
      while (strlen(temp) < 32) 
         strcat(temp," ");
      strout(temp);
      if (col++) {
         strout("\r\n");
         col = 0;
      }
      proto1 = proto1->next;
   }
   if (col)
      strout("\r\n");
   return;
}

/* Find the user in the config file */

int _pascal find_config(int handle,char *name,struct user_cfg *uscfg)
{
   int x,y;

   x = 0;
   do_reset = FALSE;
   lseek(handle,0L,SEEK_SET);		/* Rewind to begginning */
   while (1) {
      y = read(handle,(char *) uscfg,sizeof(struct user_cfg));
      if( y < sizeof(struct user_cfg)) 
         return(-1);
      if (strcmpi(uscfg->name,name) == 0) { /* It's found! */
         return(x);
      }
      x++;		/* Try next user */
   }
}

/* Find a blank slot in the config file */


int _pascal find_blconfig(int handle)
{
   int x,y;

   x = 0;
   lseek(handle,0L,SEEK_SET);		/* Rewind to begginning */
   while (1) {
      y = read(handle,(char *) &uscfg,sizeof(struct user_cfg));
      if( y < sizeof(struct user_cfg)) 
         return(-1);
      if (uscfg.name[0] == 0)  /* It's found! */
         return(x);
      x++;		/* Try next user */
   }
}

int _pascal timeon(void)
{
   clock_t elapsed;
   int t;

   elapsed = clock();
   elapsed = startsecs - elapsed;
   elapsed = elapsed / CLOCKS_PER_SEC;		/* Compute seconds */
   t = (int) (elapsed / 60);
   if (t < 0)
      t = t * (-1);
   return(t);
}

void _pascal timeremain(void)
{
   int x;
   char tstr[18];

   x = LastUser.timeremaining - timeon();		/* Fetch # of minutes on */
   setcolor(TextAttr[TIME_LEFT]);
   sprintf(tstr,"{%d min left} ",x);
   strout(tstr);   
}

struct proto_st * _pascal get_curprotolnk(void)
{
   int x;
   struct proto_st *proto1;
   struct proto_st *prev;

   proto1 = PROTO_1;		/* Point to first 1! */
   for (x = 1; x < (int) USERCFG.protocol && proto1 != NULL; x++) {
      prev = proto1;
      proto1 = proto1->next;
   }
   if (proto1 == NULL) 
      return(prev);
   return(proto1);
}

struct repupl_st * _pascal get_curreplnk(void)
{
   int x;
   struct repupl_st *upl1;
   struct repupl_st *prev;

   upl1 = REPUPL_1;		/* Point to first 1! */
   for (x = 1; x < (int) USERCFG.protocol && upl1 != NULL; x++) {
      prev = upl1;
      upl1 = upl1->next;
   }
   if (upl1 == NULL) 
      return(prev);
   return(upl1);
}

struct packer_st * _pascal get_curpacklnk(void)
{
   int x;
   struct packer_st *pack1;
   struct packer_st *prev;

   if (!USERCFG.packer)
      return NULL;

   pack1 = PACKER_1;		/* Point to first 1! */
   for (x = 1; x < (int) USERCFG.packer && pack1 != NULL; x++) {
      prev = pack1;
      pack1 = pack1->next;
   }
   if (pack1 == NULL) 
      return(prev);
   return(pack1);
}

int _pascal get_kminute(void)
{
   int x,y;

   x = LastUser.flag / 10; 		/* Compute cps */
   y = LastUser.flag;
   switch (LastUser.flag) {
      case 300:
         x -=  (x * 0.08f);		/* Slop off 8% */
         break;

      case 1200:
         x -=  (x * 0.18f);		/* Slop off 18% */
         break;

      case 2400:
         x -=  (x * 0.22f); 
         break;

      case 4800:
         x -=  (x * 0.25f); 
         break;

      case 9600:
         x -= (x * 0.28f);	 
         break;

      default:		/* Anything above 9600 baud */
         x -= (x * 0.28f);	 
         break;
   }
   x *= 60;		/* Compute to minutes */
   return x;
}

/* The sysop menu */
void _pascal Sys_menu(void)
{
   int x,choice,handl1,tuser;
   char *p1;

   choice = TRUE;

   while (choice) {
      setcolor(TextAttr[STD_TEXT]);
      if (SysopMode) {
         strout("\r\n\r\nLocal Sysop menu. Select a choice.\r\n");
         strout("----------------------------------\r\n");
      }
      else {
         strout("\r\n\r\nSysop menu. Select a choice.\r\n");
         strout("----------------------------\r\n");
      }
      setcolor(TextAttr[MENU_KEY]);
      strout("[D]elete a user          [E]rase user msg areas\r\n");
      strout("[F]orce reconfig         [K]ill old users\r\n");
      strout("[L]ist users             [V]iew user msg areas\r\n");
      strout("[Q]uit\r\n");
      if (!SysopMode) {
         strout("\r\n");
         timeremain();
      }
      else strout("[P]ack\r\n\r\n");
      setcolor(TextAttr[PROMPT_TEXT]);
      strout("Choice --> ");
      x = chrin();
      if (isalpha(x))
         x &= 0x00df;
      chrout((char) x);
      strout("\r\n");
      switch (x) {
         case 'D':		/* Delete single users */
            delete_users();
            break;

         case 'E':		/* Erase user message areas */
            if (test_task()) {
               setcolor(TextAttr[ATTN_TEXT]);
               strout("\r\nMaxMail is currently active in another partition. Try later.\r\n");
               break;
            }
            setcolor(TextAttr[PROMPT_TEXT]);
            strout("\r\nForce all users to re-do their msg area selection,\r\n");
            strout("   or just single user? [a,s,Q] ");
            x = chrin();
            if (isalpha(x))
               x &= 0xdf;
            chrout((char) x);
            strout("\r\n");
            if(x == '\r')
               x = 'Q';
            if (x == 'A') {
               if (!do_reset) {
                  strout("Are you sure you want this? ");
                  if (getyn(FALSE))
                     do_reset = reset_config(USRCFG_MSGUPD);
               }
               else {
                  setcolor(TextAttr[ATTN_TEXT]);
                  strout("\r\nYou have already reset all the users during this session.\r\n");
               }
            }
            else if(x == 'S'){		/* Single user reset */
               strout("Enter user's name ---> ");
               strin(temp);
               if (!IsLocal)
                  strout("\r\n");
               p1 = strtok(temp,"\t\r\n");
               if (p1) {
                  handl1 = sopen(CfgFile,O_RDWR | O_BINARY | O_CREAT,SH_DENYNO,S_IREAD);
                  if (handl1 == -1)
                     aborterror(FILEOPEN,"Error opening User config file");
                  tuser = find_config(handl1,p1,&uscfg);
                  if (tuser < 0) {
                     setcolor(TextAttr[ATTN_TEXT]);
                     strout("Sorry but that user name does not exist.\r\n");
                  }
                  else {
                     uscfg.flags |= USRCFG_MSGUPD;
                     lseek(handl1,(long) (tuser * sizeof(struct user_cfg)),SEEK_SET);
                     write(handl1,(struct user_cfg *) &uscfg,sizeof(struct user_cfg));
                  }
                  close(handl1);
               }
            }
            break;

         case 'V':		/* List user message areas */
            examine_user();
            break;

         case 'F':		/* Force all users to re-do configuration */
            if (test_task()) {
               setcolor(TextAttr[ATTN_TEXT]);
               strout("\r\nMaxMail is currently active in another partition. Try later.\r\n");
               break;
            }
            setcolor(TextAttr[PROMPT_TEXT]);
            strout("\r\nForce all users to re-do their configuration,\r\n");
            strout("   or just single user? [a,s,Q] ");
            x = chrin();
            if (isalpha(x))
               x &= 0xdf;
            chrout((char) x);
            strout("\r\n");
            if(x == '\r')
               x = 'Q';
            if (x == 'A') {
               if (!do_reset) {
                  strout("Are you sure you want this? ");
                  if (getyn(FALSE))
                     do_reset = reset_config(USRCFG_FUPD);
               }
               else {
                  setcolor(TextAttr[ATTN_TEXT]);
                  strout("\r\nYou have already reset all the users during this session.\r\n");
               }
            }
            else if(x == 'S'){		/* Single user reset */
               strout("Enter user's name ---> ");
               strin(temp);
               if (!IsLocal)
                  strout("\r\n");
               p1 = strtok(temp,"\t\r\n");
               if (p1) {
                  handl1 = sopen(CfgFile,O_RDWR | O_BINARY | O_CREAT,SH_DENYNO,S_IREAD);
                  if (handl1 == -1)
                     aborterror(FILEOPEN,"Error opening User config file");
                  tuser = find_config(handl1,p1,&uscfg);
                  if (tuser < 0) {
                     setcolor(TextAttr[ATTN_TEXT]);
                     strout("Sorry but that user name does not exist.\r\n");
                  }
                  else {
                     uscfg.flags |= USRCFG_FUPD;
                     lseek(handl1,(long) (tuser * sizeof(struct user_cfg)),SEEK_SET);
                     write(handl1,(struct user_cfg *) &uscfg,sizeof(struct user_cfg));
                  }
                  close(handl1);
               }
            }
            break;

         case 'K':		/* Kill older users */
            setcolor(TextAttr[PROMPT_TEXT]);
            strout("\r\nThis will delete all MaxMail users who are no longer active on your bbs\r\n");
            strout("Are you sure you want this? ");
            if (getyn(FALSE))
               kill_oldusers();
            break;

         case 'L':		/* User stats */
            user_stats();
            break;

         case 'P':
            if (SysopMode)
               PackUsers();
            break;

         case 'Q':		/* Quit */
            choice = FALSE;
            break;
      }
   }
   strout("\r\n\r\n");
   setcolor(TextAttr[STD_TEXT]);
   return;
}

/* Display selected message areas, if flag is TRUE, only areas
    with new messages, else show ALL selected areas */
void _pascal show_areas(int flag)
{
   int x,col,msgpos;
   struct msgupd_st *msgupd;

   msgupd = MSGUPD_1;
   setcolor(TextAttr[HILITE_TEXT]);
   while (msgupd) {
      msgpos = 0;
      col = 0;
      while (msgupd && msgpos < 20) {
         fseek(afile,msgupd->areaindex,SEEK_SET);
         fread(&AREA,astrlen,1,afile);
         if ((flag && msgupd->msgcount) || !flag) {
            setcolor(TextAttr[STD_TEXT]);
            sprintf(temp,"[%03d]",msgupd->areano);
            strout(temp);
            setcolor(TextAttr[HILITE_TEXT]);
            strncpy(temp1,AREA.msginfo,13);
            temp1[13] = 0;
            sprintf(temp," %s",temp1);
            while (strlen(temp) <= 14) 
               strcat(temp," ");
            strout(temp);
            if (msgupd->msgcount) {
               sprintf(temp,"(%003u new msgs) ",msgupd->msgcount);
               strout(temp);
            }
            else strout("(No new msgs)  ");
      /* Build the flags display */
            temp[0] = 0;
            if (msgupd->attribute & ECHOMAIL)
               strcat(temp,"E");
            else if(msgupd->attribute & SYSMAIL)
               strcat(temp,"S");
            else strcat(temp,"L");
            if (msgupd->flags & AREA_FORCED) 
               strcat(temp,"*");		/* Forced area symbol */
            while (strlen(temp) < 4)
               strcat(temp," ");
            setcolor(TextAttr[PROMPT_TEXT]);
            strout(temp);
            setcolor(TextAttr[HILITE_TEXT]);
            if (col == 1) {
               strout("\r\n");
               col = 0;   
            }
            else col++;
            msgpos++;
         }
         msgupd = msgupd->next;
      }
      if (msgupd) {
         setcolor(TextAttr[STD_TEXT]);
         strout("\r\nPress ESC to abort or any other key to continue");
         x = chrin();
         strout("\r\n");
         if (x == 0x1b)
            msgupd = NULL;
      }
   }
   setcolor(TextAttr[STD_TEXT]);
   if (col)
      strout("\r\n");
}

int _pascal is_selarea(int msgnum)
{
   struct msgupd_st *msgupd;
   int x;

   msgupd = MSGUPD_1;
   x = 1;
   while (msgupd) {
      if (msgupd->areano == (word) msgnum)
         return x;		/* Return linked list number */
      msgupd = msgupd->next;
      x++;
   }
   return(FALSE);
}

void _pascal update_msgs(void)
{
   struct msgupd_st *msgupd;
   int x = FALSE;

   msgupd = MSGUPD_1;
   while (msgupd) {
      if (msgupd->update) {
         msgupd->startmsg += msgupd->readmsgs;
         adjust_lastread(msgupd->msgpath,msgupd->startmsg);
         x = TRUE;
      }
      msgupd = msgupd->next;
   }
   if (x) {
      setcolor(TextAttr[HILITE_TEXT]);
      strout("\r\nYour lastread message pointers are now being updated\r\n");
   }
}

void _pascal adjust_lastread(char *msgpath,word num)
{
   char LastRdFile[65];
   long pos;
   int x;

   if (LastUser.lastread_ptr)
      sprintf(LastRdFile,"%slastread.bbs",msgpath);
   else sprintf(LastRdFile,"%slastread",msgpath);  
   x = sopen(LastRdFile,O_RDWR | O_BINARY,SH_DENYNO,S_IWRITE);	/* Poke into the lastread file */
   if (x >= 0) {		/* Is it there? */
      pos = (long) (LastUser.lastread_ptr * sizeof(word));
      if ((lseek(x,pos,SEEK_SET)) == pos) 
         write(x,(word *) &num,sizeof(word));
      close(x);
   }
}

word _pascal get_lastread(char *msgpath)
{
   char LastRdFile[65];
   int x;
   FILE *rdfile;
   word num;
   long pos;
   struct stat fbuf;

   if (LastUser.lastread_ptr)		/* Is it the #0 user? */
      sprintf(LastRdFile,"%slastread.bbs",msgpath);
   else sprintf(LastRdFile,"%slastread",msgpath);  
   x = sopen(LastRdFile,O_RDONLY | O_BINARY,SH_DENYNO,S_IREAD);	/* Poke into the lastread file */
   if (x >= 0) {		/* Is it there? */
      pos = (long) (LastUser.lastread_ptr * sizeof(word));
      if ((lseek(x,pos,SEEK_SET)) == pos) {
         read(x,(word *) &num,sizeof(word));
         close(x);
      }
      else {		/* We need to append or create file */
         close(x);		/* Re-open as hi level i/o, it's easier */
         stat(LastRdFile,&fbuf);		/* We need size */
         rdfile = _fsopen(LastRdFile,"ab+",SH_DENYNO);		/* Create */
         if (rdfile != NULL) {		/* We have to append lastread file */
            num = (word) (fbuf.st_size / sizeof(word));		/* How many ptrs so far? */
            x = (int) LastUser.lastread_ptr - num;		/* How many to add */
            num = 0;
            while (x) {
               fwrite((word *) &num,sizeof(word),1,rdfile);		/* Append as many as needed */
               x--;
            }
            fclose(rdfile);
         }
         else num = 0;
      }
   }
   else {		/* Create it then */
      num = 0;
      rdfile = _fsopen(LastRdFile,"wb+",SH_DENYNO);		/* Create */
      if (rdfile != NULL) {		/* We may have to build lastread file */
         if (LastUser.lastread_ptr) {		/* Not sysop */
            x = LastUser.lastread_ptr;
            while (x) {
               fwrite((word *) &num,sizeof(word),1,rdfile);
               x--;
            }
         }
         else fwrite((word *) &num,sizeof(word),1,rdfile);
         fclose(rdfile);
      }
   }
   return num;
}

struct msgupd_st * _pascal find_area(int msgnum)
{
   struct msgupd_st *msgst;

   msgst = MSGUPD_1;
   while (msgst) {
      if (msgst->areano == (word) msgnum)
         break;
      msgst = msgst->next;
   }
   return msgst;
}

void _pascal delay_s(unsigned n)
/* n = Number of seconds */
{
   n *= 1000;		/* Convert to seconds */
   delay_ms(n);
}

void _pascal delay_ms(unsigned n)
 /* milleseconds to delay */
{
    struct timeb timebuf;
    long end_time;
    unsigned end_millitm;

    /* get current time and calculate ending time */
    ftime(&timebuf);
    end_time = timebuf.time + (n / 1000);
    end_millitm = timebuf.millitm + (n % 1000);
    if (end_millitm >= 1000) {
        ++end_time;
        end_millitm -= 1000;
    }

    /* loop until ending time reached */
    do ftime(&timebuf);
    while (timebuf.time < end_time ||
          (timebuf.time == end_time && timebuf.millitm < end_millitm));
}

int _pascal find_realuser(char *name,int handle)
{
   int x;
   struct _usr USER;

   lseek(handle,0L,SEEK_SET);
   x = read(handle,(char *) &USER,user_slen);
   while (x == user_slen) {
      if (strcmpi(name,USER.name) == 0) {
         if (USER.flag & UFLAG_deleted)
            return(FALSE);		/* It's there but it is deleted! */
         else return(TRUE);
      }
      x = read(handle,(char *) &USER,user_slen);
   }
   return(FALSE);		/* Couldn't find it */
}

/* Test to see if this message area is a skip one, if so, return TRUE */
int _pascal isskiparea(int msgarea)
{
   int x;

   if (LastUser.priv >= ASSTSYSOP || msgarea == 0)
      return FALSE;	/* Sysops should always be able to get ALL areas */

   for (x = 0; x < SkipCount; x++) {
      if(msgarea == *(SkipAreas[x]))
         return TRUE;
   }
   return FALSE;
}

/* Test to see if this message area is a forced one, if so, return TRUE */
int _pascal isForcearea(int msgarea)
{
   int x;

/*   if (LastUser.priv >= ASSTSYSOP || msgarea == 0)
      return FALSE; */ 	/* Sysops should'nt be forced */

   for (x = 0; x < ForceCount; x++) {
      if(msgarea == *(ForceAreas[x]))
         return TRUE;
   }
   return FALSE;
}

void _pascal MarkForce(void)
{
   int x;
   struct msgupd_st *new;
   struct msgupd_st *msgupd;
   struct msgupd_st *prev;

   for (x = 0; x < ForceCount; x++)  {
      msgmark(*ForceAreas[x]);
      msgupd = MSGUPD_1;
      new = build1_area(*ForceAreas[x],FALSE);
      if (new) {
         USERCFG.totselareas++;
         if (MSGUPD_1 == NULL) 
            MSGUPD_1 = new;
         else {		/* Attach it to the end */
            prev = msgupd;
            while (msgupd) {
               prev = msgupd;
               msgupd = msgupd->next;
            }
            prev->next = new;
         }
         new->next = NULL;
         new->update = FALSE;
      }
   }
}

void _pascal erase_arc()
{
   struct   find_t c_file;
   char tname[13];
   struct msgupd_st *msgupd;


   tempdir();
   

   if (QWKDir[0]) 
      chdir("..");

   unlink(ArcFile);
   unlink(StatName);
   unlink(TxtFile);

   if (QWKDir[0]) 
      chdir(QWKDir);

   if (USERCFG.msgfrmt == 1) {
      if (_dos_findfirst("*.ndx",_A_NORMAL,&c_file) == 0) {
         unlink(c_file.name);
         while (_dos_findnext(&c_file) == 0)
            unlink(c_file.name);
      }
      unlink(ArcFile);
      unlink("Messages.dat");
      unlink("Control.dat");
      unlink("NewFiles.dat");
      sprintf(tname,"%s.rep",BBSid);
      unlink(tname);
   }

   msgupd = MSGUPD_1;
   while (msgupd) {		/* Remove update flag */
      msgupd->update = FALSE;
      msgupd = msgupd->next;
   }

/* Force arc recreation */
   PackDone = FALSE;
   FirstArc = -1;
   homedir();
}

/* Strip leading and trailing whitespace */
char * _pascal stripwhite(char *strng)
{
   char *p,*pp;

   while (*strng && isspace(*strng)) {		/* Trim leading space */
      pp = strng;
      p = strng + 1;
      while (*pp) {		/* Shift up characters */
         *pp = *p;
         p++;
         pp++;
      }
   }

   if (!*strng)		/* We have reached end of string */
      return(NULL);

   p = strng + (strlen(strng) - 1);		/* Point to tail */
   while (isspace(*p)) 
      p--;
   p[1] = 0;		/* Terminate at start of trailing space */
   return(strng);
}

/* Turn on given color attribute for video */
void _pascal setcolor(int color)
{
   byte back,fore;
   byte bit2,bit0;
   char attstr[20];

   fore = (byte) (color & 0x000f);
   back = (byte) (color & 0x00070);
   back = (byte) (back >> 4);
   sprintf(attstr,"\x1b[1;%d;%dm",back+40,fore+30);

   if (LastUser.ansi) {
      printf("\x1b[0m");		/* Reset attributes */
      FossSendStr("\x1b[0m");		/* Reset attributes */
      if (color & 0x0080) {		/* Test blink bit */
         printf("\x1b[5m");
         FossSendStr("\x1b[5m");
      }
      printf(attstr);
      FossSendStr(attstr);
   }

   else if (LastUser.avatar) {
      color |= 0x8;		/* Turn on intensity for foreground */
      printf(attstr); /* Print ANSI instead of avatar to local console */

/* Reverse bits 2 and 0 */
      bit2 = (byte) (color & 0x04);
      bit0 = (byte) (color & 0x01);
      color &= 0xfa;		/* Turn off bit 0 and bit 2 */
      color |= (bit0 << 2);
      color |= (bit2 >> 2);

      sprintf(attstr,"\x16\x01%c",color);
      FossSendStr(attstr);		/* Send out attribute byte */
   }

   CurColor = color;
   if ((word) color == TextAttr[ATTN_TEXT]) {
      FossSendStr("\07");
      if (IsLocal)
         printf("\07");
   }
}

/* Initiate chat mode with user */
void _pascal chat(void)
{
   unsigned ch;
   int key,col;
   int minutes;

/*   if (IsLocal)
      return; */		/* Makes no sense to chat with yourself! */

   logit("Sysop <-> User in chat mode",'#');

   setcolor(TextAttr[HILITE_TEXT]);
   strout("\r\n\r\nCHAT MODE: \r\n");
   col = 0;
   while (1) {
      minutes = timeon();		/* Establish a counter */
      while ((ch = FossGetCh()) == 0xFFFF) {
         check_carrier();
         if (timeon() > minutes + IDLETIME) {		/* User is idle too long */
            aborterror(USERIDLE,NULL);
         }
      }
      minutes = 0;		/* Reset idle time! */
      key = ch & 0xff;
      if (key == '\r') {		/* Carraige return */
         col = 0;
         chrout('\n');
      }
      else if (key == '\b') {		/* Backspace */
         chrout((char) key);
         chrout(' ');
         chrout((char) key);
      }
      else if (key == '\x1b')		/* Escape */
         break;
      else {
         chrout((char) key);
         col++;
         if (col >= 79) {		/* Crude word wrap */
            col = 0;
            chrout('\n');
         }
      }
   }
   logit("Chat end",'#');
   strout("\r\n\r\nEND CHAT:\r\n");
}

void _pascal Holler(void)
{
   int x;
   unsigned ch;

   if (Hollercount >= 3) {
      setcolor(TextAttr[ATTN_TEXT]);
      strout("\r\nYou have already hollered for the sysop 3 times.\r\n");
      return;
   }
   if (!Hollercount)		/* First time */
      logit("User is hollering for sysop",'#');
   setcolor(TextAttr[HILITE_TEXT]);
   strout("\r\nHollering for sysop. Press ESC to abort.....\r\n");
   setcolor(TextAttr[MENU_KEY]);
   printf("(SYSOP) Press ALT-C to start chat\r\n"); /* Show on local console */
   setcolor(TextAttr[HILITE_TEXT]);
   for (x=0; x < Bells; x++) {
      if (!IsLocal) {
         FossSendCh(0x07);
         FossSendCh(0x07);
         FossSendCh(0x07);
         FossSendCh(0x07);
      }
      tone(650,100);		/* Send bell to local console */
      tone(450,100);
      ch = FossGetCh();
      if (ch != 0xffff) {
         if (ch == 0x2e00) {
            chat();
            Hollercount = -1;
            break;
         }
         if ((ch & 0x00ff) == 0x1b)		/* Escape */
            break;
      }
      delay_ms(500);		/* Delay 1/2 second */
   }
   Hollercount++;
}

void _pascal PackUsers(void)
{
   int handl1,handl2,y;
   int old,new;
   char drive[_MAX_DRIVE];
   char dir[_MAX_DIR];
   char fname[_MAX_FNAME];
   char tname[_MAX_PATH];

   handl1 = sopen(CfgFile,O_RDWR | O_BINARY,SH_DENYRW,S_IREAD);
   if (handl1 == -1) {
      setcolor(TextAttr[ATTN_TEXT]);
      strout("\r\nCan't open user file");
      aborterror(FILEOPEN,"Error opening User config file");
   }

   _splitpath(CfgFile,drive,dir,fname,temp);
   sprintf(tname,"%s%s%s.$$$",drive,dir,fname);

   handl2 = sopen(tname,O_CREAT | O_RDWR | O_BINARY,SH_DENYWR,S_IWRITE);
   if (handl2 == -1) {
      close(handl1);
      setcolor(TextAttr[ATTN_TEXT]);
      strout("\r\nCan't open temp file");
      aborterror(FILEOPEN,"Error opening temp User config file");
   }

   old = new = 0;
   y = read(handl1,(char *) &uscfg,sizeof(struct user_cfg));
   while (y == sizeof(struct user_cfg)) {
      old++;
      if (uscfg.name[0]) { /* Write only the current ones */
         write(handl2,(char *) &uscfg,sizeof(struct user_cfg));
         new++;
         strout("*");
      }
      else strout(".");
      y = read(handl1,(char *) &uscfg,sizeof(struct user_cfg));
   }
   strout("\r\n");
   close(handl1);
   close(handl2);
   if (new != old) {
      setcolor(TextAttr[HILITE_TEXT]);
      sprintf(temp1,"%d empty records of %d total records were deleted.\r\n",old - new,old);
      strout(temp1);
      sprintf(tname,"%s%s%s.bak",drive,dir,fname);
      unlink(tname);		/* Remove older backup */
      if(!rename(CfgFile,tname))	{	/* Move current file to backup */
         sprintf(temp1,"\r\nCreating MaxMail user backup file %s\r\n",tname);
         strout(temp1);
         sprintf(tname,"%s%s%s.$$$",drive,dir,fname);
         rename(tname,CfgFile); 	/* Rename new file */
      }
      else {
         sprintf(tname,"%s%s%s.$$$",drive,dir,fname);
         unlink(tname);		/* Remove this new version */
      }
   }
   else unlink(tname);		/* Delete temp file then */
}


/* Create a left justified string (NOT null terminated!) padded with blanks */
void _pascal ljstring(char *dest,char *src,int len)
{
   int x;
   char *p;

   strncpy(dest,src,len);
   x = strlen(dest);
   p = dest + x;
   while (x < len) {
      *p = ' ';
      p++;
      x++;
   }
}

int _pascal copy2temp(char *src)
{
   char fname[_MAX_FNAME];
   char fext[_MAX_EXT];
   char tname[_MAX_PATH];

   _splitpath(src,tname,tname,fname,fext);

   if (tdir[0])
      sprintf(tname,"%s\\",tdir);

   if (QWKDir[0]) {
      strcat(tname,QWKDir);
      strcat(tname,"\\");
   }
   strcat(tname,fname);
   strcat(tname,fext);		/* Now we have fule destname */
   return(copyfile(src,tname));

}

int _pascal copyfile(char *srcname, char *destname)
{
   int src,dest;
   unsigned size,tt;
   char _far *buff;

   dest = sopen(destname,O_CREAT | O_TRUNC | O_BINARY | O_RDWR,SH_DENYNO,S_IWRITE);
   if (dest < 0)
      return -1;

   src = sopen(srcname,O_BINARY | O_RDWR,SH_DENYNO,S_IREAD);
   if (src < 0) {
      close(dest);
      unlink(destname);		/* Remove it */
      return -1;
   }

   buff = (char _far *) _fmalloc((1024 * 10) + 5);
   if (buff == NULL)
      aborterror(BADALLOC,NULL);

   _dos_read(src,buff,1024 * 10,&size);
   while (size) {
      if(_dos_write(dest,buff,size,&tt))
         break;
      if(_dos_read(src,buff,1024 * 10,&size))
         break;
   }
   close(dest);
   close(src);
   _ffree(buff);
   return 0;
}

/* Move to our home directory */
void _pascal homedir(void)
{
   if (QWKDir[0])
      chdir("..");

   if (tdir[0])
      switch_dir(PRM(sys_path));

}

/* Switch to tempdir if used */
void _pascal tempdir(void)
{
   if (tdir[0])
      switch_dir(tdir);

   if (QWKDir[0])
      if(chdir(QWKDir) != 0)
         QWKDir[0] = 0;
}

