/*****************************************************************************/
/*                                                                           */
/*      asmail - AfterStep Mail                                              */
/*                                                                           */
/*      By Per Liden                                                         */
/*      per@oden.rsn.hk-r.se                                                 */
/*      http://oden.rsn.hk-r.se/~per                                         */
/*                                                                           */
/*****************************************************************************/

#include "config.h"

/* Headers ****************************************************************** */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/xpm.h>
#include <X11/extensions/shape.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#ifdef __QNX__
#include <X11/IntrinsicP.h> /* for XtResizeWidget() */
#include <unix.h> /* for gethostname() */
#endif

#include "pop.h"

/* Externals **************************************************************** */
extern char *safemalloc(int length);

/* XPM struct and icons ***************************************************** */
typedef struct _XpmIcon {
    Pixmap pixmap;
    Pixmap mask;
    XpmAttributes attributes;
    struct XpmIcon *next;
} XpmIcon;

XpmIcon *Frame = NULL, *NoMail = NULL, *OldMail = NULL, *NewMail = NULL, *Current;

#include "pixmaps/frame.xpm"
#include "pixmaps/newmail.xpm"
#include "pixmaps/oldmail.xpm"
#include "pixmaps/nomail.xpm"
#include "pixmaps/newmail_s.xpm"
#include "pixmaps/oldmail_s.xpm"
#include "pixmaps/nomail_s.xpm"

/* Functions **************************************************************** */
int CheckMailBox(char **Mbox, int NumOfMbox);
int IsNewMail(char *Mbox, int index);
int IsMail(char *Mbox, int index);
int CheckTimeStamp(char **Mbox, int NumOfMbox);
int GetGStatus(void);
void CheckMailCallback(XtPointer XtP, XtIntervalId * XtI);
void Help(void);
void CreateWindow(int argc, char *argv[]);
void MakeSettings(int argc, char *argv[]);
void ParseCfgFile(FILE * File);
void StartLoop(void);
XpmIcon *GetXPM(char **data);
void FreeXPM(XpmIcon *Icon);
void LoadXPMFile(XpmIcon * Icon, char *XpmFile);
void XPMError(int Code);
void RedrawWindow(void);
void ButtonHandler(Widget W, XtPointer P, XEvent * E);
void ExposeHandler(Widget W, XtPointer P, XEvent * E);
void StructureNotifyHandler(Widget W, XtPointer P, XEvent * E);
void AnimCallback(XtPointer XtP, XtIntervalId * XtI);
Pixel GetColor(char *name);
void AddXpmToAnim(XpmIcon * Icon, XpmIcon * List, int ListNo);
void Quit(Widget w, XEvent *event, String *params, Cardinal *num_params);

int IsPopBox(char *string);
int TotalNewMail(void);
void shorten(char *line);
int TotalMail(void);

/* Global stuff ************************************************************* */
#define FILENAME		".asmailrc"
#define DEFINTERVAL		5	/* Default interval 5 sec */
#define DEFANIMATIONSPEED	0	/* Default animationspeed is none */
#define LEFTBUTTON		1	/* Left mousebutton id */
#define RIGHTBUTTON		3	/* Right mousebutton id */
#define MAXNUMOFMAILBOXES	256	/* Maximim number of mailboxes */
#define XPMCLOSENESS 32768
Display *Disp = 0;
Window Root;
XtActionsRec asmail_actions[] =
{
    { "Quit", Quit }
};
Atom wm_delete_win;
char Execute[256] = "";
char NewMailExecute[256] = "";
char ExecuteOnUpdate[256] = "";
char *MailBoxes[MAXNUMOFMAILBOXES];
int NumOfMailBoxes = 0;
int ExecuteFlag = 0;
int NewMailExecuteFlag = 0;
int ExecuteOnUpdateFlag = 0;
int AlwaysNewMailExecute = 0;
int IsNew = 1;
int Interval = DEFINTERVAL;
int Beep = 1;
int Shape = 1;
int *NewMailCounter = 0;
int *MailCounter = 0;
int TimeStampMode = 0;
int LockShape = 0;
int *IStatus = 0;
int *GStatus = 0;
int IsMapped;
int NumOfMsgX = 10;
int NumOfMsgY = 10;
int NumOfMsgMode = 0;
XTextItem NumOfMsg;
Pixel NumOfMsgColor = -1; /* defaults to white? */
GC WinGC;
Widget MainWin;
XtAppContext AsmailApp;
int AnimationSpeed[3] =
{DEFANIMATIONSPEED,
 DEFANIMATIONSPEED,
 DEFANIMATIONSPEED};
char HelpMessage[] =
{
    "asmail - Version 0.52.1, by Per Liden (per@oden.rsn.hk-r.se)\n"
    "usage:  asmail [file] [options ...] \n"
    "file:\n"
    "        configuration file to use (default ~/.asmailrc)\n"
    "options:\n"
    "        -geometry <geom>     position of asmail\n"
    "        -display  <disp>     specifies the display to use\n"
    "        -title    <string>   title string\n"
    "        -name     <string>   client instance, icon and title strings\n"
    "\n"
};

/*****************************************************************************/
int main(int argc, char *argv[])
{
    CreateWindow(argc, argv);
    StartLoop();
    return 0; /* This will never happen */
}

/*****************************************************************************/
void Help()
{
    /* Print help message */
    fprintf(stderr, HelpMessage);
    exit(1);
}

/****************************************************************************/
void MakeSettings(int argc, char *argv[])
{
    char CfgFile[256];
    FILE *File = 0;
	int i;

    /* Get config file */
    if (argc == 1) {
	strcpy(CfgFile, (char *) getenv("HOME"));
        strcat(CfgFile, "/");
	strcat(CfgFile, FILENAME);
    } else if (argc == 2) {
	/* Check if option */
	if (argv[1][0] == '-')
	    Help();
	else
	    strcpy(CfgFile, argv[1]);
    } else
	Help();


    /* Open config file */
    File = fopen(CfgFile, "r");
    if (File != NULL) {
	ParseCfgFile(File);
	fclose(File);
    } else
	    fprintf(stderr, "asmail: '%s' not found in ~/, using default settings\n", FILENAME);

    /* Check interval */
    if (!Interval) {
	fprintf(stderr, "asmail: invalid update frequency specified, using default\n");
	Interval = DEFINTERVAL;	/* Use default */
    }
    /* Check animation speed */
	for (i = 0; i < 3; i++)
    	if (AnimationSpeed[i]) AnimationSpeed[i] *= 10;
    /* If no MailFile option search for $MAIL */
    if (NumOfMailBoxes == 0) {
	   if (getenv("MAIL") != NULL) {
	      NumOfMailBoxes = 1;
          GStatus = (int *) calloc(NumOfMailBoxes, sizeof(int));
          IStatus = (int *) calloc(NumOfMailBoxes, sizeof(int));
          NewMailCounter = (int *) calloc(NumOfMailBoxes, sizeof(int));
          MailCounter = (int *) calloc(NumOfMailBoxes, sizeof(int));
          MailBoxes[0] = (char *) calloc(strlen((char *) getenv("MAIL")) + 1, sizeof(char));
          strcpy(MailBoxes[0], (char *) getenv("MAIL"));
       } else {
           fprintf(stderr, "asmail: no mailfile specified\n");
           exit(1);
       }
    }
    /* Check NumOfMsgMode */
    if (NumOfMsgMode < 0 || NumOfMsgMode > 3) {
	fprintf(stderr, "asmail: invalid NumOfMsgMode specified\n");
	exit(1);
    }
	/* Load or discard default XPM files and unset animation as required */
	if (Shape && Frame)
		FreeXPM(Frame);
	else if (!Shape && !Frame)
		Frame = GetXPM(frame);
	if (!NoMail)
	{
		NoMail = GetXPM((Shape) ? nomail_s : nomail);
		NoMail->next = (struct XpmIcon *)NoMail;
		AnimationSpeed[0] = 0;
		Current = NoMail;
	}
	if (!OldMail)
	{
		OldMail = GetXPM((Shape) ? oldmail_s : oldmail);
		OldMail->next = (struct XpmIcon *)OldMail;
		AnimationSpeed[1] = 0;
	}
	if (!NewMail)
	{
		NewMail = GetXPM((Shape) ? newmail_s : newmail);
		NewMail->next = (struct XpmIcon *)NewMail;
		AnimationSpeed[2] = 0;
	}
}

/****************************************************************************/
void AddXpmToAnim(XpmIcon * Icon, XpmIcon * List, int ListNo)
{
    static XpmIcon *First[3];
    static XpmIcon *Last[3];
    static Count[3] =
    {0, 0, 0};

    if (Count[ListNo] == 0) {
	switch (ListNo) {
	case 0:
	    NoMail = Icon;
	    break;
	case 1:
	    OldMail = Icon;
	    break;
	case 2:
	    NewMail = Icon;
	    break;
	}
	First[ListNo] = Icon;
	List = First[ListNo];
	List->next = (struct XpmIcon *) List;
	Last[ListNo] = First[ListNo];
	Count[ListNo]++;
    } else {
	Last[ListNo]->next = (struct XpmIcon *) Icon;
	Last[ListNo] = Icon;
	Last[ListNo]->next = (struct XpmIcon *) First[ListNo];
    }
}
/**********************************************************************/
void shorten(char *line)
{
    /* Removes blanks before and after a line */
    int i = 0, j = 0;
    
    if (line[strlen(line)-1] == '\n')
        line[strlen(line)-1] = '\0';
    
    while (isblank(line[i]) && line[i]) i++;
    if (i == 0 || i == strlen(line))
        return;
    while (line[i])
        line[j++] = line[i++];
    line[j] = '\0';
    
    i = strlen(line);
    while (isblank(line[i]) && i > 0) i--;
    if (line[i] == '\n') i--;
    line[++i] = '\0';
}
/****************************************************************************/
int isComment(FILE *file, char *buffer)
{
	if (buffer[0] == '#' || buffer[0] == '\0')
	{
		/* Goto next line */
		fgets(buffer, 255, file);
		return 1;
	}
	return 0;
}
/****************************************************************************/
void ParseCfgFile(FILE * File)
{
    char Buffer[256];
    int i;
    char c;
    XpmIcon *Icon;

    while (!feof(File)) {
	/* Read string */
	fscanf(File, "%s", Buffer);

	/* Comment */
	if (isComment(File, Buffer))
		;
	/* NoBeep */
	else if (!strcasecmp(Buffer, "NoBeep")) {
	    Beep = 0;
	}
	/* NoShape */
	else if (!strcasecmp(Buffer, "NoShape")) {
	    Shape = 0;
	}
	/* LockShape */
	else if (!strcasecmp(Buffer, "LockShape")) {
	    LockShape = 1;
	}
	/* MailFiles <...> */
	else if (!strcasecmp(Buffer, "MailFiles")) {
	    i = 0;
	    while (1) {
    	   fgets(Buffer, 256, File);
           if (Buffer[0] == '#' || Buffer[0] == '\0') continue;
           shorten(Buffer);
           /* End */
           if (!strcasecmp(Buffer, "End")) {
               /* If no mailfiles specified */
               if (i == 0) {
                   fprintf(stderr, "asmail: no mailfiles specified in config");
                   exit(1);
               }
               NumOfMailBoxes = i;
               GStatus = (int *) calloc(NumOfMailBoxes, sizeof(int));
               IStatus = (int *) calloc(NumOfMailBoxes, sizeof(int));
               NewMailCounter = (int *) calloc(NumOfMailBoxes, sizeof(int));
               MailCounter = (int *) calloc(NumOfMailBoxes, sizeof(int));
               break;
           }
           /* Mailfile */
           else {
               /* Check maximin number of mailboxes */
               if (i > MAXNUMOFMAILBOXES) {
                   fprintf(stderr, "asmail: too many mailboxes specified");
                   exit(1);
               }
               /* Add mailbox */
               MailBoxes[i] = (char *) calloc(strlen(Buffer) + 1, sizeof(char));
               strcpy(MailBoxes[i], Buffer);
               i++;
           }
	    }
	}
	/* Update <sec> */
	else if (!strcasecmp(Buffer, "Update")) {
	    fscanf(File, "%d ", &Interval);
	}
	/* Execute <program> */
	else if (!strcasecmp(Buffer, "Execute")) {
	    fgetc(File);
	    fgets(Execute, 255, File);
	    ExecuteFlag = 1;
	}
	/* ExecuteOnUpdate <program> */
	else if (!strcasecmp(Buffer, "ExecuteOnUpdate")) {
	    fgetc(File);
	    fgets(ExecuteOnUpdate, 255, File);
	    ExecuteOnUpdateFlag = 1;
	}
	/* NumOfMsgMode <mode> */
	else if (!strcasecmp(Buffer, "NumOfMsgMode")) {
	    fscanf(File, "%d ", &NumOfMsgMode);
	    NumOfMsg.chars = malloc(50);
	    NumOfMsg.font = XLoadFont(Disp, "-*-lucida-*-*-*-*-*-*-*-*-*-*-*-*");
	    NumOfMsg.delta = 1;
	}
	/* NumOfMsgPosition <x> <y> */
	else if (!strcasecmp(Buffer, "NumOfMsgPosition")) {
	    fscanf(File, "%d ", &NumOfMsgX);
	    fscanf(File, "%d ", &NumOfMsgY);
	}
	/* NumOfMsgFont <string> */
	else if (!strcasecmp(Buffer, "NumOfMsgFont")) {
	    fscanf(File, "%s ", Buffer);
	    NumOfMsg.font = XLoadFont(Disp, Buffer);
	}
    /* NumOfMsgColor <string> */
    else if (!strcasecmp(Buffer, "NumOfMsgColor")) {
        fscanf(File, "%s ", Buffer);
        if (!(NumOfMsgColor = GetColor(Buffer)))
            NumOfMsgColor = GetColor("white");
	}
	/* NewMailExecute <program> */
	else if (!strcasecmp(Buffer, "NewMailExecute")) {
	    fgetc(File);
	    fgets(NewMailExecute, 255, File);
	    NewMailExecuteFlag = 1;
	}
	/* AlwaysNewMailExecute */
	else if (!strcasecmp(Buffer, "AlwaysNewMailExecute")) {
	    AlwaysNewMailExecute = 1;
	}
	/* TimeStampMode */
	else if (!strcasecmp(Buffer, "TimeStampMode")) {
	    TimeStampMode = 1;
	}
	/* AnimationSpeed <nomail> <oldmail> <newmail> */
	else if (!strcasecmp(Buffer, "AnimationSpeed")) {
	    fscanf(File, "%d ", &AnimationSpeed[0]);
	    fscanf(File, "%d ", &AnimationSpeed[1]);
	    fscanf(File, "%d ", &AnimationSpeed[2]);
	}
	/* Frame <XPM file> */
	else if (!strcasecmp(Buffer, "Frame")) {
		free(Frame);
		fscanf(File, "%s ", Buffer);
		if (!Shape) {
			Frame = (XpmIcon *) calloc(1, sizeof(XpmIcon));
			LoadXPMFile(Frame, Buffer);
		}
	}
	/* NoMail <...> */
	else if (!strcasecmp(Buffer, "NoMail")) {
	    free(NoMail);
	    while (1) {
		fscanf(File, "%s ", Buffer);
		if (isComment(File, Buffer)) continue;
		if (!strcasecmp(Buffer, "End"))
		    break;
		Icon = (XpmIcon *) calloc(1, sizeof(XpmIcon));
		LoadXPMFile(Icon, Buffer);
		AddXpmToAnim(Icon, NoMail, 0);
	    }
	    Current = NoMail;
	}
	/* OldMail <...> */
	else if (!strcasecmp(Buffer, "OldMail")) {
	    free(OldMail);
	    while (1) {
		fscanf(File, "%s ", Buffer);
		if (isComment(File, Buffer)) continue;
		if (!strcasecmp(Buffer, "End"))
		    break;
		Icon = (XpmIcon *) calloc(1, sizeof(XpmIcon));
		LoadXPMFile(Icon, Buffer);
		AddXpmToAnim(Icon, OldMail, 1);
	    }
	}
	/* NewMail <...> */
	else if (!strcasecmp(Buffer, "NewMail")) {
	    free(NewMail);
	    while (1) {
		fscanf(File, "%s ", Buffer);
		if (isComment(File, Buffer)) continue;
		if (!strcasecmp(Buffer, "End"))
		    break;
		Icon = (XpmIcon *) calloc(1, sizeof(XpmIcon));
		LoadXPMFile(Icon, Buffer);
		AddXpmToAnim(Icon, NewMail, 2);
	    }
	}
	/* Unknown keyword */
	else {
	    for (i = 0, c = 0; i < strlen(Buffer); i++)
		c += isgraph(Buffer[i]);
	    if (c != 0) {
		fprintf(stderr, "asmail: unknown keyword '%s'\n", Buffer);
		exit(1);
	    }
	}
    }
}
/****************************************************************************/
void Quit(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    if (event->type == ClientMessage &&
        event->xclient.data.l[0] != wm_delete_win) {
        XBell(Disp, 0);
        return;
    }
    XFreeGC(Disp, WinGC);
    XCloseDisplay(Disp);
    exit (0);
}
/****************************************************************************/
void CreateWindow(int argc, char *argv[])
{
    XSizeHints SizeHints;
    XGCValues gcv;
    unsigned long gcm;

    /* Create Main window */
    MainWin = XtAppInitialize(&AsmailApp, "asmail", NULL, 0, &argc, argv, NULL, NULL, 0);
    Disp = XtDisplay(MainWin);
    Root = RootWindow(Disp, DefaultScreen(Disp));

    /* Add eventhandlers */
    XtAddEventHandler(MainWin, ExposureMask, FALSE, (XtEventHandler) ExposeHandler, NULL);
    XtAddEventHandler(MainWin, ButtonPressMask, FALSE, (XtEventHandler) ButtonHandler, NULL);
    XtAddEventHandler(MainWin, StructureNotifyMask, FALSE,
                   (XtEventHandler) StructureNotifyHandler, NULL);

    /* Add actions */
    XtAppAddActions(AsmailApp, asmail_actions, XtNumber(asmail_actions));
    XtOverrideTranslations(MainWin,
                   XtParseTranslationTable("<Message>WM_PROTOCOLS: Quit()"));

    /* Make settings */
    MakeSettings(argc, argv);

    /* Set window attributes */
    if (Frame)
    {
        SizeHints.width = Frame->attributes.width;
        SizeHints.height = Frame->attributes.height;
    }
    else
    {
        SizeHints.width = Current->attributes.width;
        SizeHints.height = Current->attributes.height;
    }
    XtResizeWidget(MainWin, SizeHints.width, SizeHints.height, 1);
    XtRealizeWidget(MainWin);
    SizeHints.flags = USPosition | PSize | PMinSize | PMaxSize;
    SizeHints.min_width = SizeHints.max_width = SizeHints.width;
    SizeHints.min_height = SizeHints.max_height = SizeHints.height;
    XSetWMNormalHints(Disp, XtWindow(MainWin), &SizeHints);

    /* Create GC */
    gcm = GCForeground | GCBackground | GCGraphicsExposures;
    gcv.foreground = GetColor("white");
    gcv.background = GetColor("white");
    gcv.graphics_exposures = False;
    WinGC = XCreateGC(Disp, Root, gcm, &gcv);

    /* Add protocol messages */
    wm_delete_win = XInternAtom(Disp, "WM_DELETE_WINDOW", False);
    (void) XSetWMProtocols(Disp, XtWindow(MainWin), &wm_delete_win, 1);

    /* Map mainwindow */
    XtMapWidget(MainWin);
}
/****************************************************************************/ 
int TotalNewMail(void)
{
    int i, c = 0;
    
    for (i = 0; i < NumOfMailBoxes; i++)
        c += NewMailCounter[i];

    return c;
}
/*****************************************************************************/
int TotalMail(void)
{
    int i, c = 0;
    
    for (i = 0; i < NumOfMailBoxes; i++)
        c += MailCounter[i];
    
    return c;
}
/****************************************************************************/ 
void WMIconName(void)
{
	static Window wnd = None;
	static int lastall = 0, lastnew = 0;
	int all = TotalMail(), new = TotalNewMail();
	char *w_name, i_name[50];

#if 0
	printf("%d/%d\n", all, new);
#endif

	if (lastall == all && lastnew == new) return;
	lastall = all;
	lastnew = new;

	if (wnd == None) wnd = XtWindow(MainWin);

	XFetchName(Disp, wnd, &w_name);

	if (all > 0)
	{
		if (new > 0)
			sprintf(i_name, "%s (%d new in %d)", w_name, new, all);
		else
			sprintf(i_name, "%s (%d msg%s)", w_name, all,
					(all != 1) ? "s" : "");

		XSetIconName(Disp, wnd, i_name);
	}
	else
		XSetIconName(Disp, wnd, w_name);

	XFree(w_name);
}
/***************************************************************************/
void RedrawWindow(void)
{
    XGCValues gcv;
    int GS = GetGStatus();
    static int LockFrame = 0, x = 0, y = 0;
/*
    static int lastall = 0, lastnew = 0;
*/
    int all, new;

    /* Select icon */
    switch (GS) {
    case 0:
	Current = NoMail;
	break;
    case 1:
	Current = OldMail;
	break;
    case 2:
	Current = NewMail;
	break;
    }				/* Shape window */
    if (Shape) {
	XShapeCombineMask(Disp, XtWindow(MainWin), ShapeBounding, 0, 0,
			  Current->mask, ShapeSet);
	if (LockShape)
	    Shape = 0;
    }
    else if (Frame && !LockFrame)
    {
        XCopyArea(Disp, Frame->pixmap, XtWindow(MainWin), WinGC, 0, 0,
                Frame->attributes.width, Frame->attributes.height, 0, 0);
        x = (Frame->attributes.width - Current->attributes.width) / 2;
        y = (Frame->attributes.height - Current->attributes.height) / 2;
        if (LockShape) LockFrame = 1;
    }

    /* Draw icon */
    XCopyArea(Disp, Current->pixmap, XtWindow(MainWin), WinGC, 0, 0,
	    Current->attributes.width, Current->attributes.height, x, y);

	/* Draw number of messages */
	if (GS && NumOfMsgMode) {
		all = TotalMail();
		new = TotalNewMail();
/*
		if (lastall != all || lastnew != new) {
			lastall = all;
			lastnew = new;
*/
			/* Create string */
			switch (NumOfMsgMode) {
				case 1:
					sprintf(NumOfMsg.chars, "%d/%d", new, all);
					break;
				case 2:
					sprintf(NumOfMsg.chars, "%d", new);
					break;
				case 3:
					sprintf(NumOfMsg.chars, "%d", all);
					break;
			}
			gcv.foreground = NumOfMsgColor;
			XChangeGC(Disp, WinGC, GCForeground, &gcv);
			NumOfMsg.nchars = strlen(NumOfMsg.chars);
			XDrawText(Disp, XtWindow(MainWin), WinGC, x + NumOfMsgX, y + NumOfMsgY,
					&NumOfMsg, 1);
/*
		}
*/
	}

	/* Update WM icon name */
	WMIconName();
}

/****************************************************************************/
void ButtonHandler(Widget W, XtPointer P, XEvent * E)
{
    int i;
    int Redraw = 0;

    /* Left button */
    if (E->xbutton.button == LEFTBUTTON && ExecuteFlag == 1)
	system(Execute);

    /* Right button */
    else if (E->xbutton.button == RIGHTBUTTON) {
	for (i = 0; i < NumOfMailBoxes; i++) {
	    if (GStatus[i] == 2) {
		if (TimeStampMode) {
		    GStatus[i] = 1;
		    IStatus[i] = 1;
		} else {
		    GStatus[i] = 1;
		    IStatus[i] = 3;
		    NewMailCounter[i] = IsNewMail(MailBoxes[i], i);
		}
		Redraw++;
	    }
	}
    }
    if (Redraw)
	RedrawWindow();
}

/****************************************************************************/
void ExposeHandler(Widget W, XtPointer P, XEvent * E)
{
    if (Frame)
        XCopyArea(Disp, Frame->pixmap, XtWindow(MainWin), WinGC, 0, 0,
                Frame->attributes.width, Frame->attributes.height, 0, 0);
    RedrawWindow();
}

/****************************************************************************/
void StructureNotifyHandler(Widget W, XtPointer P, XEvent * E)
{
	if (E->type == MapNotify)
		IsMapped = 1;
	else if (E->type == UnmapNotify)
		IsMapped = 0;
}

/****************************************************************************/ 
void SetAnimTimeout(void)
{
	int i, j = 0;

	if (IsMapped)
	{
		/* Are there any animated statuses? */
		for (i = 0; i < 3; i++)
			if ((j = AnimationSpeed[i])) break;
		/* Is this status animated? */
		i = AnimationSpeed[GetGStatus()];
	}
	else
		i = 1000; /* 1 second */

	/* If unmapped, use a longish value. Otherwise, if there    */
	/* are animations, use this status's value before any other */
	if (i || j)
	   	XtAppAddTimeOut(AsmailApp, (unsigned long) ((i) ? i : j),
			    (XtTimerCallbackProc) AnimCallback, NULL);
}
/****************************************************************************/ 
void StartLoop(void)
{
    /* Check mailbox at statup */
    if (CheckMailBox(MailBoxes, NumOfMailBoxes))
	   RedrawWindow();

    /* Set timeout */
    XtAppAddTimeOut(AsmailApp, (unsigned long) (Interval * 1000),
		    (XtTimerCallbackProc) CheckMailCallback, NULL);

    /* Set animation timeout */
    SetAnimTimeout();

    /* Start looping */
    XFlush(Disp);
    XtAppMainLoop(AsmailApp);
}
/****************************************************************************/
void AnimCallback(XtPointer XtP, XtIntervalId * XtI)
{
	int aGS = AnimationSpeed[GetGStatus()];

    /* Change to next image */
    NoMail = (XpmIcon *) NoMail->next;
    OldMail = (XpmIcon *) OldMail->next;
    NewMail = (XpmIcon *) NewMail->next;

    /* Redraw */
	if (IsMapped && aGS) RedrawWindow();

    /* Set animation timeout */
    SetAnimTimeout();
}
/****************************************************************************/
void CheckMailCallback(XtPointer XtP, XtIntervalId * XtI)
{
    /* Check if ExecuteOnUpdate */
    if (ExecuteOnUpdateFlag)
	system(ExecuteOnUpdate);

    /* Check mailbox */
    if (TimeStampMode && CheckTimeStamp(MailBoxes, NumOfMailBoxes))
	RedrawWindow();
    else {
	if (CheckMailBox(MailBoxes, NumOfMailBoxes))
	    RedrawWindow();
    }				/* Set timeout */
    XtAppAddTimeOut(AsmailApp, (unsigned long) (Interval * 1000),
		    (XtTimerCallbackProc) CheckMailCallback, NULL);
}
/************************************************************/
int CheckTimeStamp(char **Mbox, int NumOfMbox)
{
    struct stat St;
    int i;
    int Result = 0;
    int ExecFlag = 0;
    int BeepFlag = 0;

    if (NumOfMbox == 0)
	return 0;

    for (i = 0; i < NumOfMbox; i++) {
	/* Check if modified */
	if (!stat(Mbox[i], &St)) {
	    if (St.st_mtime >= St.st_atime) {
		/* Changed */
		if (IsNew) {
		    BeepFlag = 1;

		    if (IStatus[i] == 2 && AlwaysNewMailExecute && NewMailExecuteFlag)
			ExecFlag = 1;
		    else if (IStatus[i] != 2 && NewMailExecuteFlag)
			ExecFlag = 1;
		    IsNew = 0;
		    IStatus[i] = 2;
		    GStatus[i] = 2;
		}
		Result = 1;
	    }
	}
    }

    /* Beep and execute */
    if (BeepFlag && Beep)
	XBell(Disp, 0);
    if (ExecFlag)
	system(NewMailExecute);

    return Result;
}
/****************************************************************************/
int CheckMailBox(char **Mbox, int NumOfMbox)
{
    static int iCheckingMailbox = 0;
    int i;
    int Prev = 0;
    int oNewMailCounter = 0, nNewMailCounter = 0;
    int Result = 0;
    int BeepFlag = 0;
    int ExecFlag = 0;

  if (!iCheckingMailbox) {
	iCheckingMailbox = 1;
	for (i = 0; i < NumOfMbox; i++) {
	    Prev = GStatus[i];
        oNewMailCounter = NewMailCounter[i];
/************************************************************/
	    if (IsMail(Mbox[i], i)) { /* There is a kind of mail waiting */
            if ((nNewMailCounter = IsNewMail(Mbox[i], i))) {
/* Allow for shrinking new mail counts
		          if (nNewMailCounter > oNewMailCounter) {
*/
		          if (nNewMailCounter != oNewMailCounter) {
                      if (IsNew) {
                          if (IStatus[i] == 2 && AlwaysNewMailExecute && NewMailExecuteFlag)
                              ExecFlag = 1;
                          else if (IStatus[i] != 2 && NewMailExecuteFlag)
                              ExecFlag = 1;
                          BeepFlag = 1;
                          IsNew = 0;
                          if (!IsPopBox(Mbox[i]))
                              NewMailCounter[i] = nNewMailCounter;
                      }
                      IStatus[i] = 2;
                      GStatus[i] = 2;

                  } else {
                      IsNew = 1;
                      if (IStatus[i] == 3)
                          GStatus[i] = 1;
                      else
                          GStatus[i] = 2;
                  }

            } else {
                IsNew = 1;
                IStatus[i] = 1;
                GStatus[i] = 1;
                NewMailCounter[i] = 0;
            }
	    } else {
            IsNew = 1;
            IStatus[i] = 0;
            GStatus[i] = 0;
            NewMailCounter[i] = 0;
            MailCounter[i] = 0;
	    }
/************************************************************/
	    iCheckingMailbox = 0;
	    Result |= (Prev != GStatus[i]);
	}
  }
  /* Check for execute and beep */
  if (ExecFlag)
      system(NewMailExecute);
  if (Beep && BeepFlag)
      XBell(Disp, 0);

  return Result;
}

/****************************************************************************/
int IsNewMail(char *Mbox, int index)
{
    FILE *MailFile = 0;
    char Buffer[30] = "";
    int NewMailn = 0;
    
    if (IsPopBox(Mbox))
        return NewMailCounter[index];
    
    MailFile = fopen(Mbox, "r");
    MailCounter[index] = 0;

    if (MailFile == NULL)
	   return 0;

    while (!feof(MailFile)) {
	   fgets(Buffer, 29, MailFile);
       if (!strncmp(Buffer, "From ", 5)) {
           MailCounter[index]++;
               NewMailn++;
/* Changes meaning of "new mail" to "unread mail"
       } else if (!strncmp(Buffer, "Status: RO", 10))
*/
       } else if (!strncmp(Buffer, "Status: R", 9))
           NewMailn--;
    }
    fclose(MailFile);

    return (NewMailn);
}
/****************************************************************************/
int IsPopBox(char *string)
{
    if ((string[0] == ':' && string[1] == ':') ||
        (string[0] == '>' && string[1] == '>'))
        return 1;
    else
        return 0;
}
/****************************************************************************/
int IsMail(char *Mbox, int index)
{
    FILE *MailFile = 0;
    int Ret = 0;
    char *Exec;
    int ReturnVal, NewMailn, OldMails;

    if (IsPopBox(Mbox)) {
        Exec = (char *)malloc(strlen(Mbox));
        strncpy(Exec, Mbox, strlen(Mbox)+1);
        
        if (Exec == NULL)
            return 0;
        ReturnVal = pop(Exec, &NewMailn, &OldMails);
        free(Exec);
        
        if (ReturnVal >= 0) {
            MailCounter[index] = NewMailn;
            NewMailCounter[index] = NewMailn - OldMails;
            return ReturnVal;
        } else
            return 0;
    }
        
    MailFile = fopen(Mbox, "r");
    if (MailFile == 0)
	   return 0;
    else {
	   fgetc(MailFile);
       if (feof(MailFile))
           Ret = 0;
       else
           Ret = 1;

       fclose(MailFile);
    }
    return Ret;
}

/****************************************************************************/
int GetGStatus(void)
{
    int i;
    int j = 0;

    /* Get highest GStatus */
    for (i = 0; i < NumOfMailBoxes; i++)
	if (j < GStatus[i])
	    j = GStatus[i];
    return j;
}

/****************************************************************************/
XpmIcon *GetXPM(char **data)
{
	XpmIcon *icon = (XpmIcon *) calloc(1, sizeof(XpmIcon));

	icon->attributes.valuemask |= XpmCloseness;
	icon->attributes.closeness = XPMCLOSENESS;

	XPMError(XpmCreatePixmapFromData(Disp, Root,
					 data,
					 &icon->pixmap,
					 &icon->mask,
					 &icon->attributes));

	icon->next = (struct XpmIcon *) icon;

	return icon;
}
/****************************************************************************/
void LoadXPMFile(XpmIcon * Icon, char *XpmFile)
{
    char *Home, *realfilename;

    /* home dir ? */
    if (strncmp(XpmFile, "~/", 2) == 0) {
	/* get home */
	Home = getenv("HOME");
	if (Home == NULL)
	    Home = "./";
	/* alloc it */
	realfilename = (char *) safemalloc(strlen(Home) + strlen(&XpmFile[2]) + 3);
	/* put it */
	strcpy(realfilename, Home);
	strcat(realfilename, "/");
	strcat(realfilename, &XpmFile[2]);
    } else
	realfilename = XpmFile;

	Icon->attributes.valuemask |= XpmCloseness;
	Icon->attributes.closeness = XPMCLOSENESS;

    XPMError(XpmReadFileToPixmap(Disp, Root,
			    realfilename,
			    &Icon->pixmap,
			    &Icon->mask,
			    &Icon->attributes));

    /* Check if mask is ok */
/* Test only if window is shaped
    if (Icon->mask == 0) {
*/
    if (Shape && Icon->mask == 0) {
	fprintf(stderr, "asmail: '%s' does not have a transparent color\n", realfilename);
	exit(1);
    }
}
/****************************************************************************/
void FreeXPM(XpmIcon *Icon)
{
	XpmFreeAttributes(&Icon->attributes);
	XpmFree((char *)Icon->pixmap);
	XpmFree((char *)Icon->mask);

	Icon = NULL;
}
/****************************************************************************/
void XPMError(int Code)
{
    switch (Code) {
    case 0:
	break;
    case 1:
    case -4:
	fprintf(stderr, "asmail: not enough free color cells\n");
	break;
    case -1:
    case -2:
	fprintf(stderr, "asmail: could not load xpm\n");
	break;
    case -3:
	fprintf(stderr, "asmail: not enough memory free\n");
	break;
    default:
	fprintf(stderr, "asmail: unknown xpm-error\n");
	break;
    }
    if (Code != 0)
	exit(1);
}

/****************************************************************************/
Pixel GetColor(char *ColorName)
{
    XColor Color;
    XWindowAttributes Attributes;

    XGetWindowAttributes(Disp, Root, &Attributes);
    Color.pixel = 0;

    if (!XParseColor(Disp, Attributes.colormap, ColorName, &Color))
	fprintf(stderr, "asmail: can't parse %s\n", ColorName);
    else if (!XAllocColor(Disp, Attributes.colormap, &Color))
	fprintf(stderr, "asmail: can't allocate %s\n", ColorName);

    return Color.pixel;
}
/* END OF FILE ************************************************************* */
