/*********************************************************************
 *
 *   This is ADT, a user friendly frontend for Aminet FTP sites.
 *
 *   To re-setup adt, start it using adt -n
 *
 *********************************************************************/

/* Include files */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifdef SYSV
#include <netdb.h>
#else
#include <sys/param.h>
#endif
#include <pwd.h>
#include <errno.h>
#include "adt.h"

/* Function protos  */

int
  adt_loadconfig(),
  adt_connect(),
  adt_readfile(),
  adt_parse_v1(),
  adt_agecmp(),
  adt_rev_agecmp(),
  adt_dircmp(),
  adt_rev_dircmp(),
  adt_namecmp(),
  adt_rev_namecmp(),
  adt_sizecmp(),
  adt_rev_sizecmp(),
  adt_sendget(),
  adt_select(),
  strnicmp(),
  stricmp();

void
  (* adt_quit)(),
  (* adt_status)(),
  (* adt_more)(),
  (* adt_refresh)(),
  (* adt_refresh_list)(),
  main(),
  adt_exit(),
  adt_xit(),
  adt_Quit(),
  adt_set_mode(),
  adt_free_entries(),
  adt_free_tags(),
  adt_sort(),
  adt_mode_server(),
  adt_mode_help(),
  adt_mode_tag(),
  adt_mode_new(),
  adt_mode_recent(),
  adt_mode_all(),
  adt_mode_long(),
  adt_goto_start(),
  adt_goto_line(),
  adt_go_forw(),
  adt_go_back(),
  adt_nocr(),
  adt_addtag(),
  adt_remtag(),
  adt_saveconfig(),
  adt_tag(),
  adt_info(),
  adt_download(),
  adt_readme(),
  adt_cd();

char
  *adt_gettok(),
  *adt_copyof();

extern void
  cur_init(),
  cur_main();

extern int
  ftp_get(),
  ftp_connect();

extern char
  *sys_errlist[];

extern TAG
  HelpList[];

extern int
  NumHelp;
  

/* Function's #define */

#define adt_toupper(x) ((x)>='a' && (x)<='z' ? (x)-'a'+'A' : (x))

/* Globals */

static SERVER servers[]={    /* Server's information */
  {"SCANDINAVIA",  "ftp.luth.se",             "pub/aminet/" },
  {"SWITZERLAND",  "amiga.physik.unizh.ch",   "pub/aminet/" },
  {"SWITZERLAND",  "litamiga.epfl.ch",        "pub/aminet/" },
  {"GERMANY",      "ftp.uni-kl.de",           "pub/aminet/" },
  {"GERMANY",      "ftp.cs.tu-berlin.de",     "pub/aminet/" },
  {"GERMANY",      "ftp.th-darmstadt.de",     "pub/aminet/" },
  {"USA",          "ftp.etsu.edu",            "pub/aminet/" },
  {"USA",          "ftp.wustl.edu",           "pub/aminet/" },
  {"USA",          "oes.orst.edu",            "pub/aminet/"  },
};

static TAG Servers[] = {
  { &Servers[1], NULL       , (ENTRY *)&servers[0] },
  { &Servers[2], &Servers[0], (ENTRY *)&servers[1] },
  { &Servers[3], &Servers[1], (ENTRY *)&servers[2] },
  { &Servers[4], &Servers[2], (ENTRY *)&servers[3] },
  { &Servers[5], &Servers[3], (ENTRY *)&servers[4] },
  { &Servers[6], &Servers[4], (ENTRY *)&servers[5] },
  { &Servers[7], &Servers[5], (ENTRY *)&servers[6] },
  { &Servers[8], &Servers[6], (ENTRY *)&servers[7] },
  { NULL       , &Servers[7], (ENTRY *)&servers[8] },
};

struct {
  int (*func)();
  char *text;
} Sort[] = {
  { NULL,            "nothing"  },
  { adt_namecmp,     "name"     },
  { adt_rev_namecmp, "rev-name" },
  { adt_sizecmp,     "size"     },
  { adt_rev_sizecmp, "rev-size" },
  { adt_dircmp ,     "dir"      },
  { adt_rev_dircmp , "rev-dir"  },
  { adt_agecmp ,     "age"      },
  { adt_rev_agecmp , "rev-age"  }
};

pid_t pid;

ENTRY *Entry;

TAG
  *Visible,              /* Pointer to the entries to display */
  *ServerList = &Servers[0],           /* List of known servers */
  *Page = NULL,          /* Beggining of current page */
  *OldPage = NULL,       /* Old page pointer */
  *Active,               /* Currently active (highlighted entry) */
  *OldActive,            /* Old Active pointer */
  *Tag = NULL;           /* Pointer to list of tagged items */

int
  Save = 0,              /* Non-zero to mean saveconfig in config mode */
  KeepResult,            /* 0 = erase status on refresh(), 1 = don't */
  Mode = -1,             /* default operation mode (anything but M_SERVER) */
  OldTagMode,            /* Mode to return to from M_TAG */
  OldHelpMode,           /* Mode to return to from M_HELP */
  NumServers = 9,        /* Total number of servers */
  NumTagged = 0,         /* Number of tagged entries */
  NumVisible,            /* Total number of entries in Visible list */
  SortMode,              /* adt's sort mode */
  Offset,                /* Offset of Active in the list */
  OldOffset,             /* Offset of OldActive in the list */
  Window;                /* Window size */

char
  gotall = 0,
  Result[256],
  GBuf[256];

/*------------config------------*/

long PrevTime, ThisTime;
char Long;
char Configfile[200];

char *login, *HomeDir;
char hostname[MAXHOSTNAMELEN];
TAG *Server = &Servers[0];
int ServerOffset = 0;             /* Offset of Server in list */

/* ----------------- Actual code ----------------- */

void main(argc, argv)
     int argc;
     char *argv[];
{
  struct passwd *pw;
  
  adt_quit = adt_exit;
  
  if ((pw=getpwuid(getuid())) == NULL) {
    fprintf(stderr,"adt: can't get user's info\n");
    HomeDir = adt_copyof(getenv("HOME"));
    login = adt_copyof(getenv("USER"));
  }
  else {
    HomeDir = adt_copyof(pw->pw_dir);
    login = adt_copyof(pw->pw_name);
  }
  
  sprintf(Configfile,"%s/.adtrc",HomeDir);
  
  if (gethostname(hostname,MAXHOSTNAMELEN)) {
    fprintf(stderr,"adt: can't get hostname\n");
    strcpy(hostname,getenv("HOST"));
  }
  
  pid = getpid();
  
  cur_init();

  Save = 1;
  switch (adt_loadconfig())
    {
    case 0:     /* No errors during load */
      if (argc==2 && !strcmp(argv[1],"-n")) {  /* reconfig */
	strcpy(Result,"Select new default server");
	KeepResult = 1;
	adt_set_mode(M_SERVER);
	break;
      }

      Save = 0;
      KeepResult = 0;
      adt_set_mode(M_SERVER);
      
      if (adt_connect())
	(* adt_status)(Result);
      else
	adt_set_mode(M_NEW);
      break;
    case 1:
      strcpy(Result,"Welcome new user, select your default server");
      KeepResult = 1;
      adt_set_mode(M_SERVER);
      break;
    case 2:
      strcpy(Result,"Error reading ~/.adtrc, select your default server");
      KeepResult = 1;
      Server = Servers;
      ServerOffset = 0;

      adt_set_mode(M_SERVER);
      break;
    }
  
  cur_main();
}

/* adt_exit(): get rid of /tmp files, print 'str' on stderr */

void adt_exit(str)
     char *str;
{
  /* get rid of /tmp files */
  
  sprintf(Result,"/tmp/S%d",pid);
  (void) unlink(Result);
  sprintf(Result,"/tmp/S%d.Z",pid);
  (void) unlink(Result);
  sprintf(Result,"/tmp/R%d",pid);
  (void) unlink(Result);
  sprintf(Result,"/tmp/R%d.Z",pid);
  (void) unlink(Result);
  
  /* put out the string */
  printf("\n");
  
  if( str ) fprintf(stderr,"adt: %s\n",str);
  exit(0);
}

void adt_xit()
{
  (* adt_quit)(NULL);
}

void adt_Quit()
{
  adt_saveconfig();
  (* adt_quit)(NULL);
}

/*---------------------- parsing --------------------*/

/* breaks a string at the token 'mark', returning a pointer to the beggining
   of the string */

char *adt_gettok(ptr,mark)
     char **ptr, mark;
{
  char *t, *start = *ptr;
  
  if( (t=(char*)strchr(*ptr,mark))) {
    *t=0;
    *ptr=t+1;
  }
  return start;
}

/* Reads and parses 'name', building the Entry list as it goes */
/* returns -1 on error, 0 on success */

int adt_readfile(name)
     char *name;
{
  FILE	*in;
  
  int
    version = 0,
    err = -1;
  
  if ((in=fopen(name,"r")) == NULL)
    (* adt_quit)("file to read disappeared");
  
  if (fscanf(in,"adt-v%d",&version) != 1) { /* parse error */
    fclose(in);
    return -1;
  }
  
  /*	switch (version)
	{
	case 1: */
  err = adt_parse_v1(in);
  /*			break;
			} */
  
  fclose(in);
  return err;
}

/* Parse a version 1 index file, returns 0 on success, -1 on error */

int adt_parse_v1(in)
     FILE *in;
{
  ENTRY *last = NULL;
  ENTRY *entr;
  char *ptr;
  int cnt = 0;
  
  while(fgets(GBuf,250,in)) {
    adt_nocr(GBuf);
    
    if( !*GBuf || *GBuf=='#' )
      continue;
    
    ptr=GBuf;
    
    if ((entr = (ENTRY *) malloc(sizeof(ENTRY))) == NULL)
      (* adt_quit)("out of memory");
    
    if (!last)
      Entry = entr;
    else
      last->next = entr;
    
    entr->prev = last;
    last = entr;
    
    sscanf(adt_gettok(&ptr,'@'),"%lx",&entr->time);
    entr->dir   = adt_copyof(adt_gettok(&ptr,'@'));
    entr->file  = adt_copyof(adt_gettok(&ptr,';'));
    sscanf(adt_gettok(&ptr,'@'),"%lx",&entr->size);
    entr->readme= adt_copyof(adt_gettok(&ptr,'@'));
    entr->desc  = adt_copyof(adt_gettok(&ptr,'@'));
    
    entr->flags = 0;
    if(entr->readme[strlen(entr->readme)-1] == ';') {
      entr->readme[strlen(entr->readme)-1] = '\0';
      entr->flags |= FLAG_README;
    }
    
    if(( ptr=(char*)strchr(entr->desc,10 )))
      *ptr=0;
    if(( ptr=(char*)strchr(entr->desc,13 )))
      *ptr=0;
    cnt++;                  /* Entry processed ok, let it be known ;-) */
  }
  if (last)
    last->next = NULL;       /* Make sure last entry is linked nowhere */
  
  return (cnt == 0 ) ? -1 : 0;
}

/* Read and parse the info/adt/RECENT.adt.Z file */

int adt_read_recent(locname)
     char *locname;
{
  char buf[256];
  int  err;
  
  (* adt_status)("Downloading RECENT file list");
  sprintf(buf,"%s.Z",locname);
  if(ftp_get("info/adt/RECENT.adt.Z",0,buf)) {
    strcpy(Result,"Error downloading RECENT file list");
    (void) unlink(buf);
    return 1;
  }
  
  (void) unlink(locname);
  sprintf(buf,"uncompress %s.Z",locname);
  system(buf);
  (* adt_status)("Parsing RECENT file list");
  err = adt_readfile(locname);
  (void) unlink(locname);
  if (err != 0) {
    strcpy(Result,"Error parsing RECENT file list");
    return 2;
  }
  else
    return 0;
}

/* Read and parse the info/adt/SHORT.adt.Z file */

int adt_getshort(locname)
     char *locname;
{
  char buf[256];
  int  err;
  
  (* adt_status)("Downloading complete file list");
  sprintf(buf,"%s.Z",locname);
  if(ftp_get("info/adt/SHORT.adt.Z",0,buf)) {
    strcpy(Result,"Error downloading complete file list");
    (void) unlink(buf);
    return 1;
  }
  
  (void) unlink(locname);
  sprintf(buf,"uncompress %s.Z",locname);
  system(buf);
  (* adt_status)("Parsing complete file list");
  
  err = adt_readfile(locname);
  (void) unlink(locname);
  if (err != 0) {
    strcpy(Result,"Error parsing complete file list");
    return 2;
  }
  else
    return 0;
}

/*---------------------- memory --------------------*/

/* Allocate memory for 'str', and copy it there */
/* Returns a pointer to that memory */

char *adt_copyof(str)
     char *str;
{
  char *mem;
  
  if(!str)
    return NULL;
  if ((mem = malloc(strlen(str)+1)) == NULL)
    (* adt_quit)("out of memory");
  strcpy(mem,str);
  return mem;
}

/* Free the Entry list */

void adt_free_entries()
{
  ENTRY
    *new,
    *entry;
  
  for(entry = Entry ; entry ; entry = new) {
    new = entry->next;
    
    free(entry->file);
    free(entry->dir);
    free(entry->desc);
    free(entry->readme);
    free(entry);
  }
  Entry = NULL;
}

/* Free a list of tags */

void adt_free_tags(list)
     TAG **list;
{
  TAG
    *new,
    *entry;
  
  for (entry = *list ; entry ; entry = new ) {
    new = entry->next;
    free(entry);
  }
  *list = NULL;
}

/*---------------------- sorting --------------------*/

/* sort 'list' using key 'mode' */

void adt_sort(mode,list)
     int
       mode;
     TAG
       *list;
{
  TAG
    *tag,
    *cmp,
    *last;
  ENTRY
    *save;
  int (* func)();
  
  if (mode == SortMode)
    return;
  
  SortMode = mode;
  func = Sort[mode].func;
  OldPage = NULL;
  
  if (!func)     /* func == NULL => Don't do any sort */
    return;
  
  for (tag = list ; tag ; tag = tag->next) {
    save = tag -> entry;
    last = tag;
    for (cmp = tag->prev ;
	 cmp ;
	 cmp =  cmp->prev) {
      if ((*func)(save,cmp->entry) > 0)
	(cmp->next)->entry = cmp->entry;
      else
	break;
      last = cmp;
    }
    last->entry = save;
  }
}

int adt_agecmp(e1, e2)
     ENTRY
       *e1,
       *e2;
{
  return e1->time - e2->time;
}

int adt_rev_agecmp(e1, e2)
     ENTRY
       *e1,
       *e2;
{
  return -adt_agecmp(e1,e2);
}

int adt_dircmp(e1, e2)
     ENTRY
       *e1,
       *e2;
{
  return -strcmp(e1->dir,e2->dir);
}

int adt_rev_dircmp(e1, e2)
     ENTRY
       *e1,
       *e2;
{
  return -adt_dircmp(e1,e2);
}

int adt_namecmp(e1, e2)
     ENTRY
       *e1,
       *e2;
{
  return -stricmp(e1->file,e2->file);
}

int adt_rev_namecmp(e1, e2)
     ENTRY
       *e1,
       *e2;
{
  return -adt_namecmp(e1,e2);
}

int adt_sizecmp(e1, e2)
     ENTRY
       *e1,
       *e2;
{
  return e1->size - e2->size;
}

int adt_rev_sizecmp(e1, e2)
     ENTRY
       *e1,
       *e2;
{
  return -adt_sizecmp(e1,e2);
}

/*---------------------- modes --------------------*/

/* A general purpose entry point for adt mode changes */

void adt_set_mode(mode)
     int mode;
{
  int OldMode = Mode;
  
  switch (mode)
    {
    case M_SERVER:
      adt_mode_server();
      break;
    case M_HELP:
      adt_mode_help();
      break;
    case M_TAG:
      adt_mode_tag();
      break;
    case M_NEW:
      adt_mode_new();
      break;
    case M_RECENT:
      adt_mode_recent();
      break;
    case M_ALL:
      adt_mode_all();
      break;
    default:
      (* adt_quit)("internal error: unknown mode");
    }
  if ( OldMode != Mode )
    (* adt_refresh)();
}

void adt_mode_server()
{
  if (Mode == M_SERVER)
    return;
  if (ENTRY_MODE)
    adt_free_tags(&Visible);
  adt_free_tags(&Tag);
  NumTagged = 0;
  Visible = ServerList;
  NumVisible = NumServers;
  adt_goto_start();
  adt_goto_line(ServerOffset+1);
  Mode = M_SERVER;
}

void adt_mode_help()
{
  if (Mode == M_HELP)
    return;
  if (ENTRY_MODE)
    adt_free_tags(&Visible);
  adt_free_tags(&Tag);
  NumTagged = 0;
  Visible = HelpList;
  NumVisible = NumHelp;
  OldHelpMode = Mode;
  adt_goto_start();
  Mode = M_HELP;
  *GBuf = '\0';
  if (KeepResult)
    strcpy(GBuf,Result);
  *Result = '\0';
}

void adt_mode_tag()
{
  if (Mode == M_TAG)
    return;
  if (!Tag) {
    strcpy(Result,"No tagged entries");
    return;
  }
  if (ENTRY_MODE)
    adt_free_tags(&Visible);
  Visible = Tag;
  NumVisible = NumTagged;
  OldTagMode = Mode;
  adt_sort(SORT_NONE, Visible);
  adt_goto_start();
  Mode = M_TAG;
}

void adt_mode_new()
{
  ENTRY
    *entry;
  
  if (Mode == M_NEW)
    return;
  
  (* adt_status)("Processing....");
  if (ENTRY_MODE)
    adt_free_tags(&Visible);
  Visible = NULL;
  NumVisible = 0;
  
  for(entry = Entry; entry ; entry = entry->next)
    if( entry->time > PrevTime ) {
      adt_addtag(entry,&Visible);
      NumVisible++;
    }
  
  adt_sort(SORT_NAME,Visible);
  adt_sort(SORT_DIR,Visible);
  adt_goto_start();
  Mode = M_NEW;
}

void adt_mode_recent()
{
  ENTRY
    *entry;
  
  if (Mode == M_RECENT)
    return;

  (* adt_status)("Processing....");

  if (ENTRY_MODE)
    adt_free_tags(&Visible);
  Visible = NULL;
  NumVisible = 0;
  
  for(entry = Entry; entry ; entry = entry->next)
    if( (ThisTime-entry->time)<14*24*60*60 ) {
      adt_addtag(entry,&Visible);
      NumVisible++;
    }
  
  adt_sort(SORT_NAME,Visible);
  adt_sort(SORT_AGE,Visible);
  adt_goto_start();
  Mode = M_RECENT;
}

void adt_mode_all()
{
  int ret;
  ENTRY *entry;
  char buf[256];
  
  if (Mode == M_ALL)
    return;
  
  if (ENTRY_MODE)
    adt_free_tags(&Visible);
  Visible = NULL;
  
  adt_free_tags(&Tag);
  NumVisible = NumTagged = 0;
  
  if( !gotall ) {
    adt_free_entries();
    sprintf(buf,"/tmp/S%d",pid);
    ret = adt_getshort(buf);
    
    (* adt_status)(NULL);
    if( ret )
      return;
    gotall = 1;
  }
  
  (* adt_status)("Processing....");
  
  for(entry = Entry; entry ; entry = entry->next) {
    adt_addtag(entry,&Visible);
    NumVisible++;
  }
  
  adt_sort(SORT_NAME,Visible);
  adt_goto_start();
  Mode = M_ALL;
}

void adt_mode_long()
{
  Long ^= 1;
}

/*---------------------- moves --------------------*/

/* fast routine to do adt_goto_line(1) */

void adt_goto_start()
{
  OldOffset = Offset;
  OldActive = Active;
  OldPage = Page;
  Offset = 1;
  Active = Page = Visible;
}

/* go to a line nubered 'line'. 1 <= 'line' <= NumVisible */

void adt_goto_line(line)
     int line;
{
  if (!Active) /* don't try moving line with no list */
    return;
  if (Offset > line)
    adt_go_back(line);
  else if (Offset < line)
    adt_go_forw(line);
}

/* service routine for adt_goto_line(). Goes forward */

void adt_go_forw(line)
     int line;
{
  OldActive = Active;
  OldOffset = Offset;
  while (line != Offset && Active->next) {
    Active = Active->next;
    Offset++;
    if ((Offset % Window) == 1) { /* 1st entry on a page */
      OldPage = Page;
      Page = Active;
    }
  }
}

/* service routine for adt_goto_line(). Goes backward. */

void adt_go_back(line)
     int line;
{
  int fix = 0;
  
  OldActive = Active;
  OldOffset = Offset;
  while (line != Offset && Active->prev) {
    Active = Active->prev;
    Offset--;
    if ((Offset % Window) == 0)  /* last entry on previous page */
      fix = 1;
  }
  
  if (fix) {
    OldPage = Page;
    Page = Active;
    fix = Offset % Window;
    if (fix == 0)
      fix = Window;
    for (; fix != 1; fix--)
      Page = Page->prev;
  }
}

/*---------------------- strings --------------------*/

int strnicmp( s1, s2, n )
     char
       *s1,
       *s2;
     int
       n;
{
  int
    i;
  
  for( i=0; i<n; i++ )
    if( adt_toupper(s1[i]) != adt_toupper(s2[i]))
      return 1;
  return 0;
}

int stricmp( s1, s2 )
     char
       *s1,
       *s2;
{
  int
    i,
    d;
  
  for( i=0; s1[i]; i++ )
    if(( d = ( adt_toupper(s1[i]) - adt_toupper(s2[i]) ) ))
      return d;
  return -adt_toupper(s2[i]);
}

void adt_nocr(buf)
     char *buf;
{
  if( (buf=(char*)strchr(buf,'\n')))
    *buf=0;
}

/*---------------------- tags --------------------*/

/* create a TAG entry linked to 'list', pointing to 'entry' */
/* It is up to the calling routine to make sure this entry is */
/* not already on the list */

void adt_addtag(entry,list)
     ENTRY
       *entry;
     TAG
       **list;
{
  TAG
    *new = NULL,
    *ptr;
  
  ptr = *list;
  if (!(new =  (TAG *)malloc(sizeof(TAG))))
    (* adt_quit)("out of memory");
  
  new->entry = entry;
  *list = new;
  new->prev = NULL;
  new->next = ptr;
  if (ptr)
    ptr->prev = new;
}

/* remove an TAG entry linked in 'list', pointing to 'entry' */
/* entry is assumed to be present only once on the list. If it */
/* is not on the list, this routine does nothing */

void adt_remtag(entry,list)
     ENTRY
       *entry;
     TAG
       **list;
{
  TAG
    *tmp,
    *tmp2;
  
  tmp = *list;
  while ( tmp && tmp->entry != entry)
    tmp = tmp->next;
  
  if (tmp) {
    if ((tmp2 = tmp->prev))
      tmp2->next = tmp->next;
    
    if ((tmp2 = tmp->next))
      tmp2->prev = tmp->prev;
    
    if (*list == tmp)
      *list = tmp2;
    
    free(tmp);
  }
  
  return;
}

/*---------------------- config --------------------*/

/* -------- config - file format --------*/
/* # comment, ignored                    */
/* server = default server to connect to */
/* prevcall = time of last connection    */
/* site = country:server:dir             */ /* unimplemented */
/*        list of possible servers       */

int adt_loadconfig()
{
  FILE *in;
  char buf[256], *t, *t2;
  int scnt = 0, tcnt = 0;
  TAG *tmp;
  
  if(!(in=fopen(Configfile,"r")))   /* no ~/.adtrc   ==> */
    return 1;                       /* assume a new user */
  
  while( fgets(buf,200,in)) {
    if( *buf=='#' || !*buf )
      continue;
    
    adt_nocr(buf);
    
    for( t=buf; *t && *t!=' ' && *t!='='; t++ ) ;
    if( !*t ) continue;
    
    for( t2=t; *t2 && (*t2==' ' || *t2=='='); t2++ ) ;
    if( !*t2 ) continue;
    
    *t=0;
    if(!stricmp(buf,"server")){
      ServerOffset=0;
      for(tmp=ServerList ; tmp ; tmp =  tmp->next) {
	if (!stricmp(t2,((SERVER *)tmp->entry)->host)) break;
	ServerOffset++;
      }
      if (tmp) {
	Server=tmp;
	if (scnt != 0)   /* already had a server line ==>*/
	  return 2;      /* error in config file */
	scnt++;
      }
      else { /* server unknown */
      	return 2;
      }
    }
    
    if(!stricmp(buf,"prevcall")) {
      if(tcnt != 0)      /* already had a prevcall line ==>*/
	return 2;        /* error in config file */
      PrevTime=ThisTime=atoi(t2);
      tcnt++;
    }
  }
  fclose(in);
  if (tcnt == 0 || tcnt == 0) /* missing server or prevcall line ==>*/
    return 2;                 /* error in config file */
  return 0;                   /* All's well that ends well */
}

void adt_saveconfig()
{
  FILE *conf;
  
  if( !(conf=fopen(Configfile,"w")))
    return;
  
  fputs("# Config file for the 'adt' FTP frontend\n",conf);
  fprintf(conf,"server = %s\n",((SERVER *)Server->entry)->host);
  fprintf(conf,"prevcall = %d\n",ThisTime);
  fclose(conf);
}

/*---------------------- routines --------------------*/

/* connect to the remote host, and get a list of the remote entries */
/* returns 0 on success */

int adt_connect()
{
  char locname[256];
  
  gotall = 0;    /* reset the list value */
  
  /*  if (ENTRY_MODE)
      adt_free_tags(&Visible);  // can only get here from M_SERVER //
      adt_free_tags(&Tag);
      Tag = NULL; */
  adt_free_entries();
  
  (* adt_status)("Connecting to %s...",((SERVER *)Server->entry)->host);
  
  if (!ftp_connect(((SERVER *)Server->entry)->host,((SERVER *)Server->entry)->dir))
    return -1;
  
  sprintf(locname,"/tmp/R%d",pid);
  
  ThisTime=time(NULL);
  
  return adt_read_recent(locname);
}

/* (un)tag an entry, add it to the Tag list */

void adt_tag(tag)
     TAG *tag;
{
  ENTRY *entry;
  TAG *tmp;
  
  if(tag) {
    entry = tag->entry;
    entry->flags ^= FLAG_TAG;
    
    if (IS_TAGGED(entry)) {
      adt_addtag(entry,&Tag);
      
      /* adt_addtag() adds at start of list, move new entry to the end */
      
      for (tmp = Tag->next; tmp; tmp = tmp->next) {
	( tmp->prev) ->entry = tmp->entry;
	tmp -> entry = entry;
      }
      NumTagged++;
    }
    else{
      adt_remtag(entry,&Tag);
      NumTagged--;
      if (Mode == M_TAG)
	if (!Tag)
	  adt_set_mode(OldTagMode);
	else {
	  Visible = Tag;          /* Make sure the list is right */
	  adt_goto_start();       /* get the list offset right */
	  adt_goto_line(OldOffset);
	}
    }
  }
}

/* Show file info at the status line */

void adt_info(entry)
     ENTRY *entry;
{
  char *ftime;
  
  if (entry) {
    ftime = ctime(&(entry->time));
    adt_nocr(ftime);
    
    if (entry->size == -1)
      (* adt_status)("Name %-18s   Date %-18s     Readme-only",
		     entry->file,ftime);
    else
      (* adt_status)("Name %-18s   Date %-18s     Size %-10d",
		     entry->file,ftime,entry->size);
  }
}

/* Shows the readme for Active, if 'flag' or there's no readme, shows */
/* Active->desc in the status line */

void adt_readme(flag)
     int flag;
{
  ENTRY
    *entry;
  
  char
    title[256],
    file[256],
    buf[256],
    *ftime;
  
  if (Active) {
    entry = Active->entry;
    ftime=ctime(&entry->time);
    adt_nocr(ftime);
    
    if(HAS_README(entry) && !flag) {
      (* adt_status)("Downloading readme for %s",entry->file);
      sprintf(buf,"/tmp/%s",entry->readme);
      sprintf(file,"%s/%s",entry->dir,entry->readme);
      if (ftp_get(file,0,buf)) {
	sprintf(Result,"Error downloading readme for %s",entry->file);
	return;
      }
      
      if (entry->size == -1)
	sprintf(title,"Name %-18s   Date %-18s     Readme-only",
		entry->file,ftime);
      else
	sprintf(title,"Name %-18s   Date %-18s     Size %-10d",
		entry->file,ftime,entry->size);
      
      (* adt_more)(buf,title);
      (void) unlink(buf);
    } else {    /* No readme, or 'flag' is true */
      (* adt_status)(entry->desc);
    }
  }
}

/* Try and download a file named 'dir'/'file' of size 'size' */
/* returns 0 for failure, non-zero for success */

int adt_sendget(dir,file,size)
     char
       *dir,
       *file;
     int
       size;
{
  char
    buf[256];
  
  sprintf(buf,"%s/%s",dir,file);
  return !ftp_get(buf,size,file);
}

/* Downloads tagged files (or Active if non tagged) */
/* If 'readme' is non-zero gets the readme as well  */

void adt_download(readme)
     int readme;
{
  ENTRY *entry;
  TAG   *next;
  
  if( !Tag )
    adt_tag(Active);         /* Nothing tagged, tag current */
  
  adt_set_mode(M_TAG);
  
  next = Active;
  
  while (next) {
    next = Active->next;
    entry = Active->entry;
    if (entry->size == -1) { /* only readme */
      (* adt_status)("Downloading readme for %s",entry->file);
      if (adt_sendget(entry->dir,entry->readme,0))
	adt_tag(Active);
      else
	adt_goto_line(Offset+1);
      /*      (* adt_refresh_list)(1); */
      (* adt_refresh)();
    }
    
    if (readme) { /* all striaght cases */
      (* adt_status)("Downloading readme for %s",entry->file);
      adt_sendget(entry->dir,entry->readme,0);
    }
    (* adt_status)("Downloading %s",entry->file);
    if (adt_sendget(entry->dir,entry->file,entry->size))
      adt_tag(Active);
    else 
      adt_goto_line(Offset+1);
    
    if (!Tag)
      return;
    
    (* adt_refresh_list)(1);
    /*    (* adt_refresh)(); */
  }
}

/* Handles selection of the Active entry. Returns 0 when no screen refresh */
/* is needed. Non-zero if the entry list needs refreshing */

int adt_select()
{
  char ch;
  
  switch (Mode)
    {
    case M_SERVER:
      if (Active) {
	Server = Active;
	ServerOffset = Offset-1;
	if (Save)
	  adt_saveconfig();
	Save = 0;
	KeepResult = 0;
	if(adt_connect()) 
	  (* adt_status)(Result);
	else {
	  ch = *Result;     /* preserve Result across the refresh done */
	  adt_set_mode(M_NEW);
	  *Result = ch;     /* by adt_set_mode() */
	}
      }
      return 0;   /* adt_set_mode() does full screen refresh */
    case M_TAG:   /* This can only remove an entry */
      if (Active) {
	adt_tag(Active);
	if (!Tag)
	  return 0;
	else
	  return 1;
      }
      return 0;
    case M_NEW:
    case M_RECENT:
    case M_ALL:
      if (Active) {
	adt_tag(Active);
	adt_goto_line(Offset+1);
	return 1;
      }
      return 0;
    case M_HELP:
      return 0;  /* No extended selection on help, yet */
    }
}

/* cd to the given path */

void adt_cd(path)
     char *path;
{
  char cdpath[1000];
  
  bzero(cdpath,1000);
  
  if ((strlen(path) == 1) && (*path == '~'))
    strcpy(cdpath,HomeDir);
  else if (strncmp(path,"~/",2) == 0)
    sprintf(cdpath,"%s/%s",HomeDir,path+2);
  else
    strcpy(cdpath,path);
  
  if (chdir(cdpath) < 0)
    sprintf(Result,"Cd: %s, %s",path,sys_errlist[errno]);
  else
    sprintf(Result,"New download directory: %s",path);
  return;
}

