Initial dockapps git repo
[dockapps.git] / wmbiff-0.4.27 / wmbiff / wmbiff.c
blobec28119bcfce62e0d52789ca454a5f59f56b0885
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/keysym.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 static char wmbiff_mask_bits[64 * 64];
45 static const int wmbiff_mask_width = 64;
46 // const int wmbiff_mask_height = 64;
48 #define CHAR_WIDTH 5
49 #define CHAR_HEIGHT 7
51 #define BLINK_TIMES 8
52 #define DEFAULT_SLEEP_INTERVAL 5000
53 #define BLINK_SLEEP_INTERVAL 200
54 #define DEFAULT_LOOP 5
56 #define MAX_NUM_MAILBOXES 40
57 static mbox_t mbox[MAX_NUM_MAILBOXES];
59 /* this is the normal pixmap. */
60 static const char *skin_filename = "wmbiff-master-led.xpm";
61 /* global notify action taken (if globalnotify option is set) */
62 /*@null@*/
63 static const char *globalnotify = NULL;
64 /* path to search for pixmaps */
65 /* /usr/share/wmbiff should have the default pixmap. */
66 /* /usr/local/share/wmbiff if compiled locally. */
67 /* / is there in case a user wants to specify a complete path */
68 /* . is there for development. */
69 static const char *skin_search_path = DEFAULT_SKIN_PATH;
70 /* for gnutls */
71 /*@null@*/
72 const char *certificate_filename = NULL;
74 /* it could be argued that a better default exists. */
75 #define DEFAULT_FONT "-*-fixed-*-r-*-*-10-*-*-*-*-*-*-*"
76 /*@null@*/
77 static const char *font = NULL;
79 int debug_default = DEBUG_ERROR;
81 /* color from wmbiff's xpm, down to 24 bits. */
82 const char *foreground = "#21B3AF"; /* foreground cyan */
83 const char *background = "#202020"; /* background gray */
84 static const char *highlight = "yellow";
85 int SkipCertificateCheck = 0;
86 int Relax = 0; /* be not paranoid */
87 static int notWithdrawn = 0;
89 static unsigned int num_mailboxes = 1;
90 static const int x_origin = 5;
91 static const int y_origin = 5;
92 static int forever = 1; /* keep running. */
94 extern Window win;
95 extern Window iconwin;
97 Cursor busy_cursor, ready_cursor;
99 static __inline /*@out@ */ void *
100 malloc_ordie(size_t len)
102 void *ret = malloc(len);
103 if (ret == NULL) {
104 fprintf(stderr, "unable to allocate %d bytes\n", (int) len);
105 abort();
107 return (ret);
110 /* where vertically the mailbox sits for blitting characters. */
111 static int mbox_y(unsigned int mboxnum)
113 return ((11 * mboxnum) + y_origin);
116 /* special shortcuts for longer shell client commands */
117 static int gicuCreate( /*@notnull@ */ Pop3 pc, const char *path)
119 char buf[255];
120 if (isdigit(path[5])) {
121 sprintf(buf,
122 "shell:::echo `gnomeicu-client -u%s msgcount` new",
123 path + 5);
124 } else {
125 sprintf(buf, "shell:::echo `gnomeicu-client msgcount` new");
127 return (shellCreate(pc, buf));
130 static int fingerCreate( /*@notnull@ */ Pop3 pc, const char *path)
132 char buf[255];
133 sprintf(buf, "shell:::finger -lm %s | "
134 "perl -ne '(/^new mail/i && print \"new\");' "
135 "-e '(/^mail last read/i && print \"old\");' "
136 "-e '(/^no( unread)? mail/i && print \"no\");'", path + 7);
137 return (shellCreate(pc, buf));
140 /* Read a line from a file to obtain a pair setting=value
141 skips # and leading spaces
142 NOTE: if setting finish with 0, 1, 2, 3 or 4 last char are deleted and
143 index takes its value... if not index will get -1
144 Returns -1 if no setting=value
146 static int ReadLine(FILE * fp, /*@out@ */ char *setting,
147 /*@out@ */ char *value, /*@out@ */ int *mbox_index)
149 char buf[BUF_SIZE];
150 char *p, *q;
151 int len;
153 *setting = '\0';
154 *value = '\0';
155 *mbox_index = -1;
157 if (!fp || feof(fp))
158 return -1;
160 if (!fgets(buf, BUF_SIZE - 1, fp))
161 return -1;
163 len = strlen(buf);
165 if (buf[len - 1] == '\n') {
166 buf[len - 1] = '\0'; /* strip linefeed */
169 StripComment(buf);
171 if (!(p = strtok(buf, "=")))
172 return -1;
173 if (!(q = strtok(NULL, "\n")))
174 return -1;
176 /* Chg - Mark Hurley
177 * Date: May 8, 2001
178 * Removed for loop (which removed leading spaces)
179 * Leading & Trailing spaces need to be removed
180 * to Fix Debian bug #95849
182 FullTrim(p);
183 FullTrim(q);
185 /* strcpy(setting, p); nspring replaced with sscanf dec 2002 */
186 strcpy(value, q);
188 if (sscanf(p, "%[_a-z.]%d", setting, mbox_index) == 2) {
189 /* mailbox-specific configuration, ends in a digit */
190 if (*mbox_index < 0 || *mbox_index >= MAX_NUM_MAILBOXES) {
191 DMA(DEBUG_ERROR, "invalid mailbox number %d\n", *mbox_index);
192 exit(EXIT_FAILURE);
194 } else if (sscanf(p, "%[a-z]", setting) == 1) {
195 /* global configuration, all text. */
196 *mbox_index = -1;
197 } else {
198 /* we found an uncommented line that has an equals,
199 but is non-alphabetic. */
200 DMA(DEBUG_INFO, "unparsed setting %s\n", p);
201 return -1;
204 DMA(DEBUG_INFO, "@%s.%d=%s@\n", setting, *mbox_index, value);
205 return 1;
208 struct path_demultiplexer {
209 const char *id; /* followed by a colon */
210 int (*creator) ( /*@notnull@ */ Pop3 pc, const char *path);
213 static struct path_demultiplexer paths[] = {
214 {"pop3:", pop3Create},
215 {"pop3s:", pop3Create},
216 {"shell:", shellCreate},
217 {"gicu:", gicuCreate},
218 {"licq:", licqCreate},
219 {"finger:", fingerCreate},
220 {"imap:", imap4Create},
221 {"imaps:", imap4Create},
222 {"sslimap:", imap4Create},
223 {"maildir:", maildirCreate},
224 {"mbox:", mboxCreate},
225 {NULL, NULL}
229 static void parse_mbox_path(unsigned int item)
231 int i;
232 /* find the creator */
233 for (i = 0;
234 paths[i].id != NULL
235 && strncasecmp(mbox[item].path, paths[i].id, strlen(paths[i].id));
236 i++);
237 /* if found, execute */
238 if (paths[i].id != NULL) {
239 if (paths[i].creator((&mbox[item]), mbox[item].path) != 0) {
240 DMA(DEBUG_ERROR, "creator for mailbox %u returned failure\n",
241 item);
243 } else {
244 /* default are mbox */
245 mboxCreate((&mbox[item]), mbox[item].path);
249 static int Read_Config_File(char *filename, int *loopinterval)
251 FILE *fp;
252 char setting[BUF_SMALL], value[BUF_SIZE];
253 int mbox_index;
254 unsigned int i;
256 if (!(fp = fopen(filename, "r"))) {
257 DMA(DEBUG_ERROR, "Unable to open %s, no settings read: %s\n",
258 filename, strerror(errno));
259 return 0;
261 while (!feof(fp)) {
262 /* skanky: -1 can represent an unparsed line
263 or an error */
264 if (ReadLine(fp, setting, value, &mbox_index) == -1)
265 continue;
267 /* settings that can be global go here. */
268 if (!strcmp(setting, "interval")) {
269 *loopinterval = atoi(value);
270 continue;
271 } else if (!strcmp(setting, "askpass")) {
272 const char *askpass = strdup_ordie(value);
273 if (mbox_index == -1) {
274 DMA(DEBUG_INFO, "setting all to askpass %s\n", askpass);
275 for (i = 0; i < MAX_NUM_MAILBOXES; i++)
276 mbox[i].askpass = askpass;
277 } else {
278 mbox[mbox_index].askpass = askpass;
280 continue;
281 } else if (!strcmp(setting, "skinfile")) {
282 skin_filename = strdup_ordie(value);
283 continue;
284 } else if (!strcmp(setting, "certfile")) { /* not yet supported */
285 certificate_filename = strdup_ordie(value);
286 continue;
287 } else if (!strcmp(setting, "globalnotify")) {
288 globalnotify = strdup_ordie(value);
289 continue;
290 } else if (mbox_index == -1) {
291 DMA(DEBUG_INFO, "Unknown global setting '%s'\n", setting);
292 continue; /* Didn't read any setting.[0-5] value */
295 if (mbox_index >= MAX_NUM_MAILBOXES) {
296 DMA(DEBUG_ERROR, "Don't have %d mailboxes.\n", mbox_index);
297 continue;
300 if (1U + mbox_index > num_mailboxes
301 && mbox_index + 1 <= MAX_NUM_MAILBOXES) {
302 num_mailboxes = 1U + mbox_index;
305 /* now only local settings */
306 if (!strcmp(setting, "label.")) {
307 if (strlen(value) + 1 > BUF_SMALL) {
308 DMA(DEBUG_ERROR,
309 "Mailbox %i label string '%s' is too long.\n",
310 mbox_index, value);
311 continue;
312 } else {
313 strncpy(mbox[mbox_index].label, value, BUF_SMALL - 1);
315 } else if (!strcmp(setting, "path.")) {
316 if (strlen(value) + 1 > BUF_BIG) {
317 DMA(DEBUG_ERROR,
318 "Mailbox %i path string '%s' is too long.\n",
319 mbox_index, value);
320 continue;
321 } else {
322 strncpy(mbox[mbox_index].path, value, BUF_BIG - 1);
324 } else if (!strcmp(setting, "notify.")) {
325 if (strlen(value) + 1 > BUF_BIG) {
326 DMA(DEBUG_ERROR,
327 "Mailbox %i notify string '%s' is too long.\n",
328 mbox_index, value);
329 continue;
330 } else {
331 strncpy(mbox[mbox_index].notify, value, BUF_BIG - 1);
333 } else if (!strcmp(setting, "action.")) {
334 if (strlen(value) + 1 > BUF_BIG) {
335 DMA(DEBUG_ERROR,
336 "Mailbox %i action string '%s' is too long.\n",
337 mbox_index, value);
338 continue;
339 } else {
340 strncpy(mbox[mbox_index].action, value, BUF_BIG - 1);
342 } else if (!strcmp(setting, "action_disconnected.")) {
343 if (strlen(value) + 1 > BUF_BIG) {
344 DMA(DEBUG_ERROR,
345 "Mailbox %i action_disconnected string '%s' is too long.\n",
346 mbox_index, value);
347 continue;
348 } else {
349 strncpy(mbox[mbox_index].actiondc, value, BUF_BIG - 1);
351 } else if (!strcmp(setting, "action_new_mail.")) {
352 if (strlen(value) + 1 > BUF_BIG) {
353 DMA(DEBUG_ERROR,
354 "Mailbox %i action_new_mail string '%s' is too long.\n",
355 mbox_index, value);
356 continue;
357 } else {
358 strncpy(mbox[mbox_index].actionnew, value, BUF_BIG - 1);
360 } else if (!strcmp(setting, "action_no_new_mail.")) {
361 if (strlen(value) + 1 > BUF_BIG) {
362 DMA(DEBUG_ERROR,
363 "Mailbox %i action_no_new_mail string '%s' is too long.\n",
364 mbox_index, value);
365 continue;
366 } else {
367 strncpy(mbox[mbox_index].actionnonew, value, BUF_BIG - 1);
369 } else if (!strcmp(setting, "interval.")) {
370 mbox[mbox_index].loopinterval = atoi(value);
371 } else if (!strcmp(setting, "buttontwo.")) {
372 if (strlen(value) + 1 > BUF_BIG) {
373 DMA(DEBUG_ERROR,
374 "Mailbox %i buttontwo string '%s' is too long.\n",
375 mbox_index, value);
376 continue;
377 } else {
378 strncpy(mbox[mbox_index].button2, value, BUF_BIG - 1);
380 } else if (!strcmp(setting, "fetchcmd.")) {
381 if (strlen(value) + 1 > BUF_BIG) {
382 DMA(DEBUG_ERROR,
383 "Mailbox %i fetchcmd string '%s' is too long.\n",
384 mbox_index, value);
385 continue;
386 } else {
387 strncpy(mbox[mbox_index].fetchcmd, value, BUF_BIG - 1);
389 } else if (!strcmp(setting, "fetchinterval.")) {
390 mbox[mbox_index].fetchinterval = atoi(value);
391 } else if (!strcmp(setting, "debug.")) {
392 int debug_value = debug_default;
393 if (strcasecmp(value, "all") == 0) {
394 debug_value = DEBUG_ALL;
396 /* could disable debugging, but I want the command
397 line argument to provide all information
398 possible. */
399 mbox[mbox_index].debug = debug_value;
400 } else {
401 DMA(DEBUG_INFO, "Unknown setting '%s'\n", setting);
404 (void) fclose(fp);
405 for (i = 0; i < num_mailboxes; i++)
406 if (mbox[i].label[0] != '\0')
407 parse_mbox_path(i);
408 return 1;
412 static void init_biff(char *config_file)
414 #ifdef HAVE_GCRYPT_H
415 gcry_error_t rc;
416 #endif
417 int loopinterval = DEFAULT_LOOP;
418 unsigned int i;
420 for (i = 0; i < MAX_NUM_MAILBOXES; i++) {
421 memset(mbox[i].label, 0, BUF_SMALL);
422 memset(mbox[i].path, 0, BUF_BIG);
423 memset(mbox[i].notify, 0, BUF_BIG);
424 memset(mbox[i].action, 0, BUF_BIG);
425 memset(mbox[i].actiondc, 0, BUF_BIG);
426 memset(mbox[i].actionnew, 0, BUF_BIG);
427 memset(mbox[i].actionnonew, 0, BUF_BIG);
428 memset(mbox[i].button2, 0, BUF_BIG);
429 memset(mbox[i].fetchcmd, 0, BUF_BIG);
430 mbox[i].loopinterval = 0;
431 mbox[i].getHeaders = NULL;
432 mbox[i].releaseHeaders = NULL;
433 mbox[i].debug = debug_default;
434 mbox[i].askpass = DEFAULT_ASKPASS;
437 #ifdef HAVE_GCRYPT_H
438 /* gcrypt is a little strange, in that it doesn't
439 * seem to initialize its memory pool by itself.
440 * I believe we *expect* "Warning: using insecure memory!"
442 /* gcryctl_disable_secmem gets called before check_version -- in a message on
443 gcrypt-devel august 17 2004. */
444 if ((rc = gcry_control(GCRYCTL_DISABLE_SECMEM, 0)) != 0) {
445 DMA(DEBUG_ERROR,
446 "Error: tried to disable gcrypt warning and failed: %d\n"
447 " Message: %s\n" " libgcrypt version: %s\n", rc,
448 gcry_strerror(rc), gcry_check_version(NULL));
451 /* recently made a requirement, section 2.4 of gcrypt manual */
452 if (gcry_check_version("1.1.42") == NULL) {
453 DMA(DEBUG_ERROR, "Error: incompatible gcrypt version.\n");
457 if ((rc = gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0)) != 0) {
458 DMA(DEBUG_ERROR,
459 "Error: gcry_control() to initialize secure memory returned non-zero: %d\n"
460 " Message: %s\n"
461 " libgcrypt version: %s\n"
462 " recovering: will fail later if using CRAM-MD5 or APOP authentication.\n",
463 rc, gcry_strerror(rc), gcry_check_version(NULL));
466 #endif
468 DMA(DEBUG_INFO, "config_file = %s.\n", config_file);
469 if (!Read_Config_File(config_file, &loopinterval)) {
470 char *m;
471 /* setup defaults if there's no config */
472 if ((m = getenv("MAIL")) != NULL) {
473 /* we are using MAIL environment var. type mbox */
474 if (strlen(m) + 1 > BUF_BIG) {
475 DMA(DEBUG_ERROR,
476 "MAIL environment var '%s' is too long.\n", m);
477 } else {
478 DMA(DEBUG_INFO, "Using MAIL environment var '%s'.\n", m);
479 strncpy(mbox[0].path, m, BUF_BIG - 1);
481 } else if ((m = getenv("USER")) != NULL) {
482 /* we will use the USER env var to find an mbox name */
483 if (strlen(m) + 10 + 1 > BUF_BIG) {
484 DMA(DEBUG_ERROR,
485 "USER environment var '%s' is too long.\n", m);
486 } else {
487 DMA(DEBUG_INFO, "Using /var/mail/%s.\n", m);
488 strcpy(mbox[0].path, "/var/mail/");
489 strncat(mbox[0].path, m, BUF_BIG - 1 - 10);
490 if (mbox[0].path[9] != '/') {
491 DMA(DEBUG_ERROR,
492 "Unexpected failure to construct /var/mail/username, please "
493 "report this with your operating system info and the version of wmbiff.");
496 } else {
497 DMA(DEBUG_ERROR, "Cannot open config file '%s' nor use the "
498 "MAIL environment var.\n", config_file);
499 exit(EXIT_FAILURE);
501 if (!exists(mbox[0].path)) {
502 DMA(DEBUG_ERROR, "Cannot open config file '%s', and the "
503 "default %s doesn't exist.\n", config_file, mbox[0].path);
504 exit(EXIT_FAILURE);
506 strcpy(mbox[0].label, "Spool");
507 mboxCreate((&mbox[0]), mbox[0].path);
510 /* Make labels look right */
511 for (i = 0; i < num_mailboxes; i++) {
512 if (mbox[i].label[0] != '\0') {
513 /* append a colon, but skip if we're using fonts. */
514 if (font == NULL) {
515 int j = strlen(mbox[i].label);
516 if (j < 5) {
517 memset(mbox[i].label + j, ' ', 5 - j);
519 mbox[i].label[5] = ':';
521 /* but always end after 5 characters */
522 mbox[i].label[6] = '\0';
523 /* set global loopinterval to boxes with 0 loop */
524 if (!mbox[i].loopinterval) {
525 mbox[i].loopinterval = loopinterval;
531 static char **LoadXPM(const char *pixmap_filename)
533 char **xpm;
534 int success;
535 success = XpmReadFileToData((char *) pixmap_filename, &xpm);
536 switch (success) {
537 case XpmOpenFailed:
538 DMA(DEBUG_ERROR, "Unable to open %s\n", pixmap_filename);
539 break;
540 case XpmFileInvalid:
541 DMA(DEBUG_ERROR, "%s is not a valid pixmap\n", pixmap_filename);
542 break;
543 case XpmNoMemory:
544 DMA(DEBUG_ERROR, "Insufficient memory to read %s\n",
545 pixmap_filename);
546 break;
547 default:
548 break;
550 return (xpm);
553 /* tests as "test -f" would */
554 int exists(const char *filename)
556 struct stat st_buf;
557 DMA(DEBUG_INFO, "looking for %s\n", filename);
558 if (stat(filename, &st_buf) == 0 && S_ISREG(st_buf.st_mode)) {
559 DMA(DEBUG_INFO, "found %s\n", filename);
560 return (1);
562 return (0);
565 /* acts like execvp, with code inspired by it */
566 /* mustfree */
567 static char *search_path(const char *path, /*@notnull@ */
568 const char *find_me)
570 char *buf;
571 const char *p;
572 int len, pathlen;
573 if (strchr(find_me, '/') != NULL) {
574 return (strdup_ordie(find_me));
576 pathlen = strlen(path);
577 len = strlen(find_me) + 1;
578 buf = malloc_ordie(pathlen + len + 1);
579 memcpy(buf + pathlen + 1, find_me, len);
580 buf[pathlen] = '/';
582 for (p = path; p != NULL; path = p, path++) {
583 char *startp;
584 p = strchr(path, ':');
585 if (p == NULL) {
586 /* not found; p should point to the null char at the end */
587 startp =
588 memcpy(buf + pathlen - strlen(path), path, strlen(path));
589 } else if (p == path) {
590 /* double colon in a path apparently means try here */
591 startp = &buf[pathlen + 1];
592 } else {
593 /* copy the part between the colons to the buffer */
594 startp = memcpy(buf + pathlen - (p - path), path, p - path);
596 if (exists(startp) != 0) {
597 char *ret = strdup_ordie(startp);
598 free(buf);
599 return (ret);
602 free(buf);
603 return (NULL);
606 /* verifies that .wmbiffrc, is a file, is owned by the user,
607 is not world writeable, and is not world readable. This
608 is just to help keep passwords secure */
609 static int wmbiffrc_permissions_check(const char *wmbiffrc_fname)
611 struct stat st;
612 if (stat(wmbiffrc_fname, &st) != 0) {
613 DMA(DEBUG_ERROR, "Can't stat wmbiffrc: '%s'\n", wmbiffrc_fname);
614 return (1); /* well, it's not a bad permission
615 problem: if you can't find it,
616 neither can the bad guys.. */
618 if ((st.st_mode & S_IFDIR) != 0) {
619 DMA(DEBUG_ERROR, ".wmbiffrc '%s' is a directory!\n"
620 "exiting. don't do that.", wmbiffrc_fname);
621 exit(EXIT_FAILURE);
623 if (st.st_uid != getuid()) {
624 char *user = getenv("USER");
625 DMA(DEBUG_ERROR,
626 ".wmbiffrc '%s' isn't owned by you.\n"
627 "Verify its contents, then 'chown %s %s'\n",
628 wmbiffrc_fname, ((user != NULL) ? user : "(your username)"),
629 wmbiffrc_fname);
630 return (0);
632 if ((st.st_mode & S_IWOTH) != 0) {
633 DMA(DEBUG_ERROR, ".wmbiffrc '%s' is world writable.\n"
634 "Verify its contents, then 'chmod 0600 %s'\n",
635 wmbiffrc_fname, wmbiffrc_fname);
636 return (0);
638 if ((st.st_mode & S_IROTH) != 0) {
639 DMA(DEBUG_ERROR, ".wmbiffrc '%s' is world readable.\n"
640 "Please run 'chmod 0600 %s' and consider changing your passwords.\n",
641 wmbiffrc_fname, wmbiffrc_fname);
642 return (0);
644 return (1);
647 static void ClearDigits(unsigned int i)
649 if (font) {
650 eraseRect(39, mbox_y(i), 58, mbox_y(i + 1) - 1, background);
651 } else {
652 /* overwrite the colon */
653 copyXPMArea((10 * (CHAR_WIDTH + 1)), 64, (CHAR_WIDTH + 1),
654 (CHAR_HEIGHT + 1), 35, mbox_y(i));
655 /* blank out the number fields. */
656 copyXPMArea(39, 84, (3 * (CHAR_WIDTH + 1)), (CHAR_HEIGHT + 1), 39,
657 mbox_y(i));
661 /* Blits a string at given co-ordinates. If a ``new''
662 parameter is nonzero, all digits will be yellow */
663 static void BlitString(const char *name, int x, int y, int new)
665 if (font != NULL) {
666 /* an alternate behavior - draw the string using a font
667 instead of the pixmap. should allow pretty colors */
668 drawString(x, y + CHAR_HEIGHT + 1, name,
669 new ? highlight : foreground, background, 0);
670 } else {
671 /* normal, LED-like behavior. */
672 int i, c, k = x;
673 for (i = 0; name[i] != '\0'; i++) {
674 c = toupper(name[i]);
675 if (c >= 'A' && c <= 'Z') { /* it's a letter */
676 c -= 'A';
677 copyXPMArea(c * (CHAR_WIDTH + 1), (new ? 95 : 74),
678 (CHAR_WIDTH + 1), (CHAR_HEIGHT + 1), k, y);
679 k += (CHAR_WIDTH + 1);
680 } else { /* it's a number or symbol */
681 c -= '0';
682 if (new) {
683 copyXPMArea((c * (CHAR_WIDTH + 1)) + 65, 0,
684 (CHAR_WIDTH + 1), (CHAR_HEIGHT + 1), k, y);
685 } else {
686 copyXPMArea((c * (CHAR_WIDTH + 1)), 64,
687 (CHAR_WIDTH + 1), (CHAR_HEIGHT + 1), k, y);
689 k += (CHAR_WIDTH + 1);
696 /* Blits number to give coordinates.. two 0's, right justified */
697 static void BlitNum(int num, int x, int y, int new)
699 char buf[32];
701 sprintf(buf, "%02i", num);
703 if (font != NULL) {
704 const char *color = (new) ? highlight : foreground;
705 drawString(x + (CHAR_WIDTH * 2 + 4), y + CHAR_HEIGHT + 1, buf,
706 color, background, 1);
707 } else {
708 int newx = x;
710 if (num > 99)
711 newx -= (CHAR_WIDTH + 1);
712 if (num > 999)
713 newx -= (CHAR_WIDTH + 1);
715 BlitString(buf, newx, y, new);
719 /* helper function for displayMsgCounters, which has outgrown its name */
720 static void blitMsgCounters(unsigned int i)
722 int y_row = mbox_y(i); /* constant for each mailbox */
723 ClearDigits(i); /* Clear digits */
724 if ((mbox[i].blink_stat & 0x01) == 0) {
725 int newmail = (mbox[i].UnreadMsgs > 0) ? 1 : 0;
726 if (mbox[i].TextStatus[0] != '\0') {
727 BlitString(mbox[i].TextStatus, 39, y_row, newmail);
728 } else {
729 int mailcount =
730 (newmail) ? mbox[i].UnreadMsgs : mbox[i].TotalMsgs;
731 BlitNum(mailcount, 45, y_row, newmail);
737 * void execnotify(1) : runs notify command, if given (not null)
739 static void execnotify( /*@null@ */ const char *notifycmd)
741 if (notifycmd != NULL) { /* need to call notify() ? */
742 if (!strcasecmp(notifycmd, "beep")) { /* Internal keyword ? */
743 /* Yes, bell */
744 XBell(display, 100);
745 } else if (!strcasecmp(notifycmd, "true")) {
746 /* Yes, nothing */
747 } else {
748 /* Else call external notifyer, ignoring the pid */
749 (void) execCommand(notifycmd);
755 static void
756 displayMsgCounters(unsigned int i, int mail_stat, int *Blink_Mode)
758 switch (mail_stat) {
759 case 2: /* New mail has arrived */
760 /* Enter blink-mode for digits */
761 mbox[i].blink_stat = BLINK_TIMES * 2;
762 *Blink_Mode |= (1 << i); /* Global blink flag set for this mailbox */
763 blitMsgCounters(i);
764 execnotify(mbox[i].notify);
766 /* Autofetch on new mail arrival? */
767 if (mbox[i].fetchinterval == -1 && mbox[i].fetchcmd[0] != '\0') {
768 (void) execCommand(mbox[i].fetchcmd); /* yes */
770 break;
771 case 1: /* mailbox has been rescanned/changed */
772 blitMsgCounters(i);
773 break;
774 case 0:
775 break;
776 case -1: /* Error was detected */
777 ClearDigits(i); /* Clear digits */
778 BlitString("XX", 45, mbox_y(i), 0);
779 break;
783 /** counts mail in spool-file
784 Returned value:
785 -1 : Error was encountered
786 0 : mailbox status wasn't changed
787 1 : mailbox was changed (NO new mail)
788 2 : mailbox was changed AND new mail has arrived
790 static int count_mail(unsigned int item)
792 int rc = 0;
794 if (!mbox[item].checkMail) {
795 return -1;
798 if (mbox[item].checkMail(&(mbox[item])) < 0) {
799 /* we failed to obtain any numbers therefore set
800 * them to -1's ensuring the next pass (even if
801 * zero) will be captured correctly
803 mbox[item].TotalMsgs = -1;
804 mbox[item].UnreadMsgs = -1;
805 mbox[item].OldMsgs = -1;
806 mbox[item].OldUnreadMsgs = -1;
807 return -1;
810 if (mbox[item].UnreadMsgs > mbox[item].OldUnreadMsgs &&
811 mbox[item].UnreadMsgs > 0) {
812 rc = 2; /* New mail detected */
813 } else if (mbox[item].UnreadMsgs < mbox[item].OldUnreadMsgs ||
814 mbox[item].TotalMsgs != mbox[item].OldMsgs) {
815 rc = 1; /* mailbox was changed - NO new mail */
816 } else {
817 rc = 0; /* mailbox wasn't changed */
819 mbox[item].OldMsgs = mbox[item].TotalMsgs;
820 mbox[item].OldUnreadMsgs = mbox[item].UnreadMsgs;
821 return rc;
824 static int periodic_mail_check(void)
826 int NeedRedraw = 0;
827 static int Blink_Mode = 0; /* Bit mask, digits are in blinking
828 mode or not. Each bit for separate
829 mailbox */
830 int Sleep_Interval; /* either DEFAULT_SLEEP_INTERVAL or
831 BLINK_SLEEP_INTERVAL */
832 int NewMail = 0; /* flag for global notify */
833 unsigned int i;
834 time_t curtime = time(0);
835 for (i = 0; i < num_mailboxes; i++) {
836 if (mbox[i].label[0] != '\0') {
837 if (curtime >= mbox[i].prevtime + mbox[i].loopinterval) {
838 int mailstat = 0;
839 NeedRedraw = 1;
840 DM(&mbox[i], DEBUG_INFO,
841 "working on [%u].label=>%s< [%u].path=>%s<\n", i,
842 mbox[i].label, i, mbox[i].path);
843 DM(&mbox[i], DEBUG_INFO,
844 "curtime=%d, prevtime=%d, interval=%d\n",
845 (int) curtime, (int) mbox[i].prevtime,
846 mbox[i].loopinterval);
847 mbox[i].prevtime = curtime;
849 XDefineCursor(display, iconwin, busy_cursor);
850 RedrawWindow();
852 mailstat = count_mail(i);
854 XUndefineCursor(display, iconwin);
856 if ((mailstat == 2) && (mbox[i].notify[0] == '\0')) {
857 /* for global notify */
858 NewMail = 1;
860 displayMsgCounters(i, mailstat, &Blink_Mode);
861 /* update our idea of current time, as it
862 may have changed as we check. */
863 curtime = time(0);
865 if (mbox[i].blink_stat > 0) {
866 if (--mbox[i].blink_stat <= 0) {
867 Blink_Mode &= ~(1 << i);
868 mbox[i].blink_stat = 0;
870 displayMsgCounters(i, 1, &Blink_Mode);
871 NeedRedraw = 1;
873 if (mbox[i].fetchinterval > 0 && mbox[i].fetchcmd[0] != '\0'
874 && curtime >=
875 mbox[i].prevfetch_time + mbox[i].fetchinterval) {
877 XDefineCursor(display, iconwin, busy_cursor);
878 RedrawWindow();
880 (void) execCommand(mbox[i].fetchcmd);
882 XUndefineCursor(display, iconwin);
884 mbox[i].prevfetch_time = curtime;
889 /* exec globalnotify if there was any new mail */
890 if (NewMail == 1) {
891 execnotify(globalnotify);
894 if (Blink_Mode == 0) {
895 for (i = 0; i < num_mailboxes; i++) {
896 mbox[i].blink_stat = 0;
898 Sleep_Interval = DEFAULT_SLEEP_INTERVAL;
899 } else {
900 Sleep_Interval = BLINK_SLEEP_INTERVAL;
903 if (NeedRedraw) {
904 NeedRedraw = 0;
905 RedrawWindow();
908 return Sleep_Interval;
911 static int findTopOfMasterXPM(const char **skin_xpm)
913 int i;
914 for (i = 0; skin_xpm[i] != NULL; i++) {
915 if (strstr(skin_xpm[i], "++++++++") != NULL)
916 return i;
918 DMA(DEBUG_ERROR,
919 "couldn't find the top of the xpm file using the simple method\n");
920 exit(EXIT_FAILURE);
923 static char **CreateBackingXPM(int width, int height,
924 const char **skin_xpm)
926 char **ret = malloc_ordie(sizeof(char *) * (height + 6)
927 + sizeof(void *) /* trailing null space */ );
928 const int colors = 5;
929 const int base = colors + 1;
930 const int margin = 4;
931 int i;
932 int top = findTopOfMasterXPM(skin_xpm);
933 ret[0] = malloc_ordie(30);
934 sprintf(ret[0], "%d %d %d %d", width, height, colors, 1);
935 ret[1] = (char *) " \tc #0000FF"; /* no color */
936 ret[2] = (char *) ".\tc #202020"; /* background gray */
937 ret[2] = malloc_ordie(30);
938 sprintf(ret[2], ".\tc %s", background);
939 ret[3] = (char *) "+\tc #000000"; /* shadowed */
940 ret[4] = (char *) "@\tc #C7C3C7"; /* highlight */
941 ret[5] = (char *) ":\tc #004941"; /* led off */
942 for (i = base; i < base + height; i++) {
943 ret[i] = malloc_ordie(width);
945 for (i = base; i < base + margin; i++) {
946 memset(ret[i], ' ', width);
948 for (i = base + margin; i < height + base - margin; i++) {
949 memset(ret[i], ' ', margin);
951 if (i == base + margin) {
952 memset(ret[i] + margin, '+', width - margin - margin);
953 } else if (i == base + height - margin - 1) {
954 memset(ret[i] + margin, '@', width - margin - margin);
955 } else {
956 // " +..:::...:::...:::...:::...:::.......:::...:::...:::...@ "
957 // " +.:...:.:...:.:...:.:...:.:...:..:..:...:.:...:.:...:..@ " ",
958 ret[i][margin] = '+';
959 memset(ret[i] + margin + 1, '.', width - margin - margin - 1);
960 ret[i][width - margin - 1] = '@';
961 memcpy(ret[i],
962 skin_xpm[((i - (base + margin) - 1) % 11) + top + 1],
963 width);
966 memset(ret[i] + width - margin, ' ', margin);
968 for (i = base + height - margin; i < height + base; i++) {
969 memset(ret[i], ' ', width);
971 ret[height + base] = NULL; /* not sure if this is necessary, it just
972 seemed like a good idea */
973 return (ret);
977 * NOTE: this function assumes that the ConnectionNumber() macro
978 * will return the file descriptor of the Display struct
979 * (it does under XFree86 and solaris' openwin X)
981 static void XSleep(int millisec)
983 #ifdef HAVE_POLL
984 struct pollfd timeout;
986 timeout.fd = ConnectionNumber(display);
987 timeout.events = POLLIN;
989 poll(&timeout, 1, millisec);
990 #else
991 struct timeval to;
992 struct timeval *timeout = NULL;
993 fd_set readfds;
994 int max_fd;
996 if (millisec >= 0) {
997 timeout = &to;
998 to.tv_sec = millisec / 1000;
999 to.tv_usec = (millisec % 1000) * 1000;
1001 FD_ZERO(&readfds);
1002 FD_SET(ConnectionNumber(display), &readfds);
1003 max_fd = ConnectionNumber(display);
1005 select(max_fd + 1, &readfds, NULL, NULL, timeout);
1006 #endif
1009 const char **restart_args;
1011 static void restart_wmbiff(int sig
1012 #ifdef HAVE___ATTRIBUTE__
1013 __attribute__ ((unused))
1014 #endif
1017 DMA(DEBUG_ERROR, "exec()'ing %s\n", restart_args[0]);
1018 sleep(1);
1019 execvp(restart_args[0], (char *const *) restart_args);
1020 DMA(DEBUG_ERROR, "exec of %s failed: %s\n",
1021 restart_args[0], strerror(errno));
1022 exit(EXIT_FAILURE);
1025 extern int x_socket(void)
1027 return ConnectionNumber(display);
1029 extern void ProcessPendingEvents(void)
1031 static int but_pressed_region = -1; /* static so click can be determined */
1032 int but_released_region = -1;
1033 /* X Events */
1034 while (XPending(display)) {
1035 XEvent Event;
1036 const char *press_action;
1038 XNextEvent(display, &Event);
1040 switch (Event.type) {
1041 case Expose:
1042 if (Event.xany.window != win && Event.xany.window != iconwin) {
1043 msglst_redraw();
1044 } else {
1045 RedrawWindow();
1047 break;
1048 case DestroyNotify:
1049 XCloseDisplay(display);
1050 exit(EXIT_SUCCESS);
1051 break;
1052 case ButtonPress:
1053 but_pressed_region =
1054 CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
1055 switch (Event.xbutton.button) {
1056 case 1:
1057 press_action = mbox[but_pressed_region].action;
1058 break;
1059 case 2:
1060 press_action = mbox[but_pressed_region].button2;
1061 break;
1062 case 3:
1063 press_action = mbox[but_pressed_region].fetchcmd;
1064 break;
1065 default:
1066 press_action = NULL;
1067 break;
1070 if (press_action && strcmp(press_action, "msglst") == 0) {
1071 msglst_show(&mbox[but_pressed_region],
1072 Event.xbutton.x_root, Event.xbutton.y_root);
1074 break;
1075 case ButtonRelease:
1076 but_released_region =
1077 CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
1078 if (but_released_region == but_pressed_region
1079 && but_released_region >= 0) {
1080 const char *click_action, *extra_click_action = NULL;
1082 switch (Event.xbutton.button) {
1083 case 1: /* Left mouse-click */
1084 /* C-S-left will restart wmbiff. */
1085 if ((Event.xbutton.state & ControlMask) &&
1086 (Event.xbutton.state & ShiftMask)) {
1087 restart_wmbiff(0);
1089 /* do we need to run an extra action? */
1090 if (mbox[but_released_region].UnreadMsgs == -1) {
1091 extra_click_action =
1092 mbox[but_released_region].actiondc;
1093 } else if (mbox[but_released_region].UnreadMsgs > 0) {
1094 extra_click_action =
1095 mbox[but_released_region].actionnew;
1096 } else {
1097 extra_click_action =
1098 mbox[but_released_region].actionnonew;
1100 click_action = mbox[but_released_region].action;
1101 break;
1102 case 2: /* Middle mouse-click */
1103 click_action = mbox[but_released_region].button2;
1104 break;
1105 case 3: /* Right mouse-click */
1106 click_action = mbox[but_released_region].fetchcmd;
1107 break;
1108 default:
1109 click_action = NULL;
1110 break;
1112 if (extra_click_action != NULL
1113 && extra_click_action[0] != 0
1114 && strcmp(extra_click_action, "msglst")) {
1115 DM(&mbox[but_released_region], DEBUG_INFO,
1116 "runing: %s", extra_click_action);
1117 (void) execCommand(extra_click_action);
1119 if (click_action != NULL
1120 && click_action[0] != '\0'
1121 && strcmp(click_action, "msglst")) {
1122 DM(&mbox[but_released_region], DEBUG_INFO,
1123 "running: %s", click_action);
1124 (void) execCommand(click_action);
1128 /* a button was released, hide the message list if open */
1129 msglst_hide();
1131 but_pressed_region = -1;
1132 /* RedrawWindow(); */
1133 break;
1134 case MotionNotify:
1135 break;
1136 case KeyPress:{
1137 XKeyPressedEvent *xkpe = (XKeyPressedEvent *) & Event;
1138 KeySym ks = XKeycodeToKeysym(display, xkpe->keycode, 0);
1139 if (ks > XK_0 && ks < XK_0 + min(9U, num_mailboxes)) {
1140 const char *click_action = mbox[ks - XK_1].action;
1141 if (click_action != NULL
1142 && click_action[0] != '\0'
1143 && strcmp(click_action, "msglst")) {
1144 DM(&mbox[but_released_region], DEBUG_INFO,
1145 "running: %s", click_action);
1146 (void) execCommand(click_action);
1151 break;
1152 default:
1153 break;
1158 static void do_biff(int argc, const char **argv)
1160 unsigned int i;
1161 time_t curtime;
1162 int Sleep_Interval;
1163 const char **skin_xpm = NULL;
1164 const char **bkg_xpm = NULL;
1165 char *skin_file_path = search_path(skin_search_path, skin_filename);
1166 int wmbiff_mask_height = mbox_y(num_mailboxes) + 4;
1168 DMA(DEBUG_INFO, "running %u mailboxes w %d h %d\n", num_mailboxes,
1169 wmbiff_mask_width, wmbiff_mask_height);
1171 if (skin_file_path != NULL) {
1172 skin_xpm = (const char **) LoadXPM(skin_file_path);
1173 free(skin_file_path);
1175 if (skin_xpm == NULL) {
1176 DMA(DEBUG_ERROR, "using built-in xpm; %s wasn't found in %s\n",
1177 skin_filename, skin_search_path);
1178 skin_xpm = wmbiff_master_xpm;
1181 bkg_xpm = (const char **) CreateBackingXPM(wmbiff_mask_width,
1182 wmbiff_mask_height,
1183 skin_xpm);
1185 createXBMfromXPM(wmbiff_mask_bits, bkg_xpm,
1186 wmbiff_mask_width, wmbiff_mask_height);
1188 openXwindow(argc, argv, bkg_xpm, skin_xpm, wmbiff_mask_bits,
1189 wmbiff_mask_width, wmbiff_mask_height, notWithdrawn);
1191 /* now that display is set, we can create the cursors
1192 (mouse pointer shapes) */
1193 busy_cursor = XCreateFontCursor(display, XC_watch);
1194 ready_cursor = XCreateFontCursor(display, XC_left_ptr);
1196 if (font != NULL) {
1197 if (loadFont(font) < 0) {
1198 DMA(DEBUG_ERROR, "unable to load font. exiting.\n");
1199 exit(EXIT_FAILURE);
1201 /* make the whole background black */
1202 // removed; seems unnecessary with CreateBackingXPM
1203 // eraseRect(x_origin, y_origin,
1204 // wmbiff_mask_width - 6, wmbiff_mask_height - 6,
1205 // background);
1208 /* First time setup of button regions and labels */
1209 curtime = time(0);
1210 for (i = 0; i < num_mailboxes; i++) {
1211 /* make it easy to recover the mbox index from a mouse click */
1212 AddMouseRegion(i, x_origin, mbox_y(i), 58, mbox_y(i + 1) - 1);
1213 if (mbox[i].label[0] != '\0') {
1214 mbox[i].prevtime = mbox[i].prevfetch_time = 0;
1215 BlitString(mbox[i].label, x_origin, mbox_y(i), 0);
1219 do {
1220 /* waitpid(0, NULL, WNOHANG); */
1222 Sleep_Interval = periodic_mail_check();
1224 ProcessPendingEvents();
1226 XSleep(Sleep_Interval);
1228 while (forever); /* forever is usually true,
1229 but not when debugging with -exit */
1230 if (skin_xpm != NULL && skin_xpm != wmbiff_master_xpm) {
1231 free(skin_xpm); // added 3 jul 02, appeasing valgrind
1233 if (bkg_xpm != NULL) {
1234 free(bkg_xpm);
1238 static void sigchld_handler(int sig
1239 #ifdef HAVE___ATTRIBUTE__
1240 __attribute__ ((unused))
1241 #endif
1244 while (waitpid(0, NULL, WNOHANG) > 0);
1245 signal(SIGCHLD, sigchld_handler);
1248 static void usage(void)
1250 printf("\nwmBiff v%s"
1251 " - incoming mail checker\n"
1252 "Gennady Belyakov and others (see the README file)\n"
1253 "Please report bugs to %s\n"
1254 "\n"
1255 "usage:\n"
1256 " -bg <color> background color\n"
1257 " -c <filename> use specified config file\n"
1258 " -debug enable debugging\n"
1259 " -display <display name> use specified X display\n"
1260 " -fg <color> foreground color\n"
1261 " -font <font> font instead of LED\n"
1262 " -geometry +XPOS+YPOS initial window position\n"
1263 " -h this help screen\n"
1264 " -hi <color> highlight color for new mail\n"
1265 #ifdef USE_GNUTLS
1266 " -skip-certificate-check using TLS, don't validate the\n"
1267 " server's certificate\n"
1268 #endif
1269 " -relax assume the configuration is \n"
1270 " correct, parse it without paranoia, \n"
1271 " and assume hostnames are okay.\n"
1272 " -v print the version number\n"
1273 " +w not withdrawn: run as a window\n"
1274 "\n", PACKAGE_VERSION, PACKAGE_BUGREPORT);
1277 static void printversion(void)
1279 printf("wmbiff v%s\n", PACKAGE_VERSION);
1283 static void parse_cmd(int argc, const char **argv, /*@out@ */
1284 char *config_file)
1286 int i;
1288 config_file[0] = '\0';
1290 /* Parse Command Line */
1292 for (i = 1; i < argc; i++) {
1293 const char *arg = argv[i];
1295 if (*arg == '-') {
1296 switch (arg[1]) {
1297 case 'b':
1298 if (strcmp(arg + 1, "bg") == 0) {
1299 if (argc > (i + 1)) {
1300 background = strdup_ordie(argv[i + 1]);
1301 DMA(DEBUG_INFO, "new background: %s", foreground);
1302 i++;
1303 if (font == NULL)
1304 font = DEFAULT_FONT;
1307 break;
1308 case 'd':
1309 if (strcmp(arg + 1, "debug") == 0) {
1310 debug_default = DEBUG_ALL;
1311 } else if (strcmp(arg + 1, "display") == 0) {
1312 /* passed to X's command line parser */
1313 } else {
1314 usage();
1315 exit(EXIT_FAILURE);
1317 break;
1318 case 'f':
1319 if (strcmp(arg + 1, "fg") == 0) {
1320 if (argc > (i + 1)) {
1321 foreground = strdup_ordie(argv[i + 1]);
1322 DMA(DEBUG_INFO, "new foreground: %s", foreground);
1323 i++;
1324 if (font == NULL)
1325 font = DEFAULT_FONT;
1327 } else if (strcmp(arg + 1, "font") == 0) {
1328 if (argc > (i + 1)) {
1329 if (strcmp(argv[i + 1], "default") == 0) {
1330 font = DEFAULT_FONT;
1331 } else {
1332 font = strdup_ordie(argv[i + 1]);
1334 DMA(DEBUG_INFO, "new font: %s", font);
1335 i++;
1337 } else {
1338 usage();
1339 exit(EXIT_FAILURE);
1341 break;
1342 case 'g':
1343 if (strcmp(arg + 1, "geometry") != 0) {
1344 usage();
1345 exit(EXIT_FAILURE);
1346 } else {
1347 i++; /* gobble the argument */
1348 if (i >= argc) { /* fail if there's nothing to gobble */
1349 usage();
1350 exit(EXIT_FAILURE);
1353 break;
1354 case 'h':
1355 if (strcmp(arg + 1, "hi") == 0) {
1356 if (argc > (i + 1)) {
1357 highlight = strdup_ordie(argv[i + 1]);
1358 DMA(DEBUG_INFO, "new highlight: %s", highlight);
1359 i++;
1360 if (font == NULL)
1361 font = DEFAULT_FONT;
1363 } else if (strcmp(arg + 1, "h") == 0) {
1364 usage();
1365 exit(EXIT_SUCCESS);
1367 break;
1368 case 'v':
1369 printversion();
1370 exit(EXIT_SUCCESS);
1371 break;
1372 case 's':
1373 if (strcmp(arg + 1, "skip-certificate-check") == 0) {
1374 SkipCertificateCheck = 1;
1375 } else {
1376 usage();
1377 exit(EXIT_SUCCESS);
1380 break;
1381 case 'r':
1382 if (strcmp(arg + 1, "relax") == 0) {
1383 Relax = 1;
1384 } else {
1385 usage();
1386 exit(EXIT_SUCCESS);
1389 break;
1390 case 'c':
1391 if (argc > (i + 1)) {
1392 strncpy(config_file, argv[i + 1], 255);
1393 i++;
1395 break;
1396 case 'e': /* undocumented for debugging */
1397 if (strcmp(arg + 1, "exit") == 0) {
1398 forever = 0;
1400 break;
1401 default:
1402 usage();
1403 exit(EXIT_SUCCESS);
1404 break;
1406 } else if (*arg == '+') {
1407 switch (arg[1]) {
1408 case 'w':
1409 notWithdrawn = 1;
1410 break;
1411 default:
1412 usage();
1413 exit(EXIT_SUCCESS);
1414 break;
1420 int main(int argc, const char *argv[])
1422 char uconfig_file[256];
1424 /* hold on to the arguments we were started with; we
1425 will need them if we have to restart on sigusr1 */
1426 restart_args =
1427 (const char **) malloc((argc + 1) * sizeof(const char *));
1428 memcpy(restart_args, argv, (argc) * sizeof(const char *));
1429 restart_args[argc] = NULL;
1431 parse_cmd(argc, argv, uconfig_file);
1433 /* decide what the config file is */
1434 if (uconfig_file[0] != '\0') { /* user-specified config file */
1435 DMA(DEBUG_INFO, "Using user-specified config file '%s'.\n",
1436 uconfig_file);
1437 } else {
1438 const char *home = getenv("HOME");
1439 if (home == NULL) {
1440 DMA(DEBUG_ERROR,
1441 "$HOME undefined. Use the -c option to specify a wmbiffrc\n");
1442 exit(EXIT_FAILURE);
1444 sprintf(uconfig_file, "%s/.wmbiffrc", home);
1447 if (wmbiffrc_permissions_check(uconfig_file) == 0) {
1448 DMA(DEBUG_ERROR,
1449 "WARNING: In future versions of WMBiff, .wmbiffrc MUST be\n"
1450 "owned by the user, and not readable or writable by others.\n\n");
1452 init_biff(uconfig_file);
1453 signal(SIGCHLD, sigchld_handler);
1454 signal(SIGUSR1, restart_wmbiff);
1455 signal(SIGPIPE, SIG_IGN); /* write() may fail */
1457 do_biff(argc, argv);
1458 return 0;
1461 /* vim:set ts=4: */
1463 * Local Variables:
1464 * tab-width: 4
1465 * c-indent-level: 4
1466 * c-basic-offset: 4
1467 * End: