#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdio.h>
#include <malloc.h>

#define CONFIGPATH    "/usr/local/etc/bbgopher.conf"

#define DEFAULTTITLE  "BBGopher 1.6 (%s)"
#define DEFAULTPORT   70
#define DEFAULTHOST   "gopher"
#define DEFAULTPAGER  ""
#define DEFAULTTELNET "telnet"
#define DEFAULTTN3270 "tn3270"
#define DEFAULTGIF    ""
#define DEFAULTIMG    ""
#define DEFAULTLINES  19
#define DEFAULTDIR    ""
#define DEFAULTMAILER "/usr/lib/sendmail"
#define MAXSTACKLEN   250
#define MAXMENULEN    2500

#define G_OK         0
#define G_HOST       1
#define G_SOCKET     2
#define G_CONNECT    3
#define G_STACK      4
#define G_MENU       5
#define G_MEMORY     6
#define G_CREATE     7

typedef struct
{
   char  type;
   char* display;
   char* selector;
   char* hostname;
   short port;
} GO4ENTRY;

typedef struct
{
   char  type;
   char* display;
   char  acf[128];
   char  supported;
} TYPEENTRY;

TYPEENTRY KnownTypes[] =
{
   { '0', "TXT", "", 1 },
   { '1', "DIR", "", 1 },
   { '2', "CSO", "", 0 },
   { '3', "ERR", "", 0 },
   { '4', "HQX", "", 1 },
   { '5', "DOS", "", 1 },
   { '6', "UUE", "", 1 },
   { '7', "IDX", "", 1 },
   { '8', "TEL", "", 1 },
   { '9', "BIN", "", 1 },
   { '+', "DUP", "", 0 },
   { 'g', "GIF", "", 0 },
   { 'I', "PIC", "", 0 },
   { 'T', "TN3", "", 1 },
   { 'i', "INF", "", 1 },
   { 0, 0 }
};

char               Title[256];
char               DefHost[256];
short              DefPort;
char               HelpFile[256];
char               Pager[256];
char               Telnet[256];
char               TN3270[256];
char               GIFViewer[256];
char               IMGViewer[256];
char               Directory[256];
char               Mailer[256];
char               BookMark[256];
char               BadPorts[256];
short              Lines;
short              Twirly;
short              SkipACF;
short              SkipType;
short              ClearScreen;
GO4ENTRY           Stack[MAXSTACKLEN];
short              StackLen;
GO4ENTRY           Menu[MAXMENULEN];
short              MenuLen;
struct sockaddr_in sain;
struct hostent*    host;
int                sock;
FILE*              sd;
int                BM;

#define NUMTWIRLYCHARS 4
char TwirlyChars[NUMTWIRLYCHARS + 1] = "-\\|/";

ValidNumber( number, string )
int   number;
char* string;
{
   int   n1, n2, r;
   char* e;

   while( *string )
   {
      r = 0;
      while( *string == ' ' || *string == '\t' || *string == ',' )
         string++;
      n1 = 0;
      n2 = 0;
      if( *string >= '0' && *string <= '9' )
      {
         n1 = atoi( string );
         while( *string >= '0' && *string <= '9' )
            string++;
      }
      if( *string == '-' )
      {
         r = 1;
         string++;
         n2 = atoi( string );
         while( *string >= '0' && *string <= '9' )
            string++;
      }
      if( r )
      {
         if( number >= n1 && number <= n2 )
            return 1;
      }
      else if( number == n1 )
         return 1;
   }
   return 0;
}

CheckUser( type )
char type;
{
   FILE* fp;
   char  line[80];
   char* uid;
   int   i;

   for( i = 0; KnownTypes[i].type; i++ )
      if( KnownTypes[i].type == type )
         break;
   if( KnownTypes[i].type == 0 )
      return 0;
   fp = fopen( KnownTypes[i].acf, "r" );
   if( !fp )
      return 1;
   uid = (char*) getlogin();
   for( fgets( line, 80, fp ); !feof( fp ); fgets( line, 80, fp ) )
   {
      line[strlen( line ) - 1] = 0;
      if( strcasecmp( line, uid ) == 0 )
      {
         fclose( fp );
         return 1;
      }
   }
   fclose( fp );
   return 0;
}

SupportedType( type )
char type;
{
   int i;

   for( i = 0; KnownTypes[i].type; i++ )
      if( KnownTypes[i].type == type )
         return KnownTypes[i].supported;
   return 0;
}

newEntry( ent, type, display, selector, hostname, port )
GO4ENTRY* ent;
char      type;
char*     display;
char*     selector;
char*     hostname;
short     port;
{
   if( display && (ent->display = (char*) malloc( strlen( display ) + 1 )) == 0 )
      return 0;
   if( selector && (ent->selector = (char*) malloc( strlen( selector ) + 1 )) == 0 )
   {
      free( ent->display );
      return 0;
   }
   if( hostname && (ent->hostname = (char*) malloc( strlen( hostname ) + 1 )) == 0 )
   {
      free( ent->selector );
      free( ent->display );
      return 0;
   }
   ent->type = type;
   if( display )
      strcpy( ent->display, display );
   else
      ent->display = 0;
   if( selector )
      strcpy( ent->selector, selector );
   else
      ent->selector = 0;
   if( hostname )
      strcpy( ent->hostname, hostname );
   else
      ent->hostname = 0;
   ent->port = port;
   return 1;
}

delEntry( ent )
GO4ENTRY* ent;
{
   if( ent->hostname )
      free( ent->hostname );
   if( ent->selector )
      free( ent->selector );
   if( ent->display );
      free( ent->display );
   return 1;
}

char* TypeLookup( type )
char type;
{
   int         i;
   static char buf[10];

   for( i = 0; KnownTypes[i].type; i++ )
      if( KnownTypes[i].type == type )
         return KnownTypes[i].display;
   sprintf( buf, " %c ", type );
   return buf;
}

int DisplayLookup( display )
char* display;
{
   int i;

   for( i = 0; i < KnownTypes[i].type; i++ )
      if( !strcasecmp( KnownTypes[i].display, display ) )
         return i;
   return -1;
}

Connect( selector, hostname, port )
char* selector;
char* hostname;
short port;
{
   if( port == 0 )
      port = DefPort;
   if( ValidNumber( port, BadPorts ) )
   {
      printf( "Sorry, but access to that service is not allowed.\n\n" );
      return G_CONNECT;
   }
   if( hostname == 0 )
      hostname = DefHost;
   if( (host = gethostbyname( hostname )) == 0 )
      return G_HOST;
   if( (sock = socket( AF_INET, SOCK_STREAM, 0 )) < 0 )
      return G_SOCKET;
   bzero( &sain, sizeof(sain) );
   sain.sin_family = AF_INET;
   sain.sin_port = htons( port );
   bcopy( host->h_addr, &sain.sin_addr, host->h_length );
   if( connect( sock, (struct sockaddr*) &sain, sizeof(sain) ) < 0 )
      return G_CONNECT;
   if( selector )
      write( sock, selector, strlen( selector ) );
   write( sock, "\r\n", 2 );
   sd = fdopen( sock, "r" );
   return G_OK;
}

Disconnect()
{
   close( sock );
}

Page( file )
char* file;
{
   FILE* fp;
   char  line[256];
   int   n = 0;

   fp = fopen( file, "r" );
   if( !fp )
      return 0;
   if( ClearScreen )
      printf( "\033[2J\033[H" );
   else
      putchar( '\n' );
   for( fgets( line, 256, fp ); !feof( fp ); fgets( line, 256, fp ) )
   {
      printf( line );
      if( ++n == 22 )
      {
         printf( "[MORE...]" );
         *line = 0;
         fflush( stdin );
         fgets( line, 2, stdin );
         fflush( stdin );
         n = 0;
         if( *line == 'q' || *line == 'Q' )
            break;
      }
   }
   fclose( fp );
   return 1;
}

GetData( file, binary )
char* file;
int   binary;
{
   FILE* fp;
   int   l = 0;
   int   t = 0;
   char  line[256];
   char  ch;

   fp = fopen( file, "w" );
   if( !fp )
      return G_CREATE;
   if( Twirly )
      putchar( ' ' );
   if( binary )
      for( ch = getc( sd ); !feof( sd ); ch = getc( sd ) )
      {
         putc( ch, fp );
         if( Twirly && ++l == 1024 )
         {
            l = 0;
            putchar( '\b' );
            putchar( TwirlyChars[t] );
            t++;
            if( t == NUMTWIRLYCHARS )
               t = 0;
            fflush( stdout );
         }
      }
   else
      for( fgets( line, 255, sd ); !feof( sd ); fgets( line, 255, sd ) )
      {
         l = strlen( line ) - 1;
         if( line[l] == '\n' && line[l - 1] == '\r' )
         {
            line[l - 1] = '\n';
            line[l] = 0;
         }
         if( *line == '.' && (line[1] == '\r' || line[1] == '\n') )
            break;
         fprintf( fp, line );
         if( Twirly )
         {
            putchar( '\b' );
            putchar( TwirlyChars[t] );
            t++;
            if( t == NUMTWIRLYCHARS )
               t = 0;
            fflush( stdout );
         }
      }
   if( Twirly )
      printf( "\b \b" );
   fclose( fp );
   return G_OK;
}

GetFile( selector, hostname, port, type, binary )
char* selector;
char* hostname;
short port;
{
   char line[81];
   int  rc;

   printf( "Local name for %s file? ", type );
   fflush( stdin );
   fgets( line, 80, stdin );
   fflush( stdin );
   printf( "\n" );
   line[strlen( line ) - 1] = 0;
   if( *line == 0 )
      return G_OK;
   if( (rc = Connect( selector, hostname, port )) != G_OK )
      return rc;
   printf( "Retrieving file..." );
   fflush( stdout );
   rc = GetData( line, binary );
   printf( "\n\n" );
   Disconnect();
   return rc;
}

MailFile( filename )
char* filename;
{
   FILE* fi;
   FILE* fo;
   char* tmp;
   char  to[81];
   char  sub[81];
   char  ch;

   printf( "To [%s]: ", getlogin() );
   fflush( stdin );
   fgets( to, 80, stdin );
   to[strlen( to ) - 1] = 0;
   if( *to == 0 )
      strcpy( to, (char*) getlogin() );
   printf( "Subject: " );
   fflush( stdin );
   fgets( sub, 80, stdin );
   fflush( stdin );
   printf( "\n" );
   sub[strlen( sub ) - 1] = 0;
   if( *sub == 0 )
      strcpy( sub, "No Subject Specified" );
   printf( "Mailing file...\n\n" );
   tmp = tempnam( "/tmp", "bbgo" );
   if( (fo = fopen( tmp, "w" )) == NULL )
   {
      free( tmp );
      return G_CREATE;
   }
   fi = fopen( filename, "r" );
   fprintf( fo, "To: %s\n", to );
   fprintf( fo, "Subject: %s\n", sub );
   fprintf( fo, "\n" );
   for( ch = fgetc( fi ); !feof( fi ); ch = fgetc( fi ) )
      fputc( ch, fo );
   fclose( fi );
   fclose( fo );
   sprintf( sub, "%s %s < %s", Mailer, to, tmp );
   system( sub );
   unlink( tmp );
   free( tmp );
   return G_OK;
}

SaveFile( filename )
char* filename;
{
   FILE* fi;
   FILE* fo;
   char  line[81];
   char  temp[81];
   char* l;
   char  ch;

   printf( "Local name for TXT file? " );
   fflush( stdin );
   fgets( line, 80, stdin );
   fflush( stdin );
   printf( "\n" );
   line[strlen( line ) - 1] = 0;
   if( *line == 0 )
      return G_OK;
   if( *Directory )
   {
      l = (char*) rindex( line, '/' );
      if( l )
         *l++ = 0;
      else
         l = line;
      *temp = 0;
      if( *Directory == '~' )
      {
         strcpy( temp, (char*) getenv( "HOME" ) );
         strcat( temp, &Directory[1] );
      }
      else
         strcat( temp, Directory );
      if( temp[strlen( temp ) - 1] != '/' )
         strcat( temp, "/" );
      strcat( temp, l );
      l = temp;
   }
   else
      l = line;
   printf( "Saving file...\n\n" );
   fflush( stdout );
   if( (fo = fopen( l, "w" )) == NULL )
      return G_CREATE;
   fi = fopen( filename, "r" );
   for( ch = fgetc( fi ); !feof( fi ); ch = fgetc( fi ) )
      fputc( ch, fo );
   fclose( fi );
   fclose( fo );
   return G_OK;
}

ReadText( selector, hostname, port )
char* selector;
char* hostname;
short port;
{
   char* tmp;
   char  line[256];
   int   l;
   int   rc;

   if( (rc = Connect( selector, hostname, port )) != G_OK )
      return rc;
   printf( "Retrieving text..." );
   fflush( stdout );
   tmp = tempnam( "/tmp", "bbgo" );
   if( (rc = GetData( tmp, 0 )) != G_OK )
   {
      free( tmp );
      return rc;
   }
   printf( "\n" );
   Disconnect();
   if( *Pager == 0 || !strcasecmp( Pager, "default" ) )
      Page( tmp );
   else
   {
      sprintf( line, Pager, tmp );
      if( !strstr( Pager, "%s" ) )
      {
         strcat( line, " " );
         strcat( line, tmp );
      }
      system( line );
   }
   printf( "\nPress ENTER to continue, M to mail, S to save: " );
   fflush( stdin );
   fgets( line, 5, stdin );
   fflush( stdin );
   printf( "\n" );
   rc = G_OK;
   if( *line == 'm' || *line == 'M' )
      rc = MailFile( tmp );
   else if( *line == 's' || *line == 'S' )
      rc = SaveFile( tmp );
   unlink( tmp );
   free( tmp );
   return rc;
}

DoSearch( selector, hostname, port )
char* selector;
char* hostname;
short port;
{
   char buf[256];
   char line[81];
   int  rc;

   printf( "Please enter your search criteria (you may use 'and' and 'or'):\n" );
   fflush( stdin );
   fgets( line, 80, stdin );
   fflush( stdin );
   printf( "\n" );
   line[strlen( line ) - 1] = 0;
   if( *line == 0 )
      return G_OK;
   sprintf( buf, "%s\t%s\r\n", selector, line );
   if( (rc = Connect( buf, hostname, port )) != G_OK )
      return rc;
   printf( "Searching...\n" );
   rc = ReadMenu( "Index Search Results", buf, hostname, port, 0, 0 );
   Disconnect();
   return rc;
}

ReadMenu( display, selector, hostname, port, connect, fd )
char* display;
char* selector;
char* hostname;
short port;
int   connect;
FILE* fd;
{
   char               line[1025];
   char*              e;
   GO4ENTRY           ent;
   int                rc;
   int                t;

   if( connect )
      if( (rc = Connect( selector, hostname, port )) != G_OK )
         return rc;
   if( StackLen >= MAXSTACKLEN )
   {
      if( connect )
         Disconnect();
      return G_STACK;
   }
   if( BM )
   {
      StackLen--;
      delEntry( &Stack[StackLen] );
      StackLen--;
      BM = 0;
   }
   if( !newEntry( &Stack[StackLen], '1', display, selector, hostname, port ) )
   {
      if( connect )
         close( sock );
      return G_MEMORY;
   }
   StackLen++;
   MenuLen = 0;
   t = 0;
   if( Twirly )
      putchar( ' ' );
   if( fd == NULL )
      fd = sd;
   for( fgets( line, 1024, fd ); !feof( fd ); fgets( line, 1024, fd ) )
   {
      if( *line == '.' && (line[1] == '\r' || line[1] == '\n') )
         break;
      e = line;
      ent.type = *e++;
      ent.display = e;
      while( *e && *e != '\t' )   e++;
      if( *e )   *e++ = 0;
      ent.selector = e;
      while( *e && *e != '\t' )   e++;
      if( *e )   *e++ = 0;
      ent.hostname = e;
      while( *e && *e != '\t' )   e++;
      if( *e )   *e++ = 0;
      ent.port = atoi( e );
      if( SkipType && !SupportedType( ent.type ) )
         continue;
      if( SkipACF && !CheckUser( ent.type ) )
         continue;
      if( !newEntry( &Menu[MenuLen], ent.type, ent.display, ent.selector, ent.hostname, ent.port ) )
      {
         Disconnect();
         return G_MEMORY;
      }
      MenuLen++;
      if( MenuLen == MAXMENULEN )
         break;
      if( Twirly )
      {
         putchar( '\b' );
         putchar( TwirlyChars[t] );
         t++;
         if( t == NUMTWIRLYCHARS )
            t = 0;
         fflush( stdout );
      }
   }
   if( Twirly )
      printf( "\b \b" );
   if( connect )
      Disconnect();
   return G_OK;
}

SaveBookmark( n )
int n;
{
   FILE* fp;
   char  buf[256];

   strcpy( buf, (char*) getenv( "HOME" ) );
   strcat( buf, "/" );
   strcat( buf, BookMark );
   if( (fp = fopen( buf, "a" )) == 0 )
      return;
   fprintf( fp, "%c%s\t%s\t%s\t%d\n", Menu[n].type, Menu[n].display, Menu[n].selector, Menu[n].hostname, Menu[n].port );
   fclose( fp );
   printf( "Bookmark saved...\n\n" );
   return;
}

DelBookmark( n )
int n;
{
   FILE* fp;
   int   i;
   char  buf[256];

   strcpy( buf, (char*) getenv( "HOME" ) );
   strcat( buf, "/" );
   strcat( buf, BookMark );

   if( n < MenuLen - 1 )
      bcopy( &Menu[n + 1], &Menu[n], sizeof(GO4ENTRY) * (MenuLen - n - 1) );
   MenuLen--;
   if( (fp = fopen( buf, "w" )) == 0 )
      return;
   for( i = 0; i < MenuLen; i++ )
      fprintf( fp, "%c%s\t%s\t%s\t%d\n", Menu[i].type, Menu[i].display, Menu[i].selector, Menu[i].hostname, Menu[i].port );
   fclose( fp );
   printf( "Bookmark deleted...\n\n" );
   return;
}

CompareEntries( ent1, ent2 )
GO4ENTRY* ent1;
GO4ENTRY* ent2;
{
   return strcasecmp( ent1->display, ent2->display );
}

ReadBookmarks()
{
   FILE* fp;
   char  buf[256];

   strcpy( buf, (char*) getenv( "HOME" ) );
   strcat( buf, "/" );
   strcat( buf, BookMark );
   if( (fp = fopen( buf, "r" )) == 0 )
      return 0;
   ReadMenu( "* Bookmarks *", 0, 0, 0, 0, fp );
   fclose( fp );
   qsort( Menu, MenuLen, sizeof(GO4ENTRY), CompareEntries );
   return 1;
}

ProcessMenu()
{
   int   i, n;
   int   top;
   char* s;
   char  buf[256];

   top = 0;
page:
   if( ClearScreen )
      printf( "\033[2J\033[H" );
   printf( "%s\n%s", Title, Stack[StackLen - 1].display );
   if( Stack[StackLen - 1].hostname )
      printf( " (%s)", Stack[StackLen - 1].hostname );
   printf( "\n\n" );
   if( MenuLen == 0 )
      printf( "There are no menu items available.\n" );
   else for( i = 0, n = top; i < Lines && n < MenuLen; i++, n++ )
   {
      if( Menu[n].type == 'i' )
         printf( "%3d %-.75s\n", n + 1, Menu[n].display );
      else
         printf( "%3d <%s> %-.69s\n", n + 1, TypeLookup( Menu[n].type ), Menu[n].display );
   }
   putchar( '\n' );
loop:
   do
   {
      *buf = 0;
      if( MenuLen == 0 )
         printf( "Help, Quit or Previous: " );
      else
         printf( "[1-%d], Help, Quit, %s, List, Previous, Up, or Down: ", MenuLen, BM ? "Delete" : "=, Bookmarks, Save" );
      fflush( stdin );
      fgets( buf, 15, stdin );
      fflush( stdin );
      buf[strlen( buf ) - 1] = 0;
   } while( *buf == 0 && MenuLen <= Lines );
   printf( "\n" );
   if( *buf == 'h' || *buf == 'H' )
   {
      if( *Pager == 0 || !strcasecmp( Pager, "default" ) )
         Page( HelpFile );
      else
      {
         sprintf( buf, Pager, HelpFile );
         if( !strstr( Pager, "%s" ) )
         {
            strcat( buf, " " );
            strcat( buf, HelpFile );
         }
         system( buf );
      }
      printf( "\nPress ENTER to continue" );
      fflush( stdin );
      fgets( buf, 5, stdin );
      fflush( stdin );
      printf( "\n" );
      goto page;
   }
   if( *buf == 'q' || *buf == 'Q' )
      return -2;
   if( !BM && *buf == '=' )
   {
      for( s = buf; *s && *s != ' '; s++ );
      n = atoi( s );
      if( n < 0 || n > MenuLen )
         printf( "You must specify a valid item number to save as a bookmark (eg: s 1).\n\n" );
      else if( n == 0 )
      {
         printf( "Type:     %s\n", TypeLookup( Stack[StackLen - 1].type ) );
         printf( "Title:    %s\n", Stack[StackLen - 1].display );
         printf( "Host:     %s\n", Stack[StackLen - 1].hostname );
         printf( "Port:     %d\n", Stack[StackLen - 1].port );
         printf( "Selector: %s\n\n", Stack[StackLen - 1].selector ? Stack[StackLen - 1].selector : "" );
      }
      else
      {
         printf( "Type:     %s\n", TypeLookup( Menu[n - 1].type ) );
         printf( "Title:    %s\n", Menu[n - 1].display );
         printf( "Host:     %s\n", Menu[n - 1].hostname );
         printf( "Port:     %d\n", Menu[n - 1].port );
         printf( "Selector: %s\n\n", Menu[n - 1].selector ? Menu[n - 1].selector : "" );
      }
      goto loop;
   }
   if( !BM && (*buf == 'b' || *buf == 'B') )
      return -3;
   if( !BM && (*buf == 's' || *buf == 'S') )
   {
      for( s = buf; *s && *s != ' '; s++ );
      n = atoi( s );
      if( n < 1 || n > MenuLen )
         printf( "You must specify a valid item number to save as a bookmark (eg: s 1).\n\n" );
      else
         SaveBookmark( n - 1 );
      goto loop;
   }
   if( BM && (*buf == 'd' || *buf == 'D') )
   {
      for( s = buf; *s && *s != ' '; s++ );
      n = atoi( s );
      if( n < 1 || n > MenuLen )
         printf( "You must specify a valid item number to delete. (eg: d 1)\n\n" );
      else
         DelBookmark( n - 1 );
      goto loop;
   }
   if( *buf == 'p' || *buf == 'P' )
      return -1;
   if( *buf == 'l' || *buf == 'L' )
      goto page;
   if( *buf == 'd' || *buf == 'D' || *buf == 0 )
   {
      top += Lines - 1;
      if( top >= MenuLen - 1 )
         top = 0;
      goto page;
   }
   if( *buf == 'u' || *buf == 'U' )
   {
      if( top == 0 )
         top = MenuLen - Lines;
      else
         top -= Lines - 1;
      if( top < 0 )
         top = 0;
      goto page;
   }
   i = atoi( buf );
   if( i < 1 || i > MenuLen )
   {
      printf( "Please enter a command or a number between 1 and %d.\n\n", MenuLen );
      goto loop;
   }
   i--;
   if( Menu[i].type == 'i' )
   {
      printf( "That menu item is informational only.\n\n" );
      goto loop;
   }
   if( !SupportedType( Menu[i].type ) )
   {
      printf( "Sorry, that type of menu item isn't currently supported.\n\n" );
      goto loop;
   }
   if( !CheckUser( Menu[i].type ) )
   {
      printf( "Sorry, but you don't have access to that item.\n\n" );
      goto loop;
   }
   return i;
}

Setup()
{
   FILE* fp;
   char  line[91];
   char* var;
   char* val;
   char* e;
   int   i;

   strcpy( Title, DEFAULTTITLE );
   strcpy( DefHost, DEFAULTHOST );
   DefPort = DEFAULTPORT;
   *BadPorts = 0;
   strcpy( Pager, DEFAULTPAGER );
   strcpy( Telnet, DEFAULTTELNET );
   strcpy( TN3270, DEFAULTTN3270 );
   strcpy( Directory, DEFAULTDIR );
   Lines = DEFAULTLINES;
   Twirly = 1;
   ClearScreen = 0;
   SkipACF = 0;
   SkipType = 1;
   fp = fopen( CONFIGPATH, "r" );
   if( !fp )
      return 0;
   for( fgets( line, 90, fp ); !feof( fp ); fgets( line, 90, fp ) )
   {
      e = (char*) rindex( line, '#' );
      if( e )
         *e-- = 0;
      else
      {
         e = line + strlen( line ) - 1;
         *e-- = 0;
      }
      for( var = line; *var == ' ' || *var == '\t'; var++ );
      for( val = var; *val && *val != ' ' && *val != '\t'; val++ );
      if( *val )   *val++ = 0;
      while( *val == ' ' || *val == '\t' )
         val++;
      if( *val == '\"' )   val++;
      while( *e == ' ' || *e == '\t' )
         *e-- = 0;
      if( *e == '\"' )   *e = 0;
      if( *var == 0 || *var == '#' )
         continue;
      if( !strcasecmp( var, "TITLE" ) )
         strcpy( Title, val );
      else if( !strcasecmp( var, "SERVER" ) )
         strcpy( DefHost, val );
      else if( !strcasecmp( var, "PORT" ) )
         DefPort = atoi( val );
      else if( !strcasecmp( var, "BADPORTS" ) )
         strcpy( BadPorts, val );
      else if( !strcasecmp( var, "HELPFILE" ) )
         strcpy( HelpFile, val );
      else if( !strcasecmp( var, "PAGER" ) )
         strcpy( Pager, val );
      else if( !strcasecmp( var, "WORKDIR" ) )
         strcpy( Directory, val );
      else if( !strcasecmp( var, "MAILER" ) )
         strcpy( Mailer, val );
      else if( !strcasecmp( var, "BOOKMARK" ) )
         strcpy( BookMark, val );
      else if( !strcasecmp( var, "TELNET" ) )
         strcpy( Telnet, val );
      else if( !strcasecmp( var, "TN3270" ) )
         strcpy( TN3270, val );
      else if( !strcasecmp( var, "LINES" ) )
         Lines = atoi( val );
      else if( !strcasecmp( var, "TWIRLY" ) )
         Twirly = (*val == 'y' || *val == 'Y' || *val == '1' );
      else if( !strcasecmp( var, "CLEAR" ) )
         ClearScreen = (*val == 'y' || *val == 'Y' || *val == '1' );
      else if( !strcasecmp( var, "SKIPACF" ) )
         SkipACF = (*val == 'y' || *val == 'Y' || *val == '1' );
      else if( !strcasecmp( var, "SKIPTYPE" ) )
         SkipType = (*val == 'y' || *val == 'Y' || *val == '1' );
      else if( !strncasecmp( var, "ACF", 3 ) && (i = DisplayLookup( &var[3] )) != -1 )
         strcpy( KnownTypes[i].acf, val );
      else
         printf( "Warning: unrecognised option in configuration file (%s)\n", var );
   }
   return 1;
}

main( argc, argv )
int   argc;
char* argv[];
{
   int   ok;
   int   rc;
   int   rc2;
   char  buf[10];

   printf( "\nBBGopher 1.6 - Written by Mark Morley (September '93)\n" );
   if( !Setup() )
      printf( "Warning: no configuration file found, using defaults.\n" );
   putchar( '\n' );
   StackLen = 0;
   if( argc > 1 )
   {
      strcpy( DefHost, argv[1] );
      if( argc > 2 )
         DefPort = atoi( argv[2] );
   }
   rc = ReadMenu( "Default Gopher Server", 0, DefHost, DefPort, 1, 0 );
   if( rc != G_OK )
   {
      printf( "Sorry, but I was unable to connect to %s on port %d\n", DefHost, DefPort );
      exit( 1 );
   }
   BM = 0;
   ok = 1;
   while( ok )
   {
      rc = ProcessMenu();
      switch( rc )
      {
         case -1 : BM = 0;
                   if( StackLen > 1 )
                   {
                      StackLen--;
                      delEntry( &Stack[StackLen] );
                      StackLen--;
                      ReadMenu( Stack[StackLen].display, Stack[StackLen].selector, Stack[StackLen].hostname, Stack[StackLen].port, 1, 0 );
                   }
                   break;
         case -2 : ok = 0;
                   break;
         case -3 : if( BM == 0 )
                   {
                      ReadBookmarks();
                      BM = 1;
                   }
                   break;
         default : if( BM && StackLen > 1 )
                   {
                      StackLen--;
                      delEntry( &Stack[StackLen] );
                   }
                   BM = 0;
                   switch( Menu[rc].type )
                   {
                      case '0' : rc2 = ReadText( Menu[rc].selector, Menu[rc].hostname, Menu[rc].port );
                                 break;
                      case '1' : rc2 = ReadMenu( Menu[rc].display, Menu[rc].selector, Menu[rc].hostname, Menu[rc].port, 1, 0 );
                                 break;
                      case '4' : rc2 = GetFile( Menu[rc].selector, Menu[rc].hostname, Menu[rc].port, "Mac HQX", 1 );
                                 break;
                      case '5' : rc2 = GetFile( Menu[rc].selector, Menu[rc].hostname, Menu[rc].port, "DOS binary", 1 );
                                 break;
                      case '6' : rc2 = GetFile( Menu[rc].selector, Menu[rc].hostname, Menu[rc].port, "UU encoded", 0 );
                                 break;
                      case '7' : rc2 = DoSearch( Menu[rc].selector, Menu[rc].hostname, Menu[rc].port );
                                 break;
                      case '8' : if( *Menu[rc].selector )
                                    printf( "Try logging in as '%s'\n\n", Menu[rc].selector );
                                 sprintf( buf, "%s %s %d", Telnet, Menu[rc].hostname, Menu[rc].port ? Menu[rc].port : 23 );
                                 system( buf );
                                 rc2 = G_OK;
                                 break;
                      case '9' : rc2 = GetFile( Menu[rc].selector, Menu[rc].hostname, Menu[rc].port, "binary", 1 );
                                 break;
                      case 'T' : sprintf( buf, "%s %s %d", TN3270, Menu[rc].hostname, Menu[rc].port ? Menu[rc].port : 23 );
                                 system( buf );
                                 rc2 = G_OK;
                                 break;
                   }
                   if( rc2 != G_OK )
                   {
                      printf( "ERROR: " );
                      switch( rc2 )
                      {
                         case G_HOST    : printf( "Couldn't resolve hostname '%s'\n", Menu[rc].hostname );
                                          break;
                         case G_SOCKET  : printf( "Socket error!\n" );
                                          break;
                         case G_CONNECT : printf( "Couldn't connect to %s\n", Menu[rc].hostname );
                                          break;
                         case G_CREATE : printf( "Couldn't create local file.\n" );
                                          break;
                         default        : printf( "Code = %d\n", rc2 );
                                          break;
                      }
                      putchar( '\n' );
                   }
                   break;
      }
   }
   while( MenuLen )
   {
      MenuLen--;
      delEntry( &Menu[MenuLen] );
   }
   while( StackLen )
   {
      StackLen--;
      delEntry( &Stack[StackLen] );
   }
}

