home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Mail / mailapp-utilities-2.1-MIHS / Source / compactmail.m < prev    next >
Encoding:
Text File  |  1998-02-02  |  7.2 KB  |  259 lines

  1. /* -*-ObjC-*-
  2. *******************************************************************************
  3. *
  4. * File:         compactmail.m
  5. * RCS:          /usr/local/sources/CVS/mailapp-utilities/compactmail.m,v 1.8 1998/02/02 22:23:15 tom Exp
  6. * Description:  Compact Mail.app mailboxes (expunge deleted messages)
  7. * Author:       Carl Edman
  8. * Created:      Fri Mar 12 18:21:23 1993
  9. * Modified:     Tue Dec 16 16:06:19 1997 Tom Hageman <tom@basil.icce.rug.nl>
  10. * Language:     Objective C
  11. * Package:      mailapp-utilities
  12. * Status:       Exp
  13. *
  14. * (C) Copyright 1993, but otherwise this file is perfect freeware.
  15. *
  16. *******************************************************************************
  17. */
  18.  
  19. #import <libc.h>
  20. #import <errno.h>
  21. //#import <stdlib.h>
  22. //#import <stdio.h>
  23. //#import <string.h>
  24. //#import <time.h>
  25. #import <regex.h>
  26. //#import <sys/file.h>
  27. //#import <sys/param.h>
  28. //#import <sys/types.h>
  29. //#import <sys/stat.h>
  30. #import "compat.h"
  31. #import "mailutil.h"
  32. #import "mailtoc.h"
  33. #import "optutil.h"
  34.  
  35. #define USAGE "\
  36. Usage: %s [-nruv] [-d days] [-f from] [-s subject] [-V|-H] mbox...\n"
  37.  
  38. #define HELP "\
  39.  -n           skip mailbox if locked\n\
  40.  -r           expunge read messages too (default deleted messages only)\n\
  41.  -u           expunge all messages (even unread)\n\
  42.  -v           talkative mode\n\
  43.  -d days      restrict to messages of at least `days' days old.\n\
  44.  -f from      restrict to messages whose From: header matches (regexp)\n\
  45.  -s subject   restrict to messages whose Subject: header matches (regexp)\n\
  46.  -V           show version\n\
  47.  -H           this help\n\
  48. "
  49.  
  50. #define INDEX_STORE  "content.store"
  51.  
  52.  
  53. int main(int ac,char *av[])
  54. {
  55.    char *buf=0;
  56.    int c,pos;
  57.    int nowaitflg=0,verboseflg=0,deletereadflg=0,deleteunreadflg=0;
  58.    int days=-1;
  59.    struct regex *subjectregexp=0,*fromregexp=0;
  60.    time_t mboxtime;
  61.    int mboxdis,tocdis,msgdis,changed;
  62.    int mboxfd=-1;
  63.    FILE *tocf=NULL;
  64.    struct table_of_contents_header *toch=0;
  65.    struct message_index *mi=0;
  66.    int status=EXIT_SUCCESS;
  67.    char *attachdir;
  68.    POOL_INIT
  69.  
  70.    set_progname(av[0]);
  71.  
  72.    while((c=getopt(ac,av,"d:s:f:nruvVH"))!=EOF) switch(c)
  73.    {
  74.     case 'd':
  75.       if (days!=-1 || (days=atoi(optarg))<=0)
  76.          status=EXIT_USAGE;
  77.       break;
  78.     case 's':
  79.       if (subjectregexp || !(subjectregexp=re_compile(optarg,0)))
  80.          status=EXIT_USAGE;
  81.       break;
  82.     case 'f':
  83.       if (fromregexp || !(fromregexp=re_compile(optarg,0)))
  84.          status=EXIT_USAGE;
  85.       break;
  86.     case 'n':
  87.       nowaitflg++;
  88.       break;
  89.     case 'r':
  90.       deletereadflg++;
  91.       break;
  92.     case 'u':
  93.       deleteunreadflg++;
  94.       break;
  95.     case 'v':
  96.       verboseflg++;
  97.       break;
  98.     case 'V':
  99.       status=EXIT_VERSION;
  100.       break;
  101.     case 'H':
  102.       status=EXIT_HELP;
  103.       break;
  104.     case '?':
  105.     default:
  106.       status=EXIT_USAGE;
  107.    }
  108.    if (status==EXIT_SUCCESS && optind>=ac) status=EXIT_USAGE;
  109.    handle_usage_help_version(status, USAGE, HELP);
  110.  
  111.    for(;optind<ac;optind++)
  112.    {
  113.       if (verboseflg) fprintf(stderr,"Entering %s...\n",av[optind]);
  114.       if (cd_mbox(av[optind],1)) goto next;
  115.       if (lock_mbox(nowaitflg)) goto next;
  116.  
  117.       mboxtime=mtime("mbox");
  118.  
  119.       if (((mboxfd=open("mbox",O_RDWR))==-1) ||
  120.           ((tocf=fopen("table_of_contents","r+b"))==NULL))
  121.       {
  122.          fprintf(stderr,"%s: opening %s: %s\n",progname(),av[optind],strerror(errno));
  123.          status=EXIT_FAILURE;
  124.          goto unlock;
  125.       }
  126.  
  127.       toch=get_table_of_contents_header(tocf,0);
  128.       if (!toch)
  129.       {
  130.          fprintf(stderr,"%s: %s: invalid old table_of_contents\n",progname(),av[optind]);
  131.          status=EXIT_FAILURE;
  132.          goto unlock;
  133.       }
  134.  
  135.       if (toch->mbox_time!=mboxtime)
  136.       {
  137.          fprintf(stderr,"%s: %s: table of contents out of sync\n",progname(),av[optind]);
  138.          status=EXIT_FAILURE;
  139.          goto unlock;
  140.       }
  141.  
  142.       for(changed=FALSE,msgdis=mboxdis=tocdis=c=0;c<toch->num_msgs;c++)
  143.       {
  144.          if ((mi=get_message_index(tocf))==0)
  145.      {
  146.             fprintf(stderr,"%s: %s: reading old mbox entries: %s\n",progname(),av[optind],(errno?strerror(errno):"invalid"));
  147.             status=EXIT_FAILURE;
  148.             goto unlock;
  149.      }
  150.          if ((mi->status=='d' || mi->status=='D' || deleteunreadflg ||
  151.               (mi->status!='*' && deletereadflg)) &&
  152.              (days==-1 || message_age(mi)>=days) &&
  153.              (!subjectregexp || re_match(message_subject(mi),subjectregexp)) &&
  154.              (!fromregexp || re_match(message_from(mi),fromregexp)))
  155.      {
  156.             if (verboseflg>1)
  157.         {
  158.                fprintf(stderr,"Expunging \"%s\" from %s (%d days old).\n",message_subject(mi),message_from(mi),message_age(mi));
  159.         }
  160.         attachdir = message_reference(mi);
  161.         if (attachdir && *attachdir)
  162.         {
  163.            int pid,childpid;
  164.            int status;
  165.  
  166.            switch (childpid=fork())
  167.            {
  168.            case -1:
  169.           break;
  170.            case 0:
  171.           execl("/bin/rm", "rm", "-rf", attachdir, NULL);
  172.           _exit(255);
  173.            default:
  174.           if (verboseflg>2)
  175.           {
  176.              fprintf(stderr, "  removing attachment \"%s\".\n", attachdir);
  177.           }
  178.           while ((pid=wait((void *)&status))>=0 && pid!=childpid) ;
  179.            }
  180.         }
  181.             changed=TRUE;
  182.             msgdis++;
  183.             tocdis +=mi->record_length;
  184.             mboxdis+=mi->mes_length;
  185.      }
  186.          else
  187.      {
  188.             if (verboseflg>2)
  189.         {
  190.                fprintf(stderr,"Keeping \"%s\" from %s (%d days old).\n",message_subject(mi),message_from(mi),message_age(mi));
  191.         }
  192.             if (mboxdis!=0)
  193.         {
  194.                lseek(mboxfd,mi->mes_offset,SEEK_SET);
  195.                buf=malloc(mi->mes_length);
  196.                read(mboxfd,buf,mi->mes_length);
  197.                mi->mes_offset -= mboxdis;
  198.                lseek(mboxfd,mi->mes_offset,SEEK_SET);
  199.                write(mboxfd,buf,mi->mes_length);
  200.                free(buf); buf=0;
  201.         }
  202.  
  203.             if (tocdis!=0 || mboxdis!=0)
  204.         {
  205.                pos=ftell(tocf);
  206.                fseek(tocf,-mi->record_length-tocdis,SEEK_CUR);
  207.                put_message_index(tocf,mi);
  208.                fseek(tocf,pos,SEEK_SET);
  209.         }
  210.      }
  211.          free(mi); mi=0;
  212.       }
  213.       
  214.       if (tocdis!=0)
  215.       {
  216.      fflush(tocf);
  217.      fseek(tocf,0,SEEK_END);
  218.          pos=ftell(tocf);
  219.      fseek(tocf,0,SEEK_SET); // trying to defeat FILE <-> fd interference...
  220.          ftruncate(fileno(tocf),pos-tocdis);
  221.       }
  222.       if (mboxdis!=0)
  223.       {
  224.          pos=lseek(mboxfd,0,SEEK_END);
  225.          ftruncate(mboxfd,pos-mboxdis);
  226.       }
  227.       if (tocdis!=0 || mboxdis!=0)
  228.       {
  229.      unlink(INDEX_STORE); // XXX Should at least preserve existence of index.
  230.       }
  231.       close(mboxfd), mboxfd=-1;
  232.       
  233.       toch->num_msgs-=msgdis;
  234.       mboxtime=mtime("mbox");
  235.       if ((toch->mbox_time!=mboxtime) || changed)
  236.       {
  237.          toch->mbox_time=mboxtime;
  238.          fseek(tocf,0,SEEK_SET);
  239.          if (put_table_of_contents_header(tocf,toch))
  240.      {
  241.             fprintf(stderr,"%s: %s: writing updated table_of_contents: %s\n",progname(),av[optind],strerror(errno));
  242.             status=EXIT_FAILURE;
  243.             goto unlock;
  244.      }
  245.       }
  246.     unlock:
  247.       if(mboxfd>=0) close(mboxfd), mboxfd=-1;
  248.       if(tocf!=NULL) fclose(tocf), tocf=NULL;
  249.       unlock_mbox();
  250.     next:;
  251.       if (toch) { free(toch); toch=0; }
  252.       if (mi) { free(mi); mi=0; }
  253.       if (buf) { free(buf); buf=0; }
  254.       uncd_mbox();
  255.    }
  256.    POOL_RELEASE
  257.    return status;
  258. }
  259.