wmclockmon: update change-log
[dockapps.git] / wmbiff / wmbiff / wmbiff.c
blob724ed78856973afa34940070a6e092008b797a1d
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"
37 #include "wmbiff.h"
39 #ifdef USE_DMALLOC
40 #include <dmalloc.h>
41 #endif
44 #include "wmbiff-master-led.xpm"
45 #include "wmbiff-classic-master-led.xpm"
46 static char wmbiff_mask_bits[64 * 64];
47 static const int wmbiff_mask_width = 64;
48 // const int wmbiff_mask_height = 64;
50 #define CHAR_WIDTH 5
51 #define CHAR_HEIGHT 7
53 #define BLINK_TIMES 8
54 #define DEFAULT_SLEEP_INTERVAL 20000
55 #define BLINK_SLEEP_INTERVAL 200
56 #define DEFAULT_LOOP 5
58 #define MAX_NUM_MAILBOXES 40
59 static Pop3 mbox[MAX_NUM_MAILBOXES];
61 /* this is the normal pixmap. */
62 static const char *skin_filename = "wmbiff-master-led.xpm";
63 static const char *classic_skin_filename = "wmbiff-classic-master-led.xpm";
64 /* global notify action taken (if globalnotify option is set) */
65 static const char *globalnotify = NULL;
67 /* path to search for pixmaps */
68 /* /usr/share/wmbiff should have the default pixmap. */
69 /* /usr/local/share/wmbiff if compiled locally. */
70 /* / is there in case a user wants to specify a complete path */
71 /* . is there for development. */
72 static const char *skin_search_path = DEFAULT_SKIN_PATH;
73 /* for gnutls */
74 const char *certificate_filename = NULL;
75 /* gnutls: specify the priorities to use on the ciphers, key exchange methods,
76 macs and compression methods. */
77 const char *tls = NULL;
79 /* it could be argued that a better default exists. */
80 #define DEFAULT_FONT "-*-fixed-*-r-*-*-10-*-*-*-*-*-*-*"
81 static const char *font = NULL;
83 int debug_default = DEBUG_ERROR;
85 /* color from wmbiff's xpm, down to 24 bits. */
86 const char *foreground = "white"; /* foreground white */
87 const char *background = "#505075"; /* background blue */
88 static const char *highlight = "red";
89 const char *foreground_classic = "#21B3AF"; /* classic foreground cyan */
90 const char *background_classic = "#202020"; /* classic background gray */
91 static const char *highlight_classic = "yellow"; /* classic highlight color */
92 int SkipCertificateCheck = 0;
93 int Relax = 0; /* be not paranoid */
94 static int notWithdrawn = 0;
96 static unsigned int num_mailboxes = 1;
97 static const int x_origin = 5;
98 static const int y_origin = 5;
99 static int forever = 1; /* keep running. */
100 unsigned int custom_skin = 0; /* user has choose a custom skin */
101 static int classic_mode = 0; /* use classic behaviour/theme of wmbiff */
103 extern Window win;
104 extern Window iconwin;
106 Cursor busy_cursor, ready_cursor;
108 static __inline /*@out@ */ void *
109 malloc_ordie(size_t len)
111 void *ret = malloc(len);
112 if (ret == NULL) {
113 fprintf(stderr, "unable to allocate %d bytes\n", (int) len);
114 abort();
116 return (ret);
119 /* where vertically the mailbox sits for blitting characters. */
120 static int mbox_y(unsigned int mboxnum)
122 return ((11 * mboxnum) + y_origin);
125 /* Read a line from a file to obtain a pair setting=value
126 skips # and leading spaces
127 NOTE: if setting finish with 0, 1, 2, 3 or 4 last char are deleted and
128 index takes its value... if not index will get -1
129 Returns -1 if no setting=value
131 static int ReadLine(FILE * fp, /*@out@ */ char *setting,
132 /*@out@ */ char *value, /*@out@ */ int *mbox_index)
134 char buf[BUF_SIZE];
135 char *p, *q;
136 int len;
138 *setting = '\0';
139 *value = '\0';
140 *mbox_index = -1;
142 if (!fp || feof(fp))
143 return -1;
145 if (!fgets(buf, BUF_SIZE - 1, fp))
146 return -1;
148 len = strlen(buf);
150 if (buf[len - 1] == '\n') {
151 buf[len - 1] = '\0'; /* strip linefeed */
154 StripComment(buf);
156 if (!(p = strtok(buf, "=")))
157 return -1;
158 if (!(q = strtok(NULL, "\n")))
159 return -1;
161 /* Chg - Mark Hurley
162 * Date: May 8, 2001
163 * Removed for loop (which removed leading spaces)
164 * Leading & Trailing spaces need to be removed
165 * to Fix Debian bug #95849
167 FullTrim(p);
168 FullTrim(q);
170 /* strcpy(setting, p); nspring replaced with sscanf dec 2002 */
171 strcpy(value, q);
173 if (sscanf(p, "%[_a-z.]%d", setting, mbox_index) == 2) {
174 /* mailbox-specific configuration, ends in a digit */
175 if (*mbox_index < 0 || *mbox_index >= MAX_NUM_MAILBOXES) {
176 DMA(DEBUG_ERROR, "invalid mailbox number %d\n", *mbox_index);
177 exit(EXIT_FAILURE);
179 } else if (sscanf(p, "%[a-z]", setting) == 1) {
180 /* global configuration, all text. */
181 *mbox_index = -1;
182 } else {
183 /* we found an uncommented line that has an equals,
184 but is non-alphabetic. */
185 DMA(DEBUG_INFO, "unparsed setting %s\n", p);
186 return -1;
189 DMA(DEBUG_INFO, "@%s.%d=%s@\n", setting, *mbox_index, value);
190 return 1;
193 struct path_demultiplexer {
194 const char *id; /* followed by a colon */
195 int (*creator)( /*@notnull@ */ Pop3 *pc, const char *path);
198 static struct path_demultiplexer paths[] = {
199 {"pop3:", pop3Create},
200 {"pop3s:", pop3Create},
201 {"shell:", shellCreate},
202 {"imap:", imap4Create},
203 {"imaps:", imap4Create},
204 {"sslimap:", imap4Create},
205 {"maildir:", maildirCreate},
206 {"mbox:", mboxCreate},
207 {NULL, NULL}
211 static void parse_mbox_path(unsigned int item)
213 int i;
214 /* find the creator */
215 for (i = 0;
216 paths[i].id != NULL
217 && strncasecmp(mbox[item].path, paths[i].id, strlen(paths[i].id));
218 i++);
219 /* if found, execute */
220 if (paths[i].id != NULL) {
221 if (paths[i].creator((&mbox[item]), mbox[item].path) != 0) {
222 DMA(DEBUG_ERROR, "creator for mailbox %u returned failure\n",
223 item);
225 } else {
226 /* default are mbox */
227 mboxCreate((&mbox[item]), mbox[item].path);
231 static int Read_Config_File(char *filename, int *loopinterval)
233 FILE *fp;
234 char setting[BUF_SMALL], value[BUF_SIZE];
235 int mbox_index;
236 unsigned int i;
237 char *skin = NULL;
239 if (classic_mode) {
240 skin = strdup_ordie(skin_filename);
243 if (!(fp = fopen(filename, "r"))) {
244 DMA(DEBUG_ERROR, "Unable to open %s, no settings read: %s\n",
245 filename, strerror(errno));
246 return 0;
248 while (!feof(fp)) {
249 /* skanky: -1 can represent an unparsed line
250 or an error */
251 if (ReadLine(fp, setting, value, &mbox_index) == -1)
252 continue;
254 /* settings that can be global go here. */
255 if (!strcmp(setting, "interval")) {
256 *loopinterval = atoi(value);
257 continue;
258 } else if (!strcmp(setting, "askpass")) {
259 const char *askpass = strdup_ordie(value);
260 if (mbox_index == -1) {
261 DMA(DEBUG_INFO, "setting all to askpass %s\n", askpass);
262 for (i = 0; i < MAX_NUM_MAILBOXES; i++)
263 mbox[i].askpass = askpass;
264 } else {
265 mbox[mbox_index].askpass = askpass;
267 continue;
268 } else if (!strcmp(setting, "skinfile")) {
269 skin_filename = strdup_ordie(value);
270 custom_skin = 1;
271 continue;
272 } else if (!strcmp(setting, "certfile")) { /* not yet supported */
273 certificate_filename = strdup_ordie(value);
274 continue;
275 } else if (!strcmp(setting, "globalnotify")) {
276 globalnotify = strdup_ordie(value);
277 continue;
278 } else if (!strcmp(setting, "tls")) {
279 tls = strdup_ordie(value);
280 continue;
281 } else if (mbox_index == -1) {
282 DMA(DEBUG_INFO, "Unknown global setting '%s'\n", setting);
283 continue; /* Didn't read any setting.[0-5] value */
286 if (mbox_index >= MAX_NUM_MAILBOXES) {
287 DMA(DEBUG_ERROR, "Don't have %d mailboxes.\n", mbox_index);
288 continue;
291 if (1U + mbox_index > num_mailboxes
292 && mbox_index + 1 <= MAX_NUM_MAILBOXES) {
293 num_mailboxes = 1U + mbox_index;
296 /* now only local settings */
297 if (!strcmp(setting, "label.")) {
298 if (strlen(value) + 1 > BUF_SMALL) {
299 DMA(DEBUG_ERROR,
300 "Mailbox %i label string '%s' is too long.\n",
301 mbox_index, value);
302 continue;
303 } else {
304 strncpy(mbox[mbox_index].label, value, BUF_SMALL - 1);
306 } else if (!strcmp(setting, "path.")) {
307 if (strlen(value) + 1 > BUF_BIG) {
308 DMA(DEBUG_ERROR,
309 "Mailbox %i path string '%s' is too long.\n",
310 mbox_index, value);
311 continue;
312 } else {
313 strncpy(mbox[mbox_index].path, value, BUF_BIG - 1);
315 } else if (!strcmp(setting, "notify.")) {
316 if (strlen(value) + 1 > BUF_BIG) {
317 DMA(DEBUG_ERROR,
318 "Mailbox %i notify string '%s' is too long.\n",
319 mbox_index, value);
320 continue;
321 } else {
322 strncpy(mbox[mbox_index].notify, value, BUF_BIG - 1);
324 } else if (!strcmp(setting, "action.")) {
325 if (strlen(value) + 1 > BUF_BIG) {
326 DMA(DEBUG_ERROR,
327 "Mailbox %i action string '%s' is too long.\n",
328 mbox_index, value);
329 continue;
330 } else {
331 strncpy(mbox[mbox_index].action, value, BUF_BIG - 1);
333 } else if (!strcmp(setting, "action_disconnected.")) {
334 if (strlen(value) + 1 > BUF_BIG) {
335 DMA(DEBUG_ERROR,
336 "Mailbox %i action_disconnected string '%s' is too long.\n",
337 mbox_index, value);
338 continue;
339 } else {
340 strncpy(mbox[mbox_index].actiondc, value, BUF_BIG - 1);
342 } else if (!strcmp(setting, "action_new_mail.")) {
343 if (strlen(value) + 1 > BUF_BIG) {
344 DMA(DEBUG_ERROR,
345 "Mailbox %i action_new_mail string '%s' is too long.\n",
346 mbox_index, value);
347 continue;
348 } else {
349 strncpy(mbox[mbox_index].actionnew, value, BUF_BIG - 1);
351 } else if (!strcmp(setting, "action_no_new_mail.")) {
352 if (strlen(value) + 1 > BUF_BIG) {
353 DMA(DEBUG_ERROR,
354 "Mailbox %i action_no_new_mail string '%s' is too long.\n",
355 mbox_index, value);
356 continue;
357 } else {
358 strncpy(mbox[mbox_index].actionnonew, value, BUF_BIG - 1);
360 } else if (!strcmp(setting, "interval.")) {
361 mbox[mbox_index].loopinterval = atoi(value);
362 } else if (!strcmp(setting, "buttontwo.")) {
363 if (strlen(value) + 1 > BUF_BIG) {
364 DMA(DEBUG_ERROR,
365 "Mailbox %i buttontwo string '%s' is too long.\n",
366 mbox_index, value);
367 continue;
368 } else {
369 strncpy(mbox[mbox_index].button2, value, BUF_BIG - 1);
371 } else if (!strcmp(setting, "fetchcmd.")) {
372 if (strlen(value) + 1 > BUF_BIG) {
373 DMA(DEBUG_ERROR,
374 "Mailbox %i fetchcmd string '%s' is too long.\n",
375 mbox_index, value);
376 continue;
377 } else {
378 strncpy(mbox[mbox_index].fetchcmd, value, BUF_BIG - 1);
380 } else if (!strcmp(setting, "fetchinterval.")) {
381 mbox[mbox_index].fetchinterval = atoi(value);
382 } else if (!strcmp(setting, "debug.")) {
383 int debug_value = debug_default;
384 if (strcasecmp(value, "all") == 0) {
385 debug_value = DEBUG_ALL;
387 /* could disable debugging, but I want the command
388 line argument to provide all information
389 possible. */
390 mbox[mbox_index].debug = debug_value;
391 } else {
392 DMA(DEBUG_INFO, "Unknown setting '%s'\n", setting);
395 (void) fclose(fp);
397 if (!tls)
398 // use GnuTLS's default ciphers.
399 tls = "NORMAL";
401 if (classic_mode && skin && !strcmp(skin, skin_filename))
402 skin_filename = classic_skin_filename;
404 if (skin)
405 free(skin);
407 for (i = 0; i < num_mailboxes; i++)
408 if (mbox[i].label[0] != '\0')
409 parse_mbox_path(i);
410 return 1;
414 static void init_biff(char *config_file)
416 #ifdef HAVE_GCRYPT_H
417 gcry_error_t rc;
418 #endif
419 int loopinterval = DEFAULT_LOOP;
420 unsigned int i;
422 for (i = 0; i < MAX_NUM_MAILBOXES; i++) {
423 memset(mbox[i].label, 0, BUF_SMALL);
424 memset(mbox[i].path, 0, BUF_BIG);
425 memset(mbox[i].notify, 0, BUF_BIG);
426 memset(mbox[i].action, 0, BUF_BIG);
427 memset(mbox[i].actiondc, 0, BUF_BIG);
428 memset(mbox[i].actionnew, 0, BUF_BIG);
429 memset(mbox[i].actionnonew, 0, BUF_BIG);
430 memset(mbox[i].button2, 0, BUF_BIG);
431 memset(mbox[i].fetchcmd, 0, BUF_BIG);
432 mbox[i].loopinterval = 0;
433 mbox[i].getHeaders = NULL;
434 mbox[i].releaseHeaders = NULL;
435 mbox[i].debug = debug_default;
436 mbox[i].askpass = DEFAULT_ASKPASS;
439 #ifdef HAVE_GCRYPT_H
440 /* gcrypt is a little strange, in that it doesn't
441 * seem to initialize its memory pool by itself.
442 * I believe we *expect* "Warning: using insecure memory!"
444 /* gcryctl_disable_secmem gets called before check_version -- in a message on
445 gcrypt-devel august 17 2004. */
446 if ((rc = gcry_control(GCRYCTL_DISABLE_SECMEM, 0)) != 0) {
447 DMA(DEBUG_ERROR,
448 "Error: tried to disable gcrypt warning and failed: %d\n"
449 " Message: %s\n" " libgcrypt version: %s\n", rc,
450 gcry_strerror(rc), gcry_check_version(NULL));
453 /* recently made a requirement, section 2.4 of gcrypt manual */
454 if (gcry_check_version("1.1.42") == NULL) {
455 DMA(DEBUG_ERROR, "Error: incompatible gcrypt version.\n");
457 #endif
459 DMA(DEBUG_INFO, "config_file = %s.\n", config_file);
460 if (!Read_Config_File(config_file, &loopinterval)) {
461 char *m;
462 /* setup defaults if there's no config */
463 if ((m = getenv("MAIL")) != NULL) {
464 /* we are using MAIL environment var. type mbox */
465 if (strlen(m) + 1 > BUF_BIG) {
466 DMA(DEBUG_ERROR,
467 "MAIL environment var '%s' is too long.\n", m);
468 } else {
469 DMA(DEBUG_INFO, "Using MAIL environment var '%s'.\n", m);
470 strncpy(mbox[0].path, m, BUF_BIG - 1);
472 } else if ((m = getenv("USER")) != NULL) {
473 /* we will use the USER env var to find an mbox name */
474 if (strlen(m) + 10 + 1 > BUF_BIG) {
475 DMA(DEBUG_ERROR,
476 "USER environment var '%s' is too long.\n", m);
477 } else {
478 DMA(DEBUG_INFO, "Using /var/mail/%s.\n", m);
479 strcpy(mbox[0].path, "/var/mail/");
480 strncat(mbox[0].path, m, BUF_BIG - 1 - 10);
481 if (mbox[0].path[9] != '/') {
482 DMA(DEBUG_ERROR,
483 "Unexpected failure to construct /var/mail/username, please "
484 "report this with your operating system info and the version of wmbiff.");
487 } else {
488 DMA(DEBUG_ERROR, "Cannot open config file '%s' nor use the "
489 "MAIL environment var.\n", config_file);
490 exit(EXIT_FAILURE);
492 if (!exists(mbox[0].path)) {
493 DMA(DEBUG_ERROR, "Cannot open config file '%s', and the "
494 "default %s doesn't exist.\n", config_file, mbox[0].path);
495 exit(EXIT_FAILURE);
497 strcpy(mbox[0].label, "Spool");
498 mboxCreate((&mbox[0]), mbox[0].path);
501 /* Make labels look right */
502 for (i = 0; i < num_mailboxes; i++) {
503 if (mbox[i].label[0] != '\0') {
504 /* append a colon, but skip if we're using fonts. */
505 if (font == NULL) {
506 int j = strlen(mbox[i].label);
507 if (j < 5) {
508 memset(mbox[i].label + j, ' ', 5 - j);
511 /* but always end after 5 characters */
512 mbox[i].label[6] = '\0';
513 /* set global loopinterval to boxes with 0 loop */
514 if (!mbox[i].loopinterval) {
515 mbox[i].loopinterval = loopinterval;
521 static char **LoadXPM(const char *pixmap_filename)
523 char **xpm;
524 int success;
525 success = XpmReadFileToData((char *) pixmap_filename, &xpm);
526 switch (success) {
527 case XpmOpenFailed:
528 DMA(DEBUG_ERROR, "Unable to open %s\n", pixmap_filename);
529 break;
530 case XpmFileInvalid:
531 DMA(DEBUG_ERROR, "%s is not a valid pixmap\n", pixmap_filename);
532 break;
533 case XpmNoMemory:
534 DMA(DEBUG_ERROR, "Insufficient memory to read %s\n",
535 pixmap_filename);
536 break;
537 default:
538 break;
540 return (xpm);
543 /* tests as "test -f" would */
544 int exists(const char *filename)
546 struct stat st_buf;
547 DMA(DEBUG_INFO, "looking for %s\n", filename);
548 if (stat(filename, &st_buf) == 0 && S_ISREG(st_buf.st_mode)) {
549 DMA(DEBUG_INFO, "found %s\n", filename);
550 return (1);
552 return (0);
555 /* acts like execvp, with code inspired by it */
556 /* mustfree */
557 static char *search_path(const char *path, const char *find_me)
559 char *buf;
560 const char *p;
561 int len, pathlen;
562 if (strchr(find_me, '/') != NULL) {
563 return (strdup_ordie(find_me));
565 pathlen = strlen(path);
566 len = strlen(find_me) + 1;
567 buf = malloc_ordie(pathlen + len + 1);
568 memcpy(buf + pathlen + 1, find_me, len);
569 buf[pathlen] = '/';
571 for (p = path; p != NULL; path = p, path++) {
572 char *startp;
573 p = strchr(path, ':');
574 if (p == NULL) {
575 /* not found; p should point to the null char at the end */
576 startp =
577 memcpy(buf + pathlen - strlen(path), path, strlen(path));
578 } else if (p == path) {
579 /* double colon in a path apparently means try here */
580 startp = &buf[pathlen + 1];
581 } else {
582 /* copy the part between the colons to the buffer */
583 startp = memcpy(buf + pathlen - (p - path), path, p - path);
585 if (exists(startp) != 0) {
586 char *ret = strdup_ordie(startp);
587 free(buf);
588 return (ret);
591 free(buf);
592 return (NULL);
595 /* verifies that .wmbiffrc, is a file, is owned by the user,
596 is not world writeable, and is not world readable. This
597 is just to help keep passwords secure */
598 static int wmbiffrc_permissions_check(const char *wmbiffrc_fname)
600 struct stat st;
601 if (stat(wmbiffrc_fname, &st) != 0) {
602 DMA(DEBUG_ERROR, "Can't stat wmbiffrc: '%s'\n", wmbiffrc_fname);
603 return (1); /* well, it's not a bad permission
604 problem: if you can't find it,
605 neither can the bad guys.. */
607 if ((st.st_mode & S_IFDIR) != 0) {
608 DMA(DEBUG_ERROR, ".wmbiffrc '%s' is a directory!\n"
609 "exiting. don't do that.", wmbiffrc_fname);
610 exit(EXIT_FAILURE);
612 if (st.st_uid != getuid()) {
613 char *user = getenv("USER");
614 DMA(DEBUG_ERROR,
615 ".wmbiffrc '%s' isn't owned by you.\n"
616 "Verify its contents, then 'chown %s %s'\n",
617 wmbiffrc_fname, ((user != NULL) ? user : "(your username)"),
618 wmbiffrc_fname);
619 return (0);
621 if ((st.st_mode & S_IWOTH) != 0) {
622 DMA(DEBUG_ERROR, ".wmbiffrc '%s' is world writable.\n"
623 "Verify its contents, then 'chmod 0600 %s'\n",
624 wmbiffrc_fname, wmbiffrc_fname);
625 return (0);
627 if ((st.st_mode & S_IROTH) != 0) {
628 DMA(DEBUG_ERROR, ".wmbiffrc '%s' is world readable.\n"
629 "Please run 'chmod 0600 %s' and consider changing your passwords.\n",
630 wmbiffrc_fname, wmbiffrc_fname);
631 return (0);
633 return (1);
636 static void ClearDigits(unsigned int i)
638 if (font) {
639 eraseRect(39, mbox_y(i), 58, mbox_y(i + 1) - 1, background);
640 } else {
641 /* overwrite the colon */
642 copyXPMArea((10 * (CHAR_WIDTH + 1)), 64, (CHAR_WIDTH + 1),
643 (CHAR_HEIGHT + 1), 35, mbox_y(i));
644 /* blank out the number fields. */
645 copyXPMArea(39, 84, (3 * (CHAR_WIDTH + 1)), (CHAR_HEIGHT + 1), 39,
646 mbox_y(i));
650 /* Blits a string at given co-ordinates. If a ``new''
651 parameter is nonzero, all digits will be yellow */
652 static void BlitString(const char *name, int x, int y, int new)
654 if (font != NULL) {
655 /* an alternate behavior - draw the string using a font
656 instead of the pixmap. should allow pretty colors */
657 drawString(x, y + CHAR_HEIGHT + 1, name,
658 new ? highlight : foreground, background, 0);
659 } else {
660 /* normal, LED-like behavior. */
661 int i, c, k = x;
662 for (i = 0; name[i] != '\0'; i++) {
663 c = toupper(name[i]);
664 if (c >= 'A' && c <= 'Z') { /* it's a letter */
665 c -= 'A';
666 copyXPMArea(c * (CHAR_WIDTH + 1), (new ? 95 : 74),
667 (CHAR_WIDTH + 1), (CHAR_HEIGHT + 1), k, y);
668 k += (CHAR_WIDTH + 1);
669 } else { /* it's a number or symbol */
670 c -= '0';
671 if (new) {
672 copyXPMArea((c * (CHAR_WIDTH + 1)) + 65, 0,
673 (CHAR_WIDTH + 1), (CHAR_HEIGHT + 1), k, y);
674 } else {
675 copyXPMArea((c * (CHAR_WIDTH + 1)), 64,
676 (CHAR_WIDTH + 1), (CHAR_HEIGHT + 1), k, y);
678 k += (CHAR_WIDTH + 1);
685 /* Blits number to give coordinates.. two 0's, right justified */
686 static void BlitNum(int num, int x, int y, int new)
688 char buf[32];
690 sprintf(buf, "%02i", num);
692 if (font != NULL) {
693 const char *color = (new) ? highlight : foreground;
694 drawString(x + (CHAR_WIDTH * 2 + 4), y + CHAR_HEIGHT + 1, buf,
695 color, background, 1);
696 } else {
697 int newx = x;
699 if (num > 99)
700 newx -= (CHAR_WIDTH + 1);
701 if (num > 999)
702 newx -= (CHAR_WIDTH + 1);
704 BlitString(buf, newx, y, new);
708 /* helper function for displayMsgCounters, which has outgrown its name */
709 static void blitMsgCounters(unsigned int i)
711 int y_row = mbox_y(i); /* constant for each mailbox */
712 ClearDigits(i); /* Clear digits */
713 if ((mbox[i].blink_stat & 0x01) == 0) {
714 int newmail = (mbox[i].UnreadMsgs > 0) ? 1 : 0;
715 if (mbox[i].TextStatus[0] != '\0') {
716 BlitString(mbox[i].TextStatus, 39, y_row, newmail);
717 } else {
718 int mailcount =
719 (newmail) ? mbox[i].UnreadMsgs : ( classic_mode ? mbox[i].TotalMsgs : 0 );
720 BlitNum(mailcount, 45, y_row, newmail);
726 * void execnotify(1) : runs notify command, if given (not null)
728 static void execnotify( /*@null@ */ const char *notifycmd)
730 if (notifycmd != NULL) { /* need to call notify() ? */
731 if (classic_mode && !strcasecmp(notifycmd, "beep"))
732 XBell(display, 100);
733 else if (!strcasecmp(notifycmd, "true")) {
734 /* Yes, nothing */
735 } else {
736 /* Else call external notifyer, ignoring the pid */
737 (void) execCommand(notifycmd);
743 static void
744 displayMsgCounters(unsigned int i, int mail_stat, int *Blink_Mode)
746 switch (mail_stat) {
747 case 2: /* New mail has arrived */
748 /* Enter blink-mode for digits */
749 mbox[i].blink_stat = BLINK_TIMES * 2;
750 *Blink_Mode |= (1 << i); /* Global blink flag set for this mailbox */
751 blitMsgCounters(i);
752 execnotify(mbox[i].notify);
753 break;
754 case 1: /* mailbox has been rescanned/changed */
755 blitMsgCounters(i);
756 break;
757 case 0:
758 break;
759 case -1: /* Error was detected */
760 ClearDigits(i); /* Clear digits */
761 BlitString("XX", 45, mbox_y(i), 0);
762 break;
766 /** counts mail in spool-file
767 Returned value:
768 -1 : Error was encountered
769 0 : mailbox status wasn't changed
770 1 : mailbox was changed (NO new mail)
771 2 : mailbox was changed AND new mail has arrived
773 static int count_mail(unsigned int item)
775 int rc = 0;
777 if (!mbox[item].checkMail) {
778 return -1;
781 if (mbox[item].checkMail(&(mbox[item])) < 0) {
782 /* we failed to obtain any numbers therefore set
783 * them to -1's ensuring the next pass (even if
784 * zero) will be captured correctly
786 mbox[item].TotalMsgs = -1;
787 mbox[item].UnreadMsgs = -1;
788 mbox[item].OldMsgs = -1;
789 mbox[item].OldUnreadMsgs = -1;
790 return -1;
793 if (mbox[item].UnreadMsgs > mbox[item].OldUnreadMsgs &&
794 mbox[item].UnreadMsgs > 0) {
795 rc = 2; /* New mail detected */
796 } else if (mbox[item].UnreadMsgs < mbox[item].OldUnreadMsgs ||
797 mbox[item].TotalMsgs != mbox[item].OldMsgs) {
798 rc = 1; /* mailbox was changed - NO new mail */
799 } else {
800 rc = 0; /* mailbox wasn't changed */
802 mbox[item].OldMsgs = mbox[item].TotalMsgs;
803 mbox[item].OldUnreadMsgs = mbox[item].UnreadMsgs;
804 return rc;
807 static int periodic_mail_check(void)
809 int NeedRedraw = 0;
810 static int Blink_Mode = 0; /* Bit mask, digits are in blinking
811 mode or not. Each bit for separate
812 mailbox */
813 int Sleep_Interval; /* either DEFAULT_SLEEP_INTERVAL or
814 BLINK_SLEEP_INTERVAL */
815 int NewMail = 0; /* flag for global notify */
816 unsigned int i;
817 time_t curtime = time(0);
818 for (i = 0; i < num_mailboxes; i++) {
819 if (mbox[i].label[0] != '\0') {
820 if (curtime >= mbox[i].prevtime + mbox[i].loopinterval) {
821 int mailstat = 0;
822 NeedRedraw = 1;
823 DM(&mbox[i], DEBUG_INFO,
824 "working on [%u].label=>%s< [%u].path=>%s<\n", i,
825 mbox[i].label, i, mbox[i].path);
826 DM(&mbox[i], DEBUG_INFO,
827 "curtime=%d, prevtime=%d, interval=%d\n",
828 (int) curtime, (int) mbox[i].prevtime,
829 mbox[i].loopinterval);
830 mbox[i].prevtime = curtime;
832 XDefineCursor(display, iconwin, busy_cursor);
833 RedrawWindow();
835 mailstat = count_mail(i);
837 XUndefineCursor(display, iconwin);
839 /* Global notify */
840 if (mailstat == 2)
841 NewMail = 1;
843 displayMsgCounters(i, mailstat, &Blink_Mode);
844 /* update our idea of current time, as it
845 may have changed as we check. */
846 curtime = time(0);
848 if (mbox[i].blink_stat > 0) {
849 if (--mbox[i].blink_stat <= 0) {
850 Blink_Mode &= ~(1 << i);
851 mbox[i].blink_stat = 0;
853 displayMsgCounters(i, 1, &Blink_Mode);
854 NeedRedraw = 1;
856 if (mbox[i].fetchinterval > 0 && mbox[i].fetchcmd[0] != '\0'
857 && curtime >=
858 mbox[i].prevfetch_time + mbox[i].fetchinterval) {
860 XDefineCursor(display, iconwin, busy_cursor);
861 RedrawWindow();
863 (void) execCommand(mbox[i].fetchcmd);
865 XUndefineCursor(display, iconwin);
867 mbox[i].prevfetch_time = curtime;
872 /* exec globalnotify if there was any new mail */
873 if (NewMail == 1)
874 execnotify(globalnotify);
876 if (Blink_Mode == 0) {
877 for (i = 0; i < num_mailboxes; i++) {
878 mbox[i].blink_stat = 0;
880 Sleep_Interval = DEFAULT_SLEEP_INTERVAL;
881 } else {
882 Sleep_Interval = BLINK_SLEEP_INTERVAL;
885 if (NeedRedraw) {
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;
913 int i;
914 int top = findTopOfMasterXPM(skin_xpm);
916 ret[0] = malloc_ordie(30);
917 sprintf(ret[0], "%d %d %d %d", width, height, colors, 1);
918 ret[1] = (char *) " \tc #0000FF"; /* no color */
919 ret[2] = malloc_ordie(30);
920 sprintf(ret[2], ".\tc %s", background);
921 ret[3] = (char *) "+\tc #000000"; /* shadowed */
922 ret[4] = (char *) "@\tc #C7C3C7"; /* highlight */
923 ret[5] = (char *) ":\tc #004941"; /* led off */
925 for (i = base; i < base + height; i++) {
927 ret[i] = malloc_ordie(width);
929 if (i < base + margin) {
930 memset(ret[i], ' ', width);
931 } else if (i < height + base - margin) {
933 memset(ret[i], ' ', margin);
935 if (i == base + margin) {
936 memset(ret[i] + margin, '+', width - margin - margin);
937 } else if (i == base + height - margin - 1) {
938 memset(ret[i] + margin, '@', width - margin - margin);
939 } else {
940 // " +..:::...:::...:::...:::...:::.......:::...:::...:::...@ "
941 // " +.:...:.:...:.:...:.:...:.:...:..:..:...:.:...:.:...:..@ " ",
942 ret[i][margin] = '+';
943 memset(ret[i] + margin + 1, '.',
944 width - margin - margin - 1);
945 ret[i][width - margin - 1] = '@';
946 memcpy(ret[i],
947 skin_xpm[((i - (base + margin) - 1) % 11) + top + 1],
948 width);
951 memset(ret[i] + width - margin, ' ', margin);
953 } else {
954 memset(ret[i], ' ', width);
959 * Not sure if this is necessary, it just seemed like a good idea
961 ret[height + base] = NULL;
962 return (ret);
966 * NOTE: this function assumes that the ConnectionNumber() macro
967 * will return the file descriptor of the Display struct
968 * (it does under XFree86 and solaris' openwin X)
970 static void XSleep(int millisec)
972 #ifdef HAVE_POLL
973 struct pollfd timeout;
975 timeout.fd = ConnectionNumber(display);
976 timeout.events = POLLIN;
978 poll(&timeout, 1, millisec);
979 #else
980 struct timeval to;
981 struct timeval *timeout = NULL;
982 fd_set readfds;
983 int max_fd;
985 if (millisec >= 0) {
986 timeout = &to;
987 to.tv_sec = millisec / 1000;
988 to.tv_usec = (millisec % 1000) * 1000;
990 FD_ZERO(&readfds);
991 FD_SET(ConnectionNumber(display), &readfds);
992 max_fd = ConnectionNumber(display);
994 select(max_fd + 1, &readfds, NULL, NULL, timeout);
995 #endif
998 const char **restart_args;
1000 static void restart_wmbiff(int sig
1001 #ifdef HAVE___ATTRIBUTE__
1002 __attribute__ ((unused))
1003 #endif
1006 if (restart_args) {
1007 DMA(DEBUG_ERROR, "exec()'ing %s\n", restart_args[0]);
1008 sleep(1);
1009 execvp(restart_args[0], (char *const *) restart_args);
1010 DMA(DEBUG_ERROR, "exec of %s failed: %s\n",
1011 restart_args[0], strerror(errno));
1012 exit(EXIT_FAILURE);
1014 else
1015 fprintf(stderr, "Unable to restart wmbiff: missing restart arguments (NULL)!\n");
1018 int x_socket(void)
1020 return ConnectionNumber(display);
1022 void ProcessPendingEvents(void)
1024 static int but_pressed_region = -1; /* static so click can be determined */
1025 int but_released_region = -1;
1026 /* X Events */
1027 while (XPending(display)) {
1028 XEvent Event;
1029 const char *press_action;
1031 XNextEvent(display, &Event);
1033 switch (Event.type) {
1034 case Expose:
1035 if (Event.xany.window != win && Event.xany.window != iconwin) {
1036 msglst_redraw();
1037 } else {
1038 RedrawWindow();
1040 break;
1041 case DestroyNotify:
1042 XCloseDisplay(display);
1043 exit(EXIT_SUCCESS);
1044 break;
1045 case ButtonPress:
1046 but_pressed_region =
1047 CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
1048 switch (Event.xbutton.button) {
1049 case 1:
1050 press_action = mbox[but_pressed_region].action;
1051 break;
1052 case 2:
1053 press_action = mbox[but_pressed_region].button2;
1054 break;
1055 case 3:
1056 press_action = mbox[but_pressed_region].fetchcmd;
1057 break;
1058 default:
1059 press_action = NULL;
1060 break;
1063 if (press_action && strcmp(press_action, "msglst") == 0) {
1064 msglst_show(&mbox[but_pressed_region],
1065 Event.xbutton.x_root, Event.xbutton.y_root);
1067 break;
1068 case ButtonRelease:
1069 but_released_region =
1070 CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
1071 if (but_released_region == but_pressed_region
1072 && but_released_region >= 0) {
1073 const char *click_action, *extra_click_action = NULL;
1075 switch (Event.xbutton.button) {
1076 case 1: /* Left mouse-click */
1077 /* C-S-left will restart wmbiff. */
1078 if ((Event.xbutton.state & ControlMask) &&
1079 (Event.xbutton.state & ShiftMask)) {
1080 restart_wmbiff(0);
1082 /* do we need to run an extra action? */
1083 if (mbox[but_released_region].UnreadMsgs == -1) {
1084 extra_click_action =
1085 mbox[but_released_region].actiondc;
1086 } else if (mbox[but_released_region].UnreadMsgs > 0) {
1087 extra_click_action =
1088 mbox[but_released_region].actionnew;
1089 } else {
1090 extra_click_action =
1091 mbox[but_released_region].actionnonew;
1093 click_action = mbox[but_released_region].action;
1094 break;
1095 case 2: /* Middle mouse-click */
1096 click_action = mbox[but_released_region].button2;
1097 break;
1098 case 3: /* Right mouse-click */
1099 click_action = mbox[but_released_region].fetchcmd;
1100 break;
1101 default:
1102 click_action = NULL;
1103 break;
1105 if (extra_click_action != NULL
1106 && extra_click_action[0] != 0
1107 && strcmp(extra_click_action, "msglst")) {
1108 DM(&mbox[but_released_region], DEBUG_INFO,
1109 "running: %s", extra_click_action);
1110 (void) execCommand(extra_click_action);
1112 if (click_action != NULL
1113 && click_action[0] != '\0'
1114 && strcmp(click_action, "msglst")) {
1115 DM(&mbox[but_released_region], DEBUG_INFO,
1116 "running: %s", click_action);
1117 (void) execCommand(click_action);
1121 /* a button was released, hide the message list if open */
1122 msglst_hide();
1124 but_pressed_region = -1;
1125 /* RedrawWindow(); */
1126 break;
1127 case MotionNotify:
1128 break;
1129 case KeyPress:{
1130 XKeyPressedEvent *xkpe = (XKeyPressedEvent *) & Event;
1131 KeySym ks = XkbKeycodeToKeysym(display, xkpe->keycode, 0, 0);
1132 if (ks > XK_0 && ks < XK_0 + min(9U, num_mailboxes)) {
1133 const char *click_action = mbox[ks - XK_1].action;
1134 if (click_action != NULL
1135 && click_action[0] != '\0'
1136 && strcmp(click_action, "msglst")) {
1137 DM(&mbox[but_released_region], DEBUG_INFO,
1138 "running: %s", click_action);
1139 (void) execCommand(click_action);
1144 break;
1145 default:
1146 break;
1151 static void do_biff(int argc, const char **argv)
1153 unsigned int i;
1154 int Sleep_Interval;
1155 const char **skin_xpm = NULL;
1156 const char **bkg_xpm = NULL;
1157 char *skin_file_path = search_path(skin_search_path, skin_filename);
1158 int wmbiff_mask_height = mbox_y(num_mailboxes) + 4;
1160 DMA(DEBUG_INFO, "running %u mailboxes w %d h %d\n", num_mailboxes,
1161 wmbiff_mask_width, wmbiff_mask_height);
1163 if (skin_file_path != NULL) {
1164 skin_xpm = (const char **) LoadXPM(skin_file_path);
1165 free(skin_file_path);
1167 if (skin_xpm == NULL) {
1168 DMA(DEBUG_ERROR, "using built-in xpm; %s wasn't found in %s\n",
1169 skin_filename, skin_search_path);
1170 skin_xpm = (classic_mode ? wmbiff_classic_master_xpm : wmbiff_master_xpm);
1173 bkg_xpm = (const char **) CreateBackingXPM(wmbiff_mask_width, wmbiff_mask_height, skin_xpm);
1175 createXBMfromXPM(wmbiff_mask_bits, (const char**)bkg_xpm,
1176 wmbiff_mask_width, wmbiff_mask_height);
1178 openXwindow(argc, argv, (const char**)bkg_xpm, skin_xpm, wmbiff_mask_bits,
1179 wmbiff_mask_width, wmbiff_mask_height, notWithdrawn);
1181 /* now that display is set, we can create the cursors
1182 (mouse pointer shapes) */
1183 busy_cursor = XCreateFontCursor(display, XC_watch);
1184 ready_cursor = XCreateFontCursor(display, XC_left_ptr);
1186 if (font != NULL) {
1187 if (loadFont(font) < 0) {
1188 DMA(DEBUG_ERROR, "unable to load font. exiting.\n");
1189 exit(EXIT_FAILURE);
1193 /* First time setup of button regions and labels */
1194 for (i = 0; i < num_mailboxes; i++) {
1195 /* make it easy to recover the mbox index from a mouse click */
1196 AddMouseRegion(i, x_origin, mbox_y(i), 58, mbox_y(i + 1) - 1);
1197 if (mbox[i].label[0] != '\0') {
1198 mbox[i].prevtime = mbox[i].prevfetch_time = 0;
1199 BlitString(mbox[i].label, x_origin, mbox_y(i), 0);
1203 do {
1205 Sleep_Interval = periodic_mail_check();
1206 ProcessPendingEvents();
1207 XSleep(Sleep_Interval);
1209 while (forever); /* forever is usually true,
1210 but not when debugging with -exit */
1211 if (skin_xpm != NULL && skin_xpm != wmbiff_master_xpm
1212 && skin_xpm != wmbiff_classic_master_xpm) {
1213 free(skin_xpm); // added 3 jul 02, appeasing valgrind
1215 if (bkg_xpm != NULL) {
1216 // Allocated in CreateBackingXPM()
1217 free((void *)bkg_xpm[0]);
1218 free((void *)bkg_xpm[2]);
1219 int mem_block;
1220 for (mem_block = 6; mem_block < 6 + wmbiff_mask_height; mem_block++)
1221 free((void *)bkg_xpm[mem_block]);
1222 free(bkg_xpm);
1226 static void sigchld_handler(int sig
1227 #ifdef HAVE___ATTRIBUTE__
1228 __attribute__ ((unused))
1229 #endif
1232 while (waitpid(0, NULL, WNOHANG) > 0);
1233 signal(SIGCHLD, sigchld_handler);
1236 static void usage(void)
1238 printf("\nwmBiff v%s"
1239 " - incoming mail checker\n"
1240 "Gennady Belyakov and others (see the README file)\n"
1241 "Please report bugs to %s\n"
1242 "\n"
1243 "usage:\n"
1244 " -bg <color> background color\n"
1245 " -c <filename> use specified config file\n"
1246 " -debug enable debugging\n"
1247 " -display <display name> use specified X display\n"
1248 " -fg <color> foreground color\n"
1249 " -font <font> font instead of LED\n"
1250 " -geometry +XPOS+YPOS initial window position\n"
1251 " -h this help screen\n"
1252 " -hi <color> highlight color for new mail\n"
1253 #ifdef USE_GNUTLS
1254 " -skip-certificate-check using TLS, don't validate the\n"
1255 " server's certificate\n"
1256 #endif
1257 " -relax assume the configuration is \n"
1258 " correct, parse it without paranoia, \n"
1259 " and assume hostnames are okay.\n"
1260 " -o enable classical mode look and behaviour\n"
1261 " -v print the version number\n"
1262 " +w not withdrawn: run as a window\n"
1263 "\n", PACKAGE_VERSION, PACKAGE_BUGREPORT);
1266 static void printversion(void)
1268 printf("wmbiff v%s\n", PACKAGE_VERSION);
1272 static void parse_cmd(int argc, const char **argv, char *config_file)
1274 int i;
1275 int fg = 0, bg = 0, hi = 0;
1277 config_file[0] = '\0';
1279 /* Parse Command Line */
1281 for (i = 1; i < argc; i++) {
1282 const char *arg = argv[i];
1284 if (*arg == '-') {
1285 switch (arg[1]) {
1286 case 'b':
1287 if (strcmp(arg + 1, "bg") == 0) {
1288 if (argc > (i + 1)) {
1289 background = strdup_ordie(argv[i + 1]);
1290 bg = 1;
1291 DMA(DEBUG_INFO, "new background: '%s'\n", foreground);
1292 i++;
1293 if (font == NULL)
1294 font = DEFAULT_FONT;
1297 break;
1298 case 'd':
1299 if (strcmp(arg + 1, "debug") == 0) {
1300 debug_default = DEBUG_ALL;
1301 } else if (strcmp(arg + 1, "display") == 0) {
1302 /* passed to X's command line parser */
1303 } else {
1304 usage();
1305 exit(EXIT_FAILURE);
1307 break;
1308 case 'f':
1309 if (strcmp(arg + 1, "fg") == 0) {
1310 if (argc > (i + 1)) {
1311 foreground = strdup_ordie(argv[i + 1]);
1312 fg = 1;
1313 DMA(DEBUG_INFO, "new foreground: '%s'\n", foreground);
1314 i++;
1315 if (font == NULL)
1316 font = DEFAULT_FONT;
1318 } else if (strcmp(arg + 1, "font") == 0) {
1319 if (argc > (i + 1)) {
1320 if (strcmp(argv[i + 1], "default") == 0) {
1321 font = DEFAULT_FONT;
1322 } else {
1323 font = strdup_ordie(argv[i + 1]);
1325 DMA(DEBUG_INFO, "new font: '%s'\n", font);
1326 i++;
1328 } else {
1329 usage();
1330 exit(EXIT_FAILURE);
1332 break;
1333 case 'g':
1334 if (strcmp(arg + 1, "geometry") != 0) {
1335 usage();
1336 exit(EXIT_FAILURE);
1337 } else {
1338 i++; /* gobble the argument */
1339 if (i >= argc) { /* fail if there's nothing to gobble */
1340 usage();
1341 exit(EXIT_FAILURE);
1344 break;
1345 case 'h':
1346 if (strcmp(arg + 1, "hi") == 0) {
1347 if (argc > (i + 1)) {
1348 highlight = strdup_ordie(argv[i + 1]);
1349 hi = 1;
1350 DMA(DEBUG_INFO, "new highlight: '%s'\n", highlight);
1351 i++;
1352 if (font == NULL)
1353 font = DEFAULT_FONT;
1355 } else if (strcmp(arg + 1, "h") == 0) {
1356 usage();
1357 exit(EXIT_SUCCESS);
1359 break;
1360 case 'v':
1361 printversion();
1362 exit(EXIT_SUCCESS);
1363 break;
1364 case 's':
1365 if (strcmp(arg + 1, "skip-certificate-check") == 0) {
1366 SkipCertificateCheck = 1;
1367 } else {
1368 usage();
1369 exit(EXIT_SUCCESS);
1372 break;
1373 case 'r':
1374 if (strcmp(arg + 1, "relax") == 0) {
1375 Relax = 1;
1376 } else {
1377 usage();
1378 exit(EXIT_SUCCESS);
1381 break;
1382 case 'c':
1383 if (argc > (i + 1)) {
1384 strncpy(config_file, argv[i + 1], 255);
1385 i++;
1387 break;
1388 case 'e': /* undocumented for debugging */
1389 if (strcmp(arg + 1, "exit") == 0) {
1390 forever = 0;
1392 break;
1393 case 'o': /* use classic behaviour/theme */
1394 classic_mode = 1;
1395 break;
1396 default:
1397 usage();
1398 exit(EXIT_SUCCESS);
1399 break;
1401 } else if (*arg == '+') {
1402 switch (arg[1]) {
1403 case 'w':
1404 notWithdrawn = 1;
1405 break;
1406 default:
1407 usage();
1408 exit(EXIT_SUCCESS);
1409 break;
1414 if (classic_mode) {
1415 /* load classic colors if user did not override them */
1416 if(!fg)
1417 foreground = foreground_classic;
1418 if(!bg)
1419 background = background_classic;
1420 if(!hi)
1421 highlight = highlight_classic;
1425 int main(int argc, const char *argv[])
1427 char uconfig_file[256];
1429 /* hold on to the arguments we were started with; we
1430 will need them if we have to restart on sigusr1 */
1431 restart_args =
1432 (const char **) malloc((argc + 1) * sizeof(const char *));
1433 if (restart_args) {
1434 memcpy(restart_args, argv, (argc) * sizeof(const char *));
1435 restart_args[argc] = NULL;
1438 parse_cmd(argc, argv, uconfig_file);
1440 /* decide what the config file is */
1441 if (uconfig_file[0] != '\0') { /* user-specified config file */
1442 DMA(DEBUG_INFO, "Using user-specified config file '%s'.\n",
1443 uconfig_file);
1444 } else {
1445 const char *home = getenv("HOME");
1446 if (home == NULL) {
1447 DMA(DEBUG_ERROR,
1448 "$HOME undefined. Use the -c option to specify a wmbiffrc\n");
1449 exit(EXIT_FAILURE);
1451 sprintf(uconfig_file, "%s/.wmbiffrc", home);
1454 if (wmbiffrc_permissions_check(uconfig_file) == 0) {
1455 DMA(DEBUG_ERROR,
1456 "WARNING: In future versions of WMBiff, .wmbiffrc MUST be\n"
1457 "owned by the user, and not readable or writable by others.\n\n");
1459 init_biff(uconfig_file);
1460 signal(SIGCHLD, sigchld_handler);
1461 signal(SIGUSR1, restart_wmbiff);
1462 signal(SIGPIPE, SIG_IGN); /* write() may fail */
1464 do_biff(argc, argv);
1466 // free resources
1467 if (restart_args)
1468 free(restart_args);
1469 if (custom_skin)
1470 free((void *)skin_filename);
1472 return 0;