/* gopher.c
 *
 * Part of the Internet Gopher program, copyright (C) 1991, 1992
 * University of Minnesota Microcomputer Workstation and Networks Center
 *
 * See the README file for information about the Gopher program.
 */

#include "gopher.h"

void describe_gopher();

/*
** Open a connection to another host using telnet or tn3270
*/

void
do_tel_3270(ZeGopher)
  GopherStruct *ZeGopher;
{
     char sMessage1[128];
     char sMessage2[128];
     char sTelCmd[128]; 
     char ch;

     /* retrieve the gopher information for the telnet command*/

     clear();
     CURcenterline(CursesScreen,"Warning!!!!!, you are about to leave the Internet", 1);
     CURcenterline(CursesScreen,"Gopher program and connect to another host.", 2);
     CURcenterline(CursesScreen,"If you get stuck press the control key and the ] key,",3);
     CURcenterline(CursesScreen,"and then type quit",4);
     
     sprintf(sMessage1,"Now connecting to %s", GSgetHost(ZeGopher));
     if (*GSgetPath(ZeGopher) != '\0')
	  sprintf(sMessage2,"Use the account name \"%s\" to log in",
		  GSgetPath(ZeGopher));
     else
	  sMessage2[0] = '\0';

     CURcenterline(CursesScreen,sMessage1, LINES/2 -1);
     CURcenterline(CursesScreen,sMessage2, LINES/2 +1);
     CURcenterline(CursesScreen,"Press return to connect, q to cancel: ", LINES - 1);
     refresh();

     while (ch = CURgetch(CursesScreen)) {
	  if (ch == 'q')
	       return;
	  if (ch == '\n')
	       break;
     }

     CURexit(CursesScreen);

     if (GSgetType(ZeGopher) == 'T') {
	  /**** A TN3270 connection ****/
	  sprintf(sTelCmd, "%s %s", STRget(TN3270Command),
		  GSgetHost(ZeGopher));
     } else

	  if (GSgetPort(ZeGopher) != 0 && GSgetPort(ZeGopher) != 23) 
	       sprintf(sTelCmd, "%s %s %d",
		       STRget(TelnetCommand), GSgetHost(ZeGopher),
		       GSgetPort(ZeGopher)); 
	  else 
	       sprintf(sTelCmd, "%s %s", STRget(TelnetCommand), 
		       GSgetHost(ZeGopher));
     
     CURexit(CursesScreen);
     system(sTelCmd);
     CURenter(CursesScreen);
     return;
}





/*
** do_index gets keywords from the user to search for.  It returns
** it to the calling process.  This storage is volotile. Callers should
** make a copy if they want to call do_index multiple times.
*/

char* do_index(ZeGopher)
  GopherStruct *ZeGopher;
{
     static char *inputline = NULL;

     if (inputline == NULL) {
	  inputline = malloc(sizeof(char)*256);
	  if (inputline == NULL)
	       perror("Out of memory"), exit(-1);
	  *inputline = '\0';
     }

     if (CURGetOneOption(CursesScreen, "Index word(s) to search for: ", inputline) == -1)
	  return(NULL);

     if (*inputline == '\0')
	  return(NULL);
     else
	  return(inputline);
}


/*
 * this procedure just retrieves binary data from the socket and
 * pumps it into a "play" process.
 */

#define BUFSIZE 1400  /* A pretty good value for ethernet */

void
suck_sound(sockfd)
  int sockfd;
{
     FILE *Play;
     int j;
     char buf[BUFSIZE];

     if (*STRget(PlayCommand) == '\0') {
	  /*** Hey! no play command, bummer ***/
	  CursesErrorMsg("Sorry, this machine doesn't support sounds");

	  return;
     }

     Play = popen(STRget(PlayCommand), "w");
	  
     
     while(1) {
          j = read(sockfd, buf, BUFSIZE);
	  
	  if (j == 0)
	       break;
	  
	  fwrite(buf, 1, j, Play);
     }
}


/*
 * fork off a sound process to siphon the data across the net.
 * So the user can listen to tunage while browsing the directories.
 */

void
do_sound(ZeGopher)
  GopherStruct *ZeGopher;
{
     int sockfd;
     char sTmp[5];
     BOOLEAN Waitforchld = FALSE;

     sTmp[0] = '\0';

     if ((sockfd = GSconnect(ZeGopher)) <0) {
	  check_sock(sockfd, GSgetHost(ZeGopher));
	  return;
     }

     /** Send out the request **/

     writestring(sockfd, GSgetPath(ZeGopher));
     writestring(sockfd, "\r\n");

     /** Okay, it's cool, we can fork off **/

     if (SOUNDCHILD != 0)
	  Waitforchld = TRUE;


     if ( (SOUNDCHILD = fork()) < 0)
	  ;/* Fork Error */
     
     else if (SOUNDCHILD == 0) {  /* Child Process */
	  wait(SIGCHLD);
	  suck_sound(sockfd);
	  exit(0);
     }
     
     /* Parent Process */
     
     close(sockfd);
     return;
}
	       


char *
strcasestr(inputline, match)
  char *inputline;
  char *match;
{
     int matchlen=0;
     int i, inlen;

     matchlen = strlen(match);
     inlen = strlen(inputline);

     for(i=0; i<inlen; i++) {
	  if (strncasecmp(inputline+i, match, matchlen)==0)
	       return(inputline+i);
     }

     return(NULL);
}



/*
 * Replace the searched words with backspaces and underline characters.
 */

static char sGBoldoutput[20];  /*** Used for stripping weird stuff from
				    term strings ***/
static int iGposition = 0;     /*** Pointer into the Boldoutput string **/

/*** Used by tputs() ***/

int
Boldoutchar(c)
  char c;
{
     sGBoldoutput[iGposition++] = c;
     return(c);
}


void
Boldit(inputline, outputline, MungeSearchstr)
  char *inputline, *outputline, *MungeSearchstr;
{
     char words[20][40];  /** A reasonable guess **/
     int numchars, lowwordnum, wordcount, i;
     char *cp, *lowword;

     bzero(outputline, 512); /** non portable!!!! ***/
     
     while (isspace(*MungeSearchstr)) /** Strip off spaces **/
	  MungeSearchstr++;
	  
     for (wordcount=0; wordcount<20; wordcount++) {

	  while (isspace(*MungeSearchstr)) /** Strip off spaces **/
	       MungeSearchstr++;
	  
	  numchars = sreadword(MungeSearchstr, words[wordcount], 40);
	  MungeSearchstr += numchars;
	  if (numchars == 0)
	       break;
	  if (strcmp(words[wordcount], "and")==0 ||
	      strcmp(words[wordcount], "or")==0 ||
	      strcmp(words[wordcount], "not")==0) {
	       words[wordcount][0] = '\0';
	       wordcount--;
	  }
     }


     /** Find the first word in the line **/

     while (*inputline!='\0') {
	  lowword = NULL;

	  for (i=0; i< wordcount; i++) {
	       cp = strcasestr(inputline, words[i]);
	       if (cp != NULL)
		    if (cp < lowword || lowword == NULL) {
			 lowword = cp;
			 lowwordnum = i;
		    }
	  }

	  if (lowword == NULL) {
	       strcpy(outputline, inputline);
	       return;
	  }
	  else {
	       strncpy(outputline, inputline, lowword - inputline);
	       outputline += (lowword - inputline);
	       inputline = lowword;
	       
	       iGposition = 0;
	       tputs(CURgetHighon(CursesScreen), 1, Boldoutchar);
	       sGBoldoutput[iGposition] = '\0';
	       strcpy(outputline, sGBoldoutput);
	       outputline += strlen(sGBoldoutput);

	       strncpy(outputline, inputline, strlen(words[lowwordnum]));
	       inputline += strlen(words[lowwordnum]);
	       outputline += strlen(words[lowwordnum]);


	       iGposition = 0;
	       tputs(CURgetHighoff(CursesScreen), 1, Boldoutchar);
	       sGBoldoutput[iGposition] = '\0';
	       strcpy(outputline, sGBoldoutput);
	       outputline += strlen(sGBoldoutput);

	  }
     }
}


/**
*** Show file takes a gopher text thing, writes it to a file
*** and passes it to your favorite pager.
**/

void
showfile(ZeGopher)
  GopherStruct *ZeGopher;
{
     int i=0, iLength, sockfd;
     char tmpfilename[HALFLINE];
     FILE *tmpfile;
     char inputline[512];
     char outputline[512];
     char sTmp[5];
     int twirlcounter=0;

     sTmp[0] = '\0';

     if ((sockfd = GSconnect(ZeGopher)) <0) {
	  check_sock(sockfd, GSgetHost(ZeGopher));
	  return;
     }

     /** Send out the request **/

     writestring(sockfd, GSgetPath(ZeGopher));
     writestring(sockfd, "\r\n");

     /** Open a temporary file **/

     sprintf(tmpfilename, "/tmp/gopher.%d",getpid());

     if ((tmpfile = fopen(tmpfilename, "w")) == NULL)
	  fprintf(stderr, "Couldn't make a tmp file!\n"), exit(-1);

     for(;;) {
	  /** make a mark for every page **/
	  twirlcounter++;

	  if ((twirlcounter % 25) == 0)
	       twirl();

	  iLength = readline(sockfd, inputline, 512);
	  outputline[0] = '\0';
	  if (iLength == 0)
	       break;

	  ZapCRLF(inputline);
	  
	  
	  /*** Ugly hack ahead..... ***/

          if (GSgetType(ZeGopher) == A_CSO) {
	       if (inputline[0] == '2')
		    break;

	       if ((inputline[0] >= '3') && (inputline[0] <= '9'))  {
		    fprintf(tmpfile, "%s\n", GSgetPath(ZeGopher));
		    fprintf(tmpfile, "%s\n", inputline+4);
		    break;
	       }
	       if (inputline[0] == '-') {
		    if (inputline[5] + (inputline[6] == ':' ? 0 : inputline[6]) != i)
			 fprintf(tmpfile, "-------------------------------------------------------\n");
		    i = inputline[5] + (inputline[6] == ':' ? 0 : inputline[6]);
		    fputs(inputline+7, tmpfile);
		    fputc('\n', tmpfile);
	       }
	  }

          if (GSgetType(ZeGopher) == A_FILE) {
	       if ((inputline[0] == '.') && (inputline[1] == '\0'))
		    break;
	       else {
		    /*** Underline searched words, except and, or and not ***/
		    if (Searchstring != NULL) {
			 Boldit(inputline, outputline, Searchstring);
		    }
		    else
			 strcpy(outputline, inputline);
		    fputs(outputline, tmpfile);
		    fputc('\n', tmpfile);
	       }
	  }
	  else if (GSgetType(ZeGopher) == A_MIME) {
	       if ((inputline[0] == '.') && (inputline[1] == '\0'))
		    break;
	       else {
		    fputs(inputline, tmpfile);
		    fputc('\n', tmpfile);
	       }
	  }

     }

     fprintf(tmpfile, "\012 \n\n");  /** Work around a bug in xterm n' curses*/
     (void)fclose(tmpfile);

     if (GSgetType(ZeGopher) == A_MIME)
	  display_mime(tmpfilename, GSgetTitle(ZeGopher));
     else
	  display_file(tmpfilename, GSgetTitle(ZeGopher));

     /** Good little clients clean up after themselves..**/

     if (unlink(tmpfilename)!=0)
	  fprintf(stderr, "Couldn't unlink!!!\n"), exit(-1);

     CURenter(CursesScreen);

     return;
}


/**
*** getfile takes an open socket connection, and writes it to a file.
*** Very much line showfile(), except no '.' check, no underlining,
*** no displaying and so on.  And precious little error checking :-(
**/
 
void
getfile(ZeGopher)
  GopherStruct *ZeGopher;
{
     char *p, buf[BUFSIZE];   /* hope this is long enough :-) */
     int cc, sockfd, filefd;


     int counter=0;
     char outstring[256];


     /* Try to construct a nice default filename to save in */
     p = rindex(GSgetPath(ZeGopher), '/' );
     if ( p && *(p+1) ) {
        strcpy( buf, p+1 );
     } else {
        /* no slashes, leave it empty */
        buf[0] = '\0';
     }
     if (CURGetOneOption(CursesScreen, "Save in file: ",buf) < 0)
	  return;

     if (*buf == '\0' ) {  /* empty name */
        Draw_Status("Retrieval abandoned");
        return;
     }
     /* Something there, maybe just spaces.  Oh well. */

     /* open the file - if it already exists, we're about to clobber it */
     if ( (filefd = open( buf, O_WRONLY|O_TRUNC|O_CREAT, 0666 )) < 0 ) {
        /* should give more info here */
        CursesErrorMsg( "Could not open file" );
        return;
     }

     Draw_Status("Connecting to server...");
     refresh();
     if ((sockfd = GSconnect(ZeGopher)) <0) {
        check_sock(sockfd, GSgetHost(ZeGopher));
        close(filefd);
        return;
     }

     /** Send out the request **/
     writestring(sockfd, GSgetPath(ZeGopher));
     writestring(sockfd, "\r\n");

     Draw_Status("Receiving file...");
     refresh();

     /* read and read until we get EOF */
     while ( (cc = read( sockfd, buf, BUFSIZE )) > 0 ) {
	  /* hope we don't have error while writing - I'm so lazy */
	  counter += cc;
/*	  sprintf(outstring,"This time: %d, Total: %d",cc, counter);
	  CursesErrorMsg(outstring);*/
	  if (write( filefd, buf, cc ) <=0)
	       CursesErrorMsg("Problems Writing");
	  twirl();
     }
     close( filefd ); /* should error check */
     if ( cc < 0 )
        CursesErrorMsg( "Error in file" );
     else
        CursesErrorMsg( "File received ok" );
     
     close( sockfd );
}



/*
** Pushgopher takes a GopherThing pointer and adds it to it's stack.
**
** Ick this must be fixed!
*/

void
pushgopher(ZeDir)
  GopherDirObj *ZeDir;
{

     OldDirs[iLevel]= ZeDir;
     iLevel ++;
}

/*
** If the stack is empty, popgopher returns a -1
*/

int
popgopher(ZeDir)
  GopherDirObj **ZeDir;
{

     if (iLevel == 0)
	  return(-1);

     iLevel --;

     *ZeDir =  OldDirs[iLevel];

     return(0);
}


void check_sock(sockfd, host)
  int sockfd;
  char *host;
{
     char DispString[WHOLELINE];
     char Response[HALFLINE];

     Response[0] = '\0';

     if (sockfd <0) {
	  sprintf(DispString, "Cannot connect to host %s: ", host);
	  CursesErrorMsg(DispString);
     }
}


/**************
** This bit of code catches control-c's, it cleans up the curses stuff.
*/
void
controlc()
{
     char buf[HALFLINE];

     sprintf(buf, "/tmp/gopher.%d", getpid());
     if (unlink(buf) < 0)
	  fprintf(stderr, "could not unlink %s\n", buf);

     CURexit(CursesScreen);
     exit(0);
}


/**************
** This bit of code catches window size change signals
**/

void
sizechange()
{
     int lines, cols;
     
#ifdef  TIOCGWINSZ
     static struct      winsize zewinsize;        /* 4.3 BSD window sizing */
#endif

     lines = LINES;
     cols  = COLS;
     
#ifdef  TIOCGWINSZ
     if (ioctl(0, TIOCGWINSZ, (char *) &zewinsize) == 0) {
	  lines = zewinsize.ws_row;
	  cols  = zewinsize.ws_col;
     } else {
#endif
	  /* code here to use sizes from termcap/terminfo, not yet... */
	  ;
#ifdef  TIOCGWINSZ
     }

     if (lines != LINES || cols != COLS) {
	  LINES = lines;
	  COLS  = cols;
	  CURresize(CursesScreen);
     
	  scline(-1, 1, CurrentDir);
     }

#endif
}



/**********
**
** Set up all the global variables.
**
***********/

void
Initialize()
{

     PagerCommand    = STRnew();
     PrinterCommand  = STRnew();
     TelnetCommand   = STRnew();
     TN3270Command   = STRnew();
     PlayCommand     = STRnew();
     MailCommand     = STRnew();
     EditorCommand   = STRnew();
     MIMECommand     = STRnew();
     /** get defaults from the rc file **/

     set_defs();
     read_rc();
     read_env();


     /*** Set up the curses environment ***/
     
     CursesScreen = CURnew();

     if (strcmp(CURgetTerm(CursesScreen), "unknown")==0)
	  fprintf(stderr, "I don't understand your terminal type\n"), exit(-1);


     /*** Make a signal handler for window size changes ***/

#ifdef SIGWINCH
     CURsetSIGWINCH(CursesScreen, sizechange);
     if (signal(SIGWINCH, sizechange)==(void*)-1)
	  perror("signal died:\n"), exit(-1);

#endif

     if (signal(SIGINT, controlc) == (void*)-1)
	  perror("signal died:\n"), exit(-1);

     /*** Init MainWindow ****/
     CURenter(CursesScreen);
}


static char *GlobalOptions[] =  
{"Pager Command", "Print Command", "Telnet Command", "Sound Command", "Mail Command", "3270 Emulator Command", "MIME pager"};


void
SetOptions()
{
     static char Response[MAXRESP][MAXSTR];

     if (SecureMode) {
	  CursesErrorMsg("Sorry, you are not allowed to set options in secure mode.");
	  return;
     }
     
     strcpy(Response[0], STRget(PagerCommand));
     strcpy(Response[1], STRget(PrinterCommand));
     strcpy(Response[2], STRget(TelnetCommand));
     strcpy(Response[3], STRget(PlayCommand));
     strcpy(Response[4], STRget(MailCommand));
     strcpy(Response[5], STRget(TN3270Command));
     strcpy(Response[6], STRget(MIMECommand));

     Get_Options("Internet Gopher Options", "", 7, GlobalOptions, Response);

     STRset(PagerCommand, Response[0]);
     STRset(PrinterCommand, Response[1]);
     STRset(TelnetCommand, Response[2]);
     STRset(PlayCommand, Response[3]);
     STRset(EditorCommand, Response[4]);
     STRset(TN3270Command, Response[5]);
     STRset(MIMECommand, Response[6]);
     ChangedDefs = TRUE;
}


/* This should be a generalized stack type.  This is icky for now... */
static int SavedLinenum[512];
static int SavedLinePtr = 0;

int
main(argc, argv)
  int argc;
  char *argv[];
{
     int iLine=0;
     int iNum=0;
     BOOLEAN bDone = FALSE;
     char sTmp[80];
     GopherStruct *RootGopher;
     int TypedChar;
     /*** for getopt processing ***/
     int c;
     extern char *optarg;
     extern int optind;
     int errflag =0;
     int Garbled = TRUE;
     boolean Bkmarksfirst = FALSE;

     RootGopher = GSnew();
     SavedLinenum[SavedLinePtr] = 1;


     GSsetType (RootGopher, A_DIRECTORY);   
     GSsetPath (RootGopher,"");
     GSsetHost (RootGopher, DEFAULT_HOST);
     GSsetPort (RootGopher,  GOPHER_PORT);

     sTmp[0] = '\0';

     while ((c = getopt(argc, argv, "Dsbp:t:")) != -1)
	  switch (c) {
	  case 's':
	       SecureMode = TRUE;
	       break;
	  case 'p':
	       GSsetPath(RootGopher, optarg);
	       break;
	  case 't':
	       GSsetTitle(RootGopher, optarg);
	       break;
	  case 'D':
	       DEBUG = TRUE;
	       break;
	  case 'b':
	       Bkmarksfirst = TRUE;
	       break;
	  case '?':
	       errflag++;
	  }


     if (errflag) {
	  fprintf(stderr, "Usage: %s [-sb] [-p path] [-t title] [hostname port]\n", argv[0]);
	  exit(-1);
     }
     
     if (optind < argc) {
	  GSsetHost(RootGopher, argv[optind]);
	  optind++;
     }

     if (optind < argc) {
	  GSsetPort(RootGopher, atoi(argv[optind]));
	  optind++;
     }

     /*** If the title hasn't been set, then add a default title **/
     if (GSgetTitle(RootGopher) == NULL) {
	  sprintf(sTmp, "Root gopher server: %s", GSgetHost(RootGopher));
	  GSsetTitle(RootGopher, sTmp);
     }

     /*** Set up global variables, etc. ***/

     Initialize();

     iLine = 1;

     if (Bkmarksfirst) {
	  CurrentDir = BookmarkDir;
	  
	  if (CurrentDir != NULL)
	       GDaddGS(CurrentDir, RootGopher);
     } else
	  process_request(RootGopher);

     if (CurrentDir == NULL) {
	  /*
	   * We didn't get anything from that gopher server.  Either
	   * it is down, doesn't exist, or is empty or otherwise
	   * busted.
	   */
	  
	  CURexit(CursesScreen);
	  fprintf(stderr,
		  "%s: Nothing received for main menu, can't continue\n", argv[0]);
	  exit(1);
     }

     if (GDgetNumitems(CurrentDir) <= 0) {
	  CURexit(CursesScreen);
	  fprintf(stderr,
		  "%s: Nothing received for main menu, can't continue\n", argv[0]);
	  exit(1);
     }	  

     while (bDone == FALSE)
     {
	  iLine = GetMenu(CurrentDir, &TypedChar, iLine, Garbled);
	  Garbled = TRUE;

	  switch(TypedChar)
	  {
	  case '\r':
	  case '\n':
	       /*** Select the designated item ***/
	       iNum = iLine - 1;
	       if (GSgetType(GDgetEntry(CurrentDir, iNum)) == A_DIRECTORY || 
		   GSgetType(GDgetEntry(CurrentDir, iNum)) == A_INDEX) {
		    SavedLinenum[++SavedLinePtr] = iLine;
		    iLine=1;
	       }
	       if (process_request(GDgetEntry(CurrentDir, iNum))==1)
		    iLine= iNum+1;
	       break;
	       
	  case '\014':
	       /*** Redraw the screen ***/
	       break;

	  case '\0':
	       /*** What the heck? ***/
	       CursesErrorMsg("Strange Error occurred!");
	       break;
	       
	  case 'u':
	  case 'U': 
	  {
	       GopherDirObj *tempGdir;

	       /*** Go up a directory level ***/
	       iNum=0;
	       tempGdir = CurrentDir;
	       /** Don't destroy root level directory, or bookmarks **/
	       if (popgopher(&CurrentDir)==0 && tempGdir != CurrentDir) {
		    if (tempGdir != BookmarkDir)
			 GDdestroy(tempGdir);
		    iLine = SavedLinenum[(SavedLinePtr ==0) ? 0:SavedLinePtr--];
	       }
	  }
	       break;

	  case 'v':  /** View bookmark list **/
	  {
	       if (BookmarkDir == NULL) {
		    CursesErrorMsg("No bookmarks are defined");
		    break;
	       }

	       SavedLinenum[++SavedLinePtr] = iLine;
	       iLine=1;

	       /** Don't push an empty gopher directory... **/
	       if (CurrentDir != NULL)
		    pushgopher(CurrentDir); 
	       
	       CurrentDir = BookmarkDir;

	       break;
	  }	       

	  case 'a': /** add current item as a bookmark **/
	  {
	       GopherObj *tmpgs;
	       char newtitle[256];
	       
	       if (BookmarkDir == NULL) {
 		    BookmarkDir = GDnew(32);
		    GDsetTitle(BookmarkDir, "Bookmarks");
	       }

	       tmpgs = GSnew();
	       GScpy(tmpgs, GDgetEntry(CurrentDir, iLine-1));
	       
	       strcpy(newtitle, GSgetTitle(tmpgs));
	       if (CURGetOneOption(CursesScreen, "Name for this bookmark? ", newtitle) <0)
		    break;
	       if (*newtitle == '\0')
		    break;

	       GSsetTitle(tmpgs, newtitle);
	       GDaddGS(BookmarkDir, tmpgs);
	       GSdestroy(tmpgs);

	       ChangedDefs = TRUE;

	       break;
	  }

	  case 'A': /*** Add current directory/search as a bookmark **/
	  {
	       GopherObj *tmpgs;
	       char newtitle[256];

	       if (BookmarkDir == NULL) {
		    BookmarkDir = GDnew(32);
		    GDsetTitle(BookmarkDir, "Bookmarks");
	       }

	       if (CurrentDir == BookmarkDir) {
		    CursesErrorMsg("Sorry, can't make a bookmark of bookmarks");
		    break;
	       }
	       
	       tmpgs = GSnew();
	       if (iLevel == 0)
		    GScpy(tmpgs, RootGopher);
	       else
		    GScpy(tmpgs, GDgetEntry(OldDirs[iLevel-1], SavedLinenum[SavedLinePtr]-1));
	       
	       strcpy(newtitle, GDgetTitle(CurrentDir));
	       if (CURGetOneOption(CursesScreen, "Name for this bookmark? ", newtitle)<0)
		    break;
	       if (*newtitle == '\0')
		    break;

	       GSsetTitle(tmpgs, newtitle);

	       /*** Freeze the search, if there was one. ***/
	       if (GSgetType(tmpgs) == '7') {
		    char ickypath[512];
		    strcpy(ickypath, GSgetPath(tmpgs));
		    strcat(ickypath, "\t");
		    strcat(ickypath, Searchstring);
		    GSsetPath(tmpgs, ickypath);
		    GSsetType(tmpgs, '1');
	       }

	       GDaddGS(BookmarkDir, tmpgs);

	       ChangedDefs = TRUE;

	       break;
	  }

	  case 'd':  /*** Delete a bookmark ***/
	  {
	       GopherDirObj *tempgd;
	       int i;


	       if (GDgetNumitems(CurrentDir) == 1) {
		    /** Last item in the directory
		        Pop up a directory **/
		    iNum=0;
		    tempgd = CurrentDir;
		    /** Don't destroy root level directory, or bookmarks **/
		    if (popgopher(&CurrentDir)==0 && tempgd != CurrentDir) {
			 if (tempgd != BookmarkDir)
			      GDdestroy(tempgd);
			 iLine = SavedLinenum[(SavedLinePtr ==0) ? 0:SavedLinePtr--];
		    } else {
			 CursesErrorMsg("Sorry, can't delete top level directory.");
		    }
		    ChangedDefs = TRUE;
	       }


	       tempgd = GDnew(GDgetNumitems(CurrentDir)+1);

	       for (i=0; i<GDgetNumitems(CurrentDir); i++) {
		    if (i != (iLine -1))
			 GDaddGS(tempgd, GDgetEntry(CurrentDir, i));
	       }
	       GDsetTitle(tempgd, GDgetTitle(CurrentDir));

	       GDdestroy(CurrentDir);

	       if (CurrentDir == BookmarkDir)
		    BookmarkDir = tempgd;
	       
	       CurrentDir = tempgd;
	       
	       ChangedDefs = TRUE;

	       break;
	  }

	  case 's':
	       /*** Save the thing in a file ***/
	       break;
	       
	  case 'M':
	  case 'm': 
	  {
	       GopherDirObj *tempGdir = NULL;
	       iNum = 0;
	       
	       while (popgopher(&CurrentDir) != -1) {
		    if (tempGdir != NULL)
			 GDdestroy(tempGdir);
		    tempGdir = CurrentDir;
		    iLine = SavedLinenum[(SavedLinePtr ==0) ? 0:SavedLinePtr--];
	       }

	  }

	       break;

	  case 'q':
	       /*** Quit the program ***/
	  {
	       char yesno[3];
	       
	       yesno[0] = 'y';
	       yesno[1] = '\0';

	       CURgetYesorNo(CursesScreen, "Really quit (y/n) ?", yesno);
	       if (*yesno == 'y') {
		    bDone = TRUE;
		    sleep(1);
		    CURexit(CursesScreen);
		    break;
	       }

	       sleep(1);
	       break;
	  }
	  case 'Q':
	       /*** Quit the program, don't ask ***/
	       bDone = TRUE;
	       CURexit(CursesScreen);
	       break;
	       
	  case 'O':
	       /*** Change various program things ***/
	       SetOptions();
	       break;
		      
	  case '=':
	       describe_gopher("Gopher Item Information", 
			       GDgetEntry(CurrentDir, iLine -1));
	       break;

	  case '^':
	  {
	       if (iLevel == 0)
		    describe_gopher("Gopher Directory Information",
				    RootGopher);
	       else
		    describe_gopher("Gopher Directory Information",
				    GDgetEntry(OldDirs[iLevel-1],
					       SavedLinenum[SavedLinePtr] -1));
	       break;
	  }
			       

	  case '?':
	       /*** Display help file ***/
	       CURexit(CursesScreen);
	       display_file(GOPHERHELP, "Gopher Help File");
	       CURenter(CursesScreen);
	       break;

	  default :
	       CURBeep(CursesScreen);
	       Garbled = FALSE;
	       break;

	  }
     }
     if (ChangedDefs)
	  write_rc();

     GDdestroy(CurrentDir);
     exit(0);
     
     return(0);
}     


int
Load_Index(ZeGopher)
  GopherStruct *ZeGopher;
{
     Draw_Status("Searching Text...");
     refresh();

     return(Load_Index_or_Dir(ZeGopher, Searchstring));
}

int
Load_Dir(ZeGopher)
  GopherStruct *ZeGopher;
{
     Searchstring= NULL;
     return(Load_Index_or_Dir(ZeGopher, NULL));
}


int
twirl()
{
     static int twirlnum = 0;
     static char *twirls = "-/|\\";

     addch('\b');
     addch(*(twirls + (twirlnum++ % 4 )));
     refresh();

     return(0);
}

int
Load_Index_or_Dir(ZeGopher, Searchmungestr)
  GopherStruct *ZeGopher;
  char *Searchmungestr;
{
     int failed = 0;
     int sockfd;
     int i;
     char sTmp[10];
     static char DirTitle[512];
     GopherDirObj *NewDir = NULL;

     NewDir = GDnew(32);

     sTmp[0]= '\0';

     Draw_Status("Connecting..."); refresh();

     if ((sockfd = GSconnect(ZeGopher)) <0) {
	  check_sock(sockfd, GSgetHost(ZeGopher));
	  failed = 1;
     }
     else {
	  if (GSgetType(ZeGopher) == A_DIRECTORY) {
	       Draw_Status("Retrieving Directory..."); refresh();
	       writestring(sockfd, GSgetPath(ZeGopher));
	       writestring(sockfd, "\r\n");
	  }
	  else if (GSgetType(ZeGopher) == A_INDEX) {
	       Draw_Status("Searching..."); refresh();
	       writestring(sockfd, GSgetPath(ZeGopher));
	       writestring(sockfd, "\t");
	       writestring(sockfd, Searchmungestr);
	       writestring(sockfd, "\r\n");
	  }

	  if (GSgetType(ZeGopher) == A_INDEX) {
	       sprintf(DirTitle, "%s: %s", GSgetTitle(ZeGopher), Searchmungestr);
	       GDsetTitle(NewDir, DirTitle);
	  }
          else
	       GDsetTitle(NewDir, GSgetTitle(ZeGopher));

	  /** Don't push an empty gopher directory... **/
	  if (CurrentDir != NULL)
	       pushgopher(CurrentDir); 

	  CurrentDir = NewDir;
	  
	  i = GDfromNet(NewDir, sockfd, twirl);

	  if (i <= 0) {
	       CursesErrorMsg("Nothing available.");
	       popgopher(&CurrentDir);
	       SavedLinePtr--;
	       failed = 1;
	  }
     }
     i = close(sockfd);
     return(failed);
}



int
process_request(ZeGopher)
  GopherStruct *ZeGopher;
{
     int failed=0;

     switch(GSgetType(ZeGopher)) {
     case -1:
	  break;

     case A_EVENT:
/*	  HandleEvent(ZeGopher);*/
	  break;

     case A_FILE:
	  Draw_Status("Receiving Text...");
	  refresh();
	  showfile(ZeGopher);
	  break;

     case A_MACHEX:
     case A_PCHEX:
     case A_UNIXBIN:
	  if (!SecureMode)
	       getfile(ZeGopher);
	  else
	       CursesErrorMsg("Sorry, cannot transfer files in securemode");
	  break;
	  
     case A_DIRECTORY:
	  Draw_Status("Receiving Directory...");
	  refresh();
	  failed = Load_Dir(ZeGopher);
	  break;
		    
     case A_TELNET:
     case A_TN3270:
	  do_tel_3270(ZeGopher);
	  break;

     case A_MIME:
	  Draw_Status("Receiving MIME...");
	  refresh();
	  showfile(ZeGopher);
	  break;

     case A_INDEX:
	  Draw_Status("Searching Text...");
	  refresh();
	  Searchstring = do_index(ZeGopher);
	  if (Searchstring != NULL)
	       failed=Load_Index(ZeGopher);
	  else
	       failed = 1;
	  break;
	  
     case A_CSO:
	  do_cso(ZeGopher);
	  break;

     case A_SOUND:
	  Draw_Status("Receiving Sound...");
	  refresh();
	  do_sound(ZeGopher);
	  break;

     case A_HTML:
	  Draw_Status("Receiving HTML page...");
	  refresh();
	  do_html(ZeGopher);
	  break;
     }
     return(failed);
}


void
describe_gopher(banner, ZeGopher)
  char *banner;
  GopherStruct *ZeGopher;
{
     char tmpfilename[20];
     FILE *tmpfile;

     CURexit(CursesScreen);	/* do this *before* possible exit() below */

     sprintf(tmpfilename,"/tmp/gopher.%d", getpid());
     if ((tmpfile = fopen(tmpfilename, "w")) == NULL)
	   fprintf(stderr, "Couldn't make a tmp file!\n"), exit(-1);

     fprintf(tmpfile,"Name=%s\nType=%c\nPort=%d\nPath=%s\nHost=%s\n\n",
	     GSgetTitle(ZeGopher),
	     GSgetType(ZeGopher),
	     GSgetPort(ZeGopher),
	     GSgetPath(ZeGopher),
	     GSgetHost(ZeGopher));

     fclose(tmpfile);

     display_file(tmpfilename, banner);
     if (unlink(tmpfilename) != 0)
	  fprintf(stderr, "Couldn't unlink!!!\n"), exit(-1);

     CURenter(CursesScreen); /* do this after unlink fails */

     return;
}
