/* This file was contributed by Justo Alonso in support of maildir++ quota
files. I (PH) modified it as follows:

1. Take out the check_dir_size() function and put it back into appendfile.c,
because it is basic Exim code and not part of this contribution.

2. Changed the C++ comments into C ones, to stop the compiler complaining.

3. Initialized the variable "oneline" to stop the compiler complaining about
its not being used.

4. Casts of some argumente for function calls with char or uschar args.
Also a few other casts to stop compiler warnings.

5. Commented out the variables dirp and de and some others too, as they appear
not to be used.

6. Commented out the functions countsubdir, countcurnew, docount, statcurnew,
and statsubdir, as they appear not be be used.

7. Put a limit on the infinite loop in makenewmaildirsizename; after 10
tries, it gives up.

8. Removed the #include of sys/dir.h because this file does not exist on some
operating systems, and does not seem to be needed anyway.

Maybe one day this code could all be cleaned up and put into the standard Exim
format for functions, with suitable heading comments saying what each one does.
*/


/*
** Copyright 1998 - 2003 Double Precision, Inc.
** See COPYING for distribution information.
*/

/* The COPYING file says this:

  This software is released under the GPL, version 2 (see COPYING.GPL).
  Additionally, compiling, linking, and/or using the OpenSSL toolkit in
  conjunction with this software is allowed.

which I understand to mean that there is no problem with including this code in
Exim and making modifications as necessary. */


#include       <sys/types.h>
#include       <dirent.h>
#include       <sys/types.h>
#include       <sys/stat.h>

#include       "../mytypes.h"
#include       "tf_mdirquota.h"
#include       <stdio.h>
#include       <stdlib.h>
#include       <string.h>
#include       <errno.h>
#include       <fcntl.h>
#include       <unistd.h>
#include       <time.h>

/***** Variable is never referenced
static const char rcsid[]="$Id: maildirquota.c,v 1.21 2003/02/13 03:19:45 mrsam Exp $";
*****/

static void parsequotastr(const char *, struct maildirquota *);

/* Read the maildirsize file */

static int maildir_openquotafile_init(struct maildirsize *info,
				     const char *maildir,
				     const char *newquota);

static int do_maildir_openquotafile(struct maildirsize *info,
				   const char *filename,
				   const char *newquota);

int maildir_openquotafile(struct maildirsize *info, const char *maildir)
{
       return (maildir_openquotafile_init(info, maildir, NULL));
}

static int maildir_openquotafile_init(struct maildirsize *info,
				     const char *maildir,
				     const char *newquota)
{
       int rc;

       char    *buf=(char *)malloc(strlen(maildir)+sizeof("/maildirfolder"));

       memset(info, 0, sizeof(*info));

       info->fd= -1;

       if (!buf)
	       return (-1);

       strcat(strcpy(buf, maildir), "/maildirfolder");
       if (stat(buf, &info->statbuf) == 0)     /* Go to parent */
       {
	       strcat(strcpy(buf, maildir), "/..");

	       rc=maildir_openquotafile_init(info, buf, newquota);
	       free(buf);
	       return rc;
       }

       /* If the directory don't exist, return error */
       if (stat(maildir, &info->statbuf) != 0)
       {
	       free(buf);
	       return(0);
       }

       info->maildir=strdup(maildir);

       if (!info->maildir)
       {
	       free(buf);
	       return (-1);
       }

       strcat(strcpy(info->maildirsizefile=buf, maildir), "/maildirsize");

       rc=do_maildir_openquotafile(info, buf, newquota);

       if (rc == 0)
	       return (0);

       free(buf);
       free(info->maildir);
       return (rc);
}


static int do_maildir_openquotafile(struct maildirsize *info,
				   const char *filename,
				   const char *newquota)
{
       char buf[2048];
       char *p;
       unsigned l;
       int n;
       int first;
       int oneline = 0;

       /*
       ** When setting a new quota, we don't care about the existing
       ** maildirsize.
       */

       if ((info->fd=(newquota ? open("/dev/null", O_RDWR):
		      Uopen(filename,
				       O_RDWR|O_APPEND, 0))) < 0)
	       return (0);     /* No quota */

       if (newquota)
       {
	       parsequotastr(newquota, &info->quota);

	       if (info->quota.nbytes == 0 &&
		   info->quota.nmessages == 0)
	       {
		       close(info->fd);
		       info->fd= -1;
		       errno=EINVAL;
		       return (-1);
	       }
	       info->recalculation_needed=1;
	       return (0);
       }

       p=buf;
       l=sizeof(buf);

       while (l)
       {
	       n=read(info->fd, p, l);
	       if (n < 0)
	       {
		       close(info->fd);
		       info->fd= -1;
		       return (-1);
	       }
	       if (n == 0)     break;
	       p += n;
	       l -= n;
       }

       if (fstat(info->fd, &info->statbuf))    /* maildir too big */
       {
	       close(info->fd);
	       info->fd= -1;
	       return (-1);
       }

       if (l == 0)     /*
		       ** maildirsize overflowed, still need to read its
		       ** quota
		       */
       {
	       p[-1]=0;
	       p=strchr(buf, '\n');
	       if (p)
		       *p=0;
	       parsequotastr(buf, &info->quota);
	       info->recalculation_needed=1;
	       return (0);
       }


       info->size.nbytes=0;
       info->size.nmessages=0;
       info->nlines=0;
       *p=0;
       p=buf;
       first=1;
       while (*p)
       {
	       long n=0;
	       int c=0;
	       char    *q=p;

	       while (*p)
		       if (*p++ == '\n')
		       {
			       p[-1]=0;
			       break;
		       }

	       oneline = 0;

	       if (first)
	       {
		       parsequotastr(q, &info->quota);
		       first=0;
		       oneline = 1;
		       continue;
	       }
	       sscanf(q, "%ld %d", &n, &c);
	       info->size.nbytes += n;
	       info->size.nmessages += c;
	       ++ info->nlines;
       }
       if( oneline == 1 )
       {
	       info->recalculation_needed=1;
       }
       return (0);
}

static void parsequotastr(const char *quota, struct maildirquota *q)
{
       off_t i;

       q->nbytes=0;
       q->nmessages=0;

       while (quota && *quota)
       {
	       if (*quota < '0' || *quota > '9')
	       {
		       ++quota;
		       continue;
	       }
	       i=0;
	       while (*quota >= '0' && *quota <= '9')
		       i=i*10 + (*quota++ - '0');
	       switch (*quota) {
	       case MDQUOTA_SIZE:
		       q->nbytes=i;
		       break;
	       case MDQUOTA_COUNT:
		       q->nmessages=i;
		       break;
	       }
       }
}


void maildir_closequotafile(struct maildirsize *info)
{
       if (info->maildir)
	       free (info->maildir);
       info->maildir=NULL;

       if (info->maildirsizefile)
	       free (info->maildirsizefile);
       info->maildirsizefile=NULL;

       if (info->fd >= 0)
	       close(info->fd);
       info->fd= -1;
}

/**
 ** Check if size > quota, and calculate by how much
 */

static int checkOneQuota(off_t size, off_t quota, int *percentage);

static int checkQuota(struct maildirquota *size,
		     struct maildirquota *quota, int *percentage)
{
       int b_quota;
       int n_quota;

       if (checkOneQuota(size->nbytes, quota->nbytes, &b_quota) ||
	   checkOneQuota(size->nmessages, quota->nmessages,
			 &n_quota))
       {
	       if (percentage)
		       *percentage= 100;
	       errno=ENOSPC;
	       return -1;
       }

       if (b_quota < n_quota)
	       b_quota=n_quota;

       if (percentage)
	       *percentage=b_quota;
       return (0);
}

static int checkOneQuota(off_t size, off_t quota, int *percentage)
{
       int x=1;

       if (quota == 0) /* No quota */
       {
	       *percentage=0;
	       return (0);
       }

       if (size > quota)
       {
	       *percentage=100;
	       return (-1);
       }

       if (quota > 20000000)
	       x=1024;

       *percentage= quota > 0 ? (size/x) * 100 / (quota/x):0;
       return 0;
}

static char *makenewmaildirsizename(const char *, int *);


/**** These functions appears not to be used
static int countcurnew(const char *, time_t *, off_t *, unsigned *);
static int countsubdir(const char *, const char *,
	       time_t *, off_t *, unsigned *);
static int statcurnew(const char *, time_t *);
static int statsubdir(const char *, const char *, time_t *);

static int docount(const char *, time_t *, off_t *, unsigned *);
****/


static int     doaddquota(struct maildirsize *, int, long, int, int);

static int docheckquota(struct maildirsize *info,
		       long xtra_size,
		       int xtra_cnt, int *percentage);

int maildir_checkquota(struct maildirsize *info,
		      long xtra_size,
		      int xtra_cnt)
{
       int     dummy;

       return (docheckquota(info, xtra_size, xtra_cnt, &dummy));
}

int maildir_readquota(struct maildirsize *info)
{
       int     percentage=0;

       (void)docheckquota(info, 0, 0, &percentage);
       return (percentage);
}

static int docheckquota(struct maildirsize *info,
		       long xtra_size,
		       int xtra_cnt,
		       int *percentage)
{
       char    *newmaildirsizename;
       int     maildirsize_fd;
       off_t   maildirsize_size;
       unsigned maildirsize_cnt;

       time_t  tm;

       /***** These variables appear not be us used.
       time_t  maxtime;
       DIR     *dirp;
       struct dirent *de;
       *******/

       struct maildirquota new_quota;

       *percentage=0;

       if (info->fd < 0)       /* No quota */
	       return (0);

       new_quota=info->size;

       new_quota.nbytes += xtra_size;
       new_quota.nmessages += xtra_cnt;

       if (!info->recalculation_needed &&
	   checkQuota(&new_quota, &info->quota, percentage) == 0)
	       return (0);     /* New size is under quota */

       /*
       ** Overquota, see if it's time to recalculate the quota anyway
       */

       time(&tm);
       if (!info->recalculation_needed &&
	   info->nlines == 1 && tm < info->statbuf.st_mtime + 15*60)
	       return (-1);


       /****** This variable appears not to be used
       maxtime=0;
       ******/

       maildirsize_size=0;
       maildirsize_cnt=0;

       maildirsize_size = check_dir_size(US info->maildir,
         (int *) &maildirsize_cnt, info->regex);

       newmaildirsizename=makenewmaildirsizename(info->maildir,
						 &maildirsize_fd);
       if (!newmaildirsizename)
       {
	       errno=EIO;
	       return (-1);
       }

       if (doaddquota(info, maildirsize_fd, maildirsize_size,
	       maildirsize_cnt, 1))
       {
	       unlink(newmaildirsizename);
	       free(newmaildirsizename);
	       close(maildirsize_fd);
	       errno=EIO;
	       return (-1);
       }

       if (rename(newmaildirsizename, info->maildirsizefile))
       {
	       unlink(newmaildirsizename);
	       free(newmaildirsizename);
	       close(maildirsize_fd);
	       errno=EIO;
	       return (-1);
       }

       info->recalculation_needed=0;
       info->size.nbytes=maildirsize_size;
       info->size.nmessages=maildirsize_cnt;
       info->nlines=1;
       close(info->fd);
       info->fd=maildirsize_fd;

       *percentage=0;

       new_quota=info->size;

       new_quota.nbytes += xtra_size;
       new_quota.nmessages += xtra_cnt;

       return checkQuota(&new_quota, &info->quota, percentage);
}

int    maildir_addquota(struct maildirsize *info,
			long maildirsize_size, int maildirsize_cnt)
{
       if (info->fd < 0)
	       return (0);

       return (doaddquota(info, info->fd, maildirsize_size,
			  maildirsize_cnt, 0));
}

static int doaddquota(struct maildirsize *info, int maildirsize_fd,
		     long maildirsize_size, int maildirsize_cnt,
		     int isnew)
{
       char    n[NUMBUFSIZE];
       char    buf[NUMBUFSIZE * 4 + 32 ];
       char *p;
       int cnt;

       buf[0]=0;

       if (isnew)
       {
	       if (info->quota.nbytes > 0)
	       {
		       char b[2];

		       b[0]=MDQUOTA_SIZE;
		       b[1]=0;

		       strcat(strcat(buf, libmail_str_off_t(info->quota.nbytes, n)),
			      b);
	       }

	       if (info->quota.nmessages > 0)
	       {
		       char b[2];

		       b[0]=MDQUOTA_COUNT;
		       b[1]=0;

		       if (buf[0] != 0)
			       strcat(buf, ",");

		       strcat(strcat(buf,
				     libmail_str_size_t(info->quota.nmessages, n)),
			      b);
	       }
	       strcat(buf, "\n");
       }

       sprintf(buf + strlen(buf),
	       "%8s ", libmail_str_off_t(maildirsize_size, n));

       sprintf(buf + strlen(buf),
	       "%4s\n", libmail_str_off_t(maildirsize_cnt, n));

       p=buf;
       cnt=strlen(buf);

       while (cnt > 0)
       {
	       int c=write( maildirsize_fd, p, cnt);

	       if (c < 0)
		       return (-1);

	       cnt -= c;
	       p += c;
       }

       return (0);
}


/* New maildirsize is built in the tmp subdirectory */

static char *makenewmaildirsizename(const char *dir, int *fd)
{
char   hostname[256];
struct stat stat_buf;
time_t t;
char   *p;
int i;

       hostname[0]=0;
       hostname[sizeof(hostname)-1]=0;
       gethostname(hostname, sizeof(hostname)-1);
       p=(char *)malloc(strlen(dir)+strlen(hostname)+130);
       if (!p) return (0);

       for (i = 0; i < 10; i++)
       {
       char    tbuf[NUMBUFSIZE];
       char    pbuf[NUMBUFSIZE];

               time(&t);
	       strcat(strcpy(p, dir), "/tmp/");
	       sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s",
		       libmail_str_time_t(t, tbuf),
		       libmail_str_pid_t(getpid(), pbuf), hostname);

	       if (stat( (const char *)p, &stat_buf) < 0 &&
		       (*fd=Uopen(p,
			       O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0)
		       break;
	       sleep(3);
       }
       if (i >= 10) return (0);
       return (p);
}

/**** This function appears not to be used
static int statcurnew(const char *dir, time_t *maxtimestamp)
{
char   *p=(char *)malloc(strlen(dir)+5);
struct stat    stat_buf;

       if (!p) return (-1);
       strcat(strcpy(p, dir), "/cur");
       if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
	       *maxtimestamp=stat_buf.st_mtime;
       strcat(strcpy(p, dir), "/new");
       if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
	       *maxtimestamp=stat_buf.st_mtime;
       free(p);
       return (0);
}
****/


/**** This function appears not to be used
static int statsubdir(const char *dir, const char *subdir, time_t *maxtime)
{
char   *p;
int    n;

       if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
	       strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
	       return (0);

       p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
       if (!p) return (-1);
       strcat(strcat(strcpy(p, dir), "/"), subdir);
       n=statcurnew(p, maxtime);
       free(p);
       return (n);
}
****/



/**** This function appears not to be used
static int countcurnew(const char *dir, time_t *maxtime,
       off_t *sizep, unsigned *cntp)
{
char   *p=(char *)malloc(strlen(dir)+5);
int    n;

       if (!p) return (-1);
       strcat(strcpy(p, dir), "/new");
       n=docount(p, maxtime, sizep, cntp);
       if (n == 0)
       {
	       strcat(strcpy(p, dir), "/cur");
	       n=docount(p, maxtime, sizep, cntp);
       }
       free(p);
       return (n);
}
****/


/***** This function appears not to be used
static int countsubdir(const char *dir, const char *subdir, time_t *maxtime,
       off_t *sizep, unsigned *cntp)
{
char   *p;
int    n;

       if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
	    strcmp(subdir, "..") == 0 ||
	    ! maildirquota_countfolder(subdir))
	       return (0);

       p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
       if (!p) return (2);
       strcat(strcat(strcpy(p, dir), "/"), subdir);
       n=countcurnew(p, maxtime, sizep, cntp);
       free(p);
       return (n);
}
****/


/* This function appears not to be used */
#ifdef NEVER
static int docount(const char *dir, time_t *dirstamp,
       off_t *sizep, unsigned *cntp)
{
struct stat    stat_buf;
char   *p;
DIR    *dirp;
struct dirent *de;
unsigned long  s;

       if (stat(dir, &stat_buf))       return (0);     /* Ignore */
       if (stat_buf.st_mtime > *dirstamp)      *dirstamp=stat_buf.st_mtime;
       if ((dirp=opendir(dir)) == 0)   return (0);
       while ((de=readdir(dirp)) != 0)
       {
       const char *n=de->d_name;

	       if (*n == '.')  continue;

	       if (!maildirquota_countfile(n))
		       continue;

	       if (maildir_parsequota(n, &s) == 0)
		       stat_buf.st_size=s;
	       else
	       {
		       p=(char *)malloc(strlen(dir)+strlen(n)+2);
		       if (!p)
		       {
			       closedir(dirp);
			       return (-1);
		       }
		       strcat(strcat(strcpy(p, dir), "/"), n);
		       if (stat(p, &stat_buf))
		       {
			       free(p);
			       continue;
		       }
		       free(p);
	       }
	       *sizep += stat_buf.st_size;
	       ++*cntp;
       }

#if    CLOSEDIR_VOID
       closedir(dirp);
#else
       if (closedir(dirp))
	       return (-1);
#endif
       return (0);
}
#endif  /* NEVER */



int maildirquota_countfolder(const char *folder)
{
#ifdef TRASHQUOTA

#else

       if (strcmp(folder, "." TRASH) == 0 ||
	   strcmp(folder, "." TRASH "/") == 0)
	       return (0);

       for ( ; *folder; folder++)
	       if (*folder == '/' &&
		   (strcmp(folder+1, "." TRASH) == 0 ||
		    strcmp(folder+1, "." TRASH "/") == 0))
		       return (0);
#endif
       return (1);
}

int maildirquota_countfile(const char *n)
{
#ifdef TRASHQUOTA

#else
       const char *nn=strrchr(n, '/');

       if (nn != NULL)
	       n=nn+1;

       /* do not count msgs marked as deleted */

       for ( ; *n; n++)
       {
	       if (n[0] != MDIRSEP[0] || n[1] != '2' ||
		   n[2] != ',')        continue;
	       n += 3;
	       while (*n >= 'A' && *n <= 'Z')
	       {
		       if (*n == 'T')  return (0);
		       ++n;
	       }
	       break;
       }
#endif
       return (1);
}

/*
** Prepare to add something to the maildir
*/

int maildir_quota_add_start(const char *maildir,
			   struct maildirsize *info,
			   long msgsize, int nmsgs,
			   const char *newquota)
{
       struct maildirquota mq;
       int i;

       if ( maildir_openquotafile(info, maildir))
	       info->fd= -1;

       if (newquota != NULL)
       {
	       parsequotastr(newquota, &mq);

	       if ((mq.nbytes > 0 || mq.nmessages > 0) &&
		   (info->fd < 0 || info->quota.nbytes != mq.nbytes ||
		    info->quota.nmessages != mq.nmessages))
	       {
		       if (info->fd < 0)
		       {
			       maildir_quota_set(maildir, newquota);
			       if (maildir_openquotafile(info, maildir))
				       info->fd= -1;
		       }
		       else
		       {
			       info->quota=mq;
			       info->recalculation_needed=1;
		       }
	       }
       }
       if (info->fd < 0)
	       return (0);     /* No quota set on this maildir */

       for (i=0; i<5; i++)
       {
	       int rc;

	       rc=maildir_checkquota(info, msgsize, nmsgs);
	       if (rc == 0)
		       return (0);

	       if (errno != EAGAIN)
	       {
		       maildir_closequotafile(info);
		       return (-1);
	       }
       }
       maildir_closequotafile(info);

       /* Cannot recover from a race condition, just punt */

       return (0);
}

void maildir_quota_add_end(struct maildirsize *info,
			  long msgsize, int nmsgs)
{
       maildir_addquota(info, msgsize, nmsgs);
       maildir_closequotafile(info);
}

void maildir_quota_deleted(const char *maildir,
			  long nbytes, /* Must be negative */
			  int nmsgs)   /* Must be negative */
{
       struct maildirsize info;

       if ( maildir_openquotafile(&info, maildir))
	       return;

       maildir_checkquota(&info, nbytes, nmsgs); /* Cleanup */
       maildir_addquota(&info, nbytes, nmsgs);
       maildir_closequotafile(&info);
}

void maildir_quota_recalculate(const char *maildir)
{
       struct maildirsize info;

       if (maildir_openquotafile(&info, maildir))
	       return;
       info.recalculation_needed=1;

       maildir_readquota(&info);
       maildir_closequotafile(&info);
}

int maildir_quota_delundel_start(const char *maildir,
				struct maildirsize *info,
				long msgsize, int nmsgs)
{
#if TRASHQUOTA
       return (0);
#else
       if (nmsgs < 0)
       {
	       maildir_quota_deleted(maildir, msgsize, nmsgs);
	       return (0);     /* Always allowed */
       }

       return maildir_quota_add_start(maildir, info, msgsize, nmsgs, NULL);
#endif
}

void maildir_quota_delundel_end(struct maildirsize *info,
			       long msgsize, int nmsgs)
{
#if TRASHQUOTA
       return;
#else
       if (nmsgs < 0)
	       return;

       maildir_quota_add_end(info, msgsize, nmsgs);
#endif
}


void maildir_quota_set(const char *dir, const char *quota)
{
       struct maildirsize info;

       if (maildir_openquotafile_init(&info, dir, quota) == 0)
       {
	       maildir_checkquota(&info, 0, 0);
	       maildir_closequotafile(&info);
       }
}

int maildir_parsequota(const char *n, unsigned long *s)
{
const char *o;
int    yes;

       if ((o=strrchr(n, '/')) == 0)   o=n;

       for (; *o; o++)
	       if (*o == MDIRSEP[0])   break;
       yes=0;
       for ( ; o >= n; --o)
       {
	       if (*o == '/')  break;

	       if (*o == ',' && o[1] == 'S' && o[2] == '=')
	       {
		       yes=1;
		       o += 3;
		       break;
	       }
       }
       if (yes)
       {
	       *s=0;
	       while (*o >= '0' && *o <= '9')
		       *s= *s*10 + (*o++ - '0');
	       return (0);
       }
       return (-1);
}

char *libmail_str_off_t(off_t t, char *arg)
{
       char    buf[NUMBUFSIZE];
       char    *p=buf+sizeof(buf)-1;
       int     isneg=0;

       if (t < 0)
       {
	       t= -t;
	       isneg=1;
       }

       *p=0;
       do
       {
	       *--p= '0' + (t % 10);
	       t=t / 10;
       } while(t);

       if (isneg)
	       *--p='-';

       return (strcpy(arg, p));
}

static void cat_n(char *buf, unsigned long n)
{
char    bb[NUMBUFSIZE+1];
char    *p=bb+sizeof(bb)-1;

        *p=0;
        do
        {
                *--p = "0123456789"[n % 10];
                n=n/10;
        } while (n);
        strcat(buf, p);
}

char *libmail_str_sizekb(unsigned long n, char *sizebuf)
{
        /* If size is less than 1K bytes, display it as 0.xK */

        if (n < 1024)
        {
                strcpy(sizebuf, "0.");
                cat_n(sizebuf, (int)(10 * n / 1024 ));
                strcat(sizebuf, "K");
        }
        /* If size is less than 1 meg, display is as xK */

        else if (n < 1024 * 1024)
        {
                *sizebuf=0;
                cat_n(sizebuf, (unsigned long)(n+512)/1024);
                strcat(sizebuf, "K");
        }

        /* Otherwise, display in megabytes */

        else
        {
        unsigned long nm=(unsigned long)((double)n / (1024.0 * 1024.0) * 10);

                *sizebuf=0;
                cat_n( sizebuf, nm / 10);
                strcat(sizebuf, ".");
                cat_n( sizebuf, nm % 10);
                strcat(sizebuf, "M");
        }

       return (sizebuf);
}

char *libmail_str_size_t(size_t t, char *arg)
{
char   buf[NUMBUFSIZE];
char   *p=buf+sizeof(buf)-1;

       *p=0;
       do
       {
	       *--p= '0' + (t % 10);
	       t=t / 10;
       } while(t);
       return (strcpy(arg, p));
}

char *libmail_str_pid_t(pid_t t, char *arg)
{
char   buf[NUMBUFSIZE];
char   *p=buf+sizeof(buf)-1;

       *p=0;
       do
       {
	       *--p= (char)('0' + (t % 10));
	       t=t / 10;
       } while(t);
       return (strcpy(arg, p));
}

char *libmail_str_time_t(time_t t, char *arg)
{
char   buf[NUMBUFSIZE];
char   *p=buf+sizeof(buf)-1;

       *p=0;
       do
       {
	       *--p= (char)('0' + (t % 10));
	       t=t / 10;
       } while(t);
       return (strcpy(arg, p));
}
