/***************************************************************************
 *                                                                         *
 *   GALFIL.C                                                              *
 *                                                                         *
 *   Copyright (c) 1993-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                           *
 *   File Libraries - Main Routines                                        *
 *                                                                         *
 *                                                 - D. Pitchford  9/28/93 *
 *                                                                         *
 ***************************************************************************/
                              
#include "gcomm.h"
#include "majorbbs.h"
#include "gcspsrv.h"
#include "fcntl.h"
#include "galfil.h"
#include "filexfer.h"
#include "fsdbbs.h"
#include "reserve.h"
#include "galfilh.h"

#define FILREV "$Revision: 26 $"

static
struct module flmod={              /* module interface block               */
     "",                           /*    name used to refer to this module */
     lslon,                        /*    user logon supplemental routine   */
     lsinp,                        /*    input routine if selected         */
     lssta,                        /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     lshang,                       /*    hangup (lost carrier) routine     */
     lsclean,                      /*    midnight cleanup routine          */
     lsdel,                        /*    delete-account routine            */
     lsend                         /*    system shutdown routine           */
};

struct fluser *flu,                /* current user pointed to (disk)       */
              *fluarr;             /* memory array                         */

struct fluson *flo;                /* online user pointed to               */

struct flusave flusmem;            /* memory for loading/saving fluser     */

struct fllib *curlib,              /* current Library in use               */
             **alphlibs=NULL;      /* alphabetical pointers to Libraries   */

struct libdisk *libupd,            /* memory for loading/saving Libraries  */
               *libedt;            /* memory for editing Libraries         */

struct taglib *ftag;               /* current tagged file                  */

struct modeview mvarr[NVIEWS];     /* custom viewing routines              */

struct key0 countkey;              /* key for counting uploads in Libraries*/

struct key7 *kwbuf;                /* buffer for keyword insertion         */

INT srcpool;                       /* handle to pool of search structs     */
INT *srcharr;                      /* array of search handles, one per user*/
struct srcs *srcu;                 /* ptr to cur (setsrc()'d) search struct*/

union srch srcactive;              /* active, found record for speed-search*/

INT curfusr;                       /* cur (fluoff()'d) file library user   */

HMCVFILE flmsg;                    /* handle for .msg file                 */
FILE *csrc,                        /* pointer for general copying of files */
     *cdst,                        /* pointer for general copying of files */
     *movs,                        /* pointer for sysop copying of files   */
     *movd;                        /* pointer for sysop copying of files   */

DFAFILE *flldat,                   /* data file for Libraries              */
        *flfdat,                   /* data file for files                  */
        *flkdat;                   /* data file for keywords               */

CHAR *arrower,                     /* cursor key detection flag            */
     *tempdir,                     /* default user upload temp directory   */
     *nochgkey,                    /* key to never be charged to d/l       */
     *listkey,                     /* key to be able to d/l file lists     */
     *nonkwds,                     /* non-keywords                         */
     *flsysop,                     /* key required to be Library sysop     */
     *defplop,                     /* default primary Lib-Op               */
     *copydir,                     /* destination directory, copied files  */
     *defdesc,                     /* default description for a Library    */
     *deflname,                    /* name of default Library              */
     *descedit,                    /* memory for file descriptions         */
     *dargv[MAXKWDS],              /* points to defdesc, as margv to input */
     *defproto,                    /* default d/l protocol for all users   */
     flmisc[MISCSIZE],             /* array for misc. test variable        */
     movflib[FLNAMESZ],            /* array for moving files from Library  */
     movtlib[FLNAMESZ],            /* array for moving files to Library    */
     movfnam[FLFILENM],            /* array for moving files file name     */
     countlib[FLNAMESZ],           /* Library currently being counted      */
     tagtmpp[TSHLEN+1],            /* memory for temporary wild file path  */
     tagtmpf[FLFILENM];            /* memory for temporary wild file name  */

VOID *libmem;                      /* memory for Libraries                 */
VOID *libmem2;                     /* more memory for adding Libraries     */
VOID *flomem;                      /* memory for users online              */
VOID *ftagmem;                     /* memory for tagged files              */

INT numlib,                        /* number of libs memory allocated for  */
    numladd,                       /* max. number of libs to possibly add  */
    flstt,                         /* state for the File Libraries         */
    longsrch,                      /* require brute force searches         */
    vdd,                           /* half of the vda declaration          */
    freeuls,                       /* if true, never charge while uploading*/
    dargc,                         /* count of parsed words by darsdesc()  */
    notifsys,                      /* if true, notify sysops of unapp files*/
    notiflop,                      /* if true, notify libops of unapp files*/
    numoflib,                      /* actual number of Libraries           */
    comeff,                        /* estimated communications efficiency  */
    ncatlibs,                      /* actual number of top-level Libraries */
    auditall,                      /* if true, audit all file transfers    */
    unifile,                       /* if true, require unique file names   */
    clfits,                        /* if true, use cluster sizes for libs  */
    pfnceil,                       /* profanity ceiling for File Libraries */
    chgabt,                        /* if true, charge for aborted downloads*/
    defallib,                      /* search all Libraries from d/l prompt */
    systags,                       /* number of ftagmem to allocate        */
    defstyp,                       /* default search/list mode             */
    defslib,                       /* default search/list range, one/all   */
    stplist,                       /* strip ansi always in file listings   */
    countuls[ULDAYS],              /* counter of Library files added daily */
    clnlevel,                      /* level of cleanup to run              */
    kwaut,                         /* flag, keywords being written now     */
    maxjoin,                       /* # of joined libs max for fast search */
    srcindex,                      /* index for found record in speed-srch */
    fltagf,                        /* is current file tag a Library tag    */
    htmldesc;                      /* use live HTML in descriptions?       */

LONG byteroom,                     /* bytes free for uploading to a Library*/
     thresh;                       /* bytes assured free, copydir's drive  */

struct reserve *librsv=NULL;       /* File Libraries reservations domain   */

CHAR *libedits="\
LIBDATE \
LIBTIME \
LIBNAME \
LIBDOS \
LIBDESC \
LIBDESC0 \
LIBDESC1 \
LIBDESC2 \
LIBDESC3 \
LIBDESC4 \
LIBPATH \
LIBHID(ALT=YES ALT=NO MULTICHOICE) \
LIBCBD(ALT=YES ALT=NO MULTICHOICE) \
LIBRDO(ALT=YES ALT=NO MULTICHOICE) \
LIBAUL(ALT=YES ALT=NO MULTICHOICE) \
LIBADL(ALT=YES ALT=NO MULTICHOICE) \
LIBFUL(ALT=YES ALT=NO MULTICHOICE) \
LIBFDL(ALT=YES ALT=NO MULTICHOICE) \
LIBCDF \
LIBCDK \
LIBROY \
LIBMFL \
LIBMBT \
LIBMUP \
LIBVIS \
LIBDLD \
LIBULD \
LIBOVW \
LIBAPU \
LIBLOP \
LIBPLOP \
DONE(ALT=SAVE ALT=EDIT ALT=QUIT MULTICHOICE)";

CHAR *filedits="\
FILNAME \
FILLIB \
FILDATE \
FILTIME \
FILSIZE \
FILDL \
FILULD \
FILULT \
FILDLT \
FILCHG \
FILUSER \
FILTXT00 \
FILTXT01 \
FILTXT02 \
FILTXT03 \
FILTXT04 \
FILTXT05 \
FILTXT06 \
FILTXT07 \
FILTXT08 \
FILTXT09 \
FILTXT10 \
FILTXT11 \
FILTXT12 \
FILTXT13 \
FILTXT14 \
DONE(ALT=SAVE ALT=EDIT ALT=QUIT MULTICHOICE)";

CHAR *libeditf="\
LIBDATE=%s%c\
LIBTIME=%s%c\
LIBNAME=%s%c\
LIBDOS=%s%c\
LIBDESC=%s%c\
LIBDESC0=%s%c\
LIBDESC1=%s%c\
LIBDESC2=%s%c\
LIBDESC3=%s%c\
LIBDESC4=%s%c\
LIBPATH=%s%c\
LIBHID=%s%c\
LIBCBD=%s%c\
LIBRDO=%s%c\
LIBAUL=%s%c\
LIBADL=%s%c\
LIBFUL=%s%c\
LIBFDL=%s%c\
LIBCDF=%ld%c\
LIBCDK=%ld%c\
LIBROY=%d%c\
LIBMFL=%ld%c\
LIBMBT=%ld%c\
LIBMUP=%ld%c\
LIBVIS=%s%c\
LIBDLD=%s%c\
LIBULD=%s%c\
LIBOVW=%s%c\
LIBAPU=%s%c\
LIBLOP=%s%c\
LIBPLOP=%s%c";

CHAR *fileditf="\
FILNAME=%s%c\
FILLIB=%s%c\
FILDATE=%s%c\
FILTIME=%s%c\
FILSIZE=%s%c\
FILDL=%s%c\
FILULD=%s%c\
FILULT=%s%c\
FILDLT=%s%c\
FILCHG=%s%c\
FILUSER=%s%c\
FILTXT00=%s%c\
FILTXT01=%s%c\
FILTXT02=%s%c\
FILTXT03=%s%c\
FILTXT04=%s%c\
FILTXT05=%s%c\
FILTXT06=%s%c\
FILTXT07=%s%c\
FILTXT08=%s%c\
FILTXT09=%s%c\
FILTXT10=%s%c\
FILTXT11=%s%c\
FILTXT12=%s%c\
FILTXT13=%s%c\
FILTXT14=%s%c";

VOID EXPORT
init__galfil(VOID)                 /* system initialization routine        */
{
     INT ncpers,ninmem,i;
     UINT areasiz;

     stzcpy(flmod.descrp,gmdnam("galfil.mdf"),MNMSIZ);
     flstt=register_module(&flmod);
     flmsg=opnmsg("galfil.mcv");
     iniopts();
     flfdat=dfaOpen("galfilf2.dat",FLFILREC+DESCSIZ,NULL);
     flldat=dfaOpen("galfill2.dat",sizeof(struct libdisk),NULL);
     numlib=(INT)dfaCountRec()+5;
     numladd=numlib+20;
     if (!longsrch) {
          flkdat=dfaOpen("galfilk2.dat",FLKEYREC,NULL);
     }
     if (tempdir[0] == '\0') {
          tempdir="libtemp";
     }
     if (copydir[0] == '\0' || weirddir(copydir)) {
          copydir="libcopy";
     }
     arrower=(CHAR *)alczer(nterms);
     descedit=(CHAR *)alczer(DESCSIZ);
     libedt=(struct libdisk *)alczer(sizeof(struct libdisk));
     libupd=(struct libdisk *)alczer(sizeof(struct libdisk));
     fluarr=(struct fluser *)alczer(nterms*sizeof(struct fluser));
     areasiz=sizeof(struct srcs)-sizeof(CHAR);
     areasiz+=maxjoin*sizeof(union srch)*NUINRS;
     ncpers=numopt(NCPERS,1,150);
     ninmem=max(2,(nterms-1)/ncpers+1);
     srcpool=newpool(areasiz,nterms,ninmem);
     srcharr=(INT *)alcmem(nterms*sizeof(INT));
     for (i=0 ; i < nterms ; i++) {
          srcharr[i]=rsvarea(srcpool);
          if (srcharr[i] == -1) {
               catastro("CAN'T RESERVE LIBRARY SEARCH MEMORY AREA!");
          }
     }
     kwbuf=(struct key7 *)alczer(SIZKWBUF*sizeof(struct key7));
     kwaut=0;
     ftagmem=alcblok(systags,sizeof(struct taglib));
     flomem=alcblok(nterms,sizeof(struct fluson));
     libmem=alcblok(numlib,sizeof(struct fllib));
     libmem2=NULL;
     setmem(mvarr,NVIEWS*sizeof(struct modeview),0);
     register_modeview("ZIP",CMPVIEW,NULL);
     register_modeview("ARC",CMPVIEW,NULL);
     register_modeview("LZH",CMPVIEW,NULL);
     register_modeview("ZOO",CMPVIEW,NULL);
     register_modeview("ICE",CMPVIEW,NULL);
     register_modeview("PSE",CMPVIEW,NULL);
     register_modeview("ZSE",CMPVIEW,NULL);
     register_modeview("TXT",LSTVIEW,NULL);
     register_modeview("BAT",LSTVIEW,NULL);
     register_modeview("INI",LSTVIEW,NULL);
     loadlibs();
     init_galfiluz();
     vdd=max(MAXKWDS*FLKEYSIZ,fsdroom(LIBEDTN,libedits,0));
     vdd=max(vdd,fsdroom(LIBEDTA2,libedits,0));
     vdd=max(vdd,fsdroom(FILEDTN,filedits,0));
     vdd=max(vdd,fsdroom(FILEDTA,filedits,0));
     dclvda(2*vdd);
     if (sameto("_LIBOP",flsysop)) {
          shocst("GALFIL: WARNING","FLSYSOP key can't be _LIBOP pseudokey");
     }
     else {
          register_pseudok("_LIBOP",psislibop);
     }
     gmkdir(tempdir);
     gmkdir(copydir);
     purgecpy(0);
     regtvars();
     rtkick(5,libkick);
     initlibrsv();
     init_cslib();
}

VOID EXPORT
initwc__galfil(VOID)
{
     init__galfil();
}

VOID
initlibrsv(VOID)                   /* init File Library reservations domain*/
{
     if (librsv == NULL) {
          librsv=inireserve(nterms,&usrnum,3,FLNAMESZ,FLFILENM,amrlib);
     }
}

VOID
iniopts(VOID)                      /* read in configuration options        */
{
     longsrch=ynopt(LONGSRCH);
     pfnceil=numopt(PFNCEIL,0,3);
     flsysop=stgopt(FLSYSOP);
     nonkwds=stgopt(NONKWDS2);
     tempdir=checkdir(pthopt(TEMPDIR));
     comeff=numopt(COMEFF,1,100);
     thresh=lngopt(THRESH,1,1000)*1024L*1024L;
     auditall=ynopt(AUDITALL);
     chgabt=ynopt(CHGABT);
     defallib=ynopt(DEFALLIB);
     nochgkey=stgopt(NOCHGKEY);
     defdesc=stgopt(DEFDESC);
     deflname=stgopt(DEFLNAME);
     defplop=stgopt(DEFPLOP);
     listkey=stgopt(LISTKEY);
     notifsys=ynopt(NOTIFSYS);
     notiflop=ynopt(NOTIFLOP);
     unifile=ynopt(UNIFILE);
     clfits=ynopt(CLFITS);
     freeuls=ynopt(FREEULS);
     stplist=ynopt(STPLIST);
     systags=numopt(SYSTAGS,20,3000);
     copydir=checkdir(pthopt(COPYDIR));
     defproto=stgopt(DEFPROTO);
     htmldesc=ynopt(HTMLDESC);
     clnlevel=numopt(CLNLEVEL,0,2);
     switch (chropt(DEFSTYP)) {
     case 'd':
     case 'D':
          defstyp=2;
          break;
     case 'f':
     case 'F':
          defstyp=3;
          break;
     case 'k':
     case 'K':
          defstyp=4;
          break;
     case 'n':
     case 'N':
          defstyp=5;
          break;
     case 'w':
     case 'W':
          defstyp=6;
          break;
     case 'q':
     case 'Q':
          defstyp=7;
          break;
     default:
          defstyp=1;
     }
     defslib=tokopt(DEFSLIB,"ASK","CURLIB","ALL",NULL);
     if (defslib == 0) {
          defslib++;
     }
     maxjoin=numopt(MAXJOIN,5,500)+1;   /* include the top-level Library   */
}

GBOOL
psislibop(                         /* pseudokey: is user a libop?          */
INT unum,                          /* test for _LIBOP! to determine sysop  */
const CHAR *lock)
{
     if (!sameto("_LIBOP",lock)) {
          return(0);
     }
     fluoff(unum);
     if (curlib == NULL) {
          return(0);
     }
     if (sameto("_LIBOP",curlib->libop)) {
          shocst("GALFIL: WARNING",
                 "LIBOP key can't be _LIBOP in Library %s",curlib->libname);
          return(0);
     }
     if (lock[strlen(lock)-1] == '!') {
          return(haskey(flsysop));
     }
     return(isflop(curlib));
}

CHAR *
cnclib(VOID)                       /* grab a Library name                  */
{
     static CHAR retlib[FLNAMESZ];
     CHAR *rp;

     setmem(rp=retlib,FLNAMESZ,0);
     while (*nxtcmd != '\0' && *nxtcmd != ' ' && rp-retlib < FLNAMESZ-1) {
          *rp++=*nxtcmd++;
     }
     return(strupr(retlib));
}

INT
isflop(                            /* is current user an operator at all?  */
struct fllib *libptr)
{
     if (libptr == NULL) {
          return(0);
     }
     return(uhskey(usaptr->userid,flsysop)
         || (libptr->libop[0] != '\0' && uhskey(usaptr->userid,libptr->libop))
         || stricmp(usaptr->userid,libptr->primary) == 0);
}

INT
scanlib(                           /* count up total files in Library      */
INT user,                          /* ... 1=use user's access keys         */
struct fllib *libptr)
{
     INT loop,loop2;
     struct fllib *lib2;

     libptr->libs=0;
     libptr->totfiles=libptr->numfiles-libptr->appwait;
     for (loop=0 ; loop < numoflib ; loop++) {
          lib2=liboff(loop);
          if (lib2->cat[0][0] != '\0') {
               for (loop2=0 ; loop2 < FLNMCATS ; loop2++) {
                    if (sameas(lib2->cat[loop2],libptr->libname)
                       && (!user || haslibkey(lib2,lib2->keyreq))) {
                         libptr->libs++;
                         if (lib2 != libptr) {
                              libptr->totfiles+=lib2->numfiles-lib2->appwait;
                         }
                    }
               }
          }
     }
     return(libptr->libs != 0);
}

INT
wldgetag(VOID)                     /* get next tagged wild file            */
{
     struct fllib *libptr;

     if (readline(flo->fsrc)) {
          inplen=strlen(input);
          parsin();
          if (margc >= 2 && (libptr=libfind(margv[0])) != NULL) {
               dfaSetBlk(flfdat);
               if ((libptr->flags&FLGDOS)
                || dfaAcqEQ(NULL,compkey(libptr->libname,margv[1]),COMPLF)) {
                    ASSERT(strchr(margv[1],'*') == NULL
                        && strchr(margv[1],'?') == NULL);
                    sprintf(tagtmpp,"%s"SLS"%s",libpath(libptr),margv[1]);
                    if (fndfile(&flo->fb,tagtmpp,0)) {
                         getShortName(tagtmpf
                                     ,spr("%s"SLS"%s"
                                         ,libpath(libptr),flo->fb.ff_name)
                                     ,FLFILENM);
                         stlcpy(spc->libname,libptr->libname,FLNAMESZ);
                         spc->size=flo->fb.ff_fsize;
                         return(1);
                    }
               }
          }
     }
     fclose(flo->fsrc);
     flo->fsrc=NULL;
     return(0);
}

INT
dlbgnwld(VOID)                     /* begin download of wild tags          */
{
     INT tagnum;

     for (tagnum=0 ; tagnum < systags ; tagnum++) {
          if (tagoff(tagnum) == spc) {
               break;
          }
     }
  // if user aborted d/l and is trying again, delete the old tags file (don't open twice)  
     if (flo->fsrc != NULL) {
         fclose(flo->fsrc);
         flo->fsrc=NULL;
     }

     flo->fsrc=fopen(spr("%s"SLS"tags%d.fil",copydir,tagnum),FOPRB);
     return(flo->fsrc == NULL ? 0 : wldgetag());
}

INT
tshlist(                           /* tagspc handler for file lists        */
INT tshcod)
{
     INT rc=0;
     CHAR *stg,fname[FLFILENM];

     fluoff(usrnum);
     setmbk(flmsg);
     stzcpy(fname,ftgptr->tagspc,FLFILENM);
     fnmcse(fname);
     switch (tshcod) {
     case TSHDSC:                      /* Describe tagspec in English */
          if ((stg=strchr(fname,'.')) != NULL) {
               *stg='\0';
          }
          if (sameas(fname,"files!")) {
               strcpy(tshmsg,"list of files in all Libraries");
          }
          else {
               sprintf(tshmsg,"list of files in the %s Library",fname);
          }
          break;
     case TSHVIS:                                  /* Visible to this user? */
          *tshmsg='\0';
          rc=1;
          break;
     case TSHBEG:              /* Begin download, check permission, reserve */
          sprintf(tshmsg,"%s"SLS"%s",copydir,fname);
          if ((stg=strchr(fname,'.')) != NULL) {
               *stg='\0';
          }
          sprintf(ftfscb->fname,"%s.lst",fname);
          rc=1;
          break;
     case TSHEND:             /* End complete download of a file, unreserve */
          sv.dwnlds++;
          shocst("LIBRARY FILE DOWNLOAD","User %s download %s"SLS"%s",
           usaptr->userid,copydir,fname);
          unlink(spr("%s"SLS"%s",copydir,fname));
          break;
     case TSHFIN:                           /* Finish file transfer session */
          retoin();
          rc=1;
          break;
     case TSHSKP:                     /* Skip incomplete download of a file */
          shocst("LIBRARY FILE DOWNLOAD ABORTED",
           "User %s aborted dnld of %s"SLS"%s",usaptr->userid,copydir,fname);
     }
     return(rc);
}

INT
tshlib(                            /* tagspc handler for File Libraries      */
INT tshcod)
{
     static CHAR tmsgtmp[200];
     struct fllib *libptr=NULL;
     FILE *fp;
     INT rc=0;
     CHAR *stg;
     LONG chg;

     fluoff(usrnum);
     setmbk(flmsg);
     fltagf=1;
     switch (tshcod) {
     case TSHSCN:
          if ((rc=dlbgnwld()) != 0) {
               movmem(ftgptr->tagspc,tshmsg,TSLENG);
          }
          break;
     case TSHNXT:
          if ((rc=wldgetag()) != 0) {
               movmem(ftgptr->tagspc,tshmsg,TSLENG);
          }
          else {
               spc->status=-spc->status;
          }
          break;
     case TSHDSC:                      /* Describe tagspec in English */
          setmem(tmsgtmp,200,0);
          if (spc->filname[0] == '*') {
               sprintf(tmsgtmp,"%d file%s matching \"%s\" in the %s Library",
                 spc->nwld,(spc->nwld > 1 ? "s" : ""),spc->wldspc,
                 spc->reflib);
          }
          else {
               sprintf(tmsgtmp,"file %s in the %s Library",spc->filname,
                  spc->reflib);
          }
          switch (spc->status) {
          case TGCOPY:
               if (spc->size > 0L) {
                    strcat(tmsgtmp,spr(" (Not Ready - %ld%%%% Copied)",
                           100L*spc->copied/spc->size));
               }
               else {
                    strcat(tmsgtmp," (Not Ready - Copying)");
               }
               break;
          case TGLOCAL:
          case TGDLTMP:
               stg=skpwht(dnlmin(spc->size));
               strcat(tmsgtmp,spr(" (%sK, %s minute",fzer(spc->size/1024L),
                  stg));
               if (atol(stg) > 1L) {
                    strcat(tmsgtmp,"s");
               }
               chg=dnlchg(spc->size,spc->libname);
               if (chg > 0L) {
                    strcat(tmsgtmp,spr(", %ld credits",chg));
               }
               strcat(tmsgtmp,")");
               break;
          case TGINWAIT:
               strcat(tmsgtmp," (Not Ready - Queued)");
               break;
          case TGOTHER:
          case TGSERVER:
               strcat(tmsgtmp," (Not Ready - Retrieving)");
               break;
          case -TGLOCAL:
          case -TGDLTMP:
               strcat(tmsgtmp," (Removing)");
               break;
          default:
               if (spc->status < TGEMPTY) {
                    strcat(tmsgtmp," (Aborted - Removing)");
               }
          }
          stzcpy(tshmsg,tmsgtmp,TSHLEN);
          break;
     case TSHVIS:                                  /* Visible to this user? */
          *tshmsg='\0';
          rc=1;
          if (spc->filname[0] == '*') {
               break;
          }
          if (spc->libname[0] != '\0') {
               libptr=libfind(spc->libname);
          }
          if (libptr != NULL
           && (spc->status == TGLOCAL || spc->status == TGDLTMP)) {
               if (libptr->flags&FLGCBD) {
                    sprintf(tshmsg,"%s"SLS"%s"SLS"%s",copydir,libptr->libname,
                   spc->filname);
               }
               else {
                    sprintf(tshmsg,"%s"SLS"%s",libpath(libptr),spc->filname);
               }
               if ((fp=fopen(tshmsg,FOPRB)) != NULL) {
                    fread(tshmsg,1,TSHLEN,fp);
                    fclose(fp);
               }
               else {
                    if (spc->status == TGLOCAL) {
                         shocst("FILE LIBRARIES: MISSING FILE",
                                "%s"SLS"%s is missing!",
                                libptr->libname,spc->filname);
                         prfmsg(MISFILEB,spc->filname);
                    }
                    else {
                         prfmsg(MISFILE,spc->filname);
                    }
                    outprf(usrnum);
                    clrprf();
                    rc=0;
               }
          }
          break;
     case TSHBEG:              /* Begin download, check permission, reserve */
          if (spc->libname[0] != '\0') {
               libptr=libfind(spc->libname);
               strcpy(tshmsg,tagtmpp);
               strcpy(ftfscb->fname,tagtmpf);
          }
          if (libptr == NULL) {
               strcpy(tshmsg,"file is not available for download");
               break;
          }
          switch (spc->status) {
          case TGINWAIT:
          case TGCOPY:
          case TGOTHER:
          case TGSERVER:
               strcpy(tshmsg,"file is not yet available for download");
               rc=-1;
               break;
       case TGLOCAL:
          case TGDLTMP:
               if (!sameas(spc->filname,"*")) {
                    if (libptr->flags&FLGCBD) {
                         sprintf(tshmsg,"%s"SLS"%s"SLS"%s",copydir,
                                 libptr->libname,spc->filname);
                    }
                    else {
                         sprintf(tshmsg,"%s"SLS"%s",libpath(libptr),spc->filname);
                    }
                    strcpy(ftfscb->fname,spc->filname);
               }
               if (!sameas(ftfpsp->code,"V")) {
                    if (!zapcrd((libptr->flags&FLGFDL) != 0L,spc->size,libptr)) {
                         sprintf(tshmsg,
                                 "You don't have enough credits to download "
                                 "%s",ftfscb->fname);
                    }
                    else {
                         dnlcount(libptr->libname,ftfscb->fname,-1L);
                         rc=1;
                    }
               }
               else {
                    rc=1;
               }
          }
          break;
     case TSHEND:             /* End complete download of a file, unreserve */
          if (!sameas(ftfpsp->code,"V")) {
               sv.dwnlds++;
               if (spc->libname[0] != '\0') {
                    libptr=libfind(spc->libname);
               }
               if (libptr != NULL) {
                    dncfin(1,ftfscb->actbyt,libptr);
                    if (auditall || (libptr->flags&FLGADL)) {
                         shocst("LIBRARY FILE DOWNLOAD",
                    "User %s download %s"SLS"%s",usaptr->userid,
                    spc->libname,ftfscb->fname);
                    }
               }
               rstcrd();
          }
          break;
     case TSHFIN:                           /* Finish file transfer session */
          if (flo->fsrc != NULL) {
               fclose(flo->fsrc);
               flo->fsrc=NULL;
          }
          retoin();
          rc=1;
          break;
     case TSHSKP:                     /* Skip incomplete download of a file */
          if (!sameas(ftfpsp->code,"V")) {
               if (spc->libname[0] != '\0') {
                    libptr=libfind(spc->libname);
               }
               if (libptr != NULL) {
                    dncfin(0,ftfscb->actbyt,libptr);
                    if (auditall || (libptr->flags&FLGADL)) {
                         shocst("LIBRARY FILE DOWNLOAD ABORTED",
                    "User %s aborted dnld of %s"SLS"%s",
                    usaptr->userid,spc->libname,spc->filname);
                    }
               }
               rstcrd();
          }
          break;
     }
     return(rc);
}

LONG
dnlprc(                            /* price of download, one file + size   */
LONG siz,
struct fllib *libptr)
{
     return(haskey(nochgkey) || (usrptr->cltptr->flags&CRDXMT) ? 0L
         : libptr->dlchge+libptr->kdlchge*(siz/1024L));
}

VOID
fluoff(                            /* sets pointer to passed usrnum        */
INT unum)                          /* usrnum to set it to                  */
{
     INT saveu;

     if (unum < 0 || unum >= nterms) {
          unum=0;
     }
     saveu=usrnum;
     curfusr=usrnum=unum;
     curusr(usrnum);
     flo=(struct fluson *)ptrblok(flomem,usrnum);
     flu=&fluarr[usrnum];
     setlcsu(usrnum);
     curlib=setuaxs();
     setrsv(librsv);
     if (saveu != usrnum) {
          usrnum=saveu;
          if (usrnum >= 0 && usrnum < nterms) {
               curusr(usrnum);
          }
     }
}

VOID
srcoff(VOID)                       /* set up search pointer for cur fl user*/
{
     ASSERT(curfusr >= 0 && curfusr < nterms);
     srcu=areaptr(srcpool,srcharr[curfusr]);
     srcu->tn=(union srch *)srcu->unions;
     srcu->tp=(union srch *)(srcu->unions+maxjoin*sizeof(union srch));
     srcu->fn=(union srch *)(srcu->unions+maxjoin*sizeof(union srch)*2);
     srcu->fp=(union srch *)(srcu->unions+maxjoin*sizeof(union srch)*3);
}

VOID
scanlibs(VOID)                     /* set up Libraries/top-level Libraries */
{
     INT loop=0;
     struct fllib *libptr;

     ncatlibs=0;
     while (loop < numoflib) {
          libptr=liboff(loop++);
          if (libptr->libname[0] != '\0') {
               if (scanlib(0,libptr)) {
                    ncatlibs++;
               }
          }
     }
}

struct fllib *
libfind(                           /* return pointer to Library            */
const CHAR *libname)               /* ... Library name                     */
{
     INT loop;
     struct fllib *retval;

     for (loop=0 ; loop < numoflib ; loop++) {
          if (stricmp(libname,(retval=liboff(loop))->libname) == 0) {
               return(retval);
          }
     }
     return(NULL);
}

struct fllib *
liboff(                            /* return pointer to Library            */
INT libnum)                        /* Library number                       */
{
     if (libnum >= numlib) {
          if (libmem2 == NULL) {
               libmem2=alcblok(numladd,sizeof(struct fllib));
          }
          return((struct fllib *)ptrblok(libmem2,libnum-numlib));
     }
     return((struct fllib *)ptrblok(libmem,libnum));
}

struct fllib *
setuaxs(VOID)                      /* set user access by key, returns ptr  */
{                                  /* to user's current Library or NULL    */
     INT loop;
     struct fllib *retval,*flibp;

     if (flu->lib[0] == '\0'
      || (retval=libfind(flu->lib)) == NULL
      || !haslibkey(retval,retval->keyreq)) {
          setmem(flu->lib,FLNAMESZ,0);
          if ((retval=libfind(deflname)) != NULL && visilib(retval)) {
               stzcpy(flu->lib,deflname,FLNAMESZ);
          }
          else {
               for (loop=0 ; loop < numoflib ; loop++) {
                    if (valname((retval=liboff(loop))->libname)
               && visilib(retval)) {
                         stzcpy(flu->lib,retval->libname,FLNAMESZ);
                         break;
                    }
               }
          }
          flo->nlibaxs=-1;
     }
     if (flo->nlibaxs == -1) {
          flo->nlibaxs=0;
          for (loop=0 ; loop < numoflib ; loop++) {
               flibp=liboff(loop);
               if (haslibkey(flibp,flibp->keyreq)) {
                    flo->nlibaxs++;
               }
          }
     }
     return(flu->lib[0] != '\0' ? retval : NULL);
}

INT
makwdlst(                          /* make keyword list                    */
const CHAR *stg,
const CHAR *filname)
{
     CHAR filnmem[FLFILENM],*ext;

     descedit[0]='\0';
     stzcpy(filnmem,filname,FLFILENM);
     ext=strchr(strupr(filnmem),'.');
     if (ext != NULL) {
          *ext++='\0';
          if (strlen(ext) == 3) {
               strcat(descedit,ext);
               strcat(descedit," ");
          }
     }
     if (strlen(filnmem) > 2) {
          strcat(descedit,filnmem);
          strcat(descedit," ");
     }
     stzcat(descedit,stg,DESCSIZ);
     strcrep(descedit,'\r',' ');
     strupr(descedit);
     darsdesc(MAXKWDS,' ');
     bubkwds();
     return(dargc > 0);
}

INT
submit(                            /* submit a file for tagging, 1=success */
CHAR *libname,                     /* works on current user                */
CHAR *filname,
CHAR *reflib,                      /* reference lib in case true lib       */
INT view,                          /* is invisible to user,                */
INT quiet)                         /* "" acceptable, best guess            */
{
     static struct ffblk fb;
     INT loop,isdos=0,wild;
     CHAR *proprlib,*pro;
     struct fllib *libptr;
     INT who;

     outprf(usrnum);
     prf("");
     for (loop=0 ; loop < nterms ; loop++) {
          if (loop != usrnum) {
               flo=(struct fluson *)ptrblok(flomem,loop);
               if ((flo->flags&REUPLOD) && sameas(flo->miscfil,filname)) {
                    flu=&fluarr[loop];
                    if (sameas(flu->lib,libname)) {
                         if (!quiet) {
                              prfmsg(CANTAGF,filname,libname);
                         }
                         return(0);
                    }
               }
          }
     }
     fluoff(usrnum);
     if (!view && alrtag(libname,filname) > 0) {
          return(0);
     }
     libptr=libfind(libname);
     if ((who=w2read(libname,filname)) != NOCONFLICT) {
          if (!quiet) {
               if (isflop(libptr)) {
                    prfmsg(CANTAGR,filname,libname,uacoff(who)->userid);
               }
               else {
                    prfmsg(CANTAGF,filname,libname);
               }
          }
          return(0);
     }
     ftag=NULL;
     for (loop=0 ; loop < systags ; loop++) {
          if (tagoff(loop)->status == TGEMPTY) {
               ftag=tagoff(loop);
               break;
          }
     }
     if (ftag == NULL) {
          if (!quiet) {
               prfmsg(CANTAGN,filname,libname);
          }
          return(0);
     }
     dfaSetBlk(flfdat);
     if (libptr != NULL && libptr->flags&FLGDOS) {
          isdos=1;
          if (*filname != '*'
           && !fndfile(&fb,spr("%s"SLS"%s",libpath(libptr),filname),0)) {
               if (!quiet) {
                    prfmsg(CANTAGF,filname,libname);
               }
               dfaRstBlk();
               return(0);
          }
     }
     else if (*filname != '*'
           && !dfaAcqEQ(NULL,compkey(libname,filname),COMPLF)) {
          if (!quiet) {
               prfmsg(CANTAGF,filname,libname);
          }
          dfaRstBlk();
          return(0);
     }
     dfaRstBlk();
     proprlib=(libptr != NULL && !isdos ? visxlib(libptr,reflib) : libname);
     ftag->usrnum=usrnum;
     stzcpy(ftag->libname,libname,FLNAMESZ);
     stzcpy(ftag->reflib,proprlib,FLNAMESZ);
     stzcpy(ftag->filname,filname,FLFILENM);
     if (*filname == '*') {
          ftag->nwld=flo->wtnum;
          ftag->size=flo->wtsiz;
          stzcpy(ftag->wldspc,flo->miscfil,FLFILENM);
     }
     else {
          ftag->size=isdos ? fb.ff_fsize : flf->siz;
          stzcpy(ftag->udate,isdos ? ddat2srt(today()) : flf->udate,DATESZ);
     }
     ftag->tagtime=time(NULL);
     if (ftag->libname[0] == '\0' || (libptr=libfind(ftag->libname)) == NULL) {
          ftag->status=-1;
          if (quiet) {
               clrprf();
          }
          return(0);
     }
     ftag->status=((libptr->flags&FLGCBD)
        ? (libptr->path[0] != '\0' ? TGINWAIT : TGOTHER) : TGLOCAL);
     if (ftgnew()) {
          spc=ftag;
          wild=sameas(ftag->filname,"*");
          ftgptr->flags=(wild ? FTGWLD : 0);
          ftgptr->flags|=(view != NOTVIEW && !wild ? 0 : FTGABL);
          ftgptr->tshndl=tshlib;
          if (view == NOTVIEW) {
               ftgsbm("TQ");
          }
          else {
               pro="";
               if (flu->askme && view == DOWNLOD && flo->savprot[0] == '\0') {
                    ftgptr->flags|=FTGABL;
                    ftgsbm("TQ");
                    setmbk(flmsg);
                    prfmsg(CLRSCN);
                    prfmsg(DEFRIPM);
                    switch (usrptr->substt) {
                    case FILELST0:
                         prfmsg(usrptr->substt=LOGOFF3);
                         break;
                    case ENTDLFIL:
                         usrptr->substt=LOGOFF;
                         break;
                    default:
                         prfmsg(usrptr->substt=LOGOFF2);
                    }
                    outprf(usrnum);
                    prf("");
                    rstmbk();
                    echonu(usrnum);
                    btutrg(usrnum,0);
                    flo->pflg=ftuptr->numftg;
                    return(1);
               }
               if (pro[0] == '\0') {
                    pro=protocol();
               }
               if (flo->savprot[0] != '\0') {
                    pro=spr("%s",flo->savprot);
                    flo->savprot[0]='\0';
               }
               if (view == CMPVIEW) {
                    pro="V";
                    prf("[0;1;36m");
               }
               else if (view == LSTVIEW) {
                    pro="L";
               }
               else if (view == ANSVIEW) {
                    pro="A";
               }
               else if (view == CSTVIEW) {
                    prf("");
                    (*flo->rouptr)(ftag);
                    outprf(usrnum);
                    prf("");
                    return(1);
               }
               if (sameto("T",pro) && !wild) {
                    pro="";
               }
               if (pro[0] == '\0') {
                    pro="?";
               }
               ftgsbm(pro);
               if (usrptr->state != flstt) {
                    prf("");
                    outprf(usrnum);
               }
               else {
                    prf("");
                    if (!sameto("T",pro)) {
                         return(0);
                    }
               }
          }
     }
     if (quiet) {
          clrprf();
     }
     return(1);
}

INT
updusr(                            /* update user field to disk            */
struct fluser *fluptr)
{
     INT retval=-1;

     stzcpy(flusmem.userid,usaptr->userid,UIDSIZ);
     stzcpy(flusmem.modnam,"File Library",MNMSIZ);
     movmem(fluptr,&flusmem.udata,sizeof(struct fluser));
     dfaSetBlk(genbb);
     if (dfaAcqEQ(NULL,&flusmem,0)) {
          dfaUpdateV(&flusmem,sizeof(struct flusave));
          retval=1;
     }
     else if (flusmem.userid[0] != '\0') {
          dfaInsertV(&flusmem,sizeof(struct flusave));
          retval=0;
     }
     dfaRstBlk();
     return(retval);
}

CHAR *
userdir(                           /* return temp directory or filename    */
INT which)                         /* 0=dir 1=user ul 2=sdi 3=diz          */
{
     switch (which) {
     case 1:
          return(spr("%s"SLS"upload%02x",tempdir,channel[usrnum]));
     case 2:
          return(spr("%s"SLS"desc%02x.sdi",tempdir,channel[usrnum]));
     case 3:
          return(spr("%s"SLS"desc%02x.diz",tempdir,channel[usrnum]));
     }
     return(spr("%s",tempdir));
}

INT
valname(                           /* is passed name valid for a Library?  */
CHAR *libname)
{
     INT len;
     CHAR *cptr;

     len=strlen(cptr=libname);
     if (len < 1 || len > 8) {
          return(0);
     }
     while (*cptr != '\0') {
          if (!isfiln(*cptr,".")) {
               return(0);
          }
          cptr++;
     }
     if (rsvnam(libname) || profane(libname)) {
          return(0);
     }
     return(1);
}

INT
fileditv(                          /* verify FSD edit file session         */
INT fldno,
CHAR *answer)
{
     CHAR nts[9];
     LONG ndl;
     INT y,m,d;

     fluoff(usrnum);
     switch (fldno) {
     case FFDULT:
          stzcpy(nts,answer,9);
          if (nts[1] == ':') {
               nts[0]='0';
               stzcpy(&nts[1],answer,8);
          }
          if (strlen(nts) == 5) {
               strcat(nts,":00");
          }
          stzcpy(answer,nctime(dctime(nts)),9);
          if (!sameas(nts,answer)) {
               setmbk(flmsg);
               stzcpy(fsdemg,getmsg(BADTIM),MAXHLP);
               rstmbk();
               return(VFYREJ);
          }
          break;
     case FFDDL:
          ndl=atol(answer);
          if (ndl < 0L) {
               ndl=0;
          }
          if (ndl > 9999999L) {
               ndl=9999999L;
          }
          strcpy(answer,l2as(ndl));
          break;
     case FFDULD:
          fsdemg[0]='\0';
          setmbk(flmsg);
          dateDecode(answer,&y,&m,&d);
          if (!validDate(y,m,d)) {
               stlcpy(fsdemg,getmsg(BADDAT),MAXHLP);
          }
          else if (dddate(m,d,y) > today()) {
               stlcpy(fsdemg,getmsg(BADDAT2),MAXHLP);
          }
          rstmbk();
          if (fsdemg[0] != '\0') {
               return(VFYREJ);
          }
          break;
     case FFDUSER:
          if (sameas(answer,"Sysop")) {
               stzcpy(answer,"Sysop",UIDSIZ);
               break;
          }
          dfaSetBlk(accbb);
          if (!dfaAcqEQ(&acctmp,answer,0)) {
               setmbk(flmsg);
               stzcpy(fsdemg,getmsg(BADUID),MAXHLP);
               rstmbk();
               dfaRstBlk();
               return(VFYREJ);
          }
          else {
               stzcpy(answer,acctmp.userid,UIDSIZ);
          }
          dfaRstBlk();
          break;
     case FFDTXT00:
     case FFDTXT01:
     case FFDTXT02:
     case FFDTXT03:
     case FFDTXT04:
     case FFDTXT05:
     case FFDTXT06:
     case FFDTXT07:
     case FFDTXT08:
     case FFDTXT09:
     case FFDTXT10:
     case FFDTXT11:
     case FFDTXT12:
     case FFDTXT13:
     case FFDTXT14:
          if (answer[0] == '\0' && (fsdscb->newans+fldptr->ansoff)[0]== '\0') {
               fldno=fsdscb->crsfld=FFDEXIT;
          }
          else if (profane(answer)) {
               setmbk(flmsg);
               stzcpy(fsdemg,getmsg(PROFDESC),MAXHLP);
               rstmbk();
               return(VFYREJ);
          }
     }
     return(vfyadn(fldno,answer));
}

GBOOL
lsinp(VOID)                        /* main input handler                   */
{
     fluoff(usrnum);
     setftu();
     setmbk(flmsg);
     if (curlib == NULL || flu->lib[0] == '\0') {
          updusr(flu);
          if (usrptr->substt == 0) {
               prfmsg(NOAXESS);
          }
          return(FALSE);
     }
     if (usrptr->flags&ABOIP) {
          switch (usrptr->substt) {
          case VIEWASC:
               prfmsg(usrptr->substt=VIEWASC);
               break;
          case ENTRETT:
               buildview(flo->hold);
               break;
          case FILELST0:
               rebuild(2);
               break;
          case TAGMAN:
               retmenu(0);
               mainmenu();
               prfmsg(MMENU);
               break;
          case LIBLST:
               usrptr->substt=flo->retstt;
               break;
          default:
               clrprf();
          }
          prf("");
          outprf(usrnum);
          prf("");
     }
     if (!(usrptr->flags&INJOIP) && !(usrptr->flags&ABOIP)) {
          do {
               bgncnc();
               if ((flo->flags&ENTRCNC) && isripu()) {
                    prfmsg(DEFRIPM);
               }
               if (usrptr->substt == 0) {
                    cncchr();
                    usrptr->flags&=~X2MAIN;
                    usrptr->substt=MMENU;
                    flo->flags&=~ABOUPRF;
                    flo->flags|=LONGDSC;
                    flo->flags&=~INMVCPY;
                    if (morcnc() == '&') {
                         flo->flags|=ABOUPRF;
                         flo->flags|=ENTRCNC;
                         cncchr();
                    }
               }
               else {
                    switch (inpfunc()) {
                    case 0:
                         flu->cdayin=cofdat(today());
                         deresume();
                         return(FALSE);
                    case 2:
                         return(TRUE);
                    }
                    fluoff(usrnum);
                    setftu();
               }
               if (curlib == NULL) {
                    return(FALSE);
               }
               if (nxtcmd == margv[0]) { /* if inp handler didn't use any, */
                    cncall();            /* don't let it go around again   */
               }
          } while (!endcnc());
          flo->flags&=~ENTRCNC;
     }
     promptin();
     return(TRUE);
}

VOID
deresume(VOID)                     /* cancel possibility of resuming list  */
{
     flo->tagk='A'-1;
}

GBOOL
lslon(VOID)                        /* system logon routine                 */
{
     switch (usrptr->substt) {
          case -1:
               return(TRUE);
          case -2:
               rmdir(userdir(1));
               return(FALSE);
     }
     if (usrptr->flags&WSGCSU) {
          fluoff(usrnum);
     }
     else {
          genlon();
          if (curlib != NULL && (notiflop || notifsys)) {
               notify(1);
          }
     }
     if (ulfileq(1)) {
          setmbk(flmsg);
          prfmsg(HANGON);
          rstmbk();
          spinner();
          usrptr->substt=-1;
          btuinj(usrnum,CYCLE);
          outprf(usrnum);
          return(TRUE);
     }
     rmdir(userdir(1));
     return(FALSE);
}

VOID
genlon(VOID)                       /* C/S - A/A generic logon routine parts*/
{
     flu=&fluarr[usrnum];
     flo=(struct fluson *)ptrblok(flomem,usrnum);
     setmem(flu,sizeof(struct fluser),0);
     setmem(flo,sizeof(struct fluson),0);
     flo->nlibaxs=-1;
     fluoff(usrnum);
     dfaSetBlk(genbb);
     stzcpy(flusmem.userid,usaptr->userid,UIDSIZ);
     stzcpy(flusmem.modnam,"File Library",MNMSIZ);
     if (!dfaAcqEQ(&flusmem,&flusmem,0)) {
          stzcpy(flusmem.userid,usaptr->userid,UIDSIZ);
          stzcpy(flusmem.modnam,"File Library",MNMSIZ);
          setmem(&flusmem.udata,sizeof(struct fluser),0);
     }
     movmem(&flusmem.udata,flu,sizeof(struct fluser));
     curlib=setuaxs();
     dfaRstBlk();
     updusr(flu);
     if (flu->newdate[0] == '\0') {
          stzcpy(flu->newdate,ddat2srt(datofc(cofdat(today())-3)),DATESZ);
     }
     stzcpy(flo->newdate,flu->newdate,DATESZ);
}

INT
srcincx(VOID)                      /* is screen not full of search elements*/
{
     INT loop;

     if (flo->flags&REVSRCH) {
          if (flo->atend != LSTFILS && flo->atend != LSTBACK) {
               return(0);
          }
          for (loop=NLISTER-1 ; loop >= 0 ; loop--) {
               if (flo->tdline[loop] == 0) {
                    return(loop+1);
               }
          }
          return(0);
     }
     if (flo->atend != LSTFILS && flo->atend != LSTFORE) {
          return(0);
     }
     for (loop=0 ; loop < NLISTER ; loop++) {
          if (flo->tdline[loop] == 0) {
               return(loop+1);
          }
     }
     return(0);
}

INT
srcgood(VOID)                      /* display, reject, or abort search?    */
{
     INT retval=0;
     struct fllib *libptr;
     CHAR wildkey[FLFILENM];
     union srch *current,*newsrch,*old;

     srcoff();
     if (flo->keymeth > 2) {
          retval=-1;
     }
     if (srcu->nlibs > 0) {
          newsrch=(flo->flags&REVSRCH) ? &srcu->tp[srcindex]
             : &srcu->tn[srcindex];
          old=(flo->flags&REVSRCH) ? &srcu->fp[srcindex]
             : &srcu->fn[srcindex];
          movmem(newsrch,old,sizeof(union srch));
          current=&srcactive;
          switch (flo->keymeth) {
          case 3:
               if (((flo->flags&REVSRCH)
                 && dfaAcqLT(NULL,&current->key0,COMPLUF))
                 || (!(flo->flags&REVSRCH)
                 && dfaAcqGT(NULL,&current->key0,COMPLUF)
                 && sameas(flf->libname,current->key0.libname))) {
                    stzcpy(newsrch->key0.libname,flf->libname,FLNAMESZ);
                    stzcpy(newsrch->key0.udate,flf->udate,DATESZ);
                    stzcpy(newsrch->key0.filname,flf->filname,FLFILENM);
               }
               else {
                    newsrch->key0.udate[0]=(flo->flags&REVSRCH) ? 0x00 : 0xFF;
               }
               if (!dfaAcqEQ(NULL,&srcactive.key0,COMPLUF)) {
                    return(0);
               }
               break;
          case 4:
          case 7:
          case 8:
               if (((flo->flags&REVSRCH)
                && dfaAcqLT(NULL,&current->key1,COMPLF))
                || (!(flo->flags&REVSRCH)
                && dfaAcqGT(NULL,&current->key1,COMPLF)
                && sameas(flf->libname,current->key1.libname))) {
                    stzcpy(newsrch->key1.libname,flf->libname,FLNAMESZ);
                    stzcpy(newsrch->key1.filname,flf->filname,FLFILENM);
               }
               else {
                    newsrch->key1.filname[0]=(flo->flags&REVSRCH) ? 0x00 : 0xFF;
               }
               if (!dfaAcqEQ(NULL,&srcactive.key1,COMPLF)) {
                    return(0);
               }
               break;
          }
     }
     switch (flo->keymeth) {
     case 0:
          if (sameas(flf->libname,flo->u.key0.libname)) {
               retval=0;
               if ((libptr=libfind(flo->u.key0.libname)) != NULL) {
                    retval=(notapped(flf->udate) && !isflop(libptr) ? -1 : 1);
               }
          }
          if ((flo->flags&DATESRC) && stricmp(flf->udate,flo->newdate) > 0) {
               retval=0;
          }
          break;
     case 1:
          if (sameas(flf->libname,flo->u.key1.libname)) {
               retval=0;
               if ((libptr=libfind(flo->u.key1.libname)) != NULL) {
                    retval=(notapped(flf->udate) && !isflop(libptr) ? -1 : 1);
               }
          }
          break;
     case 2:
          libptr=libfind(kyd->libkey);
          if (libptr == NULL || kyd->libkey[0] == '\0'
             || !strlen(visxlib(libptr,flo->u.key2.libname))) {
               retval=-1;
          }
          else {
               retval=1;
          }
          if (!sameto(flo->u.key2.origkey,kyd->keyword)) {
               retval=0;
          }
          if (retval == 1) {
               dfaSetBlk(flfdat);
               if (!dfaAcqEQ(NULL,compkey(kyd->libkey,kyd->filname),COMPLF)) {
                    retval=-1;
               }
               if (notapped(flf->udate) && !isflop(libptr)) {
                    retval=-1;
               }
               if ((retval == 1) && (flo->kcount > 1)) {
                    retval=keysin(0);
               }
               dfaRstBlk();
          }
          break;
     case 3:
          keylib(flf->libname,flo->u.key3.libname,&retval);
          if ((flo->flags&DATESRC) && stricmp(flf->udate,flo->newdate) > 0) {
               retval=0;
          }
          break;
     case 4:
          keylib(flf->libname,flo->u.key4.libname,&retval);
          if (retval == 1 && flo->styp == 6) {
               retval=keyfig();
          }
          break;
     case 5:
          keylib(flf->libname,flo->u.key5.libname,&retval);
          break;
     case 6:
          if (sameas(flf->libname,flo->u.key1.libname)) {
               if ((libptr=libfind(flo->u.key1.libname)) == NULL) {
                    retval=0;
               }
               else if (notapped(flf->udate) && !isflop(libptr)) {
                    retval=-1;
               }
               else if (!compwild(flo->miscfil,flf->filname)) {
                    retval=(strlen(wldstuff(wildkey))
                         && !sameto(wildkey,flf->filname) ? 0 : -1);
               }
               else {
                    retval=1;
               }
          }
          else {
               retval=0;
          }
          break;
     case 7:
          keylib(flf->libname,flo->u.key4.libname,&retval);
          if (retval == 1 && !compwild(flo->miscfil,flf->filname)) {
               retval=(strlen(wldstuff(wildkey))
                    && !sameto(wildkey,flf->filname) ? 0 : -1);
          }
          break;
     case 8:
          keylib(flf->libname,flo->u.key4.libname,&retval);
          if (retval == 1 && !sameas(flf->filname,flo->u.key4.exactfn)) {
               retval=0;
          }
          break;
     case 9:
          flo->atend=LSTFORE;
          retval=1;
     }
     return(retval);
}

INT
srcnext(VOID)                      /* get next search element              */
{
     INT flag1,flag2;

     srcoff();
     flag1=((flo->flags&REVSRCH) ? 1 : 0);
     flag2=((flo->flags&NXTEQUL) ? 1 : 0);
     flo->flags&=~NXTEQUL;
     dfaSetBlk(flfdat);
     switch (flo->keymeth) {
     case 0:
          return(nextrec(flag1,flag2,NULL,flo->u.key0.libname,COMPLUF));
     case 1:
     case 6:
          return(nextrec(flag1,flag2,NULL,flo->u.key1.libname,COMPLF));
     case 2:
          dfaSetBlk(flkdat);
          return(nextrec(flag1,flag2,NULL,flo->u.key2.keyword,COMPKFL));
     case 3:
          if (srcu->nlibs > 0) {
               return(nextsrc(flag2) != NULL);
          }
          return(nextrec(flag1,flag2,NULL,flo->u.key3.udate,COMPUF));
     case 4:
     case 7:
     case 8:
          if (srcu->nlibs > 0) {
               return(nextsrc(flag2) != NULL);
          }
          return(nextrec(flag1,flag2,NULL,flo->u.key4.filname,COMPFL));
     case 5:
          return(nextrec(!flag1,flag2,NULL,flo->u.key5.numdls,COMPNLF));
     case 9:
          if (flo->u.key6.filname[0] == '\0') {
               return(fnd1st(&flo->fb,flo->srcpath,0));
          }
          return(fndnxt(&flo->fb));
     }
     return(0);
}

union srch *
nextsrc(                           /* return pointer to next join record   */
INT nxteq)
{
     union srch *best=NULL,*current;
     INT loop,si;
     CHAR badchr;

     srcoff();
     if (nxteq) {
          for (loop=0 ; loop < srcu->nlibs ; loop++) {
               current=(flo->flags&REVSRCH) ? &srcu->tp[loop]
           : &srcu->tn[loop];
               switch (flo->keymeth) {
               case 3:
                    if (sameas(current->key0.libname,flo->u.key3.libkey)) {
                         if (sameas(current->key0.udate,flo->u.key3.udate)
                            && sameas(current->key0.filname,
                            flo->u.key3.filname)) {
                              srcindex=loop;
                              movmem(current,&srcactive,sizeof(union srch));
                              return(current);
                         }
                         loop=maxjoin;
                    }
                    break;
               case 4:
               case 7:
               case 8:
                    if (sameas(current->key1.libname,flo->u.key4.libkey)) {
                         if (sameas(current->key1.filname,
                            flo->u.key4.filname)) {
                              srcindex=loop;
                              movmem(current,&srcactive,sizeof(union srch));
                              return(current);
                         }
                         loop=maxjoin;
                    }
                    break;
               }
          }
     }
     badchr=(flo->flags&REVSRCH) ? 0x00 : 0xFF;
     for (loop=0 ; loop < srcu->nlibs ; loop++) {
          current=(flo->flags&REVSRCH) ? &srcu->tp[loop] : &srcu->tn[loop];
          switch (flo->keymeth) {
          case 3:
               if (current->key0.libname[0] != '\0') {
                    if (best != NULL) {
                         si=stricmp(best->key0.udate,current->key0.udate);
                         if (si == 0) {
                              si=stricmp(best->key0.filname,
                                 current->key0.filname);
                              if (si == 0) {
                                   si=stricmp(best->key0.libname,
                                      current->key0.libname);
                              }
                         }
                         if (((flo->flags&REVSRCH) && si < 0)
                    || (!(flo->flags&REVSRCH) && si > 0)) {
                              if (current->key0.udate[0] != badchr) {
                                   best=current;
                              }
                         }
                    }
                    else if (current->key0.udate[0] != badchr) {
                         best=current;
                    }
               }
               break;
          case 4:
          case 7:
          case 8:
               if (current->key1.libname[0] != '\0') {
                    if (best != NULL) {
                         si=stricmp(best->key1.filname,current->key1.filname);
                         if (si == 0) {
                              si=stricmp(best->key1.libname,
                                 current->key1.libname);
                         }
                         if (((flo->flags&REVSRCH) && si < 0)
                    || (!(flo->flags&REVSRCH) && si > 0)) {
                              if (current->key1.filname[0] != badchr) {
                                   best=current;
                              }
                         }
                    }
                    else if (current->key1.filname[0] != badchr) {
                         best=current;
                    }
               }
               break;
          }
          if (best == current) {
               srcindex=loop;
          }
     }
     if (best != NULL) {
          movmem(best,&srcactive,sizeof(union srch));
     }
     return(best);
}

VOID
resetkey(                          /* re-set key properly for direction.   */
CHAR *libname,
CHAR *filname)
{
     dfaSetBlk(flfdat);
     if (!dfaAcqEQ(NULL,compkey(libname,filname),COMPLF)) {
          setmem(flf,sizeof(struct flfile),0);
     }
     switch (flo->keymeth) {
     case 0:
          stzcpy(flo->u.key0.libname,flf->libname,FLNAMESZ);
          stzcpy(flo->u.key0.filname,flf->filname,FLFILENM);
          stzcpy(flo->u.key0.udate,flf->udate,DATESZ);
          break;
     case 1:
     case 6:
          stzcpy(flo->u.key1.libname,flf->libname,FLNAMESZ);
          stzcpy(flo->u.key1.filname,flf->filname,FLFILENM);
          break;
     case 2:
          dfaSetBlk(flkdat);
          stzcpy(flo->u.key2.libkey,flf->libname,FLNAMESZ);
          stzcpy(flo->u.key2.filname,flf->filname,FLFILENM);
          break;
     case 3:
          stzcpy(flo->u.key3.filname,flf->filname,FLFILENM);
          stzcpy(flo->u.key3.udate,flf->udate,DATESZ);
          stzcpy(flo->u.key3.libkey,flf->libname,FLNAMESZ);
          break;
     case 4:
     case 7:
     case 8:
          stzcpy(flo->u.key4.libkey,flf->libname,FLNAMESZ);
          stzcpy(flo->u.key4.filname,flf->filname,FLFILENM);
          break;
     case 5:
          sprintf(flo->u.key5.numdls,NUMSZP,atol(flf->numdls));
          stzcpy(flo->u.key5.libkey,flf->libname,FLNAMESZ);
          stzcpy(flo->u.key5.filname,flf->filname,FLFILENM);
     }
}

VOID
rset(VOID)                         /* find key to reset to and do it       */
{
     INT loop;

     if (!(flo->flags&REVSRCH)) {
          for (loop=NLISTER-1 ; loop >= 0 ; loop--) {
               if (flo->tdline[loop] != 0) {
                    resetkey(flo->tags[loop].libname,flo->tags[loop].filname);
                    break;
               }
          }
     }
     else {
          for (loop=0 ; loop < NLISTER ; loop++) {
               if (flo->tdline[loop] != 0) {
                    resetkey(flo->tags[loop].libname,flo->tags[loop].filname);
                    break;
               }
          }
     }
}

INT                                /*   returns code describing result     */
tryMove(VOID)                      /* try to move a file                   */
{
     CHAR * cp;
     CHAR tmpsrc[GCMAXPTH],tmpdst[GCMAXPTH];

     /* check for file is already at destination */
     normspec(tmpsrc,flo->srcpath);
     normspec(tmpdst,flo->destpath);
     if (sameas(tmpsrc,tmpdst)) {
          return(INS_DONE);
     }

     /* remove any obstacles */
     unlink(flo->destpath);

     /* see if source and dest are on same drive (can just rename) */
     if ((cp=strchr(tmpsrc,':')) != NULL) {
          *cp='\0';
     }
     if ((cp=strchr(tmpdst,':')) != NULL) {
          *cp='\0';
     }

     /* move or prepare to copy */
     if (sameas(tmpsrc,tmpdst)) {
          if (rename(flo->srcpath,flo->destpath) != 0) {
               return(INS_ERROR);
          }
     }
     else {
          if ((flo->fsrc=fopen(flo->srcpath,FOPRB)) == NULL) {
               return(INS_ERROR);
          }
          if ((flo->fdst=fopen(flo->destpath,FOPWB)) == NULL) {
               fclose(flo->fsrc);
               return(INS_ERROR);
          }
          return(INS_MOVE);
     }
     return(INS_DONE);
}

INT                                /*   returns code describing result     */
insfile(                           /* insert file into file database       */
struct fllib *libptr,              /* Library                              */
CHAR *path,                        /* path where file is stored            */
const struct ffblk *f,             /* stuff including filename info        */
const CHAR *appdate,               /* approval date, NOTAPPED if unappved  */
const CHAR *u,                     /* uploaded by                          */
const CHAR *desc)                  /* var length description               */
{
     struct taglib *tag;
     INT loop;
     CHAR *key;
     FILE *fp;

     makePath(flo->srcpath,path,f->ff_name,GCMAXPTH);
     makePath(flo->destpath,libpath(libptr),f->ff_name,GCMAXPTH);
     if (libptr->flags&FLGDOS) {
          return(tryMove());
     }
     if (!okfname(f->ff_name)) {
          return(INS_BADNAME);
     }
     dfaSetBlk(flfdat);
     if (unifile && dfaAcqGE(NULL,key=rcompkey("",f->ff_name),COMPFL)
      && sameas(flf->filname,key)) {
          dfaRstBlk();
          return(INS_RENAME);
     }
     if (dfaAcqEQ(NULL,key=compkey(libptr->libname,f->ff_name),COMPLF)) {
          if (!(flo->flags&REUPLOD)) {
               if ((fp=fopen(flo->destpath,FOPRB)) != NULL) {
                    fclose(fp);
                    dfaRstBlk();
                    return(INS_RENAME);
               }
               else {
                    dfaDelete();
               }
          }
     }
     else {
          if (flo->flags&REUPLOD) {
               flo->flags&=~REUPLOD;
          }
          unlink(flo->destpath);
     }
     if (!(flo->flags&REUPLOD)) {
          setmem(flf,FLFILREC+DESCSIZ,0);
          stzcpy(flf->libname,libptr->libname,FLNAMESZ);
          stzcpy(flf->filname,f->ff_name,FLFILENM);
          stzcpy(flf->udate,appdate,DATESZ);
          flf->utime=now();
          flf->siz=f->ff_fsize;
          stzcpy(flf->fdate,ddat2srt(f->ff_fdate),DATESZ);
          flf->tim=f->ff_ftime;
          stzcpy(flf->numdls,spr(NUMSZP,0L),NUMSZ);
          stzcpy(flf->ulby,u,UIDSIZ);
          stzcpy(flf->desc,desc,DESCSIZ);
          dfaInsertV(NULL,FLFILREC+strlen(desc)+1);
     }
     else {
          for (loop=0 ; loop < systags ; loop++) {
               tag=tagoff(loop);
               if (tag->status > TGEMPTY && tag->usrnum != usrnum
                && sameas(tag->filname,f->ff_name)
                && sameas(tag->libname,libptr->libname)) {
                    prf("\rAnother user "
                        "has this file, %s, tagged for download!\r",f->ff_name);
                    outprf(usrnum);
                    prf("");
                    stzcpy(flf->filname,f->ff_name,FLFILENM);
                    stzcpy(flf->libname,libptr->libname,FLNAMESZ);
                    return(INS_RENAME);
               }
          }
          libptr->numfiles--;
          libptr->totbytes-=flclfit(flf->siz,libptr->cluster);
          if (notapped(flf->udate)) {
               libptr->appwait--;
          }
          chuldate(libptr,flf->udate,0);
          stzcpy(flf->udate,appdate,DATESZ);
          flf->utime=now();
          flf->siz=f->ff_fsize;
          stzcpy(flf->fdate,ddat2srt(f->ff_fdate),DATESZ);
          flf->tim=f->ff_ftime;
          stzcpy(flf->ulby,u,UIDSIZ);
          dfaUpdateV(NULL,FLFILREC+strlen(flf->desc)+1);
     }
     stzcpy(flo->key1.libname,libptr->libname,FLNAMESZ);
     stzcpy(flo->key1.filname,f->ff_name,FLFILENM);
     libptr->numfiles++;
     libptr->totbytes+=flclfit(f->ff_fsize,libptr->cluster);
     if (notapped(appdate)) {
          libptr->appwait++;
     }
     chuldate(libptr,appdate,1);
     libptr->flags|=LIBCHN;
     return(tryMove());
}

INT
keywordy(VOID)                     /* state handler                        */
{
     CHAR *curkwd,*lastkwd;
     INT loop;

     spinner();
     vdaptr=vdaoff(usrnum);
     if (flo->miscflag == -1) {
          dfaSetBlk(flkdat);
          if (!delkw("",flo->kwdy.filname,flo->kwdy.libkey)) {
               flo->miscflag=0;
               dfaSetBlk(flfdat);
               if (dfaAcqEQ(NULL,compkey(flo->kwdy.libkey,flo->kwdy.filname),
                            COMPLF)
                && makwdlst(flf->desc,flf->filname)) {
                    for (loop=0 ; loop < dargc ; loop++) {
                         stzcpy(&vdaptr[FLKEYSIZ*loop],dargv[loop],FLKEYSIZ);
                    }
                    flo->kcount=dargc;
               }
               dfaRstBlk();
          }
     }
     else {
          if (flo->miscflag >= flo->kcount) {
               return(0);
          }
          lastkwd=(flo->miscflag ? &vdaptr[FLKEYSIZ*(flo->miscflag-1)] : "");
          curkwd=&vdaptr[FLKEYSIZ*flo->miscflag++];
          stzcpy(flo->kwdy.keyword,curkwd,FLKEYSIZ);
          if (!sameas(curkwd,lastkwd) && !samein(spr(" %s ",curkwd),nonkwds)) {
               dfaSetBlk(flkdat);
               dfaInsert(&flo->kwdy);
               dfaRstBlk();
          }
     }
     prf("");
     outprf(usrnum);
     prf("");
     return(1);
}

INT
valpath(                           /* determine validity of a passed path  */
CHAR *path)
{
     INT flag;

     path=fixpath(path);
     flag=MKDIR(path);
     if (flag != 0 && !isdir(path)) {
#ifdef UNIX
          return(0);
#else
          if (!samend(path,":\\.") && !samend(path,":/.")) {
               if (strlen(path) != 3 || !samend(path,":.")) {
                    return(0);
               }
          }
#endif
     }
     if (flag == 0) {
          rmdir(path);
     }
     return(1);
}

INT
taglist(VOID)                      /* display taglist if there is one      */
{
     INT loop,lastidx=-1,nlfiles=0,idx=-1,numdisp=0,lim,warntime=0,blimit,
         stoprf=0;
     LONG totsize=0L,totchg=0L,totsecs=0L,pbuf;
     struct fllib *lib;

     if (ftuptr->numftg > 0) {
          moploop();
          setftu();
          prfmsg(TAGLST);
          blimit=btuoba(usrnum)-1200;
          for (loop=0 ; loop < ftuptr->numftg ; loop++) {
               ftgptr=&ftgusr[loop];
               fltagf=0;
               ftgptr->tshndl(TSHDSC);
               setmbk(flmsg);
               if (fltagf) {
                    if (spc->status == TGLOCAL || spc->status == TGDLTMP) {
                         if ((lib=libfind(spc->libname)) != NULL) {
                              totsize+=spc->size;
                              totchg+=dnlchg(spc->size,spc->libname);
                              totsecs+=dnlsecl(spc->size,lib);
                              nlfiles++;
                         }
                         else {
                              spc->status=-spc->status;
                         }
                    }
                    if (spc->status > TGEMPTY) {
                         idx++;
                    }
               }
               else {
                    idx++;
               }
               if (lastidx != idx) {
                    if ((pbuf=prfptr-prfbuf) <= blimit) {
                         setmisc(spr("%d",idx+1));
                         prfmsg(TAGLSTL);
                    }
                    else if (pbuf > blimit && !stoprf) {
                         prfmsg(MORETAGS,ftuptr->numftg-loop);
                         stoprf=1;
                    }
                    numdisp++;
               }
               lastidx=idx;
          }
          if (nlfiles > 1) {
               if (totsize > 0L) {
                    setmisc(fzer(totsize/1024L));
                    prfmsg(DNLSIZE);
                    setmisc(skpwht(dnlmin(totsize)));
                    prfmsg(DNLTIME);
                    lim=usrptr->cltptr->limday;
                    if ((((usaptr->timtdy+15L+totsecs)/60L) >= lim)
               && (lim != -1)) {
                         warntime=1;
                    }
                    lim=usrptr->cltptr->limcal;
                    if (((usrptr->minut4/4+totsecs/60L+1) >= lim)
               && (lim != -1)) {
                         warntime=1;
                    }
               }
               if (totchg > 0L) {
                    setmisc(l2as(totchg));
                    prfmsg(DNLCHGS);
                    setmisc(l2as(usaptr->creds+    /* future api */
                       (fndcls(usaptr->curcls)->dbtlmt)));
                    prfmsg(DNLYOURC);
                    if (!tstcrd(totchg)) {
                         prfmsg(numdisp > 1 ? WARNDLM : WARNDLA);
                    }
               }
               if (warntime) {
                    prfmsg(numdisp > 1 ? WARNTMM : WARNTMA);
               }
          }
          if (uwaitcpy(usrnum) == 1) {
               prfmsg(WARNCOPY);
          }
          switch (numdisp) {
          case 0:
               clrprf();
               if (flo->flags&OFTGMAN) {
                    usrptr->substt=PRESENTR;
                    flo->retstt=FILELST0;
                    inpfunc();
                    prf("");
               }
               else {
                    retmenu(0);
               }
               break;
          case 1:
               prfmsg(TAGFOOTA);
               break;
          default:
               prfmsg(TAGFOOTN,lastidx+1,-(lastidx+1));
          }
     }
     return(ftuptr->numftg);
}

INT
zapcrd(                            /* check for/stop charging credits      */
INT yes,
LONG siz,
struct fllib *dlib)
{
     INT retval=1;

     flo->crdrat=usrptr->crdrat;
     flo->nozap=(INT)(usrptr->flags&NOZAP);
     if ((siz > 0L) && (dlib != NULL)) {
          if (!tstcrd(dnlchg(siz,dlib->libname))) {
               retval=0;
          }
     }
     if (yes) {
          usrptr->crdrat=0;
          usrptr->flags|=NOZAP;
     }
     return(retval);
}

struct fllib *
keylib(                            /* function to search with              */
CHAR *libfound,
CHAR *libname,
INT *retval)
{
     struct fllib *libptr;

     libptr=libfind(libfound);
     if (libptr == NULL || libfound[0] == '\0'
      || !strlen(visxlib(libptr,libname))) {
          return(libptr);
     }
     *retval=1;
     if (sameas(libfound,flf->libname)
        && notapped(flf->udate) && !isflop(libptr)) {
          *retval=-1;
     }
     return(libptr);
}

struct fllib *
nxtlib(VOID)                       /* find next Library in flu's structure */
{
     struct fllib *retval;

     while (flo->index < numoflib) {
          retval=liboff(flo->index++);
          if (valname(retval->libname) && visilib(retval)) {
               return(retval);
          }
     }
     return(NULL);
}

VOID
optiprf(VOID)                      /* optimize prfbuf                      */
{
     CHAR *spstart=NULL;

     for (prfptr=prfbuf ; *prfptr != '\0' ; prfptr++) {
          if (spstart == NULL) {
               if (*prfptr == ' ') {
                    spstart=prfptr;
               }
          }
          else if (*prfptr != ' ') {
               if (*prfptr == '\r') {
                    strcpy(spstart,prfptr);
                    prfptr=spstart;
               }
               spstart=NULL;
          }
     }
}

VOID
bgnfiled(                          /* begin file edit / or just display    */
INT display)
{
     CHAR *stgptr;
     INT loop;

     dfaSetBlk(flfdat);
     setmem(descedit,DESCSIZ,0);
     if (!dfaAcqEQ(NULL,&flo->key1,COMPLF)) {
          return;
     }
     fnmcse(flo->key1.filname);
     stzcpy(flo->fdate,flf->fdate,DATESZ);
     flo->ftim=flf->tim;
     stzcpy(flo->udate,flf->udate,DATESZ);
     flo->siz=flf->siz;
     stzcpy(flo->ulby,flf->ulby,UIDSIZ);
     flo->timeul=flf->utime;
     flo->numdls=atol(flf->numdls);
     stzcpy(descedit,flf->desc,DESCSIZ);
     if (isansiu()) {
          if (!display) {
               fsdrhd(spr("%s",getmsg(EDITINFO)));
               outprf(usrnum);
          }
          fileditp(FILEDTA,(display ? -1 : 1),display);
          stgptr=(display ? getmsg(fsdusr->tmpmsg) : getasc(fsdusr->tmpmsg));
          if (display) {
               tpwipe(stgptr,FFDEXIT,12,3);
               fsddsp(stgptr);
               optiprf();
               outprf(usrnum);
               return;
          }
          tpwipe(stgptr,FFDDLT,6,1);
          tpwipe(stgptr,FFDCHG,3,1);
          fsdbkg(stgptr);
          fsdscb->flddat[FFDNAME].flags|=FFFAVD;
          fsdscb->flddat[FFDLIB].flags|=FFFAVD;
          fsdscb->flddat[FFDDATE].flags|=FFFAVD;
          fsdscb->flddat[FFDTIME].flags|=FFFAVD;
          fsdscb->flddat[FFDSIZE].flags|=FFFAVD;
          fsdscb->flddat[FFDDLT].flags|=FFFAVD;
          fsdscb->flddat[FFDCHG].flags|=FFFAVD;
          if (!isflop(curlib)) {
               fsdscb->flddat[FFDUSER].flags|=FFFAVD;
               fsdscb->flddat[FFDDL].flags|=FFFAVD;
               fsdscb->flddat[FFDULD].flags|=FFFAVD;
               fsdscb->flddat[FFDULT].flags|=FFFAVD;
          }
          if (notapped(flf->udate)) {
               fsdscb->flddat[FFDULD].flags|=FFFAVD;
               fsdscb->flddat[FFDULT].flags|=FFFAVD;
          }
          fsdego(fileditv,fileditd);
          optiprf();
     }
     else {
          if (display) {
               prfmsg(FILEDTX,flo->key1.filname,srt2datl(flo->fdate),
                      flo->key1.libname,nctime(flo->ftim),l2as(flo->siz));
               stgptr="UNAPPROVED";
               if (!notapped(flo->udate)) {
                    stgptr=srt2datl(flo->udate);
               }
               prfmsg(FILEDTY,flo->ulby,l2as(flo->numdls),stgptr,
                      nctime(flo->timeul),dnlmin(flo->siz),
                      l2as(dnlchg(flo->siz,flo->key1.libname)));
               darsdesc(NDESCLN,'\r');
               for (loop=0 ; loop < NDESCLN ; loop++) {
                    if (dargv[loop][0] != '\0') {
                         if (loop == 0) {
                              prfmsg(FILEDITD);
                         }
                         prf("   %s\r",dargv[loop]);
                    }
               }
          }
          else {
               fileditp(FILEDTN,-1,0);
               btuoes(usrnum,1);
               usrptr->substt=FILEDTN;
               prfmsg(FILEDTX,flo->key1.filname,srt2datl(flo->fdate),
                      flo->key1.libname,nctime(flo->ftim),l2as(flo->siz));
               prfmsg(FILEDITZ);
          }
     }
     if (isansiu()) {
          powprf();
     }
     else {
          outprf(usrnum);
     }
     clrprf();
}

INT
uwaitcpy(                          /* does this user have files copying?   */
INT unum)
{
     INT loop,tg=0;
     struct taglib *tagptr;

     for (loop=0 ; loop < systags ; loop++) {
          tagptr=tagoff(loop);
          if (tagptr->usrnum == unum) {
               switch (tagptr->status) {
               case TGINWAIT:
               case TGOTHER:
               case TGSERVER:
               case TGCOPY:
                    return(1);
               default:
                    tg=1;
               case TGEMPTY:
                    break;
               }
          }
     }
     return(tg ? 0 : -1);
}

VOID
notifu(VOID)                       /* notify user optionally, assumes ftag */
{
     if (onsysn(uacoff(ftag->usrnum)->userid,1)) {
          if (!(othusp->flags&NOINJO) && !uwaitcpy(othusn)
             && (ntagged(othuap->userid) > 0)) {
               setmbk(flmsg);
               prfmlt(BEEP);
               if (othusp->state == flstt && othusp->substt == PLSWAIT) {
                    othusp->substt=TAGMAN;
               }
               prfmlt(YERFILE);
               rstmbk();
               injoth();
               clrmlt();
          }
     }
     else {
          ftag->status=-ftag->status;
     }
}

INT
ntagged(                           /* # of files user has tagged           */
CHAR *uid)
{
     struct taglib *tag;
     INT loop,retval=0;

     if (!onsysn(uid,1)) {
          return(0);
     }
     for (loop=0 ; loop < systags ; loop++) {
          tag=tagoff(loop);
          if (tag->status > TGEMPTY && othusn == tag->usrnum) {
               retval++;
          }
     }
     return(retval);
}

VOID
checkoth(VOID)                     /* check if others waiting on same file */
{
     struct taglib *test;
     struct libcsfil *ctest;
     INT loop;
     CHAR *l,*f;

     l=(ftag == NULL ? lcsarr[usrnum].libname : ftag->libname);
     f=(ftag == NULL ? lcsarr[usrnum].filname : ftag->filname);
     for (loop=0 ; loop < systags ; loop++) {
          test=tagoff(loop);
          if (test != ftag) {
               switch (test->status) {
               case TGINWAIT:
               case TGOTHER:
               case TGSERVER:
                    if (sameas(test->filname,f) && sameas(test->libname,l)) {
                         test->status=TGDLTMP;
                         notifu();
                    }
               }
          }
     }
     for (loop=0 ; loop < nterms ; loop++) {
          ctest=&lcsarr[loop];
          if (ftag != NULL || loop != usrnum) {
               switch (ctest->status) {
               case CDFILE:
               case EXTCDF:
               case EXTCDC:
                    if (sameas(ctest->filname,f) && sameas(ctest->libname,l)) {
                         ctest->status=CDDONE;
                    }
               }
          }
     }
}

VOID
ulcount(VOID)                      /* ongoing count of uploads this week   */
{
     INT retval=0,ago;
     struct fllib *libptr;

     dfaSetBlk(flfdat);
     if (dfaAcqGT(NULL,&countkey,COMPLUF) && sameas(flf->libname,countlib)) {
          stzcpy(countkey.filname,flf->filname,FLFILENM);
          stzcpy(countkey.udate,flf->udate,DATESZ);
          ago=cofdat(today())-cofdat(dcdate(srt2datl(flf->udate)));
          if (ago >= 0 && ago < ULDAYS) {
               countuls[ago]++;
               retval=1;
          }
     }
     if (!retval) {
          libptr=libfind(countlib);
          countlib[0]='\0';
          if (libptr != NULL) {
               for (ago=0 ; ago < ULDAYS ; ago++) {
                    libptr->newfiles[ago]=countuls[ago];
               }
               libptr->flags|=LIBCHN;
          }
     }
     dfaRstBlk();
}

VOID
inicount(                          /* initialize counting a Library        */
CHAR *libname)
{
     INT loop;

     setmem(&countkey,sizeof(struct key0),0);
     stzcpy(countkey.libname,libname,FLNAMESZ);
     stzcpy(countkey.udate,"0000009",DATESZ);
     for (loop=0 ; loop < ULDAYS ; loop++) {
          countuls[loop]=0;
     }
     stzcpy(countlib,libname,FLNAMESZ);
}

INT
addkwnow(VOID)                     /* add the first keyword waiting...     */
{

     if (kwbuf[0].keyword[0] != '\0') {
          dfaSetBlk(flkdat);
          if (!dfaAcqEQ(NULL,&kwbuf[0],0)) {
               dfaInsert(&kwbuf[0]);
          }
          dfaRstBlk();
          movmem(&kwbuf[1],&kwbuf[0],(SIZKWBUF-1)*sizeof(struct key7));
          setmem(&kwbuf[SIZKWBUF-1],sizeof(struct key7),0);
          return(1);
     }
     return(0);
}

VOID
addkw(                             /* add keyword to disk buffer memory    */
const CHAR *keyword,
const CHAR *filname,
const CHAR *libname)
{
     struct key7 *kwp;
     INT loop;

     if (samein(spr(" %s ",keyword),nonkwds)) {
          return;
     }
     if ((kwp=&kwbuf[SIZKWBUF-1])->keyword[0] != '\0') {
          addkwnow();
     }
     else {
          for (loop=0 ; loop < SIZKWBUF ; loop++) {
               if ((kwp=&kwbuf[loop])->keyword[0] == '\0') {
                    break;
               }
          }
     }
     stzcpy(kwp->keyword,keyword,FLKEYSIZ);
     stzcpy(kwp->filname,filname,FLFILENM);
     stzcpy(kwp->libname,libname,FLNAMESZ);
}

VOID
keywdaut(                          /* insert keywords to .DAT one at a time*/
INT taskid)
{
     if (!addkwnow()) {
          mfytask(taskid,NULL);
          kwaut=0;
     }
}

INT
delkw(
const CHAR *keyword,
const CHAR *filname,
const CHAR *libname)
{
     struct key7 *kwp;
     INT loop,retval=0;

     if (libname[0] == '\0') {
          return(0);
     }
     for (loop=0 ; loop < SIZKWBUF ; loop++) {
          if ((kwp=&kwbuf[loop])->libname[0] == '\0') {
               break;
          }
          if (sameas(libname,kwp->libname)) {
               if (keyword[0] == '\0' || sameas(keyword,kwp->keyword)) {
                    if (sameas(filname,kwp->filname)) {
                         movmem(&kwbuf[loop+1],&kwbuf[loop],
                            (SIZKWBUF-(loop+1))*sizeof(struct key7));
                         setmem(&kwbuf[SIZKWBUF-1],sizeof(struct key7),0);
                         return(1);
                    }
               }
          }
     }
     dfaSetBlk(flkdat);
     if (filname[0] != '\0') {
          if (dfaAcqEQ(NULL,rcompkey(libname,filname),COMPFL_K)) {
               dfaDelete();
               retval=1;
          }
     }
     dfaRstBlk();
     return(retval);
}

VOID
copyaut(                           /* copy a file, in chunks               */
INT taskid)
{
     static LONG sleft;
     INT readsiz,close=0,csu;
     CHAR *stg,dpath[80];
     struct fllib *libptr;
     struct ffblk fb;

     if ((csu=findbest(TGCOPY)) == 0) {
          mfytask(taskid,NULL);
          return;
     }
     if (!space()) {
          return;
     }
     if (ftag == NULL) { /* therefore it's a c/s tag */
          csu--;
          if (csrc == NULL && cdst == NULL) {
               libptr=libfind(lcsarr[csu].libname);
               if (libptr == NULL) {
                    lcsarr[csu].status=-lcsarr[csu].status;
                    return;
               }
               stg=spr("%s"SLS"%s",libpath(libptr),lcsarr[csu].filname);
               readsiz=fndfile(&fb,stg,0);
               sprintf(dpath,"%s"SLS"%s",copydir,libptr->libname);
               MKDIR(dpath);
               strcat(dpath,SLS);
               strcat(dpath,lcsarr[csu].filname);
               cdst=fopen(dpath,FOPWB);
               if (cdst != NULL) {
                    if (readsiz && (csrc=fopen(stg,FOPRB)) != NULL) {
                         sleft=fb.ff_fsize;
                    }
                    else {
                         lcsarr[csu].status=-lcsarr[csu].status;
                         fclose(cdst);
                         cdst=NULL;
                         unlink(dpath);
                    }
               }
               else {
                    lcsarr[csu].status=-lcsarr[csu].status;
               }
               return;
          }
          readsiz=1024;
          if (sleft < 1024L) {
               readsiz=(INT)sleft;
          }
          sleft-=(LONG)readsiz;
          if ((INT)fread(vdatmp,readsiz,1,csrc) <= 0
           || (INT)fwrite(vdatmp,readsiz,1,cdst) <= 0) {
               lcsarr[csu].status=-lcsarr[csu].status;
               close=1;
          }
          if (close || sleft == 0L) {
               fclose(csrc);
               csrc=NULL;
               fclose(cdst);
               cdst=NULL;
               libptr=libfind(lcsarr[csu].libname);
               if (libptr != NULL) {
                    stg=spr("%s"SLS"%s",libpath(libptr),lcsarr[csu].filname);
                    sprintf(dpath,"%s"SLS"%s",copydir,libptr->libname);
                    MKDIR(dpath);
                    strcat(dpath,SLS);
                    strcat(dpath,lcsarr[csu].filname);
                    touch(stg,dpath);
               }
               if (!close) {
                    lcsarr[csu].status=CDDONE;
                    usrnum=csu;
                    checkoth();
               }
          }
          return;
     }
     if (csrc == NULL && cdst == NULL) {
          libptr=libfind(ftag->libname);
          if (libptr == NULL) {
               ftag->status=-ftag->status;
               return;
          }
          stg=spr("%s"SLS"%s",libpath(libptr),ftag->filname);
          readsiz=fndfile(&fb,stg,0);
          sprintf(dpath,"%s"SLS"%s",copydir,ftag->libname);
          MKDIR(dpath);
          strcat(dpath,SLS);
          strcat(dpath,ftag->filname);
          cdst=fopen(dpath,FOPWB);
          if (cdst != NULL) {
               if (readsiz && (csrc=fopen(stg,FOPRB)) != NULL) {
                    ftag->size=sleft=fb.ff_fsize;
               }
               else {
                    ftag->status=-ftag->status;
                    fclose(cdst);
                    cdst=NULL;
                    unlink(dpath);
               }
          }
          else {
               ftag->status=-ftag->status;
          }
          return;
     }
     readsiz=1024;
     if (sleft < 1024L) {
          readsiz=(INT)sleft;
     }
     sleft-=(LONG)readsiz;
     ftag->copied+=readsiz;
     if ((INT)fread(vdatmp,readsiz,1,csrc) <= 0
      || (INT)fwrite(vdatmp,readsiz,1,cdst) <= 0) {
          ftag->status=-ftag->status;
          close=1;
     }
     if (close || sleft == 0L) {
          fclose(csrc);
          csrc=NULL;
          fclose(cdst);
          cdst=NULL;
          libptr=libfind(ftag->libname);
          if (libptr != NULL) {
               stg=spr("%s"SLS"%s",libpath(libptr),ftag->filname);
               sprintf(dpath,"%s"SLS"%s",copydir,ftag->libname);
               MKDIR(dpath);
               strcat(dpath,SLS);
               strcat(dpath,ftag->filname);
               touch(stg,dpath);
          }
          if (!close) {
               ftag->status=TGDLTMP;
               notifu();
               checkoth();
          }
     }
}

VOID
doalr(VOID)                        /* re-mark files already tagged         */
{
     INT loop;

     for (loop=0 ; loop < NLISTER ; loop++) {
          if (alrtag(flo->tags[loop].libname,flo->tags[loop].filname)) {
               if (flo->tdline[loop] > 0) {
                    flo->tdline[loop]=-flo->tdline[loop];
               }
          }
     }
}

VOID
dsearch(                           /* recursively get how user searches    */
INT which,
INT x)
{
     INT mode;

     switch (which) {
     case 0:
          wipetag();
          deresume();
          setmem(flo->keylist,FLKEYLST,0);
          if (x == 0) {
               x=defstyp;
          }
          switch (x) {
          default:
          case 1:
               usrptr->substt=FLSTYPA;
               break;
          case 7:
               flo->styp=2;
               flo->flags|=DATESRC;
               dsearch(1,((flo->flags&ABOUPRF) ? 1 : flu->mylib));
               break;
          case 2:
               flo->flags&=~DATESRC;
          case 5:
               flo->styp=x;
               dsearch(1,((flo->flags&ABOUPRF) ? 1 : flu->mylib));
               break;
          case 3:
          case 6:
               flo->styp=(x == 3 ? 3 : 7);
               usrptr->substt=ASKNAME;
               break;
          case 4:
               flo->styp=4;
               usrptr->substt=ASKWORD;
               btumil(usrnum,FLKEYLST-1);
          }
          break;
     case 1:
          if (x == 0) {
               x=defslib;
          }
          switch (x) {
          default:
          case 1:
               usrptr->substt=FLSLIB;
               break;
          case 2:
          case 3:
               usrptr->substt=MMENU;
               switch (flo->styp) {
               case 3:
               case 4:
                    mode=flo->styp-1;
                    break;
               case 6:
                    mode=4;
                    break;
               case 5:
                    mode=5;
                    break;
               case 7:
                    mode=6;
                    break;
               default:
                    mode=1;
               }
               srcinit(mode,(x == 2) ? flu->lib : "");
          }
     }
}

VOID
fileditd(                          /* FSD finish editing file desc/etc.    */
SHORT save)
{
     INT loop,len,tim;
     CHAR temp[UIDSIZ],date[DATESZ],*sptr;

     flu=&fluarr[usrnum];
     if (flu->lib[0] == '\0') {
          return;
     }
     setmbk(flmsg);
     curusr(usrnum);
     fluoff(usrnum);
     usrptr->state=flstt;
     usrptr->substt=flo->retstt;
     if ((fsdscb->chgcnt == 0) && (libfind(libedt->lib.libname) != NULL)) {
          prfmsg(NOCH);
     }
     else if (!save) {
          prfmsg(CHQUIT);
     }
     else {
          prfmsg(SAVED);
          setmem(descedit,DESCSIZ,0);
          for (loop=0 ; loop < NDESCLN ; loop++) {
               len=strlen(descedit);
               fsdfxt(FFDTXT00+loop,sptr=&descedit[strlen(descedit)],SDESCLN);
               if ((!loop && sameas(sptr,DESCFNON))
             || (loop == 1 && sameas(sptr,DESCFNO2))) {
                    *sptr='\0';
               }
               if (strlen(descedit) > len) {
                    strcat(descedit,"\r");
               }
          }
          fsdfxt(FFDUSER,flo->ulby,UIDSIZ);
          fsdfxt(FFDDL,temp,NUMSZ);
          flo->numdls=atol(temp);
          fsdfxt(FFDULD,temp,UIDSIZ);
          stzcpy(date,dat2srt(temp),DATESZ);
          fsdfxt(FFDULT,temp,UIDSIZ);
          tim=dctime(temp);
          dfaSetBlk(flfdat);
          if (dfaAcqEQ(NULL,&flo->key1,COMPLF)) {
               stzcpy(flf->ulby,flo->ulby,UIDSIZ);
               sprintf(flf->numdls,NUMSZP,flo->numdls);
               stzcpy(flf->desc,descedit,DESCSIZ);
               stzcpy(flf->udate,(atoi(date) != 0) ? date : NOTAPPED,DATESZ);
               flf->utime=tim;
               dfaUpdateV(NULL,FLFILREC+strlen(flf->desc)+1);
               flo->retstt=usrptr->substt;
               if (!longsrch && kwdout()) {
                    outprf(usrnum);
                    clrprf();
                    return;
               }
               usrptr->substt=flo->retstt;
          }
     }
     outprf(usrnum);
     if (flo->retstt == AUTOLOG) {
          usrptr->substt=AUTOLOG;
          btuinj(usrnum,CYCLE);
     }
     else {
          btucli(usrnum);
          fluoff(usrnum);
          promptin();
     }
     clrprf();
}

VOID
fileditp(                          /* FSD utility function                 */
INT msgno,
INT amode,
INT display)
{
     CHAR *appdate,*proprlib;
     struct fllib *libptr;

     libptr=libfind(flo->key1.libname);
     proprlib=((display && libptr != NULL) ? visxlib(libptr,flo->lrange)
            : flo->key1.libname);
     appdate=(notapped(flo->udate) ? NULL : srt2datl(flo->udate));
     fsdroom(msgno,filedits,amode);
     darsdesc(NDESCLN,'\r');
     sprintf(vdatmp,fileditf,
          flo->key1.filname,'\0',
          proprlib,'\0',
          srt2datl(flo->fdate),'\0',
          spr("%s",nctime(flo->ftim)),'\0',
          spr("%s bytes",l2as(flo->siz)),'\0',
          l2as(flo->numdls),'\0',
          (appdate == NULL ? "UNAPP" : appdate),'\0',
          spr("%s",nctime(flo->timeul)),'\0',
          display ? spr("%s [32mminutes",dnlmin(flo->siz)) : "",'\0',
          display ? l2as(dnlchg(flo->siz,flo->key1.libname)) : "",'\0',
          flo->ulby,'\0',
          (display || strlen(dargv[0]) ? dargv[0] : DESCFNON),'\0',
          (display || strlen(dargv[1]) ? dargv[1] : DESCFNO2),'\0',
          dargv[2],'\0',
          dargv[3],'\0',
          dargv[4],'\0',
          dargv[5],'\0',
          dargv[6],'\0',
          dargv[7],'\0',
          dargv[8],'\0',
          dargv[9],'\0',
          dargv[10],'\0',
          dargv[11],'\0',
          dargv[12],'\0',
          dargv[13],'\0',
          dargv[14],'\0');
     fsdapr(vdaptr,vdasiz,vdatmp);
}

VOID
lsclean(VOID)                      /* nightly cleanup routine              */
{
     switch (clnlevel) {
     case 2:
     case 1:
          countupl();
          cleanit(clnlevel == 2 ? 1 : 0);
     default:
          do {
          } while (libchg());
     }
     purgecpy(1);
}

VOID
lsdel(                             /* deletes Library user reference       */
CHAR *uid)
{
     dfaSetBlk(genbb);
     stzcpy(flusmem.userid,uid,UIDSIZ);
     stzcpy(flusmem.modnam,"File Library",MNMSIZ);
     if (dfaAcqEQ(NULL,&flusmem,0)) {
          dfaDelete();
     }
}

VOID
lsend(VOID)                        /* system shutdown                      */
{
     while (libchg()) {
     }
     if (kwaut) {
          while (addkwnow()) {
          }
          kwaut=0;
     }
     if (flmsg != NULL) {
          clsmsg(flmsg);
     }
     if (csrc != NULL) {
          fclose(csrc);
     }
     if (cdst != NULL) {
          fclose(cdst);
     }
     if (movs != NULL) {
          fclose(movs);
     }
     if (movd != NULL) {
          fclose(movd);
     }
     dfaClose(flfdat);
     dfaClose(flldat);
     if (!longsrch) {
          dfaClose(flkdat);
     }
     purgecpy(0);
}

VOID
purgecpy(                          /* purge copy and/or upload directories */
INT rmvdirs)
{
     struct ffblk dirs,fils;

     if (fnd1st(&dirs,spr("%s"SLS STAR,copydir),FAMDIR)) {
          do {
               if ((dirs.ff_attrib&FAMDIR) && dirs.ff_name[0] != '.') {
                    if (fnd1st(&fils,spr("%s"SLS"%s"SLS STAR,
                                         copydir,dirs.ff_name),0)) {
                         do {
                              unlink(spr("%s"SLS"%s"SLS"%s",
                                         copydir,dirs.ff_name,fils.ff_name));
                         } while (fndnxt(&fils));
                    }
                    if (rmvdirs) {
                         rmdir(spr("%s"SLS"%s",copydir,dirs.ff_name));
                    }
               }
          } while (fndnxt(&dirs));
     }
     if (rmvdirs && fnd1st(&fils,spr("%s"SLS STAR,userdir(0)),0)) {
          do {
               unlink(spr("%s"SLS"%s",userdir(0),fils.ff_name));
          } while (fndnxt(&fils));
     }
}

VOID
lshang(VOID)                       /* user hangup routine                  */
{
     CHAR fname[FLFILENM];
     INT ctr=0;

     if (usrptr->state == flstt) {
          echonu(usrnum);
          btutrg(usrnum,0);
     }
     genhup();
     if (flo->flags&AXSUSER) {
          while (ulfileq(2) && ctr < 5) {
               ctr++;
          }
          rmdir(userdir(1));
          sprintf(fname,"*.X%02X",channel[usrnum]);
          if (fnd1st(&flo->fb,spr("%s"SLS"%s",copydir,fname),0)) {
               do {
                    unlink(spr("%s"SLS"%s",copydir,flo->fb.ff_name));
               } while (fndnxt(&flo->fb));
          }
     }
}

VOID
genhup(VOID)                       /* C/S - A/A generic hangup routine     */
{
     fluoff(usrnum);
     if (lcsarr[usrnum].status != 0) {
          lcsarr[usrnum].status=-abs(lcsarr[usrnum].status);
          csclean(usrnum);
     }
     setmem(&viewarr[usrnum],sizeof(struct libcsfil),0);
     if (flo->cszview.state != 0) {
          fclose(flo->cszview.fp);
          flo->cszview.state=0;
     }
     if (flo->fsrc != NULL) {
          fclose(flo->fsrc);
     }
     if (flo->fdst != NULL) {
          fclose(flo->fdst);
     }
     flo->fdst=flo->fsrc=NULL;
     donewrit(-1);
     updusr(flu);
     flu->lib[0]='\0';
}

LONG
flclfit(                           /* return size of file, using CLFITS    */
LONG filsiz,                       /*   size of file                       */
USHORT cluster)                    /*   cluster size                       */
{
     return(clfits ? clfit(filsiz,cluster) : filsiz);
}
