/*
 * @(#)Mailwatch.c - MODIFIED for use as an active icon in the xmail program.
 *
 * Author:  Dan Heller <island!argv@sun.com>
 * This code was stolen from Mailbox.c --the widget which supports "xbiff"
 * written by Jim Fulton which was apparently stolen from Clock.c, the widget
 * which supports "xclock."  Note, you are witnessing the big bang theory of
 * software development (everything is a subclass of universeWidgetClass).
 *
 * Major changes:
 * XtRemoveTimeOut() is called before calling XtAddTimeOut().  The original
 * xbiff would eventually timeout all the time rather than every 30 seconds
 * because the old timer was never removed.
 *
 * User can specify any icon he chooses for either the up flag or the down
 * flag.  Icons don't need to be the same size (defaults to flagup/flagdown).
 *
 * When new mail comes in, a user supplied callback function is invoked.
 *
 * The mailbox flag goes up when there is new mail _and_ the user hasn't
 * read it yet.  As soon as the user updates the access time on the mailbox,
 * the flag goes down.  This removes the incredibly annoying habit xbiff
 * had where you read some mail but not delete it from the mailbox and the
 * flag would remain up.
 *
 * Destroy() will now destroy the flagup and flagdown pixmaps.
 *
** July 1991 - Michael C. Wagnitz - National Semiconductor Corporation
** The following modifications were made for use in xmail.
**
** Added support for colored icons via the XPixMap format library libXpm.a
**
** November 1990 - Michael C. Wagnitz - National Semiconductor Corporation
** The following modifications were made for use in xmail.
**
** Added options to display username or host name in icon window.
**
** November 1989 - Michael C. Wagnitz - National Semiconductor Corporation
**
** Removed button handler, custom cursor, and 'from()' reader functions.
** Added reset_mailbox() function, my own icons (56x56 bits), and also
** added timer initialization to Initialize() routine (we might not ever be
** Realized, if the user never iconifies my parent).  This also fixes a Sun4
** bug for trying to remove an initial interval_id with a garbage address.
** Also changed check_mailbox() to test access time vs. modified and zero size
** of file, rather than trying to track our last access or increase in size.
**
** Copyright 1990,1991 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. reset_mailbox,
** 2. set_host,
** 3. set_user,
** 4. set_none,
** 5. GetHostName, and
** 6. XMyLocateXPixmapFile.
**
** Author:  Michael C. Wagnitz - National Semiconductor Corporation
*/


#include <X11/Xos.h>
#include <X11/IntrinsicP.h>		/* for toolkit stuff */
#include <X11/cursorfont.h>		/* for cursor constants */
#include <X11/StringDefs.h>		/* for useful atom names */
#include <X11/Xaw/XawInit.h>
#include "MailwatchP.h"			/* for implementation mailbox stuff */
#include <stdio.h>			/* for printing error messages */
#include <sys/stat.h>			/* for stat() */
#include <sys/param.h>			/* for MAXHOSTNAMELEN */
#include <pwd.h>			/* for getting username */

#ifndef	XPM
#include <X11/Xmu/Converters.h>		/* for XmuCvtStringToBitmap */
#include "icon.mail"			/* for flag up (mail present) bits */
#include "icon.nomail"			/* for flag down (mail not here) */
#else
#include <X11/xpm.h>			/* for XPixMap colored icons */
#include <X11/Xmu/CvtCache.h>
#include "mail.xpm"			/* for flag up (mail present) bits */
#include "nomail.xpm"			/* for flag down (mail not here) */

#define	mail_width	64
#define	mail_height	64
#define	nomail_width	64
#define	nomail_height	64
#endif

#ifndef	MAXHOSTNAMELEN
#define	MAXHOSTNAMELEN	64
#endif

#define between(x, i, y)   ((i) < (x) ? (x) : (i) > (y) ? (y) : (i))

#define REMOVE_TIMEOUT(id) if (!id) ; else XtRemoveTimeOut(id)

extern XtActionProc	Quit();			/* termination procedure */

static struct stat	stbuf;
static Boolean		SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList, Cardinal *);
static Pixmap		make_pixmap(Display *dpy, MailwatchWidget w, Pixmap bitmap, 
		                    int depth, int *widthp, int *heightp);
static GC		GetNormalGC(MailwatchWidget w), GetInvertGC(MailwatchWidget w);
static void		GetMailFile(MailwatchWidget w), 
			GetUserName(MailwatchWidget w), GetHostName(MailwatchWidget w),
			CloseDown(MailwatchWidget w, int status), 
			check_mailbox(MailwatchWidget w, Boolean force_redraw), 
			redraw_mailbox(MailwatchWidget m),
			ClassInitialize(void), 
			Initialize(Widget request, Widget new, ArgList, Cardinal *), 
			Destroy(Widget gw),
			Realize(Widget gw, XtValueMask *valuemaskp, XSetWindowAttributes *attr), 
			Redisplay(Widget gw, XEvent *, Region), 
			clock_tic(XtPointer client_data, XtIntervalId *id), set_title(String str);
extern void		reset_mailbox(Widget gw, int down), 
			set_host(Widget, XEvent *, String *, Cardinal *), 
			set_user(Widget, XEvent *, String *, Cardinal *), 
			set_none(Widget, XEvent *, String *, Cardinal *);
extern Widget		toplevel;

extern	int		RootWidth;
extern	int		RootHeight;

#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))

static char defaultTranslations[] =
    "<Key>h:		set-host()	\n\
     <Key>q:		Quit(q)		\n\
     <Key>u:		set-user()	\n\
     <Key>space:	set-none()";

static XtActionsRec actionsList[] = {
    { "set-host",	set_host	},
    { "set-user",	set_user	},
    { "set-none",	set_none	},
};

/* Initialization of defaults */
#define offset(field) XtOffset(MailwatchWidget,mailbox.field)
#define goffset(field) XtOffset(Widget,core.field)

static XtResource resources[] = {
    {XtNupdate, XtCInterval, XtRInt, sizeof(int),
	offset(update), XtRImmediate, (XtPointer) 30 },
    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
	offset(foreground_pixel), XtRString, XtDefaultForeground },
    {XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
	goffset(background_pixel), XtRString, XtDefaultBackground },
    {XtNreverseVideo, XtCBoolean, XtRBoolean, sizeof(Boolean),
	offset(reverseVideo), XtRImmediate, (XtPointer) False },
    {XtNbell, XtCBoolean, XtRBoolean, sizeof(Boolean),
	offset(bell), XtRImmediate, (XtPointer) True },
    {XtNfile, XtCFile, XtRString, sizeof(String),
	offset(filename), XtRString, NULL},
#ifndef	XPM
    {XtNmailFlag, XtCMailFlag, XtRBitmap, sizeof(Pixmap),
	offset(mail.bitmap), XtRString, NULL},
    {XtNnoMailFlag, XtCNoMailFlag, XtRBitmap, sizeof(Pixmap),
	offset(nomail.bitmap), XtRString, NULL},
#else
    {XtNmailFlag, XtCMailFlag, XtRString, sizeof(String),
	offset(mail.bitmap), XtRString, NULL},
    {XtNnoMailFlag, XtCNoMailFlag, XtRString, sizeof(String),
  	offset(nomail.bitmap), XtRString, NULL},
#endif
    {XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
	offset(callback), XtRCallback, NULL},
    {"useName", XtCBoolean, XtRBoolean, sizeof(Boolean),
	offset(useName), XtRString, "FALSE" },
    {"useHost", XtCBoolean, XtRBoolean, sizeof(Boolean),
	offset(useHost), XtRString, "FALSE" },
    { XtNfont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
	offset(font), XtRString, XtDefaultFont},
};

#undef offset
#undef goffset

MailwatchClassRec mailwatchClassRec = {
    {	/* core fields */
	/* superclass		*/ &widgetClassRec,
	/* class_name		*/ "Mailwatch",
	/* widget_size		*/ sizeof(MailwatchRec),
	/* class_initialize	*/ ClassInitialize,
	/* class_part_initialize*/ NULL,
	/* class_inited 	*/ FALSE,
	/* initialize		*/ Initialize,
	/* initialize_hook	*/ NULL,
	/* realize		*/ Realize,
	/* actions		*/ actionsList,
	/* num_actions		*/ XtNumber(actionsList),
	/* resources		*/ resources,
	/* resource_count	*/ XtNumber(resources),
	/* xrm_class		*/ 0,
	/* compress_motion	*/ TRUE,
	/* compress_exposure	*/ TRUE,
	/* compress_enterleave	*/ TRUE,
	/* visible_interest	*/ FALSE,
	/* destroy		*/ Destroy,
	/* resize		*/ NULL,
	/* expose		*/ Redisplay,
	/* set_values		*/ SetValues,
	/* set_values_hook	*/ NULL,
	/* set_values_almost	*/ XtInheritSetValuesAlmost,
	/* get_values_hook	*/ NULL,
	/* accept_focus		*/ NULL,
	/* version		*/ XtVersion,
	/* callback_private	*/ NULL,
	/* tm_table		*/ defaultTranslations,
	/* query_geometry	*/ NULL,
    }
};

WidgetClass mailwatchWidgetClass = (WidgetClass) & mailwatchClassRec;

/*
 * private procedures
 */
static Pixmap
make_pixmap (Display *dpy, MailwatchWidget w, Pixmap bitmap, int depth, int *widthp, int *heightp)
{
 Window root;
 int x, y;
 unsigned int width, height, bw, dep;
 unsigned long fore, back;

 
 if (bitmap == 0 ||
     ! XGetGeometry(dpy, bitmap, &root, &x, &y, &width, &height, &bw, &dep))
    return None;

 *widthp = (int) width;
 *heightp = (int) height;
 fore = w->mailbox.foreground_pixel;
 back = w->core.background_pixel;
 return XmuCreatePixmapFromBitmap(dpy, w->core.window, bitmap, 
				      width, height, depth, fore, back);
}


static void
ClassInitialize(void)
{
#ifndef XPM
 static XtConvertArgRec screenConvertArg[] = {
    { XtWidgetBaseOffset, (XtPointer) XtOffset(Widget, core.screen), sizeof(Screen *) }
    };
#endif

 XawInitializeWidgetSet();

#ifndef XPM
 XtAddConverter (XtRString, XtRBitmap, XmuCvtStringToBitmap,
		    screenConvertArg, XtNumber(screenConvertArg));
#endif

 return;
} /* ClassInitialize */


/* ARGSUSED */
static void
Initialize(Widget request, Widget new, ArgList args, Cardinal *numArgs)
{
    MailwatchWidget w = (MailwatchWidget) new;

    GetUserName(w);

    GetHostName(w);

    if (!w->mailbox.filename)
	GetMailFile(w);

    if (w->mailbox.reverseVideo) {
	Pixel tmp;

	tmp = w->mailbox.foreground_pixel;
	w->mailbox.foreground_pixel = w->core.background_pixel;
	w->core.background_pixel = tmp;
    }

    GetNormalGC(w);

    GetInvertGC(w);

    w->mailbox.flag_up = FALSE;		/* because it hasn't been shown yet */
    w->mailbox.mail.pixmap = None;
    w->mailbox.nomail.pixmap = None;

    w->mailbox.last_access = (stat(w->mailbox.filename, &stbuf) == 0) ?
                             stbuf.st_atime : 0 ;	/* last accessed */
    return;
}


/* ARGSUSED */
static void
clock_tic(XtPointer client_data, XtIntervalId *id)
{
    MailwatchWidget w = (MailwatchWidget) client_data;

    check_mailbox(w, FALSE);
}


static void
set_title(String str)
{
 String		c, name, title;
 Window		win  = XtWindow(toplevel);
 Display	*dpy = XtDisplay(toplevel);


 (void) XFetchName(dpy, win, &name);
 if (! name)
    name = XtNewString("xmail");
 else
    if ((c = (char *)strrchr(name, '_')) != NULL)
        *c = '\0';

 if (! *str)
    title = (String) XtMalloc((unsigned) strlen(name) + 1);
 else
    title = (String) XtMalloc((unsigned) strlen(name) + strlen(str) + 2);

 if (! title)
    XtError("xmail: Insufficient memory to allocate title space");

 if (! *str)
    (void) sprintf(title, "%s", name);
 else
    (void) sprintf(title, "%s_%s", name, str);

 XStoreName(dpy, win, title);

 XtFree((String) title);
 XtFree((String) name);
} /* end - set_title */


void
set_host(Widget widget, XEvent *event, String *params, Cardinal *num_params)
{
 register MailwatchWidget m = (MailwatchWidget) widget;
 register Display	*dpy = XtDisplay(m);
 register Window	win = XtWindow(m);
 register int		x, y, h, w;


 set_title(m->mailbox.mailhost);

 m->mailbox.useHost = True;
 m->mailbox.useName = False;
 h = m->mailbox.font->max_bounds.width - m->mailbox.font->max_bounds.lbearing;
 w = strlen(m->mailbox.mailhost);
 h = (h * w) - 2;
 x = max((int)m->core.width - h, 2);			/* if (x < 2) x = 2; */
 x /= 2;
 y = m->core.height - m->mailbox.font->descent;

 XFillRectangle(dpy, win, m->mailbox.invert_GC,
                      0, y - m->mailbox.font->ascent, m->core.width,
                      m->mailbox.font->ascent + m->mailbox.font->descent);

 XDrawString(dpy, win, m->mailbox.normal_GC, x, y, m->mailbox.mailhost, w);
} /* end - set_host */


void
set_user(Widget widget, XEvent *event, String *params, Cardinal *numParams)
{
 register MailwatchWidget m = (MailwatchWidget) widget;
 register Display	*dpy = XtDisplay(m);
 register Window	win = XtWindow(m);
 register int		x, y, h, w;


 set_title(m->mailbox.username);

 m->mailbox.useHost = False;
 m->mailbox.useName = True;
 h = m->mailbox.font->max_bounds.width - m->mailbox.font->max_bounds.lbearing;
 w = strlen(m->mailbox.username);
 h = (h * w) - 2;
 x = max((int)m->core.width - h, 2);			/* if (x < 2) x = 2; */
 x /= 2;
 y = m->core.height - m->mailbox.font->descent;

 XFillRectangle(dpy, win, m->mailbox.invert_GC,
                      0, y - m->mailbox.font->ascent, m->core.width,
                      m->mailbox.font->ascent + m->mailbox.font->descent);

 XDrawString(dpy, win, m->mailbox.normal_GC, x, y, m->mailbox.username, w);
} /* end - set_user */


void
set_none(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
 MailwatchWidget m = (MailwatchWidget) w;
 set_title("");
 m->mailbox.useHost = m->mailbox.useName = False;
 redraw_mailbox(m);
} /* end - set_none */


static GC
GetNormalGC(MailwatchWidget w)
{
    XtGCMask valuemask;
    XGCValues xgcv;

    valuemask = GCForeground | GCBackground | GCFunction | GCGraphicsExposures;
    xgcv.foreground = w->mailbox.foreground_pixel;
    xgcv.background = w->core.background_pixel;
    xgcv.function = GXcopy;
    xgcv.graphics_exposures = False;
    w->mailbox.normal_GC = XtGetGC((Widget) w, valuemask, &xgcv);
}

static GC
GetInvertGC(MailwatchWidget w)
{
    XtGCMask valuemask;
    XGCValues xgcv;

    valuemask = GCForeground | GCBackground | GCFunction | GCGraphicsExposures;
    xgcv.foreground = w->core.background_pixel;
    xgcv.background = w->mailbox.foreground_pixel;
    xgcv.function = GXcopy;
    xgcv.graphics_exposures = False;	/* this is Bool, not Boolean */
    w->mailbox.invert_GC = XtGetGC((Widget) w, valuemask, &xgcv);
}


#ifdef XPM
/*
 * split_path_string - split a colon-separated list into its constituent
 * parts; to release, free list[0] and list.  From libXmu LocBitmap.c
 */
static char **
split_path_string (src)
String	src;
{
 int nelems = 1;
 String	dst;
 String	*elemlist, *elem;

 /* count the number of elements */
 for (dst = src; *dst; dst++) if (*dst == ':') nelems++;

 /* get memory for everything */
 dst = (String) XtMalloc ((unsigned) (dst - src) + 1);
 if (!dst) return NULL;
 elemlist = (String *) XtMalloc ((unsigned) (nelems + 1) * sizeof(String));
 if (!elemlist) {
    XtFree ((String) dst);
    return NULL;
   }

 /* copy to new list and walk up nulling colons and setting list pointers */
 (void) strcpy (dst, src);
 for (elem = elemlist, src = dst; *src; src++) {
     if (*src == ':') {
        *elem++ = dst;
        *src = '\0';
        dst = src + 1;
       }
    }
 *elem = dst;

 return elemlist;
}

String			*bitmap_file_paths = NULL;

#ifndef MAXPATHLEN
#define MAXPATHLEN	256
#endif

#ifndef BITMAPDIR
#define BITMAPDIR	"/usr/include/X11/bitmaps"
#endif


/*
** @(#)XMyLocateXPixmapFile() - Cloned from libXmu LocBitmap.c for use in XPM
*/
static Pixmap
XMyLocateXPixmapFile(w, name, widthp, heightp)
Widget	w;
char	*name;
int	*widthp, *heightp;	/* RETURN */
{
 int			i;
 Bool			try_plain_name = True;
 Display		*dpy = XtDisplay(w);
 Pixmap			pixmap;					/* RETURN */
 XrmName		xrm_name[2];
 XrmClass		xrm_class[2];
 XrmRepresentation	rep_type;
 XrmValue		value;
 char			filename[MAXPATHLEN];
 char			**file_paths = NULL;
 XpmAttributes		pmAttr;
 XrmDatabase            db;
/*
** look for bitmap path (only once, because we're going to call this twice)
*/
 if (bitmap_file_paths == NULL) {
    xrm_name[0] = XrmStringToName ("bitmapFilePath");
    xrm_name[1] = (XrmName) NULL;
    xrm_class[0] = XrmStringToClass ("BitmapFilePath");
    xrm_class[1] = (XrmClass) NULL;
    db = XrmGetDatabase( dpy );
    /*
    ** XXX - warning, derefing Display * until XDisplayDatabase
    */
    if (!db)
       (void) XGetDefault (dpy, "", "");
 

    db = XrmGetDatabase( dpy );
    if (XrmQGetResource (db, xrm_name, xrm_class, &rep_type, &value) &&
        rep_type == XrmStringToQuark(XtRString)) {
       bitmap_file_paths = split_path_string(value.addr);
      }
   }
 file_paths = bitmap_file_paths;
/*
** Search order:
**    1.  name if it begins with / or ./
**    2.  "each prefix in file_paths"/name
**    3.  BITMAPDIR/name
**    4.  name if didn't begin with / or .
*/
 for (i = 1; i <= 4; i++) {
     String fn = filename;

     switch (i) {
       case 1:
            if (!(name[0] == '/' || (name[0] == '.') && name[1] == '/'))
              continue;
            fn = name;
            try_plain_name = False;
            break;
       case 2:
            if (file_paths && *file_paths) {
                (void) sprintf (filename, "%s/%s", *file_paths, name);
                file_paths++;
                i--;
                break;
            }
            continue;
       case 3:
            (void) sprintf (filename, "%s/%s", BITMAPDIR, name);
            break;
       case 4:
            if (!try_plain_name) continue;
            fn = name;
            break;
       }

     pmAttr.valuemask = XpmReturnPixels;
     if (PixmapSuccess == XpmReadFileToPixmap(dpy, w->core.window, fn,
                         &pixmap, NULL, &pmAttr))
       {
        *widthp = pmAttr.width;
        *heightp = pmAttr.height;
        return pixmap;
       }
    }
 return None;
}
#endif



static void
Realize(Widget gw, XtValueMask *valuemaskp, XSetWindowAttributes *attr)
{
#ifdef XPM
 int			x, y;
 Pixmap			bitmap, XmuLocateBitmapFile();
 XpmAttributes		pmAttr;
#endif
 MailwatchWidget	w = (MailwatchWidget) gw;
 int			depth = w->core.depth;
 register Display	*dpy = XtDisplay(w);
 Arg			args[2];


 XtCreateWindow(gw, InputOutput, (Visual *) CopyFromParent, *valuemaskp, attr);
/*
** build up the pixmaps that we'll put into the icon image
*/
 if (w->mailbox.mail.bitmap == None) {	/* if user failed to specify an icon */
#ifndef	XPM			/* Non-XPM uses [no]mail.bitmap as a pixmap */
    w->mailbox.mail.bitmap = XCreateBitmapFromData(dpy, w->core.window,
					mail_bits, mail_width, mail_height);

    w->mailbox.mail.pixmap = make_pixmap(dpy, w, w->mailbox.mail.bitmap, depth,
					 &w->mailbox.mail.width,
					 &w->mailbox.mail.height);
   } else {			/* non-XPM used XmuConvertStringToPixmap */
    w->mailbox.mail.pixmap = make_pixmap(dpy, w, w->mailbox.mail.bitmap, depth,
					 &w->mailbox.mail.width,
					 &w->mailbox.mail.height);
#else
    pmAttr.valuemask = XpmReturnPixels;
    XpmCreatePixmapFromData(dpy, w->core.window, mail_xpm,
			    &w->mailbox.mail.pixmap, NULL, &pmAttr);
    w->mailbox.mail.width = pmAttr.width;
    w->mailbox.mail.height = pmAttr.height;
   } else {
    /*
    ** Because for XPM we define mailbox.[no]mail.bitmap as a string,
    ** we must do the file conversion ourselves.  First, see if it is
    ** a plain old bitmap file.  Failing that, try for an XPM format
    ** file.  Failing that, default to our internally defined xpm icon.
    */
    if (bitmap = XmuLocateBitmapFile(w->core.screen, w->mailbox.mail.bitmap,
                                     NULL, 0, &w->mailbox.mail.width,
                                              &w->mailbox.mail.height, &x, &y))
       w->mailbox.mail.pixmap = make_pixmap(dpy, w, bitmap, depth,
                                            &w->mailbox.mail.width,
					    &w->mailbox.mail.height);

    else if (! (w->mailbox.mail.pixmap = XMyLocateXPixmapFile((Widget) w,
                                                     w->mailbox.mail.bitmap,
                                                    &w->mailbox.mail.width,
                                                    &w->mailbox.mail.height))) {
	    pmAttr.valuemask = XpmReturnPixels;
	    XpmCreatePixmapFromData(dpy, w->core.window, mail_xpm,
				    &w->mailbox.mail.pixmap, NULL, &pmAttr);
	    w->mailbox.mail.width = pmAttr.width;
	    w->mailbox.mail.height = pmAttr.height;
    }
#endif
   }

 if (w->mailbox.nomail.bitmap == None) {
#ifndef	XPM
    w->mailbox.nomail.bitmap = XCreateBitmapFromData(dpy, w->core.window,
                                 no_mail_bits, no_mail_width, no_mail_height);

    w->mailbox.nomail.pixmap = make_pixmap(dpy, w, w->mailbox.nomail.bitmap,
					 depth, &w->mailbox.nomail.width,
					 &w->mailbox.nomail.height);
   } else {
    w->mailbox.nomail.pixmap = make_pixmap(dpy, w, w->mailbox.nomail.bitmap,
					 depth, &w->mailbox.nomail.width,
					 &w->mailbox.nomail.height);
#else
    pmAttr.valuemask = XpmReturnPixels;
    XpmCreatePixmapFromData(dpy, w->core.window, nomail_xpm,
			    &w->mailbox.nomail.pixmap, NULL, &pmAttr);
    w->mailbox.nomail.width = pmAttr.width;
    w->mailbox.nomail.height = pmAttr.height;
   } else {
    if (bitmap = XmuLocateBitmapFile(w->core.screen, w->mailbox.nomail.bitmap,
                                        NULL, 0, &w->mailbox.nomail.width,
                                        &w->mailbox.nomail.height, &x, &y))
       w->mailbox.nomail.pixmap = make_pixmap(dpy, w, bitmap, depth,
					      &w->mailbox.nomail.width,
					      &w->mailbox.nomail.height);

    else if (! (w->mailbox.nomail.pixmap = XMyLocateXPixmapFile((Widget) w,
                                                   w->mailbox.nomail.bitmap,
                                                  &w->mailbox.nomail.width,
                                                  &w->mailbox.nomail.height))) {
	    pmAttr.valuemask = XpmReturnPixels;
	    XpmCreatePixmapFromData(dpy, w->core.window, nomail_xpm,
				    &w->mailbox.nomail.pixmap, NULL, &pmAttr);
	    w->mailbox.nomail.width = pmAttr.width;
	    w->mailbox.nomail.height = pmAttr.height;
    }
#endif
   }
/*
** the size of the icon should be the size of the larger icon image.
*/
 w->core.width  = max(w->mailbox.mail.width, w->mailbox.nomail.width);
 w->core.height = max(w->mailbox.mail.height, w->mailbox.nomail.height);

 XtSetArg(args[0], XtNwidth,  (XtArgVal) w->core.width);
 XtSetArg(args[1], XtNheight, (XtArgVal) w->core.height);
 XtSetValues(XtNameToWidget(toplevel, "icon"), args, 2);

/* set status check timer */
 w->mailbox.interval_id = XtAddTimeOut(w->mailbox.update * 1000,
	                                        clock_tic, (XtPointer) w);
#ifdef XPM
 if (bitmap_file_paths) {
    XtFree((String) bitmap_file_paths[0]);
    XtFree((String) bitmap_file_paths);
   }
#endif
}

static void
Destroy(Widget gw)
{
 MailwatchWidget w = (MailwatchWidget) gw;
 Display	*dpy = XtDisplay(gw);

 XtFree((String) w->mailbox.filename);
 XtFree((String) w->mailbox.username);
 XtFree((String) w->mailbox.mailhost);
 REMOVE_TIMEOUT(w->mailbox.interval_id);
 XtReleaseGC((Widget)w, w->mailbox.normal_GC);
#ifndef	XPM
 XFreePixmap(dpy, w->mailbox.mail.bitmap);
 XFreePixmap(dpy, w->mailbox.nomail.bitmap);
#endif
 XFreePixmap(dpy, w->mailbox.mail.pixmap);
 XFreePixmap(dpy, w->mailbox.nomail.pixmap);
}

static void
Redisplay(Widget gw, XEvent *event, Region r)
{
    MailwatchWidget w = (MailwatchWidget) gw;

    REMOVE_TIMEOUT(w->mailbox.interval_id);
    check_mailbox(w, TRUE);
}


/*
** Modified to NOT update the mail file timestamp via utimes(), and to
** ignore state change where mail adds a status record to the message.
** The first eliminates collisions with mail during delivery, and the
** second eliminates false triggers for new mail.  The number of times
** the terminal bell is rung is controlled by the same .mailrc resource
** (bell) used by Sunview's mailtool.
*/
static void
check_mailbox(MailwatchWidget w, Boolean force_redraw)
{
 int		i, redraw = 0;
 char		*p = NULL;


 if (stat(w->mailbox.filename, &stbuf) < 0) {		/* no mail file */
    if (w->mailbox.flag_up == TRUE) {
       force_redraw = 0;
       UnsetNewmail(w, NULL, NULL);
      }
   } else {
    if (stbuf.st_atime > stbuf.st_mtime &&
        stbuf.st_atime >= w->mailbox.last_access) {	/* mail was seen */
       w->mailbox.last_access = stbuf.st_atime;
       if (w->mailbox.flag_up == TRUE) {
          force_redraw = 0;
          UnsetNewmail(w, NULL, NULL);
         }
      } else {
       if (stbuf.st_size != 0 && 
           stbuf.st_mtime > w->mailbox.last_access) {	/* got new mail */
          w->mailbox.last_access = stbuf.st_mtime;
          if (w->mailbox.flag_up == FALSE) {
             if (w->mailbox.bell) {
                i = 1;
                if (p = (char *)GetMailrc("bell"))
                   (void) sscanf(p, "%d", &i);
                i = between(1, i, 5);
                for (; i > 0; i--)
                    XBell(XtDisplay(w), MAILBOX_VOLUME);
               }
             w->mailbox.flag_up = TRUE;
             redraw = TRUE;
             XtCallCallbacks((Widget)w, XtNcallback, NULL);
            }
         }
      }
   }

 if (redraw || force_redraw)
    redraw_mailbox(w);

 /* reset timer */
 w->mailbox.interval_id = XtAddTimeOut(w->mailbox.update * 1000,
	clock_tic, (XtPointer) w);
} /* check_mailbox */

/*
** Retrieve the user's mailbox filename - use MAIL environment value, if set
*/
static void
GetMailFile(MailwatchWidget w)
{
 if (! (w->mailbox.filename = (String) GetMailEnv("MAIL"))) {
    if (! (w->mailbox.filename = (String) XtMalloc((unsigned) strlen(MAILBOX_DIRECTORY)
	   + 2 + strlen(w->mailbox.username) + 1))) {
       (void) fprintf(stderr, "Mailbox widget: can't allocate enough memory.\n");
       CloseDown(w, 1);
      }
    (void) sprintf(w->mailbox.filename, "%s/%s\0", MAILBOX_DIRECTORY, w->mailbox.username);
   }
} /* GetMailFile */


/*
** Retrieve the mailbox user's name
*/
static void
GetUserName(MailwatchWidget w)
{
 char *username = (char *)getlogin();

 if (! username) {
    struct passwd *pw = getpwuid((uid_t) getuid());

    if (! pw) {
       (void) fprintf(stderr, "Mailbox widget: can't find your username.\n");
       CloseDown(w, 1);
      }
    username = pw->pw_name;
   }

 if (! (w->mailbox.username = XtNewString(username))) {
    XtWarning("Mailbox widget: can't allocate space for username.\n");
    CloseDown(w, 1);
   }
} /* GetUserName */


/*
** Retrieve the process host name
*/
static void
GetHostName(MailwatchWidget w)
{
 char	hostname[MAXHOSTNAMELEN];


 (void) gethostname(hostname, MAXHOSTNAMELEN);

 if (! (w->mailbox.mailhost = XtNewString(hostname))) {
    XtWarning("Mailbox widget: can't allocate space for hostname.\n");
    CloseDown(w, 1);
   }
} /* GetHostName */


static void
CloseDown(MailwatchWidget w, int status)
{
    Display *dpy = XtDisplay(w);

    XtDestroyWidget((Widget)w);
    XCloseDisplay(dpy);
    _exit(status);
}

/* ARGSUSED */
static Boolean
SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList a, Cardinal *num_args)
{
    MailwatchWidget current = (MailwatchWidget) gcurrent;
    MailwatchWidget new = (MailwatchWidget) gnew;
    Boolean redisplay = FALSE;

    if (current->mailbox.update != new->mailbox.update) {
	REMOVE_TIMEOUT(current->mailbox.interval_id);
	new->mailbox.interval_id = XtAddTimeOut(new->mailbox.update * 1000,
	    clock_tic,
	    (XtPointer) gnew);
    }
    if (current->mailbox.foreground_pixel != new->mailbox.foreground_pixel ||
	current->core.background_pixel != new->core.background_pixel) {
	XtReleaseGC((Widget)current, current->mailbox.normal_GC);
	GetNormalGC(new);
	redisplay = TRUE;
    }
    return (redisplay);
}

/*
 * drawing code
 */
static void
redraw_mailbox(MailwatchWidget m)
{
    register Display	*dpy = XtDisplay(m);
    register Window	win = XtWindow(m);
    register int	x, y;
    Pixel		back = m->core.background_pixel;
    GC			gc = m->mailbox.normal_GC;
    struct _mbimage	*im;


    if (m->mailbox.flag_up)			/* paint the "up" position */
	im = &m->mailbox.mail;
    else					/* paint the "down" position */
	im = &m->mailbox.nomail;

    /* center the picture in the window */
    x = ((int)m->core.width - im->width) / 2;
    y = ((int)m->core.height - im->height) / 2;

    XSetWindowBackground(dpy, win, back);
    XClearWindow(dpy, win);
    XCopyArea(dpy, im->pixmap, win, gc, 0, 0, im->width, im->height, x, y);

    if (m->mailbox.useHost || m->mailbox.useName) {
       if (m->mailbox.useHost) set_host((Widget)m, (XEvent *) NULL, (String *) NULL, (Cardinal *) NULL);
       else set_user((Widget)m, (XEvent *)NULL, (String *) NULL, (Cardinal *) NULL);
      }
}


void
reset_mailbox(Widget gw, int down)
{
 MailwatchWidget w = (MailwatchWidget) gw;
 char	*p;
 int	i;


 if (down)
    w->mailbox.flag_up = FALSE;
 else {
    if (w->mailbox.flag_up == FALSE) {
       if (w->mailbox.bell) {
          i = 1;
          if (p = (char *)GetMailrc("bell"))
             (void) sscanf(p, "%d", &i);
          i = between(1, i, 5);
          for (; i > 0; i--)
              XBell(XtDisplay(w), MAILBOX_VOLUME);
         }
      }
    w->mailbox.flag_up = TRUE;
   }

 redraw_mailbox(w);
} /* reset_mailbox */


syntax highlighted by Code2HTML, v. 0.9.1