/* -*-C-*-
*******************************************************************************
*
* File:         searchmail.m
* RCS:          /usr/local/sources/CVS/mailapp-utilities/searchmail.m,v 1.2 1996/12/01 17:03:30 tom Exp
* Description:  Search mailboxes 
* Author:       Carl Edman
* Created:      Fri Jun  3 20:45:43 1994
* Modified:     Fri Apr  7 09:57:27 1995 (Carl Edman) cedman@freedom.princeton.edu
* Language:     Objective C
* Package:      N/A
* Status:       Experimental (Do Not Distribute)
*
* (C) Copyright 1994, but otherwise this file is perfect freeware.
*
*******************************************************************************
*/

#import <appkit/appkit.h>
#import <indexing/indexing.h>
#import "MailBox.h"
#import "MailMessage.h"
#import "mailutil.h"

id manager;

void updatembox(const char *name)
   {
#if 0
   if (cd_mbox(mbox,0)) return nil;
   if (access(".no_search",F_OK)!=-1) return;
   /* return if up to date on mbox */
   /* Add to list if not in the list */
   
      {
      struct table_of_contents_header *toc=(void *)tocbuf;
      struct message_index *m;
      int i;
      unsigned int handle;
      unsigned int weight;
      id cursor=[manager cursorForAttributeNamed:"Position"],record;
      
      [[manager store] startTransaction];
      /* Go through all the records and validate them */
      for([cursor setFirst];[cursor isMatch];[cursor setNext])
         {
         if (![cursor getHandle:&handle andWeight:&weight]) continue;
         // if wrong mailbox continue;
         record=[manager readRecord:handle fromZone:NXDefaultMallocZone()];
         if ([record validate]) continue;
         [manager removeRecord:handle];
         [record free];
         }
      
      /* Go through entire table and create records if they don't exist */
      for(i=NXSwapBigLongToHost(toc->num_msgs),
          m=(void *)tocbuf+sizeof(struct table_of_contents_header);
          (i>0)&&((void *)m<(void *)tocbuf+toclen);
          i--,
          m=((void *)m)+NXSwapBigLongToHost(m->record_length))
         {
         unsigned long int offset=((void *)m)-((void *)tocbuf);
         
         if ([cursor setKey:&offset andLength:sizeof(offset)]) continue;
         if (record=[[MailMessage alloc] initPos:offset])
            [manager addRecord:record];
         }
      [[manager store] commitTransaction];
      [cursor free];
      }

   if (uncd_mbox()) return;
#endif
   }

void main(int ac,char *av[])
   {
   id parser, query, list;
   int c,errflg=0,queryflg=0;
   char *expr=0;
   char path[MAXPATHLEN];
   
   while((c=getopt(ac,av,"qe:"))!=EOF) switch(c)
      {
    case 'e':
      expr=optarg;
      break;
    case 'q':
      queryflg++;
      break;
    case '?':
    default:
      errflg++;      
      }

   if (expr==0)
      {
      if (optind<ac) expr=av[optind++]; else errflg++;
      }

   if (errflg)
      {
      fprintf(stderr,"Usage: %s [-q] [-e expr] expr [mbox...]\n",av[0]);
      exit(1);
      }

   if (!queryflg)
      {
      int len=256+strlen(expr);
      char *tmp;

      tmp=alloca(len);
      sprintf(tmp,"whole(Content parse(%s))",expr);
      expr=tmp;
      }

   if (optind<ac)
      {
      int len=256+strlen(expr);
      char *tmp,*end;
      
      for(c=optind;c<ac;c++) len+=16+strlen(av[c]);
      end=tmp=alloca(len);
      sprintf(tmp,"and(%s, or(",expr);
      end+=strlen(end);
      
      for(c=optind;c<ac;c++)
         {
         sprintf(end,"eq(\"Box\" %s), ",av[c]);         
         end+=strlen(end);
         }

      end[-2]=')';
      end[-1]=')';
      expr=tmp;
      }

   parser=[[IXAttributeParser alloc] init];
   
   query=[[IXAttributeQuery alloc] initQueryString:expr andAttributeParser:parser];
   if (query==nil)
      {
      fprintf(stderr,"Expression '%s' malformed.\n",expr);
      exit(2);
      }
   
   if (mailboxdir(path)<0) exit(3);
   strcat(path,"/search.store");
   manager=[[IXRecordManager alloc] initFromName:"messages" inFile:path forWriting:YES];
   if (manager==nil)
      {
      manager=[[IXRecordManager alloc] initWithName:"messages" inFile:path];
      if (manager==nil)
         {
         fprintf(stderr,"Can't create index file '%s'.\n",path);
         exit(4);
         }
      [[manager store] startTransaction];
      [manager addAttributeNamed:"Position" forSelector:@selector(position)];
      [manager setTargetClass:[MailMessage class] forAttributeNamed:"Position"];
      [manager addAttributeNamed:"Box" forSelector:@selector(box)];
      [manager setTargetClass:[MailMessage class] forAttributeNamed:"Box"];
      [manager addAttributeNamed:"Subject" forSelector:@selector(messageSubject)];
      [manager setTargetClass:[MailMessage class] forAttributeNamed:"Subject"];
      [manager addAttributeNamed:"From" forSelector:@selector(messageFrom)];
      [manager setTargetClass:[MailMessage class] forAttributeNamed:"From"];
      [manager addAttributeNamed:"To" forSelector:@selector(messageTo)];
      [manager setTargetClass:[MailMessage class] forAttributeNamed:"To"];
      [manager addAttributeNamed:"Date" forSelector:@selector(messageDate)];
      [manager setTargetClass:[MailMessage class] forAttributeNamed:"Date"];
      [manager addAttributeNamed:"Message-Id" forSelector:@selector(messageId)];
      [manager setTargetClass:[MailMessage class] forAttributeNamed:"Message-Id"];
      [manager addAttributeNamed:"Next-Reference" forSelector:@selector(messageNextReference)];
      [manager setTargetClass:[MailMessage class] forAttributeNamed:"Next-Reference"];
      [manager addAttributeNamed:"Content" forSelector:@selector(messageContent)];
      [manager setTargetClass:[MailMessage class] forAttributeNamed:"Content"];
      [manager setParser:parser forAttributeNamed:"Content"];
      [[manager store] commitTransaction];
      }
   
   if (optind<ac)
      for(c=optind;c<ac;c++) updatembox(av[c]);
   else
      forall_mboxes(&updatembox);
   
   if ([[manager store] areTransactionsEnabled])
      {
      [[manager store] startTransaction];
      [[manager store] compact];
      [[manager store] commitTransaction];
      }
   
   if (list=[query evaluateFor:manager])
      {
      const char *m;
      id message;
      int count=[list count];
      
      for(c=0;c<count;c++)
         {
         message=[list objectAt:c];
         m=[message messageHeaders];
         fputs(m,stdout);         
         fputc('\n',stdout);
         m=[message messageContent];         
         fputs(m,stdout);
         }
      
      [[list freeObjects] free];
      list=nil;
      }

   [query free];
   }

// NXStream *NXMapFile(const char *pathName, NX_READONLY);
// void NXGetMemoryBuffer(NXStream *stream, char **streambuf, int *len, int *maxlen)
// void NXCloseMemory(NXStream *stream, NX_FREEBUFFER);
