Subject: FvwmTaskBar Maildir patch

From: "Jim C. Brown" <[email-masked]>
Date: Tue, 3 Aug 2004 21:32:28 -0400

This patch, which applies to fvwm 2.5.10, gives FvwmTaskBar the ability to
read from a qemu Maildir mail box. It needs some work (may not be a compliant
Maildir reader) but it seems to work.

I don't feel confortable playing around with the Makefiles so I just added
#define USE_MAILDIR at the top of Goodies.c (I'm hoping that someone who
knows more can do this right).

-- 
Infinite complexity begets infinite beauty.
Infinite precision begets infinite perfection.
--- Goodies.c.orig	Sun Jun 29 20:55:22 2003
+++ Goodies.c	Tue Aug  3 19:47:43 2004
@@ -16,10 +16,15 @@
 
 #include "config.h"
 
+#define USE_MAILDIR
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <fcntl.h>
+#ifdef USE_MAILDIR
+#include <dirent.h>
+#endif
 #include <pwd.h>
 #include "libs/ftime.h"
 #include <sys/stat.h>
@@ -712,13 +717,75 @@
 /* (based on the code of 'coolmail' By Byron C. Darrah */
 /*-----------------------------------------------------*/
 
+#ifdef USE_MAILDIR
+static int oldsize = 0;
+
+/* filter to show only new entries */
+int maildir_filter(const struct dirent * a)
+{
+    if (!strcmp(a->d_name, ".") || !strcmp(a->d_name, ".."))
+    {
+	return 0;
+    }
+    return 1;
+}
+#endif
+
 void cool_get_inboxstatus()
 {
+#ifdef USE_MAILDIR
+   int  newsize;
+   struct dirent **newlist;
+   struct dirent **curlist;
+   char * alt_mailpath;
+   int curent, newent;
+#else
    static off_t oldsize = 0;
    off_t  newsize;
    struct stat st;
    int fd;
+#endif
+
+#ifdef USE_MAILDIR
+   alt_mailpath = malloc(strlen(mailpath) + strlen("/new") + 1);
+   strcpy(alt_mailpath, mailpath);
+   strcat(alt_mailpath, "/new");
+   newent = scandir(alt_mailpath, &newlist, maildir_filter, alphasort);
+   free(alt_mailpath);
+
+   alt_mailpath = malloc(strlen(mailpath) + strlen("/cur") + 1);
+   strcpy(alt_mailpath, mailpath);
+   strcat(alt_mailpath, "/cur");
+   curent = scandir(alt_mailpath, &curlist, maildir_filter, alphasort);
+   free(alt_mailpath);
+
+   newsize = curent + newent;
 
+   if (newent > 0)
+   {
+	anymail = 1;
+	unreadmail = 1;
+   }
+   else if (curent > 0)
+   {
+	anymail = 1;
+	unreadmail = 0;
+   }
+   else
+   {
+	anymail = 0;
+	unreadmail = 0;
+   }
+   if (newsize > oldsize && unreadmail)
+   {
+	newmail = 1;
+	mailcleared = 0;
+   }
+   else
+   {
+	newmail = 0;
+   }
+#else
    fd = open (mailpath, O_RDONLY, 0);
    if (fd < 0)
    {
@@ -750,6 +817,7 @@
       else
 	 newmail = 0;
    }
+#endif
 
    oldsize = newsize;
 }

---

From: Dominik Vogt <[email-masked]>
Date: Wed, 4 Aug 2004 12:25:39 +0200

> Maildir reader) but it seems to work.

The patch looks interesting.

> knows more can do this right).

Using compile time configuration in general and #ifdef in
particular is not the right way in fvwm.  Every feature has to be
configurable at run time.  Here is what I would do:

 - Add a new config variable, e.g. MailDir
 - Add a flag that indicates whether MailBox or MailDir is used.
 - Split the __cool_get_inboxstatus() function into two sub
   functions __cool_get_inboxstatus_mailbox() and
   cool_get_inboxstatus_maildir() and call one of them from
   cool_get_inboxstatus() according to the flag state.

> +++ Goodies.c	Tue Aug  3 19:47:43 2004

Ciao

Dominik ^_^  ^_^

 --
Dominik Vogt, [email-masked]
Reply-To: [email-masked]

---

From: "Jim C. Brown" <[email-masked]>
Date: Thu, 19 Aug 2004 23:57:21 -0400

>    cool_get_inboxstatus() according to the flag state.

Done. Set "*FvwmTaskBar: MailDir" in the config file to toggle the flag.

I considered adding Postfix-style detection (checking for the '/' at the end
of the filename). I can't decide which of the two is the better idea, so I
decided to attach both versions.

-- 
Infinite complexity begets infinite beauty.
Infinite precision begets infinite perfection.
--- Goodies.c.orig	Sun Jun 29 20:55:22 2003
+++ Goodies.c	Thu Aug 19 23:43:23 2004
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <dirent.h>
 #include <pwd.h>
 #include "libs/ftime.h"
 #include <sys/stat.h>
@@ -83,6 +84,7 @@
 int ShowTips = False;
 char *statusfont_string = NULL;
 int last_date = -1;
+Bool using_MailDir = False;
 
 void cool_get_inboxstatus();
 
@@ -173,6 +175,7 @@
   "IgnoreOldMail",
   "ShowTips",
   "DateFormat",
+  "MailDir",
   NULL
 };
 
@@ -255,6 +258,9 @@
 	    do_display_clock = False;
     }
     break;
+  case 12: /* MailDir */
+    using_MailDir = True;
+    break;
   default:
     /* unknow option */
     return False;
@@ -502,6 +508,7 @@
 	else
 	{
 		str = _("You have mail");
+		//str = using_MailDir ? _("You have mail (maildir)") : _("You have mail (mbox)");
 	}
 
 	PopupTipWindow(win_width, 0, str);
@@ -712,7 +719,17 @@
 /* (based on the code of 'coolmail' By Byron C. Darrah */
 /*-----------------------------------------------------*/
 
-void cool_get_inboxstatus()
+/* filter to show only new entries */
+int maildir_filter(const struct dirent * a)
+{
+    if (!strcmp(a->d_name, ".") || !strcmp(a->d_name, ".."))
+    {
+	return 0;
+    }
+    return 1;
+}
+
+void cool_get_inboxstatus_mbox()
 {
    static off_t oldsize = 0;
    off_t  newsize;
@@ -752,6 +769,79 @@
    }
 
    oldsize = newsize;
+}
+
+void cool_get_inboxstatus_maildir()
+{
+   static int oldsize = 0;
+   int  newsize;
+   struct dirent **newlist;
+   struct dirent **curlist;
+   char * alt_mailpath;
+   int curent, newent;
+
+   alt_mailpath = malloc(strlen(mailpath) + strlen("/new") + 1);
+   strcpy(alt_mailpath, mailpath);
+   strcat(alt_mailpath, "/new");
+   newent = scandir(alt_mailpath, &newlist, maildir_filter, alphasort);
+   free(alt_mailpath);
+
+   alt_mailpath = malloc(strlen(mailpath) + strlen("/cur") + 1);
+   strcpy(alt_mailpath, mailpath);
+   strcat(alt_mailpath, "/cur");
+   curent = scandir(alt_mailpath, &curlist, maildir_filter, alphasort);
+   free(alt_mailpath);
+
+   newsize = curent + newent;
+
+   if (newent > 0)
+   {
+	anymail = 1;
+	unreadmail = 1;
+   }
+   else if (curent > 0)
+   {
+	anymail = 1;
+	unreadmail = 0;
+   }
+   else
+   {
+	anymail = 0;
+	unreadmail = 0;
+   }
+   if (newsize > oldsize && unreadmail)
+   {
+	newmail = 1;
+	mailcleared = 0;
+   }
+   else
+   {
+	newmail = 0;
+   }
+
+   oldsize = newsize;
+
+   /* forgot this bit of garbage-cleaning in the original patch */
+   while(curent--)
+   {
+   	free(curlist[curent]);
+   }
+   free(curlist);
+   while(newent--)
+   {
+   	free(newlist[newent]);
+   }
+   free(newlist);
+}
+
+void cool_get_inboxstatus()
+{
+	if (using_MailDir)
+	{
+		cool_get_inboxstatus_maildir();
+	} else {
+		cool_get_inboxstatus_mbox();
+	}
 }
 
 /*---------------------------------------------------------------------------*/
--- Goodies.c.orig	Sun Jun 29 20:55:22 2003
+++ Goodies.c	Thu Aug 19 23:55:00 2004
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <dirent.h>
 #include <pwd.h>
 #include "libs/ftime.h"
 #include <sys/stat.h>
@@ -712,7 +713,17 @@
 /* (based on the code of 'coolmail' By Byron C. Darrah */
 /*-----------------------------------------------------*/
 
-void cool_get_inboxstatus()
+/* filter to show only new entries */
+int maildir_filter(const struct dirent * a)
+{
+    if (!strcmp(a->d_name, ".") || !strcmp(a->d_name, ".."))
+    {
+	return 0;
+    }
+    return 1;
+}
+
+void cool_get_inboxstatus_mbox()
 {
    static off_t oldsize = 0;
    off_t  newsize;
@@ -752,6 +763,79 @@
    }
 
    oldsize = newsize;
+}
+
+void cool_get_inboxstatus_maildir()
+{
+   static int oldsize = 0;
+   int  newsize;
+   struct dirent **newlist;
+   struct dirent **curlist;
+   char * alt_mailpath;
+   int curent, newent;
+
+   alt_mailpath = malloc(strlen(mailpath) + strlen("/new") + 1);
+   strcpy(alt_mailpath, mailpath);
+   strcat(alt_mailpath, "/new");
+   newent = scandir(alt_mailpath, &newlist, maildir_filter, alphasort);
+   free(alt_mailpath);
+
+   alt_mailpath = malloc(strlen(mailpath) + strlen("/cur") + 1);
+   strcpy(alt_mailpath, mailpath);
+   strcat(alt_mailpath, "/cur");
+   curent = scandir(alt_mailpath, &curlist, maildir_filter, alphasort);
+   free(alt_mailpath);
+
+   newsize = curent + newent;
+
+   if (newent > 0)
+   {
+	anymail = 1;
+	unreadmail = 1;
+   }
+   else if (curent > 0)
+   {
+	anymail = 1;
+	unreadmail = 0;
+   }
+   else
+   {
+	anymail = 0;
+	unreadmail = 0;
+   }
+   if (newsize > oldsize && unreadmail)
+   {
+	newmail = 1;
+	mailcleared = 0;
+   }
+   else
+   {
+	newmail = 0;
+   }
+
+   oldsize = newsize;
+
+   /* forgot this bit of garbage-cleaning in the original patch */
+   while(curent--)
+   {
+   	free(curlist[curent]);
+   }
+   free(curlist);
+   while(newent--)
+   {
+   	free(newlist[newent]);
+   }
+   free(newlist);
+}
+
+void cool_get_inboxstatus()
+{
+	if ((strlen(mailpath)-1) && (mailpath[strlen(mailpath)-1] == '/'))
+	{
+		cool_get_inboxstatus_maildir();
+	} else {
+		cool_get_inboxstatus_mbox();
+	}
 }
 
 /*---------------------------------------------------------------------------*/

---

From: Dominik Vogt <[email-masked]>
Date: Thu, 26 Aug 2004 10:48:06 +0200

> Done. Set "*FvwmTaskBar: MailDir" in the config file to toggle the flag.

I'd like to commit the patch, but I have a few questions/requests
first.

 * Is it *guaranteed* that "/new" and "/cur" are the proper
   subdirectory names?  Or is there any chance the user can
   configure these names?

 * You can safely remove the comments
   /* forgot this bit of garbage-cleaning in the original patch */

 * Comments with "//" can not be used as this is not allowed in
   the C89 standard.

 * Can you please add an entry to the man page, the AUTHORS and
   the modules/ChangeLog file?

 * I'm afraid of using the scandir() and alphasort() functions.
   According to the man page, these are non-standard funtions that
   come from BSD 4.3.  I guess they are not available on many
   systems.  Can the code be written without them?
 
> decided to attach both versions.

I'd use a separate config option.  I think it is less confusing.

Ciao

Dominik ^_^  ^_^

---

From: "Jim C. Brown" <[email-masked]>
Date: Fri, 27 Aug 2004 17:25:18 -0400

>    subdirectory names?  Or is there any chance the user can

If the user changes these names, all known Maildir software will break (including
qmail itself). So this is very unlikely. The Maildir format specifies 3
subdirectories, cur for current mail, new for new mail, and tmp for mail
that is still being written too/modified (this is to prevent partially
delivered mail).

>    /* forgot this bit of garbage-cleaning in the original patch */

Done.

>  * Comments with "//" can not be used as this is not allowed in

Changed to /* */

>    the modules/ChangeLog file?

How? Do you want me to just edit my versions (from fvwm 2.5.8) and send a
patch for each?

>    systems.  Can the code be written without them?

Yes, by using opendir(3) and readir(3). This new function, f_scandir, doesn't
sort the directory entries but I don't see why that would be a problem.

I'm not sure how to handle malloc() errors, so I went with returning what
it already had. Maybe it would be better to free the memory and return an error?

> I'd use a separate config option.  I think it is less confusing.

Done.

+++ Goodies.c	Fri Aug 27 17:18:16 2004

@@ -173,6 +175,7 @@
   "IgnoreOldMail",
   "ShowTips",
   "DateFormat",
+  "MailDir",
   NULL
 };
 
@@ -255,6 +258,9 @@
 	    do_display_clock = False;
     }
     break;
+  case 12: /* MailDir */
+    using_MailDir = True;
+    break;
   default:
     /* unknow option */
     return False;
@@ -502,6 +508,7 @@
 	else
 	{
 		str = _("You have mail");
+		/* str = using_MailDir ? _("You have mail (maildir)") : _("You have mail (mbox)"); */
 	}
 
@@ -712,7 +719,7 @@
 
@@ -752,6 +759,114 @@
    }
 
    oldsize = newsize;
+}
+
+int f_scandir(const char *dir, struct dirent *** namelist)
+{
+	DIR * f_dir;
+	struct dirent * f_temp;
+	/* 10 is just an arbituary number */
+	int f_count = 0, namelist_size = 10;
+	void * r_ret;
+	f_dir = opendir(dir);
+	if (f_dir == NULL) return -1;
+	namelist[0] = malloc(sizeof(struct dirent *)*10);
+	if (namelist[0] == NULL) return -1;
+	do {
+		f_temp = readdir(f_dir);
+		if (f_temp == NULL)
+			break;
+		else if (strcmp(f_temp->d_name, ".") && strcmp(f_temp->d_name, ".."))
+		{
+			if (f_count > namelist_size)
+			{
+				r_ret = realloc(namelist[0], sizeof(struct dirent)*(f_count+10));
+				/* not sure which behavior is correct */
+				/* if (r_ret == NULL) return -1; */
+				if (r_ret == NULL) return f_count;
+				namelist[0] = (struct dirent **)r_ret;
+				namelist_size = f_count + 10;
+			}
+			namelist[0][f_count] = malloc(sizeof(struct dirent));
+			/* if (namelist[0][f_count] == NULL) return -1; */
+			if (namelist[0][f_count] == NULL) return f_count;
+			memcpy(namelist[0][f_count], f_temp, sizeof(struct dirent));
+			f_count++;
+		}
+	} while (1);
+	return f_count;
+}
+
+void cool_get_inboxstatus_maildir()
+{
+   static int oldsize = 0;
+   int  newsize;
+   struct dirent **newlist;
+   struct dirent **curlist;
+   char * alt_mailpath;
+   int curent, newent;
+
+   alt_mailpath = malloc(strlen(mailpath) + strlen("/new") + 1);
+   strcpy(alt_mailpath, mailpath);
+   strcat(alt_mailpath, "/new");
+   newent = f_scandir(alt_mailpath, &newlist);
+   free(alt_mailpath);
+
+   alt_mailpath = malloc(strlen(mailpath) + strlen("/cur") + 1);
+   strcpy(alt_mailpath, mailpath);
+   strcat(alt_mailpath, "/cur");
+   curent = f_scandir(alt_mailpath, &curlist);
+   free(alt_mailpath);
+
+   newsize = curent + newent;
+
+   if (newent > 0)
+   {
+	anymail = 1;
+	unreadmail = 1;
+   }
+   else if (curent > 0)
+   {
+	anymail = 1;
+	unreadmail = 0;
+   }
+   else
+   {
+	anymail = 0;
+	unreadmail = 0;
+   }
+   if (newsize > oldsize && unreadmail)
+   {
+	newmail = 1;
+	mailcleared = 0;
+   }
+   else
+   {
+	newmail = 0;
+   }
+
+   oldsize = newsize;
+
+   while(curent--)
+   {
+   	free(curlist[curent]);
+   }
+   free(curlist);
+   while(newent--)
+   {
+   	free(newlist[newent]);
+   }
+   free(newlist);
+}
+
+void cool_get_inboxstatus()
+{
+	if (using_MailDir)
+	{
+		cool_get_inboxstatus_maildir();
+	} else {
+		cool_get_inboxstatus_mbox();
+	}
 }

---

From: Dominik Vogt <[email-masked]>
Date: Mon, 30 Aug 2004 10:14:41 +0200

> that is still being written too/modified (this is to prevent partially

Okay.

[snip]

> How? Do you want me to just edit my versions (from fvwm 2.5.8) and send a

Yes, patches are fine.  Even if there are problems applying the
patches, it's easy to do by hand for these files.  Don't bother
with AUTHORS and ChangeLog, I'va already written the entries.

> it already had. Maybe it would be better to free the memory and return an error?

Use the safemalloc() and saferealloc() functions from the library.
They take care of error handling (i.e. they abort() the program if
allocation fails).  I'll apply your patch and do these changes
myself.

Ciao

Dominik ^_^  ^_^