/*****************************************************************************
* JLWilkinson 21-Sep-1995  Moved syslog() routines to [-.object]compatible.c
*                           so they're available for OpenVMS Server use too.
* M.Dunnett  09-Mar-1995   Also invoke GSprompter() when using callable TPU
*                           pagers.
* F.Macrides 08-Mar-1995   Use strncasecmp() for getting the AskLs value in
*                           AskBlock().
* F.Macrides 02-Mar-1995   Restore use of strncasecmp(command, "builtin", 7)
*                           when checking the PAGER_COMMAND. It's not
*                           indended to include a %s, and doesn't in the
*                           conf.h definition, but users are likely to include
*                           %s when setting the pager in their RC file, and
*                           cause the check to fail.  Apparently was changed
*                           to strcasecmp() during a recent upgrade, because
*                           in *theory* that's adequate, but in *practice* it
*                           isn't. (Reported by Hunter Goatley.)
*/
/********************************************************************
* lindner
* 3.72
* 1995/01/25 23:10:07
* /home/arcwelder/GopherSrc/CVS/gopher+/gopher/ourutils.c,v
* Exp
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992,1993 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: ourutils.c
* stuff that doesn't really fit anywhere else.
*********************************************************************
* Revision History:
* ourutils.c,v
* Revision 3.72  1995/01/25  23:10:07  lindner
* Remove hard limit of 5 AskL items per form
*
* Revision 3.71  1994/12/03  01:55:43  lindner
* JL Wilkinson 16-Jul-1994 Merged in TGV's syslog() functions to allow
* the CLIENT_LOGGER and TELNET_TRACE functionality to work with
* Multinet.  Not tested against other VMS TCP/IP agents, and *NOT
* SUPPORTED* thru TGV -- please don't anybody ask TGV for help in using
* this.  Somebody who wants to use it can Email me or the
* [email protected] group and I'll try to assist them for
* MULTINET only ([email protected]).  Basic documentation is in
*
* Added Lance's post-pager RETURN synonyms.
*
* Indicate 'D'ownload as an option on exit from the pager in SecureMode
* and NoShellMode.
*
* Revision 3.70  1994/10/24  22:14:38  lindner
* Add pdf type
*
* Revision 3.69  1994/10/13  05:30:38  lindner
* Compiler complaint fixes
*
* Revision 3.68  1994/09/29  19:25:57  lindner
* More restrictive Unix file name filter
*
* Revision 3.67  1994/08/19  16:53:29  lindner
* Add casts for malloc calls
*
* Revision 3.66  1994/07/25  02:55:17  lindner
* Secure mode mods
*
* Revision 3.65  1994/07/19  20:18:36  lindner
* Remove CLIENT_LOGGER, Add default language choice
*
* Revision 3.64  1994/07/03  23:11:26  lindner
* Add internal download feature
*
* Revision 3.63  1994/06/29  07:04:41  lindner
* Mods for A_APP and builtin upload
*
* Revision 3.62  1994/06/09  04:11:07  lindner
* Added [email protected] patch for off-by-one line
* counts in AskL blocks.
*
* Revision 3.61  1994/06/03  06:11:46  lindner
* Fix for big bad cso dashes
*
* Revision 3.60  1994/05/19  14:08:04  lindner
* use fast malloc on VMS VAXC
*
* Revision 3.59  1994/05/17  05:48:03  lindner
* Massive internationalization change
*
* Revision 3.58  1994/05/14  04:13:46  lindner
* Internationalization...
*
* Revision 3.57  1994/04/29  15:13:17  lindner
* Fix for items with multiple AskL items
*
* Revision 3.56  1994/04/25  18:40:05  lindner
* Fix for uninited variable i
*
* Revision 3.55  1994/04/25  03:37:43  lindner
* Modifications for Debug() and mismatched NULL arguments, added Debugmsg
*
* Revision 3.54  1994/04/13  19:14:03  lindner
* AskL modifications
*
* Revision 3.53  1994/03/08  20:38:28  lindner
* Fix for VMS compilation
*
* Revision 3.52  1994/03/08  15:55:16  lindner
* gcc -Wall fixes
*
* Revision 3.51  1994/03/08  03:25:58  lindner
* Fix for big bad telnet bug, additions for secure process i/o
*
* Revision 3.50  1994/03/04  23:31:32  lindner
* Fix for askblock from beckett
*
* Revision 3.49  1994/02/20  16:32:58  lindner
* Modify routines to use fileio routines for reading text files
*
* Revision 3.48  1993/12/28  17:33:00  lindner
* filter out more VMS characters
*
* Revision 3.47  1993/11/29  01:12:26  lindner
* In Save_file(), disallow saving of <CSO> type since this is fetched as a
* set of indexed fields instead of as a savable file.  Also disallow
* saving of info and error types since there's nothing to save.  (Wolfe,
* Beckett, Macrides)
*
* In Choose_View(), add case for A_MOVIE.  (Macrides)
*
* Revision 3.46  1993/11/03  03:50:01  lindner
* More fixes for VMSrecords
*
* Revision 3.45  1993/11/02  21:17:44  lindner
* Better client side logging code
*
* Revision 3.44  1993/10/27  18:52:17  lindner
* Simplify VMS fixed/variable file code
*
* Revision 3.43  1993/10/26  18:22:43  lindner
* Mucho changes for VMS variable length records
*
* Revision 3.42  1993/10/22  20:33:17  lindner
* Fixed bad code in Save_file() for choosing view (Fote)
*
* Revision 3.41  1993/10/22  20:06:46  lindner
* Add optional client logging
*
* Revision 3.40  1993/10/11  17:20:43  lindner
* Fix for data/time stripping, vms multidot files, no case comparision for builtin
*
* Revision 3.39  1993/10/07  05:08:22  lindner
* In Choose_View(), remove unreachable statement.
*
* In AskBlock(), allocate memory for the form based on screen width.
*
* In Save_file(), choose a view BEFORE opening the file -- user may
* change mind and cancel, which left a zero-length file previously.
*
* In Save_file(), check to see if a view has already been chosen.  This
* change requires that Save_file() have a third argument.  Any call to
* Save_file() will now need a third argument.  The third argument should
* be a pointer to the chosen view or NULL to indicate that one has yet to
* be chosen.
*
* In GSdisplay(), add "view" as the third argument to the two "new"
* Save_file() calls to prevent Save_file() from asking the user to choose
* a view.  It is redundant in both cases.
*
* Revision 3.38  1993/09/29  22:47:21  lindner
* Fix for AskBlock memory cruft
*
* Revision 3.37  1993/09/29  21:30:29  lindner
* Fix for memory probs with ask blocks
*
* Revision 3.36  1993/09/29  20:49:42  lindner
* Remove dead code, fix for vms mail
*
* Revision 3.35  1993/09/26  09:20:54  lindner
* Change to use GSmail()
*
* Revision 3.34  1993/09/22  19:57:52  lindner
* fix for off by one and Select:
*
* Revision 3.33  1993/09/21  01:46:02  lindner
* Implement all remaining ASK block items..
*
* Revision 3.32  1993/09/18  04:44:23  lindner
* Additions to fix caching of Multiple view items
*
* Revision 3.31  1993/09/18  03:29:02  lindner
* Mucho fixes for ask block parsing..
*
* Revision 3.30  1993/09/11  05:12:41  lindner
* Many ask block fixes
*
* Revision 3.29  1993/09/08  05:23:19  lindner
* Implement Builtin pager..
*
* Revision 3.28  1993/09/08  01:12:33  lindner
* Add support for HTML and MIME on the menu displays
*
* Revision 3.27  1993/09/01  21:47:37  lindner
* Fix for compiler error on Alpha
*
* Revision 3.26  1993/08/19  20:51:52  lindner
* secure patch from mitra
*
* Revision 3.25  1993/08/19  20:30:39  lindner
* Fix problem with selecting ask item more than once
*
* Revision 3.24  1993/08/19  20:22:56  lindner
* Mitra's Debug patch
*
* Revision 3.23  1993/08/16  18:01:10  lindner
* Fix for VMSfile, mods for DEC alpha VMS
*
* Revision 3.22  1993/08/16  17:58:24  lindner
* Removed REMOTEUSER ifdefs
*
* Revision 3.21  1993/08/06  14:39:57  lindner
* One more small security patch
*
* Revision 3.20  1993/08/05  20:40:43  lindner
* Added warning message..
*
* Revision 3.19  1993/08/04  22:07:23  lindner
* Use /bin/mail instead of ucbmail
*
* Revision 3.18  1993/08/03  04:43:59  lindner
* Fix for VMS unresolved variables
*
* Revision 3.17  1993/07/30  17:31:56  lindner
* Mods to support AskP:, AskL:
*
* Revision 3.16  1993/07/29  17:20:19  lindner
* eliminate non-used variables
*
* Revision 3.15  1993/07/27  06:14:17  lindner
* Bug fix, use readrecvbuf, not readn
*
* Revision 3.14  1993/07/27  05:28:57  lindner
* Mondo Debug overhaul from Mitra
*
* Revision 3.13  1993/07/27  02:04:13  lindner
* More comments
*
* Revision 3.12  1993/07/26  20:30:05  lindner
* fix memory usage
*
* Revision 3.11  1993/07/26  15:35:28  lindner
* Fix for Unixfile to remove parens
*
* Revision 3.10  1993/07/23  04:38:43  lindner
* Mods to make client view selection pretty
*
* Revision 3.9  1993/07/20  23:14:59  lindner
* Mods to cache askdata..
*
* Revision 3.8  1993/07/07  19:43:42  lindner
* Added TPU support for pager in VMS
*
* Revision 3.7  1993/06/29  06:18:52  lindner
* recasting MIME types
*
* Revision 3.6  1993/04/15  21:27:26  lindner
* NOMAIL option, Remote user stuff, and bug in Ask
*
* Revision 3.5  1993/03/26  19:44:36  lindner
* Fix for CSO stuff and saving binary files
*
* Revision 3.4  1993/03/24  17:01:27  lindner
* Major changes to the way things are displayed/saved
*
* Revision 3.3  1993/03/18  23:32:41  lindner
* Added Note: support and default values to AskBlock
*
* Revision 3.2  1993/02/19  21:12:16  lindner
* Generalized on gopher+ stuff, strip long filenames to reasonable length
*
* Revision 3.1.1.1  1993/02/11  18:02:58  lindner
* Gopher+1.2beta release
*
* Revision 2.1  1993/02/09  22:37:50  lindner
* Much new in the way of display routines.
*
*********************************************************************/


#include "gopher.h"
#include "Stdlib.h"
#include "util.h"
#ifndef VMS
#include <pwd.h>
#else
#include <descrip.h>
#endif
#ifdef mips
char *getenv();
#endif
#include "Debug.h"
#include "fileio.h"
#include "Malloc.h"
#include "Locale.h"
#include "String.h"

void GSprompter();

int
outchar(c)
 char c;
{
       /** output the given character.  From tputs... **/
       /** Note: this CANNOT be a macro!              **/

       putc(c, stdout);
       return(c);
}


/*
* Function that twirls on the screen..
*
*/

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

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

    return(0);
}



/*
** This procedure sends a file through the mail.
*/

void
GSmail(gs)
 GopherObj *gs;
{
    char        *Filename = GSgetLocalFile(gs);
    char        *Realname = GSgetTitle(gs);

    static char *SaveName = NULL;
    char         command[512];
    FileIO      *infile, *mailit;
    char         buf[512];
    char         MailAddress[512];


    if (SaveName==NULL) {
         if ((SaveName = (char *) malloc(sizeof(char)*256)) == NULL)
              perror("Out of memory!");
         *SaveName = '\0';
    }

    if (CURGetOneOption(CursesScreen, Realname,
                        Gtxt("Mail current document to:",105),
                        SaveName)<0)
         return;

    if (*SaveName == '\0')
         return;

/*
* You should be very careful when you add characters to this list..
*
* It could create a nasty security hole!
*/

#define ACHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789%.@!-_"

    if (SecureMode || NoShellMode)
         if ( strspn(SaveName, ACHARS) != strlen(SaveName)||
             *SaveName == '-') {
              Debugmsg("mail_file - beep - bad char")
              puts(CURgetBell(CursesScreen));
              fflush(stdout);
              Draw_Status(Gtxt("Address syntax rejected...",65));
              refresh();
              return;
         }

    if (strchr(Realname, '\"') != NULL)
         Realname = Gtxt("Gopher File",91);


    Draw_Status(Gtxt("Mailing File...",106));
    refresh();

#ifdef VMS
    /* encase any address with '@' in double-quotes and add transport */
    /* if the user hasn't done it (based on code from Earl Fogel)     */
    if ((strchr(SaveName, '@') != NULL) && (strchr(SaveName, '\"') == NULL))
          sprintf(MailAddress, MAIL_ADRS, SaveName);
    else
          strcpy(MailAddress, SaveName);

    sprintf(command, "%s/subject=\"%.70s\" %s %s",
          MAIL_COMMAND, Realname, Filename, MailAddress);

    CURexit(CursesScreen);
    printf(Gtxt("Mailing file to %s...",107), MailAddress);
    FIOsystem(command);
    CURenter(CursesScreen);
#else
    sprintf(command, "%s %s",
            MAIL_COMMAND, SaveName);

    infile = FIOopenUFS(Filename, O_RDONLY, 0);
    mailit = FIOopenCmdline(command, "w");

    if (mailit == NULL || infile == NULL) {
         CursesErrorMsg(Gtxt("System problem, unable to mail",173));
         return;
    }

    if (strlen(Realname) > 70)
         Realname[70]  = '\0';

    FIOwritestring(mailit, "Subject: ");
    FIOwritestring(mailit, Realname);
    FIOwritestring(mailit, "\n\n");

    while (FIOreadline(infile, buf, sizeof(buf))) {
         twirl();
         FIOwritestring(mailit, buf);
    }

    FIOclose(mailit);
    FIOclose(infile);

#endif

}

/*
*  This fcn allows one to choose a view out of a unlimited size list
*  returns false if fails
*/

#ifndef DEFAULT_LANG    /* should be set in conf.h, but just in case */
#define DEFAULT_LANG    "En_US"     /* English (US) */
#endif


char *
Choose_View(gs)
 GopherObj *gs;
{
    int viewno;
    static char viewnlang[64];
    char *view;

    if (GSisGplus(gs)) {

         GSgetginfo(gs, TRUE);

         if (GSgetNumViews(gs) >1) {
              int choice, default_choice = -1;
              int langChoice = -1, viewChoice = -1;
              char *default_lang =  Gtxt(DEFAULT_LANG, 222);
              char **views = (char **) malloc((1 + GSgetNumViews(gs)) * sizeof(char *));
              char **choices = (char **) malloc((1 + GSgetNumViews(gs)) * sizeof(char *));

              if ( (views == NULL) || (choices == NULL) )
                   return NULL;

              for (viewno=0; viewno <GSgetNumViews(gs); viewno++) {
                   VIewobj      *thisView = GSgetView(gs, viewno);

                   views[viewno] = strdup(VIgetViewnLang(thisView, viewnlang));
                   choices[viewno] = strdup(VIgetPrettyView(thisView,viewnlang));

                   if (default_choice < 0) {
                        int langMatch = !(strcasecmp(VIgetLang(thisView), default_lang));
                        int viewMatch = !(strcasecmp(VIgetType(thisView), "text/plain"));

                        if (langMatch && viewMatch)
                             default_choice = viewno;
                        else {
                             if (langMatch && (langChoice < 1))
                                  langChoice = viewno;
                             if (viewMatch && (viewChoice < 1))
                                  viewChoice = viewno;
                        }
                   }
              }

              if (default_choice < 0) {
                   if (viewChoice >= 0)
                        default_choice = viewChoice;
                   else if (langChoice >= 0)
                        default_choice = langChoice;
                   else
                        default_choice = 0;
              }

              choices[viewno] = NULL;
              choice = CURChoice(CursesScreen, GSgetTitle(gs), choices,
                                 Gtxt("Choose a document type",73),
                                 default_choice);
              Debug("Choice was %d\n", choice);
              if (choice == -1)
                   return(NULL);

              sprintf(viewnlang, "%s", views[choice]);

              for (viewno = 0; viewno <GSgetNumViews(gs); viewno++)  {
                   free(views[viewno]);

                   free(choices[viewno]);

              }
              free(views);
              free(choices);
              return(viewnlang);
         }
         else if (GSgetNumViews(gs) == 1)
              return(VIgetViewnLang(GSgetView(gs, 0), viewnlang));
         else
              return(NULL);
    }
    else {
         switch (GSgetType(gs)) {
         case A_FILE:
         case A_CSO:
              view = "text/plain";
              break;
         case A_MIME:
              view = "message/rfc822";
              break;
         case A_MACHEX:
              view = "application/mac-binhex40";
              break;
         case A_HTML:
              view = "text/html";
              break;
         case A_GIF:
              view = "image/gif";
              break;
         case A_IMAGE:
              view = "image";
              break;
         case A_MOVIE:
              view = "video";
              break;
         case A_SOUND:
              view = "audio/basic";
              break;
         case A_TN3270:
              view = "terminal/tn3270";
              break;
         case A_TELNET:
              view = "terminal/telnet";
              break;
         case A_UNIXBIN:
         case A_PCBIN:
              view = "application/octet-stream";
              break;
         case A_PDF:
              view = "application/pdf";
              break;
         default:
              return(NULL);
         }

         return(view);
    }

}

Requestitem *
REQitemnew()
{
    Requestitem *temp;

    temp = (Requestitem*) (malloc(sizeof(Requestitem)));
    if (temp == NULL)
         return(NULL);

    temp->stowage = (char *) malloc(sizeof(char) * COLS);
    temp->stowage[0] = '\0';
    temp->choices = NULL;
    temp->prompt = NULL;

    return(temp);
}

void
REQitemdestroy(req)
 Requestitem *req;
{
    int j;

    if (req->stowage != (char*) NULL)
         free(req->stowage);

    if (req->prompt != (char*) NULL)
         free(req->prompt);

    if (req->choices != (char**) NULL) {
         for (j=0; req->choices[j] != (char*) NULL; j++)
              free(req->choices[j]);
         free(req->choices);
    }
    free(req);

}

/*
* Crude implementation of the ASK block stuff.
*/

char **
AskBlock(gs)
 GopherObj *gs;
{
    int Asknum, reqnum, AskLs = 0;
    Blockobj *bl;
    Requestitem **items;
    char **asktypes;
    char **responses = NULL;
    char askline[256];
    char *defaultval;
    int i;

    GSgetginfo(gs, TRUE);

    bl = GSfindBlock(gs, "ASK");

    if (bl == NULL)
         return(NULL);

    /** Find out how many ASKL items we have... **/
    for (Asknum=0; Asknum < BLgetNumLines(bl); Asknum++) {
         if (strncasecmp(BLgetLine(bl, Asknum), "AskL:", 5) == 0)
              AskLs++;
    }

    /** Can't guess how many askl things we'll have.. **/
    items = (Requestitem **) malloc(sizeof(Requestitem*)*
                                    (BLgetNumLines(bl) + (AskLs * 10) + 1));

    asktypes = (char **) malloc(sizeof(char*) *
                                (BLgetNumLines(bl) + (AskLs * 10) + 1));

    reqnum = 0;

    for (Asknum=0; Asknum <BLgetNumLines(bl); Asknum++, reqnum++) {
         char *askprompt, *cp;

         strcpy(askline, BLgetLine(bl, Asknum));
         items[reqnum] = REQitemnew();

         /*** find the type of question ***/
         askprompt = strchr(askline, ':');
         if (askprompt == NULL) {
              /* Empty line crashes CURRequester unless we do this*/
              items[reqnum]->prompt = strdup("");
              items[reqnum]->thing = CUR_LABEL;
              asktypes[reqnum] = strdup("Note:");
              continue;
         }
         *(askprompt+1) = '\0';
         askprompt+=2;

         /*** Zap the tabs, and load it up.. ***/
         cp = strchr(askprompt, '\t');
         if (cp != NULL) {
              defaultval = cp+1;
              *cp = '\0';
         } else
              defaultval = NULL;

         asktypes[reqnum] = strdup(askline);
         items[reqnum]->prompt = strdup(askprompt);

         if (strncasecmp(askline, "Note:", 5) == 0)
              items[reqnum]->thing = CUR_LABEL;
         else if (strncasecmp(askline, "Choose:", 7) == 0) {
              int cnum = 0;

              items[reqnum]->thing = CUR_CHOICE;
              items[reqnum]->chooseitem = 0;

              /*** CURChoice() has a limit of 99 choices ***/
              items[reqnum]->choices = (char**) malloc(sizeof(char*)*100);

              /*** add list of choices to struct **/
              while ((cp = strchr(defaultval, '\t')) != NULL) {
                   *cp = '\0';
                   items[reqnum]->choices[cnum++] = strdup(defaultval);
                   defaultval = cp+1;
              }
              if (defaultval != NULL)
                   items[reqnum]->choices[cnum++] = strdup(defaultval);

              items[reqnum]->choices[cnum] = NULL;

         }
         else if (strncasecmp(askline, "Select:", 7) == 0) {
              items[reqnum]->thing = CUR_CHOICE;
              items[reqnum]->chooseitem = 0;
              items[reqnum]->choices = (char**) malloc(sizeof(char*)*3);

              cp = strrchr(items[reqnum]->prompt, ':');
              if (cp != NULL) {
                   *cp = '\0';
                   cp++;
                   if (*cp == '1')
                        items[reqnum]->chooseitem = 1;
              }

              items[reqnum]->choices[0] = strdup(Gtxt("No",111));
              items[reqnum]->choices[1] = strdup(Gtxt("Yes",180));
              items[reqnum]->choices[2] = NULL;
         }
         else if (strncasecmp(askline, "AskP:", 5)==0)
              items[reqnum]->thing = CUR_PASSWD;
         else if (strncasecmp(askline, "Choosef:", 8) == 0)
              items[reqnum]->thing = CUR_FNAME;
         else if (strncasecmp(askline, "AskL:", 5)==0){
              int x;

              items[reqnum]->thing = CUR_LABEL;

              /** Add a bunch more... **/

              for (x=0; x < 10; x++) {
                   reqnum++;
                   items[reqnum] = REQitemnew();
                   items[reqnum]->prompt = strdup("");
                   items[reqnum]->thing = CUR_ASKL;
                   asktypes[reqnum] = NULL;
              }

         }
         else
              items[reqnum]->thing = CUR_PROMPT;

         if (defaultval != NULL)
              strncat(items[reqnum]->stowage, defaultval, COLS-1);
    }

    items[reqnum] = NULL;

    if (!CURrequester(CursesScreen, GSgetTitle(gs), items)) {
         int respnum = 0;
         char *choosefile = NULL;

         responses = (char**) malloc(sizeof(char*)*(10+reqnum));

         for (i=0; i <reqnum; i++) {
              if (strcasecmp(asktypes[i], "AskL:")==0) {
                   int alnum, extra;
                   char numlinestr[16];

                   /** Okay, figure out how many lines were typed **/
                   for (alnum=9; alnum >= 0; alnum--) {
                        if (*(items[alnum+i]->stowage) != '\0')
                             break;
                   }
                   alnum++;
                   sprintf(numlinestr, "%d", alnum);

                   responses[respnum++] = strdup(numlinestr);

                   extra = 10 - alnum;
                   while (alnum) {
                        responses[respnum++] = strdup(items[++i]->stowage);
                        alnum--;
                   }
                   i += extra;

              } else if (strcasecmp(asktypes[i], "Select:")==0) {
                   if (items[i]->chooseitem == 0)
                        responses[respnum++] = strdup("0");
                   else
                        responses[respnum++] = strdup("1");

              } else if (items[i]->thing == CUR_CHOICE) {
                   responses[respnum++] = strdup(items[i]->choices[items[i]->chooseitem]);
              }
              else if (items[i]->thing == CUR_PROMPT ||
                         items[i]->thing == CUR_PASSWD)
                   responses[respnum++] = strdup(items[i]->stowage);
         }
         responses[respnum++] = NULL;
    }
    /*** Free memory ***/
    for (i=0; i<Asknum; i++) {

         REQitemdestroy(items[i]);
         items[i] = NULL;
         if (asktypes[i]!=NULL)
              free(asktypes[i]);
    }

    free(items);
    free(asktypes);

    return(responses);
}

/*
* Handle a gplus error
*/
#define GERRMSGSIZE 25

void
Gplus_Error(sockfd)
 int sockfd;
{
    char inputline[256];
    char *errmsg[GERRMSGSIZE];
    int i;

    Debug("Gplus_Error on %d...", sockfd);
    for (i=0; i < GERRMSGSIZE;i++) {
         char *cp;

         if (readline(sockfd, inputline, sizeof(inputline)) <=0)
              break;
         ZapCRLF(inputline);
         if (strcmp(inputline, ".")==0)
              break;

         cp = inputline;
         while ((cp = strchr(cp, '\t')) != NULL)
              *cp = ' ';

         errmsg[i] = strdup(inputline);
    }

    errmsg[i] = NULL;

    CURDialog(CursesScreen, Gtxt("Gopher Transmission Error",95), errmsg);
    for (i=0; ; i++) {
         if (errmsg[i] == NULL)
              break;
         else
              free(errmsg[i]);
    }

}



/*
* This fcn transfers a file from a gopher server into f.
*
*/

boolean
GStoFile(gs, f, view, twirlfn)
 GopherObj *gs;
 FILE *f;
 char *view;
 int (*twirlfn)();
{
    int numread, sockfd;
    char buf[1024];
    int bytemethod;
    int line = 0, i;

    /*** Check for gopher+ and multiple views ***/

    Debug("GStoFile, view %s",view);
    if ((sockfd = GSconnect(gs)) <0) {
         check_sock(sockfd, GSgetHost(gs), GSgetPort(gs));
         return(FALSE);
    }

    /** Send out the request **/
    GStransmit(gs, sockfd, NULL, "+", view);
    bytemethod = GSrecvHeader(gs, sockfd);

    if (bytemethod == 0) {
         Gplus_Error(sockfd);

         return(FALSE);
    }


    Draw_Status(Gtxt("Receiving File...",126));
    refresh();


    if (GSisText(gs, view)) {
         char cso_click = '\0';

         while (readline(sockfd, buf, sizeof(buf)) > 0) {
              ZapCRLF(buf);
              line ++;
              if (*buf == '.' && *(buf+1) == '\0')
                   break;



              if (GSgetType(gs) == A_CSO) {
                   if (*buf == '2')
                        break;

                   if ((*buf >= '3') && (*buf <= '9'))  {
                        fprintf(f, "%s\n", GSgetPath(gs));
                        fprintf(f, "%s\n", buf+4);
                        break;
                   }
                   if (*buf == '-') {


                        if (buf[1] >= '3' && buf[1] <= '9') {
                             fprintf(f, "%s\n", GSgetPath(gs));
                             fprintf(f, "%s\n", buf+5);
                        }
                        else {
                             char *colonpos = strchr(buf+5,':');
                             i = '\0';
                             if (colonpos != NULL && *(colonpos-1) != cso_click) {
                                  fprintf(f, "-------------------------------------------------------\n");
                                  cso_click = *(colonpos-1);
                             }
                             fputs((colonpos ? colonpos+1 : buf+6), f);
                             fputc('\n', f);
                        }
                   }
              }
              else {
                   fputs(buf, f);
                   /** Don't cut long lines... **/
                   if (strlen(buf) < sizeof(buf))
                        putc('\n', f);
              }
              if ((line % 25) == 0)
                   twirlfn();
         }
    }
    else {

#if defined(VMS) && defined(VMSRecords)
         while((numread = readrecvbuf(sockfd, buf, 512)) > 0) {
              if (numread < 512)
                  for (i = numread; i < 512; i++)  buf[i] = 0;
              if (fwrite(buf, 512, 1, f) == 0) {
#else
         while ((numread = readrecvbuf(sockfd, buf, sizeof buf)) > 0) {
              if (fwrite(buf, numread, 1, f) == 0) {
#endif
                   CursesErrorMsg(Gtxt("Problems Writing",123));
                   closenet(sockfd);
                   return(FALSE);
              }
              twirlfn();
         }
    }

    if (GSgetType(gs) == A_CSO)
         writestring(sockfd, "quit\r\n");

    closenet(sockfd);
    return(TRUE);
}


/*
* This function makes a nice vms filename.
*/

void
VMSfile(fname)
 char *fname;
{
#ifndef VMS
    return;
#else
    char    *cp, *dot, *end;
    int     j, k;

    /*** Trim off date-size suffix, if present ***/
    end = fname + strlen(fname);
    if ((*(end - 1) == ']') && ((cp = strrchr(fname, '[')) != NULL) &&
        (cp > fname) && *(--cp) == ' ')
         *cp = '\0';

    /*** Replace illegal or problem characters ***/
    dot = fname + strlen(fname);
    for (cp = fname; cp < dot; cp++) {

         /** Replace with underscores **/
         if (*cp == ' ' || *cp == '/' || *cp == ':' ||
             *cp == '[' || *cp == ']' || *cp == '{' ||
             *cp == '}')
              *cp = '_';

         /** Replace with dashes **/
         else if (*cp == '!' || *cp == '?' || *cp == '\'' ||
                  *cp == ',' || *cp == ':' || *cp == '\"' ||
                  *cp == '+' || *cp == '@' || *cp == '\\' ||
                  *cp == '(' || *cp == ')' || *cp == '=' ||
                  *cp == '<' || *cp == '>' || *cp == '#' ||
                  *cp == '%' || *cp == '*' || *cp == '`' ||
                  *cp == '~' || *cp == '^' || *cp == '|' ||
                  *cp == '/')
              *cp = '-';
    }

    /** Collapse any serial underscores **/
    cp = fname + 1;
    j = 0;
    while (cp < dot) {
         if (fname[j] == '_' && *cp == '_')
              cp++;
         else
              fname[++j] = *cp++;
    }
    fname[++j] = '\0';

    /** Collapse any serial dashes **/
    dot = fname + (strlen(fname));
    cp = fname + 1;
    j = 0;
    while (cp < dot) {
         if (fname[j] == '-' && *cp == '-')
              cp++;
         else
              fname[++j] = *cp++;
    }
    fname[++j] = '\0';

    /** Trim any trailing or leading **/
    /** underscrores or dashes       **/
    cp = fname + (strlen(fname)) - 1;
    while (*cp == '_' || *cp == '-')
         *cp-- = '\0';
    if (fname[0] == '_' || fname[0] == '-') {
         dot = fname + (strlen(fname));
         cp = fname;
         while ((*cp == '_' || *cp == '-') && cp < dot)
              cp++;
         j = 0;
         while (cp < dot)
              fname[j++] = *cp++;
         fname[j] = '\0';
    }

    /** Replace all but the last period with _'s, or second **/
    /** to last if last is followed by a terminal Z or z,   **/
    /** e.g., convert foo.tar.Z to                          **/
    /**               foo.tar_Z                             **/
    j = strlen(fname) - 1;
    if ((dot = strrchr(fname, '.')) != NULL) {
         if (((fname[j] == 'Z' || fname[j] == 'z') && fname[j-1] == '.') &&
             (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
              *dot = '_';
              dot = strrchr(fname, '.');
         }
         cp = fname;
         while ((cp = strchr(cp, '.')) != NULL && cp < dot)
              *cp = '_';

         /** But if the root is > 39 characters, move **/
         /** the period appropriately to the left     **/
         while (dot - fname > 39) {
              *dot = '\0';
              if ((cp = strrchr(fname, '_')) != NULL) {
                   *cp  = '.';
                   *dot = '_';
              }
              else if ((cp = strrchr(fname, '-')) != NULL) {
                   *cp  = '.';
                   *dot = '_';
              }
              else {
                   *dot = '_';
                   j = strlen(fname);
                   fname[j+1] = '\0';
                   while (j > 39)
                        fname[j--] = fname[j];
                   fname[j] = '.';
              }
              dot = strrchr(fname, '.');
         }

         /** Make sure the extension is < 40 characters **/
         if ((fname + strlen(fname) - dot) > 39)
              *(dot+40) = '\0';

         /** Trim trailing dashes or underscores **/
         j = strlen(fname) - 1;
         while (fname[j] == '_' || fname[j] == '-')
              fname[j--] = '\0';
    }
    else {
         /** No period, so put one on the end, or after   **/
         /** the 39th character, trimming trailing dashes **/
         /** or underscrores                              **/
         if (strlen(fname) > 39)
              fname[39] = '\0';
         j = strlen(fname) - 1;
         while ((fname[j] == '_') || (fname[j] == '-'))
              j--;
         fname[++j] = '.';
         fname[++j] = '\0';
    }

    /** Make sure the rest of the original string in nulled, **/
    /** to avoid problems with the CURwgetstr() line editor  **/
    cp = fname + strlen(fname);
    while (cp < end)
         *cp++ = '\0';


#endif
}



/*
* This function makes a nice UNIX filename
*/

void
UNIXfile(fname)
 char *fname;
{
    char *cp;

    for (cp = fname; *cp != '\0'; cp++) {
         switch (*cp) {
         case '\'':
         case '\"':
         case '/':
         case ' ':
         case '(':
         case ')':
         case '&':
              *cp = '-';
              break;

         case '[':
         case ']':
         case '{':
         case '}':
         case '|':
         case '!':
         case '?':
         case '*':
         case '`':
         case '\\':
         case '<':
         case '>':
              *cp = '_';
              break;


         }
    }

    if (strlen(fname) >50) {
         fname[50] = '\0';
    }

    /*** Don't allow '-' or '.' as first character of filename ***/
    if (fname[0] == '-' || fname[0] == '.')
         fname[0] = '_';

}


/*
* This procedure prompts the user for a name, checks for pipe characters
* and ~ characters in the filename and saves the file to disk.
*
* If infile is NULL then contact the server and get the file.
*
* If saveto is non-NULL then don't prompt the user for a filename
* to save into, just "do it".
*/

void
Save_file(gs, saveto, view)
 GopherObj *gs;
 char *saveto, *view;
{
    char    Title[128], Userfilename[128];
    char    *cp;
    char    buf[1024];
    boolean Openpipe = FALSE;
    boolean GS2FileSucceeded = TRUE;
    FILE    *f;

    switch (GSgetType(gs)) {
    case A_CSO:
    case A_DIRECTORY:
    case A_ERROR:
    case A_INDEX:
    case A_INFO:
    case A_TELNET:
    case A_TN3270:
         if (saveto == NULL) {
              CursesErrorMsg(Gtxt("Sorry, can't save that item to a file!",154));
              return;
         }

    }

    if (view == NULL)
         view = Choose_View(gs);

    if (view == NULL || view == "")
         return;

    /*** Get the Title ***/
    strcpy(Title, GSgetTitle(gs));

    /*** Construct a nice default filename ***/

    if (saveto == NULL) {
       /* It shouldnt ever come here if Secure, but I've noticed calls
       to this in the code */
       if (NoShellMode || SecureMode) {
               CursesErrorMsg(Gtxt("Sorry, you are not allowed to do this", 64));
               return;
       }
         /*** Let them use the Title ***/
         strcpy(Userfilename, Title);

#ifdef VMS
         VMSfile(Userfilename);
#else
         UNIXfile(Userfilename);
#endif

         if (CURGetOneOption(CursesScreen, Title, Gtxt("Save in file:",136),
                             Userfilename)<0)
              return;

         saveto = Userfilename;
    }

    if (*saveto == '\0')
         return;

    if (view == NULL)
         view = Choose_View(gs);

    if (view == NULL || view == "")
         return;

#ifndef VMS
    if (*saveto == '|') {
         /** Open a pipe! **/
         Openpipe = TRUE;
         saveto++;
    }

    if (*saveto == '~') {
         /*** Save in our home directory ***/
         if (*(saveto + 1) == '/' && (cp = getenv("HOME")) != NULL) {
              /*** Expand ~ to the home directory ***/
              strcpy(buf,cp);
              buf[strlen(cp)]='/';
              strcpy(buf+strlen(cp) +1, saveto+2);
              strcpy(saveto, buf);
         } else {
         /*** Save in someone else's home directory ***/
              struct passwd *pass;

              cp = strchr(saveto,'/');
              *cp = '\0';
              pass = getpwnam(saveto+1);
              if (pass != NULL) {
                   /** align in prep for the home dir **/
                   strcpy(buf,pass->pw_dir);
                   buf[strlen(pass->pw_dir)]='/';
                   strcpy(buf+strlen(pass->pw_dir)+1,cp+1);
                   strcpy(saveto,buf);

              } else {
                   char tmpstr[256];
                   sprintf(tmpstr, Gtxt("No such user '%s'",114), saveto+1);
                   CursesErrorMsg(tmpstr);
                   return;
              }
         }
    }
#endif

#if defined(VMS) && defined(VMSRecords)
    if (GSisText(gs, view))
         /*** Save ASCII files as VARIABLE length records for VMS ***/
         f = fopen_VAR(saveto, "w+");
    else
         /*** Save binary files as FIXED 512 records for VMS ***/
         f = fopen_FIX(saveto, "w+");
#else
    if (Openpipe)
         f = popen(saveto, "w+");
    else
         f = fopen(saveto, "w+");
#endif

    if (f == NULL) {
         char tempstr[128];

         sprintf(tempstr, Gtxt("Couldn't create '%s'",80), saveto);
         CursesErrorMsg(tempstr);
         return;
    }

    if (GSgetLocalFile(gs) != NULL && strcmp(view, GSgetLocalView(gs))==0) {
         /** We've already retrieved it, copy it over... **/
         int oldfd, cc;

         oldfd = open(GSgetLocalFile(gs), O_RDONLY, 0);
         if (oldfd <0) {
              CursesErrorMsg(Gtxt("Can't open old file..",68));
              if (Openpipe)
                   pclose(f);
              else
                   fclose(f);
              return;
         }

         while ( (cc=read(oldfd, buf, sizeof buf)) > 0) {
              if (fwrite(buf, cc, 1, f) <= 0)
                   CursesErrorMsg(Gtxt("Problems Writing",123));
              twirl();
         }
         if (Openpipe)
              pclose(f);
         else
              fclose(f);

        close(oldfd);
        return;

    } else {
         /** We don't have the file yet, let's get it... **/

         GS2FileSucceeded = GStoFile(gs, f, view, twirl); /* false if fails */

         if (Openpipe)
              pclose(f);
         else
              fclose(f);

         if (!GS2FileSucceeded)
              unlink(saveto);
    }
}


/*
* This procedure saves a directory (menu) list to a file.
* It prompts the user for a filename, checks for pipe characters
* and ~ characters in the filename and saves the directory name
* and the list of items in the file to disk.
*
* This is useful after searches for which the titles of hits
* themselves contain the information one was seeking.
*/

void
Save_list(gd)
 GopherDirObj *gd;
{
    char    Title[256], Userfilename[128];
    char    *cp, *saveto;
    int     numitems, j;
    char    buf[128];
    boolean Openpipe = FALSE;
    FILE    *f;

    *Userfilename = '\0';

    sprintf(Title,"%s", GDgetTitle(gd));
    if (CURGetOneOption(CursesScreen, Title,
                        Gtxt("Save list of items in file:",137),
                        Userfilename)<0)
         return;
    saveto = Userfilename;
    if (*saveto == '\0')
         return;

#ifndef VMS
    if (*saveto == '|') {
         /** Open a pipe! **/
         Openpipe = TRUE;
         saveto++;
    }

    if (*saveto == '~') {
         /*** Save in our home directory ***/
         if (*(saveto + 1) == '/' && (cp = getenv("HOME")) != NULL) {
              /*** Expand ~ to the home directory ***/
              strcpy(buf,cp);
              buf[strlen(cp)]='/';
              strcpy(buf+strlen(cp) +1, saveto+2);
              strcpy(saveto, buf);
         } else {
         /*** Save in someone else's home directory ***/
              struct passwd *pass;

              cp = strchr(saveto,'/');
              *cp = '\0';
              pass = getpwnam(saveto+1);
              if (pass != NULL) {
                   /** align in prep for the home dir **/
                   strcpy(buf,pass->pw_dir);
                   buf[strlen(pass->pw_dir)]='/';
                   strcpy(buf+strlen(pass->pw_dir)+1,cp+1);
                   strcpy(saveto,buf);

              } else {
                   char tmpstr[256];
                   sprintf(tmpstr, Gtxt("No such user '%s'",114), saveto+1);
                   CursesErrorMsg(tmpstr);
                   return;
              }
         }
    }
#endif

#if defined(VMS) && defined(VMSRecords)
    f = fopen_VAR(saveto, "w+");
#else
    if (Openpipe)
         f = popen(saveto, "w+");
    else
         f = fopen(saveto, "w+");
#endif

    if (f == NULL) {
         char tempstr[128];

         sprintf(tempstr, Gtxt("Couldn't create '%s'",80), saveto);
         /** Should give better error messages here **/
         CursesErrorMsg(tempstr);
         return;
    }

    /*** Write the Title, and put the Searchstring, ***/
    /*** if present, on the next line.              ***/
    sprintf(buf,"  %s\n\n", GDgetTitle(gd));
    if (Searchstring != NULL)
         if ((cp = strstr(buf, Searchstring)) != NULL)
              *(cp-1) = '\n';
    if (fwrite(buf, strlen(buf), 1, f) <= 0)
         CursesErrorMsg(Gtxt("Problems Writing",123));
    twirl();

    /*** Write the Items ***/
    numitems = GDgetNumitems(gd);
    for (j = 0; j < numitems; j++) {
         sprintf(buf, "%s\n", GSgetTitle(GDgetEntry(gd, j)));
         if (fwrite(buf, strlen(buf), 1, f) <= 0) {
              CursesErrorMsg(Gtxt("Problems Writing",123));
         }
         twirl();
    }

    if (Openpipe)
         pclose(f);
    else
         fclose(f);
}



/*
** This procedure exits out of the curses environment and
** displays the file indicated by pathname to the screen
** using a pager command of some sort
*/

void
GSdisplay(gs)
 GopherObj *gs;
{
    char command[MAXSTR];
    int ch;
    char *file = GSgetLocalFile(gs);
    char *view = GSgetLocalView(gs);

#ifdef VMS
    char *cp;
    int return_status;
    /** Keep DECC from complaining **/
    struct dsc$descriptor_s  command_desc;
    command_desc.dsc$b_class   = DSC$K_CLASS_S;
    command_desc.dsc$b_dtype   = DSC$K_DTYPE_T;
#endif


    /** Search for a helper application **/
    RCdisplayCommand(GlobalRC, view, file, command);

    Debug("Displaying view %s ", view);
    Debug("command %s ", command);
    Debug("file %s", file);
    Debug("/%s \n", GSgetLocalFile(gs));

    if (strlen(command) == 0) {
         if (SecureMode || NoShellMode)
              CursesErrorMsg(Gtxt("Sorry, no viewer for the file....",166));
         else
              Save_file(gs, NULL, view);

         return;
    }


#ifdef VMS
    if (strncasecmp("TPU",command,3) == 0) {

         CURexit(CursesScreen);

         /** Make sure we invoke TPU in /READ_ONLY/NOJOURNAL mode **/
         for (cp = command; *cp; cp++)
              *cp = _toupper(*cp);
         if (strstr(command, "/READ") == NULL)
              strcat(command,"/READ");
         if (strstr(command, "/NOJOU") == NULL)
              strcat(command,"/NOJOU");

         /** Load the descriptor and call TPU **/
         command_desc.dsc$w_length  = strlen(command);
         command_desc.dsc$a_pointer = command;
         return_status = TPU$TPU(&command_desc);
         if (!return_status&1) {
              CURenter(CursesScreen);
              CursesErrorMsg(Gtxt("Error invoking callable TPU as a pager!!!",85));
              return;
         }
         if (GSgetType(gs) != A_APP) {
              GSprompter(gs, view, command);
         }
    } else
#endif
    if (strncasecmp(command, "builtin-upload", 14) == 0) {
         BuiltinUploadfile();
         return;
    }
    else if (strncasecmp(command, "builtin-download", 14) == 0) {
         BuiltinDownload(getenv("HOME"));
         return;
    }
    else if (strncasecmp(command, "builtin", 7) == 0) {
         PagerBuiltin(CursesScreen, gs);
         return;
    }
    else {
         CURexit(CursesScreen);
         FIOsystem(command);
         if (GSgetType(gs) != A_APP) {
              GSprompter(gs, view, command);
         }

    }
}


/*
* Display the prompt at the end of viewing a document
*/

void
GSprompter(gs, view, command)
 GopherObj *gs;
 char *view, *command;
{
    int ch;

#ifdef VMS
    printf(Gtxt("\nPress %sRETURN%s to continue",184),
           CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen));

    if (!SecureMode && !NoShellMode) {
         if (COLS < 78)
              printf(",\n   ");
         else
              printf(", ");
         printf(Gtxt("%sm%s to mail, %sD%s to download, %ss%s to save, or %sp%s to print:",61),
                CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen),
                CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen),
                CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen),
                CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen));
    }
#ifndef NOMAIL
    else {
         printf(Gtxt(", %sm%s to mail, %sD%s to download:",62),
                CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen),
                CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen));
    }
#else
    else {
         printf(Gtxt(", %sD%s to download:",237),
                CURgetHighon(CursesScreen), CURgetHighoff(CursesScreen));
    }
#endif
#else /*** Unix: ****/
    printf(Gtxt("\nPress <RETURN> to continue",185));

    if (!SecureMode && !NoShellMode)
         printf(Gtxt(",\n   <m> to mail, <D> to download, <s> to save, or <p> to print:",64));
#ifndef NOMAIL
    else
         printf(Gtxt(", <m> to mail, <D> to download:",63));
#else
    else
         printf(Gtxt(", <D> to download:",238));
#endif
#endif
    fflush(stdin);
    fflush(stdout);
    noecho();
    cbreak();

    ch = 0;
    while (ch == 0) {
         ch=getchar();
#ifdef VMS
         if (HadVMSInt)
              break;
#endif

         if (SecureMode || NoShellMode) {
              switch (ch) {
              case '\n':
              case '\r':
              case KEY_ENTER:
              case ' ':
              case 'q' :
              case 'Q' :
#ifdef VMS
              case '\032': /* ^Z */
#endif
                   break;
#ifndef NOMAIL
              case 'm':
#ifdef VMS
                   (void) getchar();
#endif
                   CURenter(CursesScreen);
                   GSmail(gs);
                   break;
#endif
              case 'D':
#ifdef VMS
                   (void) getchar();
#endif
                   CURenter(CursesScreen);
                   Download_file(gs);
                   break;

              default:
                   puts(CURgetBell(CursesScreen));
                   fflush(stdout);
                   ch=0;
                   break;
              }
         }
         else {

              switch(ch) {
              case '\n':
              case '\r':
              case ' ' :
              case 'q' :
              case 'Q' :
#ifdef VMS
              case '\032': /* ^Z */
#endif
                   break;
              case 's':
#ifdef VMS
                   (void) getchar();
#endif
                   CURenter(CursesScreen);
                   Draw_Status(Gtxt("Saving File...",138));
                   refresh();

                   if (!(SecureMode || NoShellMode))
                        Save_file(gs,NULL, view);
                   break;

              case 'p':
#ifdef VMS
                   (void) getchar();
#endif
                   if (!RCprintCommand(GlobalRC, view, GSgetLocalFile(gs), command)) {
                        CursesErrorMsg(Gtxt("Sorry, cannot print this document",155));
                        return;
                   }
                   FIOsystem(command);
                   break;

              case 'm':
#ifdef VMS
                   (void) getchar();
#endif
                   CURenter(CursesScreen);
                   GSmail(gs);
                   break;

              case 'D':
#ifdef VMS
                   (void) getchar();
#endif
                   CURenter(CursesScreen);
                   Download_file(gs);
                   break;

              default:
                   Debugmsg("GSdisplayAfter - beep\r\n");
                   puts(CURgetBell(CursesScreen));
                   fflush(stdout);
                   ch=0;
                   break;
              }
         }
    }

    tputs(CURgetCLS(CursesScreen),1,outchar);
    fflush(stdout);
}



/*
* This displays a single line message using CUR routines
*/

void
CursesMsg(Message, title)
 char *Message;
 char *title;
{
    char *mess[2];

    mess[0] = Message;
    mess[1] = NULL;

    Debugmsg("CursesMsg - beep\r\n")
    CURBeep(CursesScreen);

    CURDialog(CursesScreen, title,mess);

    return;

}


/*
* This does the same as above, but puts a window title on the mess
*/

void
CursesErrorMsg(Message)
 char *Message;
{
    CursesMsg(Message, Gtxt("Gopher Error",90));
}



#ifdef CLIENT_LOGGER
#if defined(VMS) && defined(MULTINET)
#include "syslog.h"
#else
#include <syslog.h>
#endif
#endif

int
logrequest(msg, gs)
 char *msg;
 GopherObj *gs;
{
#ifdef CLIENT_LOGGER
    static boolean inited = FALSE;
    char *url = "";

    if (inited == FALSE) {
         openlog("gopher", (LOG_PID), LOG_DAEMON);
         setlogmask(LOG_UPTO(LOG_INFO));
         inited = TRUE;
    }
    if (gs != NULL)
         url = GSgetURL(gs);

    syslog(LOG_INFO, "%s %s", msg, url ? url : "");

#endif
    return(0);
}