xutils.c

/* This is free software.  See LICENSE for terms.
 * Copyright 2004 - 2015, Patricia Kirk.
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/param.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

#if !defined (__linux__)
#include <net/if_media.h>
#endif

#if defined(__OpenBSD__)
#include <sys/socket.h>
#endif

#include <net/if.h>	/* Needs sys/socket.h first on OpenBSD */

#if defined (__linux__)
#include <time.h>
#include <sys/time.h>
#endif

#include "proto.h"
#include "pixmaps.h"

extern struct global g;
extern struct data fresh;

extern Pixmap icondraw, iconmask, maindraw, mainmask;
extern Display *maindisplay;
extern Window rootwindow, iconwindow, mainwindow;
extern GC icongc, maingc;
extern int mainscreen, colordepth;

struct pixstack *iconpix=NULL, *mainpix=NULL;

char *filename=NULL;
char *colornames=NULL;
char smalbuf[SMALBUF];

/* Allocate and initialize a pixstack struct and add it to the list.
 * pix->index = 0 is the background color: backxpm, tile or pixmap or pseudo trans
 *            = 1 is the foreground color: forexpm in the foreground color
 *            = 2 is the shutdown color: forexpm in #fFfF01010101
 *            = n is forexpm in any additional colors
 * 1 and 2 are switched in the list when execShutdown() is called.
 * The returned value is the position on the pixstack
 * list of the font in the color requested.
 */
int pixAlloc(char *colorname) {
    struct pixstack *i, *ipix=NULL, *iprev=NULL, *m, *mpix=NULL, *mprev=NULL;
    int index=0, found=0;

    CALLOC(ipix, 1, sizeof(struct pixstack), "pixAlloc");
    CALLOC(mpix, 1, sizeof(struct pixstack), "pixAlloc");
    STRDUP(ipix->colorname, colorname);
    STRDUP(mpix->colorname, colorname);
    if (!iconpix) {
	ipix->index = index;
	mpix->index = index;
	iconpix = ipix;
	mainpix = mpix;
    } else {
	i = iconpix;
	m = mainpix;
	while (i && m) {
	    iprev = i;
	    mprev = m;
	    if (!strcasecmp(i->colorname, colorname)) {
		FREE(ipix->colorname);
		FREE(mpix->colorname);
		FREE(ipix);
		FREE(mpix);
		found = 1;
		break;
	    }
	    i = i->next;
	    m = m->next;
	    index++;
	}
	if (!found) {
	    ipix->index = index;
	    mpix->index = index;
	    iprev->next = ipix;
	    mprev->next = mpix;
	}
    }
    return index;
}

/* There's only one mask at pixlist->next.
 * The others are all freed at startup
 * since they're all the same.
 */
void putChar(struct lineopts *line, struct pixstack *pix, int offset, int width, int left) {
    struct pixstack *list;
    Pixmap mask, draw;
    GC gc;
    int xmag, div;

    if (!g.iconized || line->panel == 1) {
	if (!g.iconized) {
	    list = mainpix;
	    mask = mainmask;
	    draw = maindraw;
	    gc = maingc;
	    xmag = g.xmag;
	    div = 1;
	} else {
	    list = iconpix;
	    mask = iconmask;
	    draw = icondraw;
	    gc = icongc;
	    xmag = 1;
	    div = g.xmag;
	}
	/* (display, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y) */
	if (g.pixmap)
	    XCopyPlane(maindisplay, list->next->mask, mask, gc,
		    ((CHARWIDTH * xmag) * (offset % 10)),
		    ((CHARHEIGHT * xmag) * (offset / 10)),
		    (width * xmag), (CHARHEIGHT * xmag),
		    (line->left + (left * xmag)), (line->upper / div), 1);
	XCopyArea(maindisplay, pix->pixmap, draw, gc,
		((CHARWIDTH * xmag) * (offset % 10)),
		((CHARHEIGHT * xmag) * (offset / 10)),
		(width * xmag), (CHARHEIGHT * xmag),
		(line->left + (left * xmag)), (line->upper / div));
    }
}

void drawSlider(struct lineopts *line) {
    struct pixstack *pix, *vpix, *list;
    Pixmap mask, draw;
    GC gc;
    int i, j, xmag, div;
    static int k, l, m, init=1;
    static struct pixstack *vipix, *vmpix, *ipix, *mpix;

    if (init) {
	/* Use the shutdown color for the
	 * current volume indicator.
	 */
	vipix = iconpix->next->next;
	vmpix = mainpix->next->next;
	init = line->color ? line->color : FORE;
	ipix = iconpix;
	mpix = mainpix;
	while (init) {
	    ipix = ipix->next;
	    mpix = mpix->next;
	    init--;
	}
	k = '|' - ASCEN;
	l = '.' - ASCEN;
	m = 'i' - ASCEN;
    }
    if (!g.iconized || line->panel == 1) {
	if (!g.iconized) {
	    list = mainpix;
	    pix = mpix;
	    vpix = vmpix;
	    mask = mainmask;
	    draw = maindraw;
	    gc = maingc;
	    xmag = g.xmag;
	    div = 1;
	} else {
	    list = iconpix;
	    pix = ipix;
	    vpix = vipix;
	    mask = iconmask;
	    draw = icondraw;
	    gc = icongc;
	    xmag = 1;
	    div = g.xmag;
	}
	/* (display, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y) */
	putChar(line, pix, '<' - ASCEN, CHARWIDTH, 2);
	/* Add a bar across the open end. */
	if (g.pixmap)
	    XCopyPlane(maindisplay, list->next->mask, mask, gc,
		    ((CHARWIDTH * xmag) * (k % 10)),
		    ((CHARHEIGHT * xmag) * (k / 10)),
		    xmag, (CHARHEIGHT * xmag),
		    (line->left + (5 * xmag)), (line->upper / div), 1);
	XCopyArea(maindisplay, pix->pixmap, draw, gc,
		((CHARWIDTH * xmag) * (k % 10)),
		((CHARHEIGHT * xmag) * (k / 10)),
		xmag, (CHARHEIGHT * xmag),
		(line->left + (5 * xmag)), (line->upper / div));
	j = parseStrtol(line->value, TRUE, "drawSlider");
	j = (fresh.volume[j] / 2) + 6 + (fresh.volume[j] > 98);
	for (i = 6; i < 58; i++) {
	    if (i == j) {
		/* Current volume level marker in red. */
		putChar(line, vpix, k, 1, i);
	    } else if ((i - 6) && i < 50 && !((i - 6) % 10)) {
		/* Make the pips. */
		if (g.pixmap) {
		    XCopyPlane(maindisplay, list->next->mask, mask, gc,
			    ((CHARWIDTH * xmag) * (m % 10)),
			    ((CHARHEIGHT * xmag) * (m / 10)) + (4 * xmag),
			    xmag, ((CHARHEIGHT - 4) * xmag),
			    (line->left + (i * xmag)), (line->upper / div) + (4 * xmag), 1);
		}
		XCopyArea(maindisplay, pix->pixmap, draw, gc,
			((CHARWIDTH * xmag) * (m % 10)),
			((CHARHEIGHT * xmag) * (m / 10)) + (4 * xmag),
			xmag, ((CHARHEIGHT - 4) * xmag),
			(line->left + (i * xmag)), (line->upper / div) + (4 * xmag));
		goto putell;
	    } else {
		putChar(line, pix, l, 1, i);
		/* The top line. */
putell:
		if (g.pixmap)
		    XCopyPlane(maindisplay, list->next->mask, mask, gc,
			    ((CHARWIDTH * xmag) * (l % 10)),
			    ((CHARHEIGHT * xmag) * (l / 10)) + (6 * xmag),
			    xmag, xmag,
			    (line->left + (i * xmag)), (line->upper / div), 1);
		XCopyArea(maindisplay, pix->pixmap, draw, gc,
			((CHARWIDTH * xmag) * (l % 10)),
			((CHARHEIGHT * xmag) * (l / 10)) + (6 * xmag),
			xmag, xmag,
			(line->left + (i * xmag)), (line->upper / div));
	    }
	}
	putChar(line, pix, '>' - ASCEN, CHARWIDTH, 58);
	if (g.pixmap)
	    XCopyPlane(maindisplay, list->next->mask, mask, gc,
			((CHARWIDTH * xmag) * (k % 10)),
			((CHARHEIGHT * xmag) * (k / 10)),
			xmag, (CHARHEIGHT * xmag),
			(line->left + (58 * xmag)), (line->upper / div), 1);
	XCopyArea(maindisplay, pix->pixmap, draw, gc,
		    ((CHARWIDTH * xmag) * (k % 10)),
		    ((CHARHEIGHT * xmag) * (k / 10)),
		    xmag, (CHARHEIGHT * xmag),
		    (line->left + (58 * xmag)), (line->upper / div));
    }
}

/* Copy an updated line into the draw window.
 */
void lineToDraw(char *buffer, struct lineopts *line) {
    int i, j, k[SHOWBUF], l=0, m=61, color;
    struct pixstack *pix;
    char showbuf[SHOWBUF];
    const int charwidth[] = {
	5, 5, 0, 1, 3, 5, 5, 5, 5, 1,	/* 39 */
	3, 3, 5, 5, 2, 4, 1, 3, 4, 4,
	4, 4, 4, 4, 4, 4, 4, 4, 1, 1,	/* 59 */
	4, 4, 4, 5, 5, 5, 5, 5, 5, 5,
	5, 5, 5, 3, 5, 5, 5, 5, 5, 5,	/* 79 */
	5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
	5, 2, 3, 2, 5, 5, 2, 4, 4, 4,	/* 99 */
	4, 4, 4, 4, 4, 1, 4, 4, 3, 5,
	4, 4, 4, 4, 4, 4, 3, 4, 5, 5,	/* 119 */
	5, 5, 5, 4, 1, 4, 5, 3, 0, 0
    };

    /* First count the unfilled columns and the
     * number of blank areas indicated with PIPEs.
     */
    for (i = 0; i < SHOWBUF; i++) {
	if (!(j = buffer[i]))
	    break;
	if (j == PIPE)
	    l++;
	else
	    m -= (charwidth[j - 30] + 1);
	if (m < 0) {
	    m = 0;
	    break;
	}
    }
    /* There may not be a PIPE if it's a user defined line. */
    if (l)
	l = m / l;
    else
	l = m;
    l += !l;
    m = 2;
    /* Now l is the width of a blank area and m
     * is the offset of the first character.
     * Calc the x offset for each of the
     * other characters.
     */
    for (i = 0; i < SHOWBUF; i++) {
	j = buffer[i];
	if (!j || m > 61)
	    break;
	k[i] = m;
	if (j == PIPE) {
	    m += l;
	    showbuf[i] = SPACE;
	} else {
	    m += charwidth[j - 30] + 1;
	    showbuf[i] = j;
	}
    }
    showbuf[i] = '\0';
    color = line->color ? line->color : FORE;
    if (g.iconized)
	pix = iconpix;
    else
	pix = mainpix;
    while (color) {
	pix = pix->next;
	color--;
    }
    for (i = 0; i < SHOWBUF; i++) {
	if (!showbuf[i])
	    break;
	j = charwidth[(int)showbuf[i] - 30];
	while (j && ((j + k[i]) > 62))
	    j--;
	if (!j)
	    continue;
	l = showbuf[i] - ASCEN;
	putChar(line, pix, l, j, k[i]);
    }
}

void formatLine(int direct, struct lineopts *line, double value, char buffer[]) {
    int display, i, j;

    if (direct) {
	/* Used for cpu splits, load averages and
	 * memory when expressed as percentage.
	 */
	i = strlen(buffer) - 1;
	if (buffer[i] == '%')
	    i--;
	buffer[i--] = ((int)value % 10) + AZERO;			/* hundredths */
	value /= 10;
	buffer[i--] = ((int)value % 10) + AZERO;			/* tenths */
	value /= 10;
	i--;
	buffer[i--] = ((int)value % 10) + AZERO;			/* ones */
	value /= 10;
	buffer[i--] = (int)value ? ((int)value % 10) + AZERO : SPACE;	/* tens */
	value /= 10;
	buffer[i] = (int)value ? ((int)value % 10) + AZERO : SPACE;	/* hundreds */
    } else {
	/* Used for free and used memory, free and used swap,
	 * share, buffer and cache memory.  First determine
	 * the units of measure and enter it in the
	 * right most space if required.
	 */
	if (value > 999999999999LL) {
	    display = value / 1000 / 1000 / 1000;
	    i = GIGS;
	} else if (value > 999999999L) {
	    display = value / 1000 / 1000;
	    i = MEGS;
	} else if (value > 999999L) {
	    display = value / 1000;
	    i = KEYS;
	} else {
	    display = value;
	    i = 0;
	}
	j = 12;
	if (i)
	    buffer[j--] = i;
	for (i = j; i > 4; i--) {
	    buffer[i] = (display % 10) + AZERO;
	    display /= 10;
	    if (!display)
		break;
	}
    }
    lineToDraw(buffer, line);
}

/* Write an entry into .../salmon.log.
 * If logbeep is set, make a brief
 * beep noise.
 */
void logMessage(char *message) {
    time_t timenow=time(NULL);
    struct tm *timeptr;
    char buffer[BUFSIZE];
    FILE *logfile;
    int i, j, k;

    snprintf(buffer, sizeof(buffer), "%s/salmon.log", g.tempdir);
    if ((logfile = fopen(buffer, "a"))) {
	timeptr = localtime(&timenow);
	strftime(buffer, sizeof(buffer), "%m/%d %H:%M:%S", timeptr);
	fprintf(logfile, "%s #%05d, %s.\n", buffer, (int)getpid(), message);
	fclose(logfile);
    }
    if (g.logbeep) {
	snprintf(smalbuf, SMALBUF, "%s", BEEPER);
	if ((logfile = fopen(smalbuf, "w"))) {
#if defined (__OpenBSD__)
	    k = 350;
	    for (i = 0; i < 10000; i++)
#else
	    k = 8000;
	    for (i = 0; i < 500; i++)
#endif
	    {
		j = i * k;
		write(fileno(logfile), &j, 1);
	    }
	    fclose(logfile);
	} else {
	    g.logbeep = 0;
	    snprintf(smalbuf, SMALBUF, "logbeep failed, %s", strerror(errno));
	    logMessage(smalbuf);
	}
    }
}

/* Used by CALLOC.
 */
void allocFailed(char *function) {

    snprintf(smalbuf, SMALBUF, "%s() failed to allocate memory", function);
    gdayMate(smalbuf, strerror(errno));
}

/* If message == NULL, exit(0) else exit(1)
 */
void gdayMate(char *message, char *errmsg) {
    char buffer[BUFSIZE];

    bzero(&buffer, BUFSIZE);
    if (message)
	snprintf(buffer, BUFSIZE, "%s", message);
    if (errmsg) {
	stringcat(buffer, ", ", BUFSIZE);
	stringcat(buffer, errmsg, BUFSIZE);
    }
    if (strlen(buffer)) {
	logMessage(buffer);
	fprintf(stderr, "\nsalmon: %s.\n\n", buffer);
    }
    if (maindisplay)
	XCloseDisplay(maindisplay);
    if (message && getpid() != g.ppid)
	sleep(1);
    exit(message != NULL);
}

int readPixmap(struct pixstack *stack, char *name) {

    stack->attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
    return (XpmReadFileToPixmap(maindisplay, rootwindow, name, &stack->pixmap,
		&stack->mask, &stack->attributes));
}

/* colorname is the name of the color from rgb.txt
 *	or hash plus a six or twelve digit hex value
 * rate is LITE or DARK from salmon.h, 1.00 or 0.55
 */
char *getColor(char *colorname, float rate) {
    XColor color;
    static char smalbuf[SMALBUF];

    bzero(&color, sizeof(color));
    if (!XParseColor(maindisplay, DefaultColormap(maindisplay, mainscreen), colorname, &color)) {
	if (!colornames) {
	    CALLOC(colornames, (20 * SMALBUF), sizeof(char), "getColor");
	}
	if (!strstr(colornames, colorname)) {
	    stringcat(colornames, colorname, (20 * SMALBUF));
	    snprintf(smalbuf, sizeof(smalbuf), "getColor() can`t parse color \"%s\"", colorname);
	    logMessage(smalbuf);
	}
	XParseColor(maindisplay, DefaultColormap(maindisplay, mainscreen), FOREGROUND, &color);
    }
    if (!XAllocColor(maindisplay, DefaultColormap(maindisplay, mainscreen), &color)) {
	snprintf(smalbuf, sizeof(smalbuf), "getColor() can`t allocate color \"%s\"", colorname);
	logMessage(smalbuf);
    }
    if (rate != LITE) {
	color.red *= rate;
	color.green *= rate;
	color.blue *= rate;
    }
    snprintf(smalbuf, sizeof(smalbuf), "#%04x%04x%04x", color.red, color.green, color.blue);
    return smalbuf;
}

void initPixlist() {
    struct pixstack *ipix=NULL, *mpix=NULL;
    int width, height, i, j, k, l, xmag;
    char **color, **xpm;
    FILE *fd;

    if (!filename) {
	CALLOC(filename, BUFPLUS, sizeof(char), "initPixlist");
    }
    if (!(color = (char **)calloc(3 * SMALBUF, sizeof(char))))
	allocFailed("initPixlist");
    for (i = 0; i < 3; i++)
	color[i] = (char *)calloc(SMALBUF, sizeof(char));
    snprintf(filename, BUFPLUS, "%s/XXXXXX", g.tempdir);
    if (mkstemp(filename) == -1)
	gdayMate("initPixlist() failed to create resize tempfile", strerror(errno));
    for (ipix = iconpix, mpix = mainpix; ipix && mpix; ipix = ipix->next, mpix = mpix->next) {
	xmag = g.xmag;
	if (g.pixmap && ipix->index)
	    snprintf(color[0], SMALBUF, ". c None");
	else
	    /* This is pointing at the background colorname. */
	    snprintf(color[0], SMALBUF, "%s%s", xpmchars[0], getColor(iconpix->colorname, LITE));
	if (!ipix->index) {
	    if (g.pixmap) {
		FREE(ipix->colorname);
		FREE(mpix->colorname);
		snprintf(color[0], SMALBUF, ". c None");
		snprintf(color[1], SMALBUF, "# c None");
		snprintf(color[2], SMALBUF, "c c None");
		if (g.panel)
		    xmag = g.xmag * 2;
	    } else {
		/* Get the foreground color for the background pixmap border. */
		snprintf(color[1], SMALBUF, "%s%s", xpmchars[1], getColor(iconpix->next->colorname, LITE));
		snprintf(color[2], SMALBUF, "%s%s", xpmchars[2], getColor(iconpix->next->colorname, DARK));
	    }
	    xpm = backxpm;
	} else {
	    snprintf(color[1], SMALBUF, "%s%s", xpmchars[1], getColor(ipix->colorname, LITE));
	    snprintf(color[2], SMALBUF, "%s%s", xpmchars[2], getColor(ipix->colorname, DARK));
	    xpm = forexpm;
	}
	/* Background or foreground xpm's from pixmaps.h */
	width = (int)strtol(xpm[0], NULL, 10);
	height = (int)strtol(strchr(xpm[0], ' '), NULL, 10);
	for (i = 0; i < 3; i++)
	    xpm[i + 1] = color[i];
	ipix->attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	if (XpmCreatePixmapFromData(maindisplay, rootwindow, xpm, &ipix->pixmap, &ipix->mask, &ipix->attributes))
	    gdayMate("initPixlist() failed to create pixmap", strerror(errno));
	if (xmag == 1) {
	    mpix->attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	    if (XpmCreatePixmapFromData(maindisplay, rootwindow, xpm, &mpix->pixmap, &mpix->mask, &mpix->attributes))
		gdayMate("initPixlist() failed to create pixmap", strerror(errno));
	} else {
	    if (!(fd = fopen(filename, "w")))
		gdayMate("initPixlist() failed to open resize tempfile", strerror(errno));
	    fprintf(fd, "/* XPM */\nstatic char *salmon[] = {\n\"%d %d 3 1\",\n",
		    width * xmag, height * xmag);
	    for (i = 0; i < 3; i++)
		fprintf(fd, "\"%s\",\n", color[i]);
	    for (i = 0; i < height; i++) {
		for (j = 0; j < xmag; j++) {
		    fprintf(fd, "\"");
		    for (k = 0; k < width; k++) {
			for (l = 0; l < xmag; l++)
			    fprintf(fd, "%c", xpm[i + 4][k]);
		    }
		    fprintf(fd, "\",\n");
		}
	    }
	    fprintf(fd, "};\n");
	    fclose(fd);
	    i = readPixmap(mpix, filename);
	    if (i)
		gdayMate("initPixlist() failed to read resized pixmap", strerror(errno));
	}
    }
    if (filename && unlink(filename)) {
	snprintf(color[0], SMALBUF, "initPixlist() failed to remove tempfile \"%s\"", filename);
	logMessage(color[0]);
    }
    if (g.pixmap && (ipix = iconpix->next->next) && (mpix = mainpix->next->next)) {
	while (ipix && mpix) {
	    /* The masks are all the same. */
	    XFreePixmap(maindisplay, ipix->mask);
	    XFreePixmap(maindisplay, mpix->mask);
	    ipix = ipix->next;
	    mpix = mpix->next;
	}
    }
    FREE(color);
    FREE(colornames);
}

/* If called with background TRUE, use resize or montage
 * to prepare the background pixmap.  If FALSE, use to
 * resize the moon graphic.
 */
void scaleGraphic(int pixmap, int background) {
    int i, x, y;
    char *buffer, *tilecmd=NULL;
    struct stat sb;
    FILE *fd;

    if (!filename) {
	CALLOC(filename, BUFPLUS, sizeof(char), "scaleGraphic");
    }
    CALLOC(buffer, BUFPLUS, sizeof(char), "scaleGraphic");
    if (background) {
	if (pixmap < 0)
	    i = snprintf(filename, BUFPLUS, "%s/t%02d.gz", g.datadir, abs(pixmap));
	else
	    i = snprintf(filename, BUFPLUS, "%s/b%02d.gz", g.datadir, pixmap);
    } else {
	i = snprintf(filename, BUFPLUS, "%s/%03d.gz", g.datadir, pixmap);
    }
    if (i >= BUFPLUS || stat(filename, &sb) == -1) {
	snprintf(buffer, BUFPLUS, "scaleGraphic() failed to stat pixmap \"%s\"", filename);
	gdayMate(buffer, strerror(errno));
    }
    snprintf(filename, BUFSIZE, "%s/XXXXXX", g.tempdir);
    if (mkstemp(filename) == -1)
	gdayMate("scaleGraphic() failed to create tempfile", strerror(errno));
    if (background) {
	if (pixmap < 0)
	    snprintf(buffer, BUFPLUS, "%s %s/t%02d.gz > %s",
		    g.unzip, g.datadir, abs(pixmap), filename);
	else
	    snprintf(buffer, BUFPLUS, "%s %s/b%02d.gz > %s",
		    g.unzip, g.datadir, pixmap, filename);
    } else {
	snprintf(buffer, BUFPLUS, "%s %s/%03d.gz > %s",
		g.unzip, g.datadir, pixmap, filename);
    }
    if ((fd = popen(buffer, "r")))
	pclose(fd);
    else
	gdayMate("scaleGraphic() failed to unzip pixmap", strerror(errno));
    if (pixmap < 0) {
	/* Using a tile.  The size is correct for the iconpix.
	 * Repeat (xmag * xmag) plus one times to fill mainpix.
	 */
	y = readPixmap(iconpix, filename);
	snprintf(buffer, BUFPLUS, "%s %dx%d ", g.tiler, g.isize, g.isize);
	x = ((strlen(filename) + 1) * ((g.xmag * g.xmag) + 1)) + strlen(buffer) + 1;
	CALLOC(tilecmd, x, sizeof(char), "scaleGraphic");
	stringcpy(tilecmd, buffer, x);
	errno = ENAMETOOLONG;
	for (i = 0; i <= (g.xmag * g.xmag); i++) {
	    stringcat(tilecmd, " ", x);
	    if ((stringcat(tilecmd, filename, x)) < 0)
		gdayMate("scaleGraphic() tile command line too long", strerror(errno));
	}
	if ((fd = popen(tilecmd, "r")))
	    pclose(fd);
	x = readPixmap(mainpix, filename);
	FREE(tilecmd);
    } else {
	if (g.xmag != 5) {
	    snprintf(buffer, BUFPLUS, "%s %dx%d %s", g.resize, g.msize, g.msize, filename);
	    if ((fd = popen(buffer, "r")))
		pclose(fd);
	    else
		gdayMate("scaleGraphic() resize command failed", strerror(errno));
	}
	if (background)
	    x = readPixmap(mainpix, filename);
	else
	    x = readPixmap(mainpix->next, filename);
	if (g.isize != g.msize) {
	    if (g.xmag != 5) {
		/* The image has already been resized once.
		 * Get a new one rather than resize it again.
		 */
		if (background)
		    snprintf(buffer, BUFPLUS, "%s %s/b%02d.gz > %s",
			    g.unzip, g.datadir, pixmap, filename);
		else
		    snprintf(buffer, BUFPLUS, "%s %s/%03d.gz > %s",
			    g.unzip, g.datadir, pixmap, filename);
		if ((fd = popen(buffer, "r")))
		    pclose(fd);
		else
		    gdayMate("scaleGraphic() resize command failed", strerror(errno));
	    }
	    snprintf(buffer, BUFPLUS, "%s %dx%d %s",
		    g.resize, g.isize, g.isize, filename);
	    if ((fd = popen(buffer, "r")))
		pclose(fd);
	}
	if (background)
	    y = readPixmap(iconpix, filename);
	else
	    y = readPixmap(iconpix->next, filename);
    }
    if (filename && unlink(filename)) {
	snprintf(buffer, BUFPLUS, "scaleGraphic() failed to remove tempfile \"%s\"", filename);
	logMessage(buffer);
    }
    FREE(buffer);
    if (x || y)
	gdayMate("scaleGraphic() failed to read pixmap", strerror(errno));
}

#if WITH_GRAPHICS
void checkMoon() {
    static int init=1;
    char *smalbuf;
    int pixmap;

    /* The moon pixmaps are 320x320 pixels.  The
     * main window will only be the correct size
     * if g.xmag is 5.  The icon window will
     * always need to be reduced to 64x64.
     */
    if (init) {
	/* If the moon is new at init, the current pixmap
	 * will be 000.gz and the screen will not be refreshed
	 * because pixmap and fresh.pixmap will match.
	 * Fix it so they never match at init.
	 */
	fresh.pixmap = -1;
	if (g.pixmap) {
	    XSetClipMask(maindisplay, icongc, iconpix->mask);
	    XSetClipMask(maindisplay, maingc, mainpix->mask);
	    /* These are only used once. */
	    XFreePixmap(maindisplay, iconpix->mask);
	    XFreePixmap(maindisplay, iconpix->pixmap);
	    XFreePixmap(maindisplay, mainpix->mask);
	    XFreePixmap(maindisplay, mainpix->pixmap);
	}
	init = 0;
    }
    CALLOC(smalbuf, SMALBUF, sizeof(char), "checkMoon");
    /* getPhase() returns ASCEN or DSCEN and phase percent in buffer. */
    getPhase(time(NULL), smalbuf);
    pixmap = (int)(roundf(strtof(strchr(smalbuf, ' '), NULL)));
    if ((int)strtol(smalbuf, NULL, 10) == DSCEN)
	/* Returned by getPhase(). */
	pixmap = 200 - pixmap;
    FREE(smalbuf);
    if (pixmap == fresh.pixmap)
	/* The pixmap hasn`t changed so go back. */
	return;
    fresh.pixmap = pixmap;
    scaleGraphic(fresh.pixmap, FALSE);
    /* These don`t change often enough to stay
     * current when the icon state is switched.
     */
    XCopyArea(maindisplay, iconpix->next->pixmap, icondraw, icongc, 0, 0, g.isize, g.isize, 0, 0);
    XCopyArea(maindisplay, mainpix->next->pixmap, maindraw, maingc, 0, 0, g.msize, g.msize, 0, 0);
}
#endif	/* WITH_GRAPHICS */

void Redraw() {
    XWindowAttributes xattribs;
    XImage *ximage;
    Pixmap mask;

    XGetWindowAttributes(maindisplay, iconwindow, &xattribs);
    g.iconized = xattribs.map_state;
    if (g.iconized) {
	if (g.pixmap) {
	    if (!g.moongraphic) {
		ximage = XGetImage(maindisplay, iconmask, 0, 0, g.isize, g.isize, colordepth, XYPixmap);
		mask = XCreateBitmapFromData(maindisplay, iconwindow, ximage->data, g.isize, g.isize);
		XDestroyImage(ximage);
		XSetClipMask(maindisplay, icongc, mask);
		XFreePixmap(maindisplay, mask);
	    }
	    XClearWindow(maindisplay, iconwindow);
	}
	XCopyArea(maindisplay, icondraw, iconwindow, icongc, 0, 0, g.isize, g.isize, 0, 0);
    } else {
	if (g.pixmap) {
	    if (!g.moongraphic) {
		ximage = XGetImage(maindisplay, mainmask, 0, 0, g.msize, g.msize, colordepth, XYPixmap);
		mask = XCreateBitmapFromData(maindisplay, mainwindow, ximage->data, g.msize, g.msize);
		XDestroyImage(ximage);
		XSetClipMask(maindisplay, maingc, mask);
		XFreePixmap(maindisplay, mask);
	    }
	    XClearWindow(maindisplay, mainwindow);
	}
	XCopyArea(maindisplay, maindraw, mainwindow, maingc, 0, 0, g.msize, g.msize, 0, 0);
    }
    g.updaterequest = 0;
}

void Update() {
    time_t timenow=time(NULL);

    if (g.refresh && (timenow - fresh.timeofdata) >= g.refresh) {
	fresh.timeofdata = timenow - (timenow % g.refresh);
#if WITH_TIMESYNC
	if (g.timeport && !g.timeserver)
	    checkSync();
#endif	/* WITH_TIMESYNC */
	g.updaterequest = 1;
    }
    checkEvents();
#if WITH_REMEXEC || WITH_TIMESYNC
    /* Check for execute requests and if
     * time server, check the request queue.
     */
    checkSocket();
#endif	/* WITH_REMEXEC || WITH_TIMESYNC */
    checkEcho();
#if WITH_CONTROL
    checkCtl();
#endif	/* WITH_CONTROL */
    if (g.updaterequest) {
#if WITH_GRAPHICS
	if (g.moongraphic)
	    checkMoon();
	else
#endif	/* WITH_GRAPHICS */
	    checkLines();
	Redraw();
    }
}
* * o * *