Enable classic mode.
[dockapps.git] / wmbiff / wmbiff / wmbiff.c
blob8ed642950b09c40e2b4fbca42b3ed1f89070bfb2
1 /* $Id: wmbiff.c,v 1.70 2005/10/07 03:07:58 bluehal Exp $ */
3 // typedef int gcry_error_t;
5 #ifdef HAVE_CONFIG_H
6 #include <config.h>
7 #endif
9 #include <time.h>
10 #include <ctype.h>
12 #ifdef HAVE_POLL
13 #include <poll.h>
14 #else
15 #include <sys/time.h>
16 #endif
18 #include <sys/wait.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <signal.h>
23 #include <X11/Xlib.h>
24 #include <X11/xpm.h>
25 #include <X11/cursorfont.h>
26 #include <X11/XKBlib.h>
28 #include <errno.h>
29 #include <string.h>
31 #include "../wmgeneral/wmgeneral.h"
32 #include "../wmgeneral/misc.h"
34 #include "Client.h"
35 #include "charutil.h"
36 #include "MessageList.h"
38 #ifdef USE_DMALLOC
39 #include <dmalloc.h>
40 #endif
43 #include "wmbiff-master-led.xpm"
44 #include "wmbiff-classic-master-led.xpm"
45 static char wmbiff_mask_bits[64 * 64];
46 static const int wmbiff_mask_width = 64;
47 // const int wmbiff_mask_height = 64;
49 #define CHAR_WIDTH 5
50 #define CHAR_HEIGHT 7
52 #define BLINK_TIMES 8
53 #define DEFAULT_SLEEP_INTERVAL 20000
54 #define BLINK_SLEEP_INTERVAL 200
55 #define DEFAULT_LOOP 5
57 #define MAX_NUM_MAILBOXES 40
58 static mbox_t mbox[MAX_NUM_MAILBOXES];
60 /* this is the normal pixmap. */
61 static const char *skin_filename = "wmbiff-master-led.xpm";
62 static const char *classic_skin_filename = "wmbiff-classic-master-led.xpm";
63 /* global notify action taken (if globalnotify option is set) */
64 static const char *globalnotify = NULL;
66 /* path to search for pixmaps */
67 /* /usr/share/wmbiff should have the default pixmap. */
68 /* /usr/local/share/wmbiff if compiled locally. */
69 /* / is there in case a user wants to specify a complete path */
70 /* . is there for development. */
71 static const char *skin_search_path = DEFAULT_SKIN_PATH;
72 /* for gnutls */
73 const char *certificate_filename = NULL;
74 /* gnutls: specify the priorities to use on the ciphers, key exchange methods,
75 macs and compression methods. */
76 const char *tls = NULL;
78 /* it could be argued that a better default exists. */
79 #define DEFAULT_FONT "-*-fixed-*-r-*-*-10-*-*-*-*-*-*-*"
80 static const char *font = NULL;
82 int debug_default = DEBUG_ERROR;
84 /* color from wmbiff's xpm, down to 24 bits. */
85 const char *foreground = "white"; /* foreground white */
86 const char *background = "#505075"; /* background blue */
87 static const char *highlight = "red";
88 const char *foreground_classic = "#21B3AF"; /* classic foreground cyan */
89 const char *background_classic = "#202020"; /* classic background gray */
90 static const char *highlight_classic = "yellow"; /* classic highlight color */
91 int SkipCertificateCheck = 0;
92 int Relax = 0; /* be not paranoid */
93 static int notWithdrawn = 0;
95 static unsigned int num_mailboxes = 1;
96 static const int x_origin = 5;
97 static const int y_origin = 5;
98 static int forever = 1; /* keep running. */
99 unsigned int custom_skin = 0; /* user has choose a custom skin */
100 static int classic_mode = 0; /* use classic behaviour/theme of wmbiff */
102 extern Window win;
103 extern Window iconwin;
105 Cursor busy_cursor, ready_cursor;
107 static __inline /*@out@ */ void *
108 malloc_ordie(size_t len)
110 void *ret = malloc(len);
111 if (ret == NULL) {
112 fprintf(stderr, "unable to allocate %d bytes\n", (int) len);
113 abort();
115 return (ret);
118 /* where vertically the mailbox sits for blitting characters. */
119 static int mbox_y(unsigned int mboxnum)
121 return ((11 * mboxnum) + y_origin);
124 /* Read a line from a file to obtain a pair setting=value
125 skips # and leading spaces
126 NOTE: if setting finish with 0, 1, 2, 3 or 4 last char are deleted and
127 index takes its value... if not index will get -1
128 Returns -1 if no setting=value
130 static int ReadLine(FILE * fp, /*@out@ */ char *setting,
131 /*@out@ */ char *value, /*@out@ */ int *mbox_index)
133 char buf[BUF_SIZE];
134 char *p, *q;
135 int len;
137 *setting = '\0';
138 *value = '\0';
139 *mbox_index = -1;
141 if (!fp || feof(fp))
142 return -1;
144 if (!fgets(buf, BUF_SIZE - 1, fp))
145 return -1;
147 len = strlen(buf);
149 if (buf[len - 1] == '\n') {
150 buf[len - 1] = '\0'; /* strip linefeed */
153 StripComment(buf);
155 if (!(p = strtok(buf, "=")))
156 return -1;
157 if (!(q = strtok(NULL, "\n")))
158 return -1;
160 /* Chg - Mark Hurley
161 * Date: May 8, 2001
162 * Removed for loop (which removed leading spaces)
163 * Leading & Trailing spaces need to be removed
164 * to Fix Debian bug #95849
166 FullTrim(p);
167 FullTrim(q);
169 /* strcpy(setting, p); nspring replaced with sscanf dec 2002 */
170 strcpy(value, q);
172 if (sscanf(p, "%[_a-z.]%d", setting, mbox_index) == 2) {
173 /* mailbox-specific configuration, ends in a digit */
174 if (*mbox_index < 0 || *mbox_index >= MAX_NUM_MAILBOXES) {
175 DMA(DEBUG_ERROR, "invalid mailbox number %d\n", *mbox_index);
176 exit(EXIT_FAILURE);
178 } else if (sscanf(p, "%[a-z]", setting) == 1) {
179 /* global configuration, all text. */
180 *mbox_index = -1;
181 } else {
182 /* we found an uncommented line that has an equals,
183 but is non-alphabetic. */
184 DMA(DEBUG_INFO, "unparsed setting %s\n", p);
185 return -1;
188 DMA(DEBUG_INFO, "@%s.%d=%s@\n", setting, *mbox_index, value);
189 return 1;
192 struct path_demultiplexer {
193 const char *id; /* followed by a colon */
194 int (*creator) ( /*@notnull@ */ Pop3 pc, const char *path);
197 static struct path_demultiplexer paths[] = {
198 {"pop3:", pop3Create},
199 {"pop3s:", pop3Create},
200 {"shell:", shellCreate},
201 {"imap:", imap4Create},
202 {"imaps:", imap4Create},
203 {"sslimap:", imap4Create},
204 {"maildir:", maildirCreate},
205 {"mbox:", mboxCreate},
206 {NULL, NULL}
210 static void parse_mbox_path(unsigned int item)
212 int i;
213 /* find the creator */
214 for (i = 0;
215 paths[i].id != NULL
216 && strncasecmp(mbox[item].path, paths[i].id, strlen(paths[i].id));
217 i++);
218 /* if found, execute */
219 if (paths[i].id != NULL) {
220 if (paths[i].creator((&mbox[item]), mbox[item].path) != 0) {
221 DMA(DEBUG_ERROR, "creator for mailbox %u returned failure\n",
222 item);
224 } else {
225 /* default are mbox */
226 mboxCreate((&mbox[item]), mbox[item].path);
230 static int Read_Config_File(char *filename, int *loopinterval)
232 FILE *fp;
233 char setting[BUF_SMALL], value[BUF_SIZE];
234 int mbox_index;
235 unsigned int i;
236 char *skin = NULL;
238 if (classic_mode) {
239 skin = strdup_ordie(skin_filename);
242 if (!(fp = fopen(filename, "r"))) {
243 DMA(DEBUG_ERROR, "Unable to open %s, no settings read: %s\n",
244 filename, strerror(errno));
245 return 0;
247 while (!feof(fp)) {
248 /* skanky: -1 can represent an unparsed line
249 or an error */
250 if (ReadLine(fp, setting, value, &mbox_index) == -1)
251 continue;
253 /* settings that can be global go here. */
254 if (!strcmp(setting, "interval")) {
255 *loopinterval = atoi(value);
256 continue;
257 } else if (!strcmp(setting, "askpass")) {
258 const char *askpass = strdup_ordie(value);
259 if (mbox_index == -1) {
260 DMA(DEBUG_INFO, "setting all to askpass %s\n", askpass);
261 for (i = 0; i < MAX_NUM_MAILBOXES; i++)
262 mbox[i].askpass = askpass;
263 } else {
264 mbox[mbox_index].askpass = askpass;
266 continue;
267 } else if (!strcmp(setting, "skinfile")) {
268 skin_filename = strdup_ordie(value);
269 custom_skin = 1;
270 continue;
271 } else if (!strcmp(setting, "certfile")) { /* not yet supported */
272 certificate_filename = strdup_ordie(value);
273 continue;
274 } else if (!strcmp(setting, "globalnotify")) {
275 globalnotify = strdup_ordie(value);
276 continue;
277 } else if (!strcmp(setting, "tls")) {
278 tls = strdup_ordie(value);
279 continue;
280 } else if (mbox_index == -1) {
281 DMA(DEBUG_INFO, "Unknown global setting '%s'\n", setting);
282 continue; /* Didn't read any setting.[0-5] value */
285 if (mbox_index >= MAX_NUM_MAILBOXES) {
286 DMA(DEBUG_ERROR, "Don't have %d mailboxes.\n", mbox_index);
287 continue;
290 if (1U + mbox_index > num_mailboxes
291 && mbox_index + 1 <= MAX_NUM_MAILBOXES) {
292 num_mailboxes = 1U + mbox_index;
295 /* now only local settings */
296 if (!strcmp(setting, "label.")) {
297 if (strlen(value) + 1 > BUF_SMALL) {
298 DMA(DEBUG_ERROR,
299 "Mailbox %i label string '%s' is too long.\n",
300 mbox_index, value);
301 continue;
302 } else {
303 strncpy(mbox[mbox_index].label, value, BUF_SMALL - 1);
305 } else if (!strcmp(setting, "path.")) {
306 if (strlen(value) + 1 > BUF_BIG) {
307 DMA(DEBUG_ERROR,
308 "Mailbox %i path string '%s' is too long.\n",
309 mbox_index, value);
310 continue;
311 } else {
312 strncpy(mbox[mbox_index].path, value, BUF_BIG - 1);
314 } else if (!strcmp(setting, "notify.")) {
315 if (strlen(value) + 1 > BUF_BIG) {
316 DMA(DEBUG_ERROR,
317 "Mailbox %i notify string '%s' is too long.\n",
318 mbox_index, value);
319 continue;
320 } else {
321 strncpy(mbox[mbox_index].notify, value, BUF_BIG - 1);
323 } else if (!strcmp(setting, "action.")) {
324 if (strlen(value) + 1 > BUF_BIG) {
325 DMA(DEBUG_ERROR,
326 "Mailbox %i action string '%s' is too long.\n",
327 mbox_index, value);
328 continue;
329 } else {
330 strncpy(mbox[mbox_index].action, value, BUF_BIG - 1);
332 } else if (!strcmp(setting, "action_disconnected.")) {
333 if (strlen(value) + 1 > BUF_BIG) {
334 DMA(DEBUG_ERROR,
335 "Mailbox %i action_disconnected string '%s' is too long.\n",
336 mbox_index, value);
337 continue;
338 } else {
339 strncpy(mbox[mbox_index].actiondc, value, BUF_BIG - 1);
341 } else if (!strcmp(setting, "action_new_mail.")) {
342 if (strlen(value) + 1 > BUF_BIG) {
343 DMA(DEBUG_ERROR,
344 "Mailbox %i action_new_mail string '%s' is too long.\n",
345 mbox_index, value);
346 continue;
347 } else {
348 strncpy(mbox[mbox_index].actionnew, value, BUF_BIG - 1);
350 } else if (!strcmp(setting, "action_no_new_mail.")) {
351 if (strlen(value) + 1 > BUF_BIG) {
352 DMA(DEBUG_ERROR,
353 "Mailbox %i action_no_new_mail string '%s' is too long.\n",
354 mbox_index, value);
355 continue;
356 } else {
357 strncpy(mbox[mbox_index].actionnonew, value, BUF_BIG - 1);
359 } else if (!strcmp(setting, "interval.")) {
360 mbox[mbox_index].loopinterval = atoi(value);
361 } else if (!strcmp(setting, "buttontwo.")) {
362 if (strlen(value) + 1 > BUF_BIG) {
363 DMA(DEBUG_ERROR,
364 "Mailbox %i buttontwo string '%s' is too long.\n",
365 mbox_index, value);
366 continue;
367 } else {
368 strncpy(mbox[mbox_index].button2, value, BUF_BIG - 1);
370 } else if (!strcmp(setting, "fetchcmd.")) {
371 if (strlen(value) + 1 > BUF_BIG) {
372 DMA(DEBUG_ERROR,
373 "Mailbox %i fetchcmd string '%s' is too long.\n",
374 mbox_index, value);
375 continue;
376 } else {
377 strncpy(mbox[mbox_index].fetchcmd, value, BUF_BIG - 1);
379 } else if (!strcmp(setting, "fetchinterval.")) {
380 mbox[mbox_index].fetchinterval = atoi(value);
381 } else if (!strcmp(setting, "debug.")) {
382 int debug_value = debug_default;
383 if (strcasecmp(value, "all") == 0) {
384 debug_value = DEBUG_ALL;
386 /* could disable debugging, but I want the command
387 line argument to provide all information
388 possible. */
389 mbox[mbox_index].debug = debug_value;
390 } else {
391 DMA(DEBUG_INFO, "Unknown setting '%s'\n", setting);
394 (void) fclose(fp);
396 if (!tls)
397 // use GnuTLS's default ciphers.
398 tls = "NORMAL";
400 if (classic_mode && skin && !strcmp(skin, skin_filename))
401 skin_filename = classic_skin_filename;
403 if (skin)
404 free(skin);
406 for (i = 0; i < num_mailboxes; i++)
407 if (mbox[i].label[0] != '\0')
408 parse_mbox_path(i);
409 return 1;
413 static void init_biff(char *config_file)
415 #ifdef HAVE_GCRYPT_H
416 gcry_error_t rc;
417 #endif
418 int loopinterval = DEFAULT_LOOP;
419 unsigned int i;
421 for (i = 0; i < MAX_NUM_MAILBOXES; i++) {
422 memset(mbox[i].label, 0, BUF_SMALL);
423 memset(mbox[i].path, 0, BUF_BIG);
424 memset(mbox[i].notify, 0, BUF_BIG);
425 memset(mbox[i].action, 0, BUF_BIG);
426 memset(mbox[i].actiondc, 0, BUF_BIG);
427 memset(mbox[i].actionnew, 0, BUF_BIG);
428 memset(mbox[i].actionnonew, 0, BUF_BIG);
429 memset(mbox[i].button2, 0, BUF_BIG);
430 memset(mbox[i].fetchcmd, 0, BUF_BIG);
431 mbox[i].loopinterval = 0;
432 mbox[i].getHeaders = NULL;
433 mbox[i].releaseHeaders = NULL;
434 mbox[i].debug = debug_default;
435 mbox[i].askpass = DEFAULT_ASKPASS;
438 #ifdef HAVE_GCRYPT_H
439 /* gcrypt is a little strange, in that it doesn't
440 * seem to initialize its memory pool by itself.
441 * I believe we *expect* "Warning: using insecure memory!"
443 /* gcryctl_disable_secmem gets called before check_version -- in a message on
444 gcrypt-devel august 17 2004. */
445 if ((rc = gcry_control(GCRYCTL_DISABLE_SECMEM, 0)) != 0) {
446 DMA(DEBUG_ERROR,
447 "Error: tried to disable gcrypt warning and failed: %d\n"
448 " Message: %s\n" " libgcrypt version: %s\n", rc,
449 gcry_strerror(rc), gcry_check_version(NULL));
452 /* recently made a requirement, section 2.4 of gcrypt manual */
453 if (gcry_check_version("1.1.42") == NULL) {
454 DMA(DEBUG_ERROR, "Error: incompatible gcrypt version.\n");
456 #endif
458 DMA(DEBUG_INFO, "config_file = %s.\n", config_file);
459 if (!Read_Config_File(config_file, &loopinterval)) {
460 char *m;
461 /* setup defaults if there's no config */
462 if ((m = getenv("MAIL")) != NULL) {
463 /* we are using MAIL environment var. type mbox */
464 if (strlen(m) + 1 > BUF_BIG) {
465 DMA(DEBUG_ERROR,
466 "MAIL environment var '%s' is too long.\n", m);
467 } else {
468 DMA(DEBUG_INFO, "Using MAIL environment var '%s'.\n", m);
469 strncpy(mbox[0].path, m, BUF_BIG - 1);
471 } else if ((m = getenv("USER")) != NULL) {
472 /* we will use the USER env var to find an mbox name */
473 if (strlen(m) + 10 + 1 > BUF_BIG) {
474 DMA(DEBUG_ERROR,
475 "USER environment var '%s' is too long.\n", m);
476 } else {
477 DMA(DEBUG_INFO, "Using /var/mail/%s.\n", m);
478 strcpy(mbox[0].path, "/var/mail/");
479 strncat(mbox[0].path, m, BUF_BIG - 1 - 10);
480 if (mbox[0].path[9] != '/') {
481 DMA(DEBUG_ERROR,
482 "Unexpected failure to construct /var/mail/username, please "
483 "report this with your operating system info and the version of wmbiff.");
486 } else {
487 DMA(DEBUG_ERROR, "Cannot open config file '%s' nor use the "
488 "MAIL environment var.\n", config_file);
489 exit(EXIT_FAILURE);
491 if (!exists(mbox[0].path)) {
492 DMA(DEBUG_ERROR, "Cannot open config file '%s', and the "
493 "default %s doesn't exist.\n", config_file, mbox[0].path);
494 exit(EXIT_FAILURE);
496 strcpy(mbox[0].label, "Spool");
497 mboxCreate((&mbox[0]), mbox[0].path);
500 /* Make labels look right */
501 for (i = 0; i < num_mailboxes; i++) {
502 if (mbox[i].label[0] != '\0') {
503 /* append a colon, but skip if we're using fonts. */
504 if (font == NULL) {
505 int j = strlen(mbox[i].label);
506 if (j < 5) {
507 memset(mbox[i].label + j, ' ', 5 - j);
510 /* but always end after 5 characters */
511 mbox[i].label[6] = '\0';
512 /* set global loopinterval to boxes with 0 loop */
513 if (!mbox[i].loopinterval) {
514 mbox[i].loopinterval = loopinterval;
520 static char **LoadXPM(const char *pixmap_filename)
522 char **xpm;
523 int success;
524 success = XpmReadFileToData((char *) pixmap_filename, &xpm);
525 switch (success) {
526 case XpmOpenFailed:
527 DMA(DEBUG_ERROR, "Unable to open %s\n", pixmap_filename);
528 break;
529 case XpmFileInvalid:
530 DMA(DEBUG_ERROR, "%s is not a valid pixmap\n", pixmap_filename);
531 break;
532 case XpmNoMemory:
533 DMA(DEBUG_ERROR, "Insufficient memory to read %s\n",
534 pixmap_filename);
535 break;
536 default:
537 break;
539 return (xpm);
542 /* tests as "test -f" would */
543 int exists(const char *filename)
545 struct stat st_buf;
546 DMA(DEBUG_INFO, "looking for %s\n", filename);
547 if (stat(filename, &st_buf) == 0 && S_ISREG(st_buf.st_mode)) {
548 DMA(DEBUG_INFO, "found %s\n", filename);
549 return (1);
551 return (0);
554 /* acts like execvp, with code inspired by it */
555 /* mustfree */
556 static char *search_path(const char *path, const char *find_me)
558 char *buf;
559 const char *p;
560 int len, pathlen;
561 if (strchr(find_me, '/') != NULL) {
562 return (strdup_ordie(find_me));
564 pathlen = strlen(path);
565 len = strlen(find_me) + 1;
566 buf = malloc_ordie(pathlen + len + 1);
567 memcpy(buf + pathlen + 1, find_me, len);
568 buf[pathlen] = '/';
570 for (p = path; p != NULL; path = p, path++) {
571 char *startp;
572 p = strchr(path, ':');
573 if (p == NULL) {
574 /* not found; p should point to the null char at the end */
575 startp =
576 memcpy(buf + pathlen - strlen(path), path, strlen(path));
577 } else if (p == path) {
578 /* double colon in a path apparently means try here */
579 startp = &buf[pathlen + 1];
580 } else {
581 /* copy the part between the colons to the buffer */
582 startp = memcpy(buf + pathlen - (p - path), path, p - path);
584 if (exists(startp) != 0) {
585 char *ret = strdup_ordie(startp);
586 free(buf);
587 return (ret);
590 free(buf);
591 return (NULL);
594 /* verifies that .wmbiffrc, is a file, is owned by the user,
595 is not world writeable, and is not world readable. This
596 is just to help keep passwords secure */
597 static int wmbiffrc_permissions_check(const char *wmbiffrc_fname)
599 struct stat st;
600 if (stat(wmbiffrc_fname, &st) != 0) {
601 DMA(DEBUG_ERROR, "Can't stat wmbiffrc: '%s'\n", wmbiffrc_fname);
602 return (1); /* well, it's not a bad permission
603 problem: if you can't find it,
604 neither can the bad guys.. */
606 if ((st.st_mode & S_IFDIR) != 0) {
607 DMA(DEBUG_ERROR, ".wmbiffrc '%s' is a directory!\n"
608 "exiting. don't do that.", wmbiffrc_fname);
609 exit(EXIT_FAILURE);
611 if (st.st_uid != getuid()) {
612 char *user = getenv("USER");
613 DMA(DEBUG_ERROR,
614 ".wmbiffrc '%s' isn't owned by you.\n"
615 "Verify its contents, then 'chown %s %s'\n",
616 wmbiffrc_fname, ((user != NULL) ? user : "(your username)"),
617 wmbiffrc_fname);
618 return (0);
620 if ((st.st_mode & S_IWOTH) != 0) {
621 DMA(DEBUG_ERROR, ".wmbiffrc '%s' is world writable.\n"
622 "Verify its contents, then 'chmod 0600 %s'\n",
623 wmbiffrc_fname, wmbiffrc_fname);
624 return (0);
626 if ((st.st_mode & S_IROTH) != 0) {
627 DMA(DEBUG_ERROR, ".wmbiffrc '%s' is world readable.\n"
628 "Please run 'chmod 0600 %s' and consider changing your passwords.\n",
629 wmbiffrc_fname, wmbiffrc_fname);
630 return (0);
632 return (1);
635 static void ClearDigits(unsigned int i)
637 if (font) {
638 eraseRect(39, mbox_y(i), 58, mbox_y(i + 1) - 1, background);
639 } else {
640 /* overwrite the colon */
641 copyXPMArea((10 * (CHAR_WIDTH + 1)), 64, (CHAR_WIDTH + 1),
642 (CHAR_HEIGHT + 1), 35, mbox_y(i));
643 /* blank out the number fields. */
644 copyXPMArea(39, 84, (3 * (CHAR_WIDTH + 1)), (CHAR_HEIGHT + 1), 39,
645 mbox_y(i));
649 /* Blits a string at given co-ordinates. If a ``new''
650 parameter is nonzero, all digits will be yellow */
651 static void BlitString(const char *name, int x, int y, int new)
653 if (font != NULL) {
654 /* an alternate behavior - draw the string using a font
655 instead of the pixmap. should allow pretty colors */
656 drawString(x, y + CHAR_HEIGHT + 1, name,
657 new ? highlight : foreground, background, 0);
658 } else {
659 /* normal, LED-like behavior. */
660 int i, c, k = x;
661 for (i = 0; name[i] != '\0'; i++) {
662 c = toupper(name[i]);
663 if (c >= 'A' && c <= 'Z') { /* it's a letter */
664 c -= 'A';
665 copyXPMArea(c * (CHAR_WIDTH + 1), (new ? 95 : 74),
666 (CHAR_WIDTH + 1), (CHAR_HEIGHT + 1), k, y);
667 k += (CHAR_WIDTH + 1);
668 } else { /* it's a number or symbol */
669 c -= '0';
670 if (new) {
671 copyXPMArea((c * (CHAR_WIDTH + 1)) + 65, 0,
672 (CHAR_WIDTH + 1), (CHAR_HEIGHT + 1), k, y);
673 } else {
674 copyXPMArea((c * (CHAR_WIDTH + 1)), 64,
675 (CHAR_WIDTH + 1), (CHAR_HEIGHT + 1), k, y);
677 k += (CHAR_WIDTH + 1);
684 /* Blits number to give coordinates.. two 0's, right justified */
685 static void BlitNum(int num, int x, int y, int new)
687 char buf[32];
689 sprintf(buf, "%02i", num);
691 if (font != NULL) {
692 const char *color = (new) ? highlight : foreground;
693 drawString(x + (CHAR_WIDTH * 2 + 4), y + CHAR_HEIGHT + 1, buf,
694 color, background, 1);
695 } else {
696 int newx = x;
698 if (num > 99)
699 newx -= (CHAR_WIDTH + 1);
700 if (num > 999)
701 newx -= (CHAR_WIDTH + 1);
703 BlitString(buf, newx, y, new);
707 /* helper function for displayMsgCounters, which has outgrown its name */
708 static void blitMsgCounters(unsigned int i)
710 int y_row = mbox_y(i); /* constant for each mailbox */
711 ClearDigits(i); /* Clear digits */
712 if ((mbox[i].blink_stat & 0x01) == 0) {
713 int newmail = (mbox[i].UnreadMsgs > 0) ? 1 : 0;
714 if (mbox[i].TextStatus[0] != '\0') {
715 BlitString(mbox[i].TextStatus, 39, y_row, newmail);
716 } else {
717 int mailcount =
718 (newmail) ? mbox[i].UnreadMsgs : ( classic_mode ? mbox[i].TotalMsgs : 0 );
719 BlitNum(mailcount, 45, y_row, newmail);
725 * void execnotify(1) : runs notify command, if given (not null)
727 static void execnotify( /*@null@ */ const char *notifycmd)
729 if (notifycmd != NULL) { /* need to call notify() ? */
730 if (classic_mode && !strcasecmp(notifycmd, "beep"))
731 XBell(display, 100);
732 else if (!strcasecmp(notifycmd, "true")) {
733 /* Yes, nothing */
734 } else {
735 /* Else call external notifyer, ignoring the pid */
736 (void) execCommand(notifycmd);
742 static void
743 displayMsgCounters(unsigned int i, int mail_stat, int *Blink_Mode)
745 switch (mail_stat) {
746 case 2: /* New mail has arrived */
747 /* Enter blink-mode for digits */
748 mbox[i].blink_stat = BLINK_TIMES * 2;
749 *Blink_Mode |= (1 << i); /* Global blink flag set for this mailbox */
750 blitMsgCounters(i);
751 execnotify(mbox[i].notify);
752 break;
753 case 1: /* mailbox has been rescanned/changed */
754 blitMsgCounters(i);
755 break;
756 case 0:
757 break;
758 case -1: /* Error was detected */
759 ClearDigits(i); /* Clear digits */
760 BlitString("XX", 45, mbox_y(i), 0);
761 break;
765 /** counts mail in spool-file
766 Returned value:
767 -1 : Error was encountered
768 0 : mailbox status wasn't changed
769 1 : mailbox was changed (NO new mail)
770 2 : mailbox was changed AND new mail has arrived
772 static int count_mail(unsigned int item)
774 int rc = 0;
776 if (!mbox[item].checkMail) {
777 return -1;
780 if (mbox[item].checkMail(&(mbox[item])) < 0) {
781 /* we failed to obtain any numbers therefore set
782 * them to -1's ensuring the next pass (even if
783 * zero) will be captured correctly
785 mbox[item].TotalMsgs = -1;
786 mbox[item].UnreadMsgs = -1;
787 mbox[item].OldMsgs = -1;
788 mbox[item].OldUnreadMsgs = -1;
789 return -1;
792 if (mbox[item].UnreadMsgs > mbox[item].OldUnreadMsgs &&
793 mbox[item].UnreadMsgs > 0) {
794 rc = 2; /* New mail detected */
795 } else if (mbox[item].UnreadMsgs < mbox[item].OldUnreadMsgs ||
796 mbox[item].TotalMsgs != mbox[item].OldMsgs) {
797 rc = 1; /* mailbox was changed - NO new mail */
798 } else {
799 rc = 0; /* mailbox wasn't changed */
801 mbox[item].OldMsgs = mbox[item].TotalMsgs;
802 mbox[item].OldUnreadMsgs = mbox[item].UnreadMsgs;
803 return rc;
806 static int periodic_mail_check(void)
808 int NeedRedraw = 0;
809 static int Blink_Mode = 0; /* Bit mask, digits are in blinking
810 mode or not. Each bit for separate
811 mailbox */
812 int Sleep_Interval; /* either DEFAULT_SLEEP_INTERVAL or
813 BLINK_SLEEP_INTERVAL */
814 int NewMail = 0; /* flag for global notify */
815 unsigned int i;
816 time_t curtime = time(0);
817 for (i = 0; i < num_mailboxes; i++) {
818 if (mbox[i].label[0] != '\0') {
819 if (curtime >= mbox[i].prevtime + mbox[i].loopinterval) {
820 int mailstat = 0;
821 NeedRedraw = 1;
822 DM(&mbox[i], DEBUG_INFO,
823 "working on [%u].label=>%s< [%u].path=>%s<\n", i,
824 mbox[i].label, i, mbox[i].path);
825 DM(&mbox[i], DEBUG_INFO,
826 "curtime=%d, prevtime=%d, interval=%d\n",
827 (int) curtime, (int) mbox[i].prevtime,
828 mbox[i].loopinterval);
829 mbox[i].prevtime = curtime;
831 XDefineCursor(display, iconwin, busy_cursor);
832 RedrawWindow();
834 mailstat = count_mail(i);
836 XUndefineCursor(display, iconwin);
838 /* Global notify */
839 if (mailstat == 2)
840 NewMail = 1;
842 displayMsgCounters(i, mailstat, &Blink_Mode);
843 /* update our idea of current time, as it
844 may have changed as we check. */
845 curtime = time(0);
847 if (mbox[i].blink_stat > 0) {
848 if (--mbox[i].blink_stat <= 0) {
849 Blink_Mode &= ~(1 << i);
850 mbox[i].blink_stat = 0;
852 displayMsgCounters(i, 1, &Blink_Mode);
853 NeedRedraw = 1;
855 if (mbox[i].fetchinterval > 0 && mbox[i].fetchcmd[0] != '\0'
856 && curtime >=
857 mbox[i].prevfetch_time + mbox[i].fetchinterval) {
859 XDefineCursor(display, iconwin, busy_cursor);
860 RedrawWindow();
862 (void) execCommand(mbox[i].fetchcmd);
864 XUndefineCursor(display, iconwin);
866 mbox[i].prevfetch_time = curtime;
871 /* exec globalnotify if there was any new mail */
872 if (NewMail == 1)
873 execnotify(globalnotify);
875 if (Blink_Mode == 0) {
876 for (i = 0; i < num_mailboxes; i++) {
877 mbox[i].blink_stat = 0;
879 Sleep_Interval = DEFAULT_SLEEP_INTERVAL;
880 } else {
881 Sleep_Interval = BLINK_SLEEP_INTERVAL;
884 if (NeedRedraw) {
885 NeedRedraw = 0;
886 RedrawWindow();
889 return Sleep_Interval;
892 static int findTopOfMasterXPM(const char **skin_xpm)
894 int i;
895 for (i = 0; skin_xpm[i] != NULL; i++) {
896 if (strstr(skin_xpm[i], "++++++++") != NULL)
897 return i;
899 DMA(DEBUG_ERROR,
900 "couldn't find the top of the xpm file using the simple method\n");
901 exit(EXIT_FAILURE);
904 static char **CreateBackingXPM(int width, int height,
905 const char **skin_xpm)
907 char **ret = malloc_ordie(sizeof(char *) * (height + 6)
908 + sizeof(void *) /* trailing null space */ );
909 const int colors = 5;
910 const int base = colors + 1;
911 const int margin = 4;
912 int i;
913 int top = findTopOfMasterXPM(skin_xpm);
914 ret[0] = malloc_ordie(30);
915 sprintf(ret[0], "%d %d %d %d", width, height, colors, 1);
916 ret[1] = (char *) " \tc #0000FF"; /* no color */
917 ret[2] = (char *) ".\tc #505075"; /* background gray */
918 ret[2] = malloc_ordie(30);
919 sprintf(ret[2], ".\tc %s", background);
920 ret[3] = (char *) "+\tc #000000"; /* shadowed */
921 ret[4] = (char *) "@\tc #C7C3C7"; /* highlight */
922 ret[5] = (char *) ":\tc #004941"; /* led off */
923 for (i = base; i < base + height; i++) {
924 ret[i] = malloc_ordie(width);
926 for (i = base; i < base + margin; i++) {
927 memset(ret[i], ' ', width);
929 for (i = base + margin; i < height + base - margin; i++) {
930 memset(ret[i], ' ', margin);
932 if (i == base + margin) {
933 memset(ret[i] + margin, '+', width - margin - margin);
934 } else if (i == base + height - margin - 1) {
935 memset(ret[i] + margin, '@', width - margin - margin);
936 } else {
937 // " +..:::...:::...:::...:::...:::.......:::...:::...:::...@ "
938 // " +.:...:.:...:.:...:.:...:.:...:..:..:...:.:...:.:...:..@ " ",
939 ret[i][margin] = '+';
940 memset(ret[i] + margin + 1, '.', width - margin - margin - 1);
941 ret[i][width - margin - 1] = '@';
942 memcpy(ret[i],
943 skin_xpm[((i - (base + margin) - 1) % 11) + top + 1],
944 width);
947 memset(ret[i] + width - margin, ' ', margin);
949 for (i = base + height - margin; i < height + base; i++) {
950 memset(ret[i], ' ', width);
952 ret[height + base] = NULL; /* not sure if this is necessary, it just
953 seemed like a good idea */
954 return (ret);
958 * NOTE: this function assumes that the ConnectionNumber() macro
959 * will return the file descriptor of the Display struct
960 * (it does under XFree86 and solaris' openwin X)
962 static void XSleep(int millisec)
964 #ifdef HAVE_POLL
965 struct pollfd timeout;
967 timeout.fd = ConnectionNumber(display);
968 timeout.events = POLLIN;
970 poll(&timeout, 1, millisec);
971 #else
972 struct timeval to;
973 struct timeval *timeout = NULL;
974 fd_set readfds;
975 int max_fd;
977 if (millisec >= 0) {
978 timeout = &to;
979 to.tv_sec = millisec / 1000;
980 to.tv_usec = (millisec % 1000) * 1000;
982 FD_ZERO(&readfds);
983 FD_SET(ConnectionNumber(display), &readfds);
984 max_fd = ConnectionNumber(display);
986 select(max_fd + 1, &readfds, NULL, NULL, timeout);
987 #endif
990 const char **restart_args;
992 static void restart_wmbiff(int sig
993 #ifdef HAVE___ATTRIBUTE__
994 __attribute__ ((unused))
995 #endif
998 if (restart_args) {
999 DMA(DEBUG_ERROR, "exec()'ing %s\n", restart_args[0]);
1000 sleep(1);
1001 execvp(restart_args[0], (char *const *) restart_args);
1002 DMA(DEBUG_ERROR, "exec of %s failed: %s\n",
1003 restart_args[0], strerror(errno));
1004 exit(EXIT_FAILURE);
1006 else
1007 fprintf(stderr, "Unable to restart wmbiff: missing restart arguments (NULL)!\n");
1010 extern int x_socket(void)
1012 return ConnectionNumber(display);
1014 extern void ProcessPendingEvents(void)
1016 static int but_pressed_region = -1; /* static so click can be determined */
1017 int but_released_region = -1;
1018 /* X Events */
1019 while (XPending(display)) {
1020 XEvent Event;
1021 const char *press_action;
1023 XNextEvent(display, &Event);
1025 switch (Event.type) {
1026 case Expose:
1027 if (Event.xany.window != win && Event.xany.window != iconwin) {
1028 msglst_redraw();
1029 } else {
1030 RedrawWindow();
1032 break;
1033 case DestroyNotify:
1034 XCloseDisplay(display);
1035 exit(EXIT_SUCCESS);
1036 break;
1037 case ButtonPress:
1038 but_pressed_region =
1039 CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
1040 switch (Event.xbutton.button) {
1041 case 1:
1042 press_action = mbox[but_pressed_region].action;
1043 break;
1044 case 2:
1045 press_action = mbox[but_pressed_region].button2;
1046 break;
1047 case 3:
1048 press_action = mbox[but_pressed_region].fetchcmd;
1049 break;
1050 default:
1051 press_action = NULL;
1052 break;
1055 if (press_action && strcmp(press_action, "msglst") == 0) {
1056 msglst_show(&mbox[but_pressed_region],
1057 Event.xbutton.x_root, Event.xbutton.y_root);
1059 break;
1060 case ButtonRelease:
1061 but_released_region =
1062 CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
1063 if (but_released_region == but_pressed_region
1064 && but_released_region >= 0) {
1065 const char *click_action, *extra_click_action = NULL;
1067 switch (Event.xbutton.button) {
1068 case 1: /* Left mouse-click */
1069 /* C-S-left will restart wmbiff. */
1070 if ((Event.xbutton.state & ControlMask) &&
1071 (Event.xbutton.state & ShiftMask)) {
1072 restart_wmbiff(0);
1074 /* do we need to run an extra action? */
1075 if (mbox[but_released_region].UnreadMsgs == -1) {
1076 extra_click_action =
1077 mbox[but_released_region].actiondc;
1078 } else if (mbox[but_released_region].UnreadMsgs > 0) {
1079 extra_click_action =
1080 mbox[but_released_region].actionnew;
1081 } else {
1082 extra_click_action =
1083 mbox[but_released_region].actionnonew;
1085 click_action = mbox[but_released_region].action;
1086 break;
1087 case 2: /* Middle mouse-click */
1088 click_action = mbox[but_released_region].button2;
1089 break;
1090 case 3: /* Right mouse-click */
1091 click_action = mbox[but_released_region].fetchcmd;
1092 break;
1093 default:
1094 click_action = NULL;
1095 break;
1097 if (extra_click_action != NULL
1098 && extra_click_action[0] != 0
1099 && strcmp(extra_click_action, "msglst")) {
1100 DM(&mbox[but_released_region], DEBUG_INFO,
1101 "runing: %s", extra_click_action);
1102 (void) execCommand(extra_click_action);
1104 if (click_action != NULL
1105 && click_action[0] != '\0'
1106 && strcmp(click_action, "msglst")) {
1107 DM(&mbox[but_released_region], DEBUG_INFO,
1108 "running: %s", click_action);
1109 (void) execCommand(click_action);
1113 /* a button was released, hide the message list if open */
1114 msglst_hide();
1116 but_pressed_region = -1;
1117 /* RedrawWindow(); */
1118 break;
1119 case MotionNotify:
1120 break;
1121 case KeyPress:{
1122 XKeyPressedEvent *xkpe = (XKeyPressedEvent *) & Event;
1123 KeySym ks = XkbKeycodeToKeysym(display, xkpe->keycode, 0, 0);
1124 if (ks > XK_0 && ks < XK_0 + min(9U, num_mailboxes)) {
1125 const char *click_action = mbox[ks - XK_1].action;
1126 if (click_action != NULL
1127 && click_action[0] != '\0'
1128 && strcmp(click_action, "msglst")) {
1129 DM(&mbox[but_released_region], DEBUG_INFO,
1130 "running: %s", click_action);
1131 (void) execCommand(click_action);
1136 break;
1137 default:
1138 break;
1143 static void do_biff(int argc, const char **argv)
1145 unsigned int i;
1146 int Sleep_Interval;
1147 const char **skin_xpm = NULL;
1148 const char **bkg_xpm = NULL;
1149 char *skin_file_path = search_path(skin_search_path, skin_filename);
1150 int wmbiff_mask_height = mbox_y(num_mailboxes) + 4;
1152 DMA(DEBUG_INFO, "running %u mailboxes w %d h %d\n", num_mailboxes,
1153 wmbiff_mask_width, wmbiff_mask_height);
1155 if (skin_file_path != NULL) {
1156 skin_xpm = (const char **) LoadXPM(skin_file_path);
1157 free(skin_file_path);
1159 if (skin_xpm == NULL) {
1160 DMA(DEBUG_ERROR, "using built-in xpm; %s wasn't found in %s\n",
1161 skin_filename, skin_search_path);
1162 skin_xpm = (classic_mode ? wmbiff_classic_master_xpm : wmbiff_master_xpm);
1165 bkg_xpm = (const char **) CreateBackingXPM(wmbiff_mask_width, wmbiff_mask_height, skin_xpm);
1167 createXBMfromXPM(wmbiff_mask_bits, (const char**)bkg_xpm,
1168 wmbiff_mask_width, wmbiff_mask_height);
1170 openXwindow(argc, argv, (const char**)bkg_xpm, skin_xpm, wmbiff_mask_bits,
1171 wmbiff_mask_width, wmbiff_mask_height, notWithdrawn);
1173 /* now that display is set, we can create the cursors
1174 (mouse pointer shapes) */
1175 busy_cursor = XCreateFontCursor(display, XC_watch);
1176 ready_cursor = XCreateFontCursor(display, XC_left_ptr);
1178 if (font != NULL) {
1179 if (loadFont(font) < 0) {
1180 DMA(DEBUG_ERROR, "unable to load font. exiting.\n");
1181 exit(EXIT_FAILURE);
1185 /* First time setup of button regions and labels */
1186 for (i = 0; i < num_mailboxes; i++) {
1187 /* make it easy to recover the mbox index from a mouse click */
1188 AddMouseRegion(i, x_origin, mbox_y(i), 58, mbox_y(i + 1) - 1);
1189 if (mbox[i].label[0] != '\0') {
1190 mbox[i].prevtime = mbox[i].prevfetch_time = 0;
1191 BlitString(mbox[i].label, x_origin, mbox_y(i), 0);
1195 do {
1197 Sleep_Interval = periodic_mail_check();
1198 ProcessPendingEvents();
1199 XSleep(Sleep_Interval);
1201 while (forever); /* forever is usually true,
1202 but not when debugging with -exit */
1203 if (skin_xpm != NULL && skin_xpm != wmbiff_master_xpm
1204 && skin_xpm != wmbiff_classic_master_xpm) {
1205 free(skin_xpm); // added 3 jul 02, appeasing valgrind
1207 if (bkg_xpm != NULL) {
1208 // Allocated in CreateBackingXPM()
1209 free((void *)bkg_xpm[0]);
1210 free((void *)bkg_xpm[2]);
1211 int mem_block;
1212 for (mem_block = 6; mem_block < 6 + wmbiff_mask_height; mem_block++)
1213 free((void *)bkg_xpm[mem_block]);
1214 free(bkg_xpm);
1218 static void sigchld_handler(int sig
1219 #ifdef HAVE___ATTRIBUTE__
1220 __attribute__ ((unused))
1221 #endif
1224 while (waitpid(0, NULL, WNOHANG) > 0);
1225 signal(SIGCHLD, sigchld_handler);
1228 static void usage(void)
1230 printf("\nwmBiff v%s"
1231 " - incoming mail checker\n"
1232 "Gennady Belyakov and others (see the README file)\n"
1233 "Please report bugs to %s\n"
1234 "\n"
1235 "usage:\n"
1236 " -bg <color> background color\n"
1237 " -c <filename> use specified config file\n"
1238 " -debug enable debugging\n"
1239 " -display <display name> use specified X display\n"
1240 " -fg <color> foreground color\n"
1241 " -font <font> font instead of LED\n"
1242 " -geometry +XPOS+YPOS initial window position\n"
1243 " -h this help screen\n"
1244 " -hi <color> highlight color for new mail\n"
1245 #ifdef USE_GNUTLS
1246 " -skip-certificate-check using TLS, don't validate the\n"
1247 " server's certificate\n"
1248 #endif
1249 " -relax assume the configuration is \n"
1250 " correct, parse it without paranoia, \n"
1251 " and assume hostnames are okay.\n"
1252 " -v print the version number\n"
1253 " +w not withdrawn: run as a window\n"
1254 "\n", PACKAGE_VERSION, PACKAGE_BUGREPORT);
1257 static void printversion(void)
1259 printf("wmbiff v%s\n", PACKAGE_VERSION);
1263 static void parse_cmd(int argc, const char **argv, char *config_file)
1265 int i;
1266 int fg = 0, bg = 0, hi = 0;
1268 config_file[0] = '\0';
1270 /* Parse Command Line */
1272 for (i = 1; i < argc; i++) {
1273 const char *arg = argv[i];
1275 if (*arg == '-') {
1276 switch (arg[1]) {
1277 case 'b':
1278 if (strcmp(arg + 1, "bg") == 0) {
1279 if (argc > (i + 1)) {
1280 background = strdup_ordie(argv[i + 1]);
1281 bg = 1;
1282 DMA(DEBUG_INFO, "new background: '%s'\n", foreground);
1283 i++;
1284 if (font == NULL)
1285 font = DEFAULT_FONT;
1288 break;
1289 case 'd':
1290 if (strcmp(arg + 1, "debug") == 0) {
1291 debug_default = DEBUG_ALL;
1292 } else if (strcmp(arg + 1, "display") == 0) {
1293 /* passed to X's command line parser */
1294 } else {
1295 usage();
1296 exit(EXIT_FAILURE);
1298 break;
1299 case 'f':
1300 if (strcmp(arg + 1, "fg") == 0) {
1301 if (argc > (i + 1)) {
1302 foreground = strdup_ordie(argv[i + 1]);
1303 fg = 1;
1304 DMA(DEBUG_INFO, "new foreground: '%s'\n", foreground);
1305 i++;
1306 if (font == NULL)
1307 font = DEFAULT_FONT;
1309 } else if (strcmp(arg + 1, "font") == 0) {
1310 if (argc > (i + 1)) {
1311 if (strcmp(argv[i + 1], "default") == 0) {
1312 font = DEFAULT_FONT;
1313 } else {
1314 font = strdup_ordie(argv[i + 1]);
1316 DMA(DEBUG_INFO, "new font: '%s'\n", font);
1317 i++;
1319 } else {
1320 usage();
1321 exit(EXIT_FAILURE);
1323 break;
1324 case 'g':
1325 if (strcmp(arg + 1, "geometry") != 0) {
1326 usage();
1327 exit(EXIT_FAILURE);
1328 } else {
1329 i++; /* gobble the argument */
1330 if (i >= argc) { /* fail if there's nothing to gobble */
1331 usage();
1332 exit(EXIT_FAILURE);
1335 break;
1336 case 'h':
1337 if (strcmp(arg + 1, "hi") == 0) {
1338 if (argc > (i + 1)) {
1339 highlight = strdup_ordie(argv[i + 1]);
1340 hi = 1;
1341 DMA(DEBUG_INFO, "new highlight: '%s'\n", highlight);
1342 i++;
1343 if (font == NULL)
1344 font = DEFAULT_FONT;
1346 } else if (strcmp(arg + 1, "h") == 0) {
1347 usage();
1348 exit(EXIT_SUCCESS);
1350 break;
1351 case 'v':
1352 printversion();
1353 exit(EXIT_SUCCESS);
1354 break;
1355 case 's':
1356 if (strcmp(arg + 1, "skip-certificate-check") == 0) {
1357 SkipCertificateCheck = 1;
1358 } else {
1359 usage();
1360 exit(EXIT_SUCCESS);
1363 break;
1364 case 'r':
1365 if (strcmp(arg + 1, "relax") == 0) {
1366 Relax = 1;
1367 } else {
1368 usage();
1369 exit(EXIT_SUCCESS);
1372 break;
1373 case 'c':
1374 if (argc > (i + 1)) {
1375 strncpy(config_file, argv[i + 1], 255);
1376 i++;
1378 break;
1379 case 'e': /* undocumented for debugging */
1380 if (strcmp(arg + 1, "exit") == 0) {
1381 forever = 0;
1383 break;
1384 case 'o': /* use classic behaviour/theme */
1385 classic_mode = 1;
1386 break;
1387 default:
1388 usage();
1389 exit(EXIT_SUCCESS);
1390 break;
1392 } else if (*arg == '+') {
1393 switch (arg[1]) {
1394 case 'w':
1395 notWithdrawn = 1;
1396 break;
1397 default:
1398 usage();
1399 exit(EXIT_SUCCESS);
1400 break;
1405 if (classic_mode) {
1406 /* load classic colors if user did not override them */
1407 if(!fg)
1408 foreground = foreground_classic;
1409 if(!bg)
1410 background = background_classic;
1411 if(!hi)
1412 highlight = highlight_classic;
1416 int main(int argc, const char *argv[])
1418 char uconfig_file[256];
1420 /* hold on to the arguments we were started with; we
1421 will need them if we have to restart on sigusr1 */
1422 restart_args =
1423 (const char **) malloc((argc + 1) * sizeof(const char *));
1424 if (restart_args) {
1425 memcpy(restart_args, argv, (argc) * sizeof(const char *));
1426 restart_args[argc] = NULL;
1429 parse_cmd(argc, argv, uconfig_file);
1431 /* decide what the config file is */
1432 if (uconfig_file[0] != '\0') { /* user-specified config file */
1433 DMA(DEBUG_INFO, "Using user-specified config file '%s'.\n",
1434 uconfig_file);
1435 } else {
1436 const char *home = getenv("HOME");
1437 if (home == NULL) {
1438 DMA(DEBUG_ERROR,
1439 "$HOME undefined. Use the -c option to specify a wmbiffrc\n");
1440 exit(EXIT_FAILURE);
1442 sprintf(uconfig_file, "%s/.wmbiffrc", home);
1445 if (wmbiffrc_permissions_check(uconfig_file) == 0) {
1446 DMA(DEBUG_ERROR,
1447 "WARNING: In future versions of WMBiff, .wmbiffrc MUST be\n"
1448 "owned by the user, and not readable or writable by others.\n\n");
1450 init_biff(uconfig_file);
1451 signal(SIGCHLD, sigchld_handler);
1452 signal(SIGUSR1, restart_wmbiff);
1453 signal(SIGPIPE, SIG_IGN); /* write() may fail */
1455 do_biff(argc, argv);
1457 // free resources
1458 if (restart_args)
1459 free(restart_args);
1460 if (custom_skin)
1461 free((void *)skin_filename);
1463 return 0;