/*
 * xmail - X window system interface to the mail program
 *
 * Copyright 1990,1991,1992 by National Semiconductor Corporation
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of National Semiconductor Corporation not
 * be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * NATIONAL SEMICONDUCTOR CORPORATION MAKES NO REPRESENTATIONS ABOUT THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS"
 * WITHOUT EXPRESS OR IMPLIED WARRANTY.  NATIONAL SEMICONDUCTOR CORPORATION
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO
 * EVENT SHALL NATIONAL SEMICONDUCTOR CORPORATION BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * The following software modules were created and are Copyrighted by
 * National Semiconductor Corporation:
 *
 *  1. initfoldir:
 *  2. str_compare:
 *  3. CheckInsert:
 *  4. EraseIt:
 *  5. DeleteChar:
 *  6. DeleteLine:
 *  7. DeleteWord:
 *  8. DoCmd:
 *  9. DoDone:
 * 10. DoNothing:
 * 11. DoReply:
 * 12. DoSave:
 * 13. DoSelected:
 * 14. Folder:
 * 15. Iconify:
 * 16. MyNotify:
 * 17. NextField:
 * 18. PrintMsg:
 * 19. Quit:
 * 20. SetAliases:
 * 21. SetNewness:
 * 22. SetFolders:
 * 23. SetMenu:
 * 24. SetPopup:
 * 25. SetSelect: and
 * 26. ShowHelp.
 *
 * Author:  Michael C. Wagnitz - National Semiconductor Corporation
 *
 */


#include "global.h"
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
#include <pwd.h>
#include "xmailregexp.h"

#ifdef	AIXV3
#include <sys/mode.h>
#endif

#ifndef	S_ISDIR
#define	S_ISDIR(m)	(((m)&S_IFMT) == S_IFDIR)
#endif

#ifdef USE_DIRENT
#include <dirent.h>
#else
#include <sys/dir.h>
#endif


void
initfoldir(void)
{
 String	cp;


 if (! foldir[0])
    if (cp = GetMailEnv("folder")) {
       if (strchr("/.", *cp))
          (void) strcpy(foldir, cp);
       else
          (void) sprintf(foldir, "%s/%s", HOME, cp);

       XtFree((String) cp);
       if (LASTCH(foldir) != '/')
          (void) strcat(foldir, "/");
      }
}


/*
** @(#)str_compare() - compare function for the qsort of folder names
*/
str_compare(char **s1, char **s2)
{
 return(strcmp(*s1, *s2));
}


/*
** @(#)CheckInsert() - prevents the user from munging up the File: prompt.
** If the current insertion point is less than the minimum StartPos, move
** the insertion point to the StartPos.
*/
/* ARGSUSED */
void
CheckInsert(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 if (XawTextGetInsertionPoint(w) < StartPos)
     XawTextSetInsertionPoint(w, (XawTextPosition) StartPos);
} /* CheckInsert */


/*
** @(#)EraseIt() - Delete the specified portion of text.
*/
void
EraseIt(Widget w, XawTextPosition i, XawTextPosition pos)
{
 XawTextBlock	textblock;

 textblock.firstPos = 0;
 textblock.length   = 0;
 textblock.ptr      = "";

 XawTextReplace(w, i, pos, &textblock);

 XawTextSetInsertionPoint(w, (XawTextPosition) i);
} /* EraseIt */


/*
** @(#)DeleteChar() - prevents the user from deleting past the File: prompt.
** If the current insertion point is greater than the minimum StartPos, then
** delete the previous character.
*/
/* ARGSUSED */
void
DeleteChar(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 XawTextPosition pos, i;

 pos = XawTextGetInsertionPoint(w);
 if (pos > StartPos) {
    i = pos - 1;
    EraseIt(w, i, pos);
   }
} /* DeleteChar */


/*
** @(#)DeleteLine() - Deletes the entire current line from the file window.
**                    Simulates the action of the KILL character (ctrl-U).
*/
/* ARGSUSED */
void
DeleteLine(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 XawTextPosition	pos, i;
 String			FBuf;
 Arg			args[1];


 pos = XawTextGetInsertionPoint(w);
 if (pos > StartPos) {
    XtSetArg(args[0], XtNstring, &FBuf);
    XtGetValues(w, args, ONE);

    for (i = pos; i > StartPos && FBuf[i - 1] != '\n'; i--);

    EraseIt(w, i, pos);
   }
} /* DeleteLine */


/*
** @(#)DeleteWord() - Erases the preceding word in the fileWindow buffer.
** Simulates the action of the WERASE character (ctrl-W).
*/
/* ARGSUSED */
void
DeleteWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 XawTextPosition	pos, i;
 String			FBuf;
 Arg			args[1];

 pos = XawTextGetInsertionPoint(w);
 if (pos > StartPos) {
    XtSetArg(args[0], XtNstring, &FBuf);
    XtGetValues(w, args, ONE);

    for (i = pos; i > StartPos && FBuf[i - 1] == ' '; i--);
    for (; i > StartPos && FBuf[i - 1] != ' '; i--);

    EraseIt(w, i, pos);
   }
} /* DeleteWord */


/* ARGSUSED */
/*
** @(#)DoCmd() - send (multi-word) command to mail
*/
void
DoCmd(Widget w, XEvent *event, String *params, Cardinal *num_params)
      		  			/* unused */
      		       			/* unused */
      		        
        	            
{
 int		i;
 char		buf[BUFSIZ];


 if (strcmp(params[0], "drop") == 0) {
    SetCursor(NORMAL);
    DropIt(w, *params, NULL);
   } else {
    buf[0] = '\0';
    for (i = 0; i < *num_params; i++) {
        if (i > 0)
           (void) strcat(buf, " ");
        (void) strcat(buf, params[i]);
       }
    DoIt(w, buf, NULL);			/* Let DoIt (CallbackProc) do work */
   }
} /* DoCmd */


/* ARGSUSED */
/*
** @(#)DoDone() - execute one of the selected message composition callbacks
*/
void
DoDone(Widget w, XEvent *event, String *params, Cardinal *num_params)
      		  			/* unused */
      		       			/* unused */
      		        
        	            		/* unused */
{
 char		buf[11];
 Widget		button;

 if (strcmp(*params, "ReEdit") == 0)
    ReEdit(w, *params, NULL);
 else if (strcmp(&params[0][1], "utograph") == 0) {
    (void) sprintf(buf, "*%s", *params);
    button = XtNameToWidget(XtParent(w), buf);
    Autograph(button, *params, NULL);
   } else {
    button = XtNameToWidget(XtParent(w), "*Deliver");
    Done(button, *params, NULL);
   }
} /* DoDone */


/* ARGSUSED */
/*
** @(#)DoNothing() - dummy action for unwanted button(s)
*/
void
DoNothing(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 return;
} /* DoNothing */


/*
** @(#)DoReply() - call Reply() CallbackProc from an ActionProc
*/
/* ARGSUSED */
void
DoReply(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 Reply(w, *params, NULL);
} /* DoReply */


/*
** @(#)DoSave() - call Save() CallbackProc from an ActionProc
*/
/* ARGSUSED */
void
DoSave(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 Save(w, *params, NULL);
} /* DoSave */


/* ARGSUSED */
/*
** @(#)DoSelected() - execute specified command using selected message number
*/
void
DoSelected(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 int		LastNumber(), num = 0;
 char		cmd[16];


 Waiting = TRUE;              /* so popup's EnterNotify won't erase the msg */
 if (! mailpid) Bell("No current mail connection\n");
 else if (num_params) {
         (void) strncpy(cmd, params[0], 16);
         num = SelectionNumber(cmd[0] == 'u');
         /*
         ** For 'next' and 'previous' commands, remember that
         ** mail's idea of the current message number may not
         ** match what the user has selected (without reading).
         */
         if (cmd[0] == 'n') {
            if (++num <= LastNumber(/* of the index */)) cmd[0] = 'p';
            else {			/* just say what would be said */
               Bell("At EOF\n");	/* if last msg num was current */
               return;
              }
           }
         if (cmd[0] == '-') {
            if (--num) cmd[0] = 'p';
            else {			/* just say what would be said if */
               Bell("Referencing before first message\n"); /* first was current */
               return;
              }
           }

         SetCursor(WATCH);
         if (num) (void) sprintf(Command, "%s %d\n", cmd, num);
         else (void) sprintf(Command, "%s\n", cmd);

         writeMail(Command);
        }
} /* DoSelected */


/*
** @(#)Folder() - change folders - must have specified folder name or error
*/
/* ARGSUSED */
void
Folder(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 XawTextPosition pos;
 String		c, p, buf;
 Widget		fw = XtNameToWidget(toplevel, "topBox.commandPanel.fileWindow");
 Arg		args[1];

#ifndef	NO_FOLDER_NAME
#define	NO_FOLDER_NAME	"Specify a folder name (in the [File: ] box) first\n"
#endif

 pos = TextGetLastPos(fw);
 if (pos - StartPos <= 0) {
    Bell(NO_FOLDER_NAME);
   } else {
    buf = NULL;
    XtSetArg(args[0], XtNstring, &buf);
    XtGetValues(fw, args, ONE);

    if (buf[pos] != '\0') buf[pos] = '\0';
    for (pos = StartPos; buf[pos] && strchr(" \t", buf[pos]);) pos++;

    if (! buf[pos]) {
       EraseIt(fw, (XawTextPosition) StartPos, pos);
       Bell(NO_FOLDER_NAME);
      } else {
       p = XtNewString(&buf[pos]);

       if (mailpid) {			/* check for commit of any changes */
          buf = NULL;
          XtSetArg(args[0], XtNlabel, &buf);
          XtGetValues(XtNameToWidget(toplevel,"topBox.titleBar.title"),args,1);

          c = strrchr(buf, 'l');  if (c) c -= 2;

          if (! c || strncmp(c, "deleted", 7) != 0 ||
              Confirm("COMMIT all changes to this folder")) {
             (void) sprintf(Command, "file %s\n", p);
             writeMail(Command);
             SetCursor(WATCH);		/* will be reset by parser routine */
             (void) strcpy(lastFolder, buf);	/* save titlebar for utimes */
            }
          XtFree((String) p);
         } else {
          /*
          ** We must first re-establish contact with the mail utility.
          ** This time, we indicate a specific mail folder to process.
          */
          XtFree((String) XMail.MFileName);
          XMail.MFileName = p;
          if (mailargc > 2 && strcmp(mailargv[mailargc - 2], "-f") == 0) {
             mailargv[mailargc - 1] = XMail.MFileName;
            } else {
             mailargv[mailargc++] = "-f";
             mailargv[mailargc++] = XMail.MFileName;
             mailargv[mailargc] = NULL;	/* list MUST be NULL terminated */
            }
          callMail(mailargv);
          (void) strcpy(Command, "Start");	/* Let em know we've re-started */
          In_Bogus_Mail_File = FALSE;	/* reset this so titleBar will chg */
          SetCursor(WATCH);		/* will be reset by parser routine */
         }
      } /* end - if a folder name was specified */
   }
} /* Folder */


/* ARGSUSED */
/*
** @(#)Iconify() - request window iconification
*/
void
Iconify(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 Display		*disp = XtDisplay(toplevel);


 if (! XIconifyWindow(disp, XtWindow(toplevel), DefaultScreen(disp)))
    XBell(disp, 33);
}


/* ARGSUSED */
/*
** @(#)MyNotify() - call widget callbacks with passed parameter
*/
void
MyNotify(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 XtCallCallbacks(w, XtNcallback, *params);
} /* MyNotify */


/*
** @(#)NextField() - warps pointer to next field in the Send command window.
** This allows carriage return to focus attention on the next data requirement.
*/
/* ARGSUSED */
void
NextField(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 String	name;
 Widget	shell;


 if (strcmp(w->core.name, "Cc") == 0)
    name = "Bcc"; else
 if (strcmp(w->core.name, "Bcc") == 0)
    name = "To"; else
 if (strcmp(w->core.name, "To") == 0)
    name = "Subject"; else
    name = "Cc";

 if ((shell = XtNameToWidget(XtParent(w), name)) != NULL)
    XWarpPointer(XtDisplay(toplevel), None, XtWindow(shell), 0,0,0,0, 15, 10);

} /* NextField */


/*
** @(#)PrintMsg() - sends the selected mail message to the system printer
*/
/* ARGSUSED */
void
PrintMsg(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 int		k, num;
 String		cp, sp;
 char		buf[BUFSIZ];


 if (! mailpid) Bell("No current mail connection\n");
 else {
    num = SelectionNumber(FALSE);	/* no current message returns zero */
    if (! num) Bell("No messages to print.\n");
    else {
       SetCursor(WATCH);		/* will be reset by parser routine */
       cp = QueryMail("|");
       if (strncmp(cp, "Unknown", 7) == 0) {	/* Then this is NOT Sun mail */
          (void) sprintf(buf, "copy %d %s", num, tmpName);
          cp = QueryMail(buf);			/* write message to a file */
          sp = strrchr(cp, '/');		/* get the size of the file */
          if (sp)
             (void) sscanf(sp, "/%d", &k);
          else {
             struct stat	sb;

             if (stat(tmpName, &sb) == 0)
                k = sb.st_size;
             else k = 0;
            }
          if (! (cp = GetMailEnv("printmail")))
             (void) sprintf(buf, "lpr -p %s && rm -f %s &", tmpName, tmpName);
          else {
             (void) sprintf(buf, "%s %s && rm -f %s &", cp, tmpName, tmpName);
             XtFree((String) cp);
            }

          (void) system(buf);
          (void) sprintf(buf, "Message %d sent to printer -- %d bytes\n",num,k);
          Bell(buf);				/* Notify user of action */
          Command[0] = '\0';			/* Toss any previous value */
          SetCursor(NORMAL);
         } else {
          if (! (cp = GetMailEnv("printmail")))
             (void) sprintf(Command, "| %d \"lpr -p\"\n", num);
          else {
             (void) sprintf(Command, "| %d \"%s\"\n", num, cp);
             XtFree((String) cp);
            }

          writeMail(Command);
         }
      }
   }
} /* PrintMsg */


/*
** @(#)Quit() - call DoQuit() CallbackProc from the Quit ActionProc
*/
/* ARGSUSED */
void
Quit(Widget w, XEvent *event, String *params, Cardinal *num_params)
      		  
      		       
      		        
        	            	/* unused */
{
 if (event->type == ClientMessage &&
    event->xclient.data.l[0] != wmDeleteWindow) {
    XBell (XtDisplay(w), 0);
    return;
   }

 DoQuit(w, *params, NULL);
} /* Quit */


/*
** @(#)SetAliases() - create a menu list of alias names
*/
/* ARGSUSED */
void
SetAliases(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 int		columns, j, k, n;
 Cardinal	numChildren = 0;
 Widget		bw, lw, popup, above, left, parent = XtParent(w);
 Display	*dpy = XtDisplay(parent);
 Window		rootW, parentW, *cL, *childList = 0;
 Arg		args[6];

 static String l_Trans = "<Btn3Up>:	MenuPopdown(aliasList)";
 static String fl_trans = "<Enter>: set()\n<Leave>: unset()\n<Btn3Up>: notify()";


 popup = XtNameToWidget(parent, "aliasList");

 if (! popup) {
    (void) alias(NULL);			/* ensure our aliases list is set */
/*
** determine proper label width by finding longest name length
*/
    j = k = columns = 0;
    for (n = 0; aliases[n]; n++)
        if ((columns = strlen(aliases[n]->name)) > k) {
           k = columns;
           j = n;
          }

    if (k > 0) {				/* some alias names exist */
       /*
       ** Make equal width command buttons which contain the alias names
       */
       XtSetArg(args[0], XtNtranslations, XtParseTranslationTable(l_Trans));
       popup = XtCreatePopupShell("aliasList", overrideShellWidgetClass, parent, args, 1);

       XtSetArg(args[0], XtNdefaultDistance, (XtArgVal) 1);
       lw = XtCreateManagedWidget("table", formWidgetClass, popup, args, 1);

       XtSetArg(args[0], XtNwidth, XTextWidth(XMail.buttonFont, aliases[j]->name, k) + 14);
       XtSetArg(args[1], XtNfont, XMail.buttonFont);
       XtSetArg(args[2], XtNtranslations, XtParseTranslationTable(fl_trans));

       columns = (int)XMail.shellWidth / args[0].value;	/* no wider than this */
       while (columns * columns > n) columns--;	/* try to shape into a square */

       k = 0;
       above = left = NULL;
       for (n = 0; aliases[n]; n++) {
           XtSetArg(args[3], XtNlabel, aliases[n]->name);
           XtSetArg(args[4], XtNfromHoriz, left);			j = 5;
           if (! left)			/* also anchor to the button above */
              XtSetArg(args[j], XtNfromVert, above);			j++;

           bw = XtCreateManagedWidget("entry", commandWidgetClass, lw, args, j);
           AddInfoHandler(bw, "Copy this alias to the current header field");

           if (! left) above = bw;
           left = bw;

           if (++k % columns == 0) {	/* make a box # columns wide */
              k = 0;
              left = NULL;		/* start next row at left edge of box */
             }
          } /* end - for all the aliases in the list */
      }	/* end - if some alias names exist */
   } /* end - if popup did not previously exist */
/*
** If the popup menu of aliases does exist, add a window-specific callback,
** set the menu's x,y coordinates and then pop it up
*/
 if (! popup)
    XBell(XtDisplay(toplevel), 33);
 else {
    if (! XtIsRealized(popup))
       XtRealizeWidget(popup);

    lw = XtNameToWidget(popup, "*table");
    if (XQueryTree(dpy, XtWindow(lw), &rootW, &parentW, &childList, &numChildren)) {
       for (cL = childList; numChildren--;) {
            bw = XtWindowToWidget(dpy, *cL++);

           if (XtHasCallbacks(bw, XtNcallback) == XtCallbackHasSome)
              XtRemoveAllCallbacks(bw, XtNcallback);

           XtAddCallback(bw, XtNcallback, (XtCallbackProc) GetAliasName, w);
          }

       XFree((Window *) childList);
      }

    SetXY(popup, w, XMail.menuX, XMail.buttonHeight / 2);

    XtPopupSpringLoaded(popup);
   }
} /* SetAliases */


/*
** @(#)SetNewness() - hatch label background of folder if recently modified
*/
void
SetNewness(Widget parent, char *path)
{
 Cardinal	numChildren = 0;
 char		*folder_name, buf[BUFSIZ];
 Display	*dpy = XtDisplay(parent);
 Window		rootW, parentW, *cL, *childList = 0;
 Widget		w;
 Arg		args[1];
 struct	stat	stb;


 if (XQueryTree(dpy, XtWindow(parent), &rootW, &parentW, &childList, &numChildren)) {
    for (cL = childList; numChildren--;) {
        w = XtWindowToWidget(dpy, *cL++);

        XtSetArg(args[0], XtNlabel, &folder_name);
        XtGetValues(w, args, 1);

        if (LASTCH(folder_name) != '/') {	/* If its not a directory... */
           (void) strcpy(buf, path);		/* build complete foldername */
           if (folder_name[0] == '+')		/* must be a toplevel folder */
              (void) strcat(buf, folder_name + 1);	/* (no plus sign) */
           else {
              (void) strcat(buf, "/");
              (void) strcat(buf, folder_name);
             }
           /*
           ** Using access versus modified timestamps is inherently unreliable
           ** in determining the newness of messages in a folder, but the
           ** alternative of examining the file for the last Status or From is
           ** horribly slow. 
           */
           if (stat(buf, &stb) == 0) {
              if (stb.st_size && stb.st_atime < stb.st_mtime)
                 XtSetArg(args[0], XtNbackgroundPixmap, hatch);
              else
                 XtSetArg(args[0], XtNbackgroundPixmap, XtUnspecifiedPixmap);
              XtSetValues(w, args, 1);
             } /* end - if readable */
          } /* end - if not a directory */
       } /* end - for each child in the list */

    XFree((Window *) childList);
   } /* end - if some children exist */
} /* end - SetNewness */


/*
** @(#)SetFolders() - create a menu list of folder names
*/
/* ARGSUSED */
void
SetFolders(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 int		m, n, total, no_List = TRUE;
 int		isAdir, freeable = FALSE;	/* If we built list then true */
 String		*ftbl = NULL;
 String		cp, List = NULL;
 char		trans[BUFSIZ], tmp[BUFSIZ], buf[BUFSIZ];
 Widget		lw, above, this_one, to_left, popup;
 Arg		args[8];
 DIR		*dirp = NULL;
 struct	stat	st_buf;


#ifdef USE_DIRENT
 struct dirent	*dp;
#else
 struct direct	*dp;
#endif

 static String dir_Trans = "<Btn1Down>: SetDirectory(%s,%s,%s)";
 static String  l_Trans = "<Btn3Up>: MenuPopdown(popupList)";
 static String fl_trans = "<Enter>: set()\n<Leave>: unset()\n\
        		   <Btn3Up>: notify() MenuPopdown(popupList)";

 static XtCallbackRec fl_callbacks[] = {
        { (XtCallbackProc) GetFolderName, NULL },
        { NULL,          NULL }
       };


 popup = XtNameToWidget(w, "popupList");

 if (! popup) {
    XtSetSensitive(w, FALSE);
    XFlush(XtDisplay(toplevel));
    initfoldir();

    if (foldir[0]) {				/* if folder variable exists */
       if (mailpid) {				/* try to get list from mail */
          List = QueryMail("folders");		/* returns a string of names */
         } else {
          if (dirp = opendir((char *)foldir)) {	/* and folder is readable... */
             n = BUFSIZ;			/* start with a BUFSIZ block */
             List = (String) XtMalloc((unsigned) n);
             List[0] = '\0';
             freeable = TRUE;
             for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
                 if (dp->d_name[0] != '.') {	/* skip parent and dot files */
                    if ((int)strlen(List) + (int)strlen(dp->d_name) + 2 > n) {
                       n += BUFSIZ;
                       List = (String) XtRealloc(List, n);
                      }
                    if (List[0]) (void) strcat(List, " ");
                    (void) strcat(List, dp->d_name);
                   }
             (void) closedir(dirp);
             List = (String) XtRealloc(List, strlen(List) + 1);
            } /* end - if folder directory is readable */
         } /* end - if mail process is running */
      } /* end - if a folder value exists */

    if (List) {				/* could be null if no current mail */
       no_List = FALSE;
       if (XM_O_BELL == match(output_pattern, List)) {
          if ((int)strlen(List) + 1 < BUFSIZ)
             (void) sprintf(tmp, "%s\n", List);
          else
             (void) sprintf(tmp, "Cannot access folders\n");
          Bell(tmp);
          if (freeable) {
             freeable = FALSE;
             XtFree((String)List);
             List = NULL;
            } else List[0] = '\0';
          no_List = TRUE;
         }
      }

    if (List && List[0]) {			/* if some folder names exist */
       no_List = FALSE;
       for (total = 1, cp = List; *cp; cp++)	/* count number of names */
           if (strchr(" \t\n", *cp)) {
              total++;			/* (increase the folder name count) */
              while (*(cp+1) && strchr(" \t\n", *(cp+1))) cp++;
             }
       ftbl = (String *) XtMalloc((unsigned) (total + 1) * sizeof(String));
       m = n = 0;
       for (cp = List; *cp; cp++) {	/* stuff word pointers into a table */
           while (*(cp+n) && ! strchr(" \t\n", *(cp+n))) n++;
           if (*(cp+n)) *(cp+n) = '\0';	/* mark off the end of this name */
           ftbl[m++] = cp;		/* save this pointer in our table */
           cp += n;			/* find start of next folder name */
           n = 0;
           while (*(cp+1) && strchr(" \t\n", *(cp+1))) cp++;
          }
       ftbl[m] = NULL;			/* NULL terminate our table */
       /*
       ** (quick) sort our table into ascending alphabetical order
       */
       if (total > 1)
          qsort((String) ftbl, total, sizeof(String), str_compare);
       /*
       ** Next, determine label width of longest word in our table
       */
       for (m = n = 0; n < total && ftbl[n]; n++) {
           if ((int)strlen(ftbl[n]) > (int)strlen(ftbl[m])) m = n;
          }
       /*
       ** Now, make equal width command buttons which contain the folder names
       */
       XtSetArg(args[0], XtNtranslations, XtParseTranslationTable(l_Trans));
       popup = XtCreatePopupShell("popupList",overrideShellWidgetClass,w,args,1);

       XtSetArg(args[0], XtNdefaultDistance, 1);
       lw = XtCreateManagedWidget("list", formWidgetClass, popup, args, 1);

       n = XTextWidth(XMail.buttonFont, ftbl[m], strlen(ftbl[m]));

       XtSetArg(args[0], XtNwidth, n + 20);
       XtSetArg(args[1], XtNfont, XMail.buttonFont);
       XtSetArg(args[2], XtNtranslations, XtParseTranslationTable(fl_trans));
       XtSetArg(args[3], XtNcallback, fl_callbacks);

       above = this_one = to_left = NULL;
       tmp[0] = '\0';
       (void) strcpy(buf, foldir);
       n = strlen(buf) - 1;
       for (m = 0; m < total; m++) {
           buf[n] = '\0';		/* restore name only value for tests */
           /*
           ** Watch out for special case of only one folder which isn't
           ** really a directory... (i.e. foldir and folder are same name)
           */
           if (0 != strcmp(ftbl[m], buf))
              (void) strcpy(tmp, "+");	/* true folders start with a 'plus' */
           (void) strcat(tmp, ftbl[m]);
           /*
           ** mark any directory 'folders' with a trailing slash '/'.
           */
           isAdir = FALSE;			/* assume its a regular file */
           (void) strcat(buf, "/");
           (void) strcat(buf, ftbl[m]);
           if (access(buf, R_OK) == 0 &&	/* IF exists and is readable */
               access(buf, X_OK) == 0 &&	/* and we can execute/search */
               stat(buf, &st_buf) == 0)
	      if (S_ISDIR(st_buf.st_mode)) { 	/* and has directory bit set */
                 isAdir = TRUE;
	         if (LASTCH(tmp) != '/')
                 (void) strcat(tmp, "/");	/* If a directory mark it so */
                } else isAdir = FALSE;

           XtSetArg(args[4], XtNlabel, tmp);
           XtSetArg(args[5], XtNfromHoriz, to_left);
           if (! to_left) XtSetArg(args[6], XtNfromVert, above);

           this_one = XtCreateManagedWidget("menubutton", commandWidgetClass, lw, args, 7);

           if (to_left == NULL) above = this_one;
           to_left = this_one;

           if ((m+1) % 5 == 0)
              to_left = NULL;
/*
** If this 'folder' is a directory, add a button to popup a menu of filenames.
*/
           if (isAdir) {
              (void) sprintf(trans, dir_Trans, &tmp[1], buf, "0");
              XtOverrideTranslations(this_one, XtParseTranslationTable(trans));
              AddInfoHandler(this_one, Folder_Info[2]);
             } else
              AddInfoHandler(this_one, Folder_Info[1]);
          } /* end - for each folder name in the table of folders */
       XtFree((String) ftbl);
      }	/* end - if some folder names exist */
    XtSetSensitive(w, TRUE);

    if (freeable)
       XtFree((String) List);
   } /* end - if no popup previously existed */
/*
** If no folders to display, tell the user why
*/
 if (! popup) {
    if (! foldir[0])
       Bell("No value set for \"folder\"\n");
    else if (dirp || no_List == FALSE) {
            if (! mailpid || no_List == FALSE)
               Bell("No mail folders exist\n");
              }
   } else {
    /*
    ** If folders menu exists, pop it up, after setting x,y coordinates
    */
    if (! XtIsRealized(popup))
       XtRealizeWidget(popup);
    /*
    ** Mark folders with new messages by changing the background Pixmap
    */
    SetNewness(XtNameToWidget(popup, "*list"), foldir);
    /*
    ** If folder list is small enough, anchor it to
    ** the folder button instead of the commandPanel
    */
    if ((int)popup->core.width + (3 * ((int)XMail.buttonWidth + 12)) <= (int)XMail.shellWidth)
       SetXY(popup, w, XMail.menuX, XMail.buttonHeight / 2);
    else
       SetXY(popup, XtNameToWidget(toplevel, "topBox.commandPanel"),
             XMail.menuX, XMail.commandHeight / 2);

    XtPopupSpringLoaded(popup);
   }
} /* SetFolders */



/* 
** @(#)SetMenu() - create a menu for toggling selected mail options
*/
/* ARGSUSED */
void
SetMenu(Widget parent, XEvent *event, String *params, Cardinal *num_params)
      		       
      		       		/* unused */
      		        	/* unused */
        	            	/* unused */
{
 int		indx;
 String		c, info;
 char		label[BUFSIZ], name[BUFSIZ];		
 Widget		menu, layout, previous, next;
 Arg	 	args[6];

 static String m_Trans = "<Btn3Up>: MenuPopdown(set_menu)";
 static String b_Trans = "<Enter>: set()\n<Leave>: reset()\n<Btn3Up>: notify() unset()";

 static String list[] = { "alwaysignore",
                           "editheaders",
                             "autoprint",
                                  "hold",
                                "expert",
                           NULL };

 static String set_info[] = {
 "Skip 'ignore'd header fields everywhere, not just during a print or read",
 "Enable editing of selected headers within the message body when sending",
 "Enable automatic printing of messages after delete and undelete commands",
 "Preserve messages in the system mailbox after they have been read",
 "Don't ask for confirmation when commiting changes or aborting a new letter",
 NULL
 };

 static String unset_info[] = {
 "Skip 'ignore'd header fields only when doing a print or read command",
 "Disallow editing of selected headers within the message body when sending",
 "Disable automatic printing of messages after delete and undelete commands",
 "Move system mailbox messages to the mbox save file after you read them",
 "Ask for confirmation before commiting any changes or aborting a new letter",
 NULL
 };


 menu = XtNameToWidget(parent, "set_menu");

 if (! menu || menu->core.being_destroyed) {
    XtSetArg(args[0], XtNtranslations, XtParseTranslationTable(m_Trans));
    menu = XtCreatePopupShell("set_menu",overrideShellWidgetClass,parent,args,1);

    XtSetArg(args[0], XtNdefaultDistance, (XtArgVal) 1);
    layout = XtCreateManagedWidget("menu", formWidgetClass, menu, args, 1);
/*
** create the menu buttons
*/
    previous = NULL;
    XtSetArg(args[0], XtNwidth, figureWidth(XMail.buttonFont) * 18 + 12);
    XtSetArg(args[1], XtNfont, XMail.buttonFont);
    XtSetArg(args[2], XtNjustify, XtJustifyLeft);
    XtSetArg(args[3], XtNtranslations, XtParseTranslationTable(b_Trans));
    for (indx = 0; list[indx] != NULL; indx++) {
        info = set_info[indx];
        (void) strcpy(label, "set ");
        if (strcmp(list[indx], "expert") == 0) {
           if (XMail.expert) {
              info = unset_info[indx];
              (void) strcat(label, "no");
             }
          } else if ((c = GetMailEnv(list[indx])) != NULL) {
              info = unset_info[indx];
              (void) strcat(label, "no");
              XtFree((String) c);
          }
        (void) strcat(label, list[indx]);	/* set window name by label */
        (void) strcpy(name, &label[4]);
        XtSetArg(args[4], XtNlabel, label);
        XtSetArg(args[5], XtNfromVert, previous);
        next = XtCreateManagedWidget(name, commandWidgetClass, layout, args, 6);
        XtAddCallback(next, XtNcallback, (XtCallbackProc) DoSet, NULL);
        AddInfoHandler(next, info);
        previous = next;
       }
   } /* end - menu creation */
 SetXY(menu, parent, XMail.menuX, XMail.buttonHeight / 2);
} /* SetMenu */


/*
** @(#)SetPopup() - place named popup at menuX, menuY relative to Widget w.
*/
/* ARGSUSED */
void
SetPopup(Widget w, XEvent *event, String *params, Cardinal *num_params)
         
               /* unused */
               
                     
{
 String		p;
 Widget		shell;


 if (*num_params == 0)
    XtError("xmail had no name parameter passed to SetPopup()");

 p = params[0];

 if ((shell = XtNameToWidget(w, p)) == NULL)
    XtError("xmail shell name passed to SetPopup() not found in list");

 SetXY(shell, w, XMail.menuX, XMail.menuY);
} /* SetPopup */


/* ARGSUSED */
/*
** @(#)SetSelect() - flag the index number of the selected message
*/
void
SetSelect(Widget w, XEvent *event, String *params, Cardinal *num_params)
         		/* unused */
              		/* unused */
               		/* unused */
                     	/* unused */
{
 markIndex(">");
} /* SetSelect */


/*
** @(#)ShowHelp() - set named string source as text for and popup help window.
*/
/* ARGSUSED */
void
ShowHelp(Widget w, XEvent *event, String *params, Cardinal *num_params)
      		  
      		       
      		        			/* unused */
        	            			/* unused */
{
 Widget		tbox = XtNameToWidget(toplevel, "topBox");
 Widget		help = XtNameToWidget(tbox, "help");
 String		name;
 helpText	*help_text;


 name = w->core.name;
 if (strcmp(name, "text") == 0 && event->type == KeyPress)
    name = "text2";

 for (help_text = HelpList; help_text; help_text = help_text->next)
     if (strcmp(name, help_text->name) == 0) {
        XawTextSetSource(XtNameToWidget(help, "helpWindow"),
                        (Widget) help_text->data, (XawTextPosition) 0);

        SetXY(help, XtNameToWidget(tbox, "textWindow.text"), XMail.helpX, XMail.helpY);

        XtPopup(help, XtGrabNone);
        break;
       }
} /* ShowHelp */
