/*

Copyright (C) 1995 Andrew Tefft. I liked Henry Spencer's terms so:

        Permission is granted to anyone to use this software for any
        purpose on any computer system, and to redistribute it freely,
        subject to the following restrictions:

        1. The author is not responsible for the consequences of use of
                this software, no matter how awful, even if they arise
                from defects in it.

        2. The origin of this software must not be misrepresented, either
                by explicit claim or by omission.

        3. Altered versions must be plainly marked as such, and must not
                be misrepresented as being the original software.

*/

#include <stdio.h>
#include <string.h>

#define TRY_FASTER 

#include "mr.h"
#include "externs.h"

void usage();
void headers();
void custom_headers();
void search();
void getcmds();
void commands();
void nputs();
struct message *gomsg();
void body();

struct message *messages[MAXMSG];
struct mbox *mboxes[1024];  /* boy that would be a lot of files! */
                            /* I picked that limit because that is */
                            /* usually how many the shell can glob */
int number,mbox_number;
struct regexp *exp1, *exp2, *exp3, *exp4;
int nonzero_hits;

main(argc,argv)
     int argc;
     char *argv[];
{
  char file[1024];
  int x;
  char *buffer;

number = 0;
mbox_number = 0;
nonzero_hits = 0;

if (argc > 2 ) usage(argv[0]);

/* compile regexps for parse time */


exp1 = regcomp("(.*) <(.*)> (.*$)"); /* name <addr> date */
exp2 = regcomp("(.*) \\((.*)\\) (.*$)");    /* addr (name) date */
exp3 = regcomp("(.*) <(.*)>"); /* name <addr> */
exp4 = regcomp("(.*) \\((.*)\\)$"); /* addr (name) nodate */


if (argc == 2) {
 strcpy(file,argv[1]);
 parse_file(file,buffer,1);
}

  getcmds();

}

/* main command loop */

void getcmds() {
  char cmdline[126],cmdline2[128];
  int args,messageno,cmd;
  struct message *msg;
  int prompt_return = 0;
  int thecase = 1;  /* = 1 for case insensitive */
  char *buffer;

  messageno=0;

  while (1) {
    printf(": ");
    if (prompt_return) putchar('\n');
    fflush(stdout);
    fgets(cmdline,126,stdin);
    fprintf(stderr,"mr got : %s\n",cmdline);

    switch ((int)*cmdline) {
    case '?':
      commands();
      break;
      
    case ':':
      prompt_return = !prompt_return;
      break;

    case 'H':
      CHECK_LOADED;
      args = sscanf(cmdline,"H %s",cmdline2);
      custom_headers(cmdline2);
      break;
   
    case 'z':
      nonzero_hits = !nonzero_hits;
      printf("%d\n",nonzero_hits);
      break;

    case 'h':
      CHECK_LOADED;
      if (*(cmdline+1)=='b') headers(HEADER);
      else if (*(cmdline+1)=='h') headers(HITS|HEADER|SEQUENCE);
      else if (*(cmdline+1)=='v') headers(HEADER|HITS|SEQUENCE|DATE);
      else if (*(cmdline+1)=='H') headers(HITS);
      else if (*(cmdline+1)=='z') headers(HEADER|SEQUENCE|NONZERO_HITS);
      else if (*(cmdline+1)=='D') headers(DATE);
      else if (*(cmdline+1)=='f') headers(SEQUENCE|MBOX|HEADER);
      else headers(HEADER|SEQUENCE);
      break;

    case '/':
      CHECK_LOADED;
      if (cmdline[1]=='\0') {
	printf("No search string specified.\n");
	break;
      }
      cmdline[strlen(cmdline)-1]='\0';
      search(&cmdline[1],thecase);
      break;
      
    case 'f':
      printf("%d\n",mbox_number+1);
      for (args=0; args<=mbox_number; args++) puts(mboxes[args]->filename);
      break;

    case 's':
      CHECK_LOADED;
      if (cmdline[1]=='s') sort(SUBJECT);
      else if (cmdline[1]=='d') sort(DATE);
      else if (cmdline[1]=='h') sort(HITS);
      else if (cmdline[1]=='a') sort(HEADER);
      else sort(SEQUENCE);
      break;

    case 'p':
    case 'P':
      CHECK_LOADED;
      args=sscanf(cmdline,"%c%d",cmdline2,&messageno);
      msg=gomsg(messageno);
      if (msg == NULL) {
	puts("?");
	break;
      }
      body(msg,*cmdline2 == 'P');
      break;

    case 'd':
    case 'u':
      CHECK_LOADED;
      args=sscanf(cmdline,"%c%d",cmdline2,&messageno);
      msg=gomsg(messageno);
      if (msg == NULL) {
        puts("?");
        break;
      }
      msg->deleted = (*cmdline == 'd');
      break;

    case 'c':
      thecase=0;
      break;
      
    case 'C':
      thecase=1;
      break;

    case 'a':
      args=sscanf(cmdline,"a %s",cmdline2);
      parse_file(cmdline2,buffer,!number);
      break;

    case 'l':
      args=sscanf(cmdline,"l %s",cmdline2);
      if (args != 1) 
	strcpy(cmdline2,DEFAULTMBOX);
      parse_file(cmdline2,buffer,1);
      break;

    case 'n':
      printf("%d\n",number);
      break;

    case 'q':
    case 'x':
      exit (0);
      break;
      
    default:
      puts("?");
    }
  }
}

/* searches messages for "key" and updates "hits" structure
   member for each message. thecase=1 gives case insensitivity */

void search(key,thecase) 
     char *key;
     int thecase;
{
  char tc,*buf;
  int i;
  
  for (i=0; i<number; i++) {
   
    tc=*(messages[i]->end);
#ifdef TRY_FASTER
    buf = malloc(messages[i]->end - messages[i]->begin + 1);
    strncpy(buf,messages[i]->begin, messages[i]->end - messages[i]->begin);
    messages[i]->hits = hits(key,buf,thecase,1);
#else
    *(messages[i]->end)='\0';
    messages[i]->hits=hits(key,messages[i]->begin,thecase,1);
    *(messages[i]->end)=tc;
#endif    
  }
}

/* prints headers of messages; you can include sequence #,
   date (unix ctime format for sorting), hits (from last search),
   or summary header with sender, date, and subject. */

void headers(which) 
     int which;
{
  int i;
  char *p;
  
  for (i=0; i< number; i++) {
    if (messages[i]->deleted) continue;
    if (nonzero_hits) if (!messages[i]->hits) continue;
    if (which & SEQUENCE) printf("%-5d",messages[i]->sequence);
    if (which & DATE) printf("%d ",messages[i]->date);
    if (which & HITS) printf("%-5d", messages[i]->hits);
    if (which & MBOX) {
      p = mboxes[messages[i]->mbox]->filename;
      if (strlen(p) > HDR_MBOX) {
	p += (strlen(p)-HDR_MBOX) + 3;
	printf("...",p);
      }
	printf(HDR_MBOX_FMT,p);
    }
    if (which & HEADER) printf("%s",messages[i]->hdr);
    if (!(which & NO_NEWLINE)) putchar('\n');
  }
}


/* show selected headers in custom order */

void custom_headers(which) 
     char *which;
{
  int i,j,l;
  char *p;
  
  l = (i<8 ? i : 8);

  for (i=0; i< number; i++) {
    if (messages[i]->deleted) continue;
    if (nonzero_hits && !messages[i]->hits) continue;

    for (j=0; j<l; j++) {
    
      if (which[j] == 's') printf("%-4d",messages[i]->sequence);
      else if (which[j] == 'D') printf("%d ",messages[i]->date);
      else if (which[j] == 'h') printf("%-4d", messages[i]->hits);
      else if (which[j] == 'f') {
	p = mboxes[messages[i]->mbox]->filename;
	if (strlen(p) > HDR_MBOX) {
	  p += (strlen(p)-HDR_MBOX) + 3;
	  printf("...",p);
	}
	printf(HDR_MBOX_FMT,p);
      }
      else if (which[j] == 'H') printf(HDR_TOT_FMT,messages[i]->hdr);
      else if (which[j] == 'a') nputs(messages[i]->hdr,HDR_ADDR);
      else if (which[j] == 'b') printf("%8d ",messages[i]->end -
				       messages[i]->begin);
      else if (which[j] == 'd') nputs((messages[i]->hdr)+HDR_ADDR+1,
				      HDR_DATE);
      else if (which[j] == 'S') printf(HDR_SUBJ_FMT,messages[i]->hdr+HDR_SUBSTART);
  }
    putchar('\n');
}
}
/* search through the list to find sequence number */

struct message *gomsg(num) 
     int num;
{
  int i;

  for (i=0; i<number; i++)
    if (messages[i]->sequence == num) return messages[i];

  return NULL;

}

  
/* prints the body of the message pointed to by msg.
   if fullhdrs=1 then include all headers otherwise print
   only "useful" headers. First line is a string representing
   the total number of bytes we'll output. */

void body(msg,fullhdrs) 
struct message *msg;
int fullhdrs;
{
  char tc;
  char *p,*p1;
  int body;

  printf("%d\n",msg->end - msg->begin);

if (!fullhdrs) {
  tc=*(msg->end);
  *(msg->end)='\0';
  puts(msg->begin);
  *(msg->end)=tc;
}
else {
  body=0;
  p = msg->begin;
  p1 = p;
  while (*p1 != NULL && *p != NULL && p < msg->end) {
    p1 = strchr(p,'\n');
    if (body==1 || 
	!strncmp(p,"Date: ",6) ||
	!strncmp(p,"Subject: ",9) ||
	!strncmp(p,"From",4) ||
	!strncmp(p,"Newsgroups: ",13) ||
	!strncmp(p,"Organization: ",15) ||
	!strncmp(p,"Reply-to: ",10) 
	)
      nputs(p,(p1-p)+1);
    p = p1 + 1;
    if (!body && *p == '\n') body=1;
  }
  putchar('\n');
}

puts(EOM_STRING);
}

void commands() {
  puts("Available commands:\n");
  puts("h		show headers with sequence numbers");
  puts("hb		show briefest headers");
  puts("hh		show headers with number of hits");
  puts("hf              show headers with mail file name");
  puts("hv		list headers with verbose (all) info");
  puts("hH		list only hits");
  puts("hD		list only numeric date");
  puts("H string        custom headers (string=[abdfhsDS] in desired order)");
  puts("            (addr(sender) bytes date file hits sequence Date(numeric) Subject");
  puts("/string		search all messages for 'string'");
  puts("s		sort by sequence number");
  puts("ss		sort by subject");
  puts("sd		sort by date");
  puts("sh		sort by hits");
  puts("sa		sort by addr of sender");
  puts("p msg		prints msg by number");
  puts("P msg		prints msg with all headers");
  puts("n		print number of messages");
  puts("d msg           deletes (actually just hides) message msg");
  puts("u msg           undeletes message msg");
  puts("l file		load specified file");
  puts("L file          limit all lists to mail from file (no arg for all)");
  puts("a file          add specified file");
  puts("f               display all mail files loaded");
  puts("x		exit");
  puts("z               toggle display of nonzero hits only (default off)");
  puts("?		show this help");
}

void usage(prog)
     char *prog;
{
  printf("Usage: %s filename\n",prog);
  exit (-1);
}

/* put string up to char n */
void nputs(s,n) 
     char *s;
     int n;
{
 char *p;

 p = s;
 while (n--) putchar(*p++);
}
