nmh-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Nmh-commits] CVS: nmh/uip Makefile.in,1.15,1.15.2.1 anno.c,1.4,1.4.2.1


From: Jon Steinhart <address@hidden>
Subject: [Nmh-commits] CVS: nmh/uip Makefile.in,1.15,1.15.2.1 anno.c,1.4,1.4.2.1 annosbr.c,1.3,1.3.2.1 burst.c,1.4,1.4.2.1 flist.c,1.4,1.4.2.1 folder.c,1.4,1.4.2.1 inc.c,1.13,1.13.2.1 install-mh.c,1.4,1.4.2.1 mhstoresbr.c,1.7,1.7.2.1 rcvstore.c,1.7,1.7.2.1 refile.c,1.4,1.4.2.1 rmf.c,1.4,1.4.2.1 rmm.c,1.4,1.4.2.1 scan.c,1.8.2.1,1.8.2.2 send.c,1.5,1.5.2.1 sendsbr.c,1.4,1.4.2.1 sortm.c,1.5,1.5.2.1 viamail.c,1.7,1.7.2.1 whatnowsbr.c,1.5,1.5.2.1
Date: Fri, 28 Feb 2003 14:08:39 -0500

Update of /cvsroot/nmh/nmh/uip
In directory subversions:/tmp/cvs-serv6328/nmh/uip

Modified Files:
      Tag: RELEASE_1_1
        Makefile.in anno.c annosbr.c burst.c flist.c folder.c inc.c 
        install-mh.c mhstoresbr.c rcvstore.c refile.c rmf.c rmm.c 
        scan.c send.c sendsbr.c sortm.c viamail.c whatnowsbr.c 
Log Message:
Merged in changes from main branch.  There are two classes of changes:
improved attachment handing for originating messages, and hooks that
provide an interface that allows external programs to to synchronize
a database with the contents of mail folders.


Index: Makefile.in
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/Makefile.in,v
retrieving revision 1.15
retrieving revision 1.15.2.1
diff -C2 -r1.15 -r1.15.2.1
*** Makefile.in 9 Jan 2001 06:40:12 -0000       1.15
--- Makefile.in 28 Feb 2003 19:08:36 -0000      1.15.2.1
***************
*** 58,62 ****
  
  # commands to build
! CMDS = ali anno burst comp dist flist folder forw mark mhbuild \
         mhlist mhmail mhn mhparam mhpath mhshow mhstore msgchk \
         msh packf pick prompter refile repl rmf rmm scan send show \
--- 58,62 ----
  
  # commands to build
! CMDS = ali anno burst comp dist flist folder forw install-mh mark mhbuild \
         mhlist mhmail mhn mhparam mhpath mhshow mhstore msgchk \
         msh packf pick prompter refile repl rmf rmm scan send show \
***************
*** 70,74 ****
  
  # misc support binaries
! MISC = ap conflict dp fmtdump install-mh mhl post rcvdist rcvpack \
         rcvstore rcvtty slocal spost viamail mhtest
  
--- 70,74 ----
  
  # misc support binaries
! MISC = ap conflict dp fmtdump mhl post rcvdist rcvpack \
         rcvstore rcvtty slocal spost viamail mhtest
  
***************
*** 252,256 ****
  
  # install everything
! install: install-cmds install-lcmds install-misc install-scmds
  
  # install commands
--- 252,256 ----
  
  # install everything
! install: install-cmds install-misc install-lcmds install-scmds
  
  # install commands
***************
*** 271,274 ****
--- 271,275 ----
        $(LN) $(bindir)/show   $(bindir)/prev
        $(LN) $(bindir)/show   $(bindir)/next
+       $(LN) $(bindir)/install-mh   $(libdir)/install-mh
  
  # install misc support binaries

Index: anno.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/anno.c,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -C2 -r1.4 -r1.4.2.1
*** anno.c      2 Jul 2002 22:09:14 -0000       1.4
--- anno.c      28 Feb 2003 19:08:36 -0000      1.4.2.1
***************
*** 8,11 ****
--- 8,47 ----
   * COPYRIGHT file in the root directory of the nmh distribution for
   * complete copyright information.
+  *
+  *    Four new options have been added: delete, list, number, and draft.
+  *    Message header fields are used by the new MIME attachment code in
+  *    the send command.  Adding features to generalize the anno command
+  *    seemed to be a better approach than the creation of a new command
+  *    whose features would overlap with those of the anno command.
+  *
+  *    The -draft option causes anno to operate on the current draft file
+  *    instead of on a message sequence.
+  *
+  *    The -delete option deletes header elements that match the -component
+  *    field name.  If -delete is used without the -text option, the first
+  *    header field whose field name matches the component name is deleted.
+  *    If the -delete is used with the -text option, and the -text argument
+  *    begins with a /, the first header field whose field name matches the
+  *    component name and whose field body matches the text is deleted.  If
+  *    the -text argument does not begin with a /, then the text is assumed
+  *    to be the last component of a path name, and the first header field
+  *    whose field name matches the component name and a field body whose
+  *    last path name component matches the text is deleted.  If the -delete
+  *    option is used with the new -number option described below, the nth
+  *    header field whose field name matches the component name is deleted.
+  *    No header fields are deleted if none of the above conditions are met.
+  *
+  *    The -list option outputs the field bodies from each header field whose
+  *    field name matches the component name, one per line.  If no -text
+  *    option is specified, only the last path name component of each field
+  *    body is output.  The entire field body is output if the -text option
+  *    is used; the contents of the -text argument are ignored.  If the -list
+  *    option is used in conjuction with the new -number option described
+  *    below, each line is numbered starting with 1.  A tab separates the
+  *    number from the field body.
+  *
+  *    The -number option works with both the -delete and -list options as
+  *    described above.  The -number option takes an optional argument.  A
+  *    value of 1 is assumed if this argument is absent.
   */
  
***************
*** 36,39 ****
--- 72,85 ----
  #define       HELPSW  7
      { "help", 0 },
+ #define       DRFTSW                   8
+     { "draft", 2 },
+ #define       LISTSW                   9
+     { "list", 1 },
+ #define       DELETESW                10
+     { "delete", 2 },
+ #define       NUMBERSW                11
+     { "number", 2 },
+ #define       APPENDSW                12
+     { "append", 1 },
      { NULL, 0 }
  };
***************
*** 54,57 ****
--- 100,109 ----
      char **argp, **arguments, **msgs;
      struct msgs *mp;
+     int               append = 0;             /* append annotations instead 
of default prepend */
+     int               delete = -1;            /* delete header element if set 
*/
+     char      *draft = (char *)0;     /* draft file name */
+     int               isdf = 0;               /* return needed for m_draft() 
*/
+     int               list = 0;               /* list header elements if set 
*/
+     int               number = 0;             /* delete specific number of 
like elements if set */
  
  #ifdef LOCALE
***************
*** 78,88 ****
        if (*cp == '-') {
            switch (smatch (++cp, switches)) {
!               case AMBIGSW: 
                    ambigsw (cp, switches);
                    done (1);
!               case UNKWNSW: 
                    adios (NULL, "-%s unknown", cp);
  
!               case HELPSW: 
                    snprintf (buf, sizeof(buf), "%s [+folder] [msgs] 
[switches]",
                        invo_name);
--- 130,140 ----
        if (*cp == '-') {
            switch (smatch (++cp, switches)) {
!               case AMBIGSW:
                    ambigsw (cp, switches);
                    done (1);
!               case UNKWNSW:
                    adios (NULL, "-%s unknown", cp);
  
!               case HELPSW:
                    snprintf (buf, sizeof(buf), "%s [+folder] [msgs] 
[switches]",
                        invo_name);
***************
*** 93,97 ****
                    done (1);
  
!               case COMPSW: 
                    if (comp)
                        adios (NULL, "only one component at a time!");
--- 145,149 ----
                    done (1);
  
!               case COMPSW:
                    if (comp)
                        adios (NULL, "only one component at a time!");
***************
*** 100,118 ****
                    continue;
  
!               case DATESW: 
                    datesw++;
                    continue;
!               case NDATESW: 
                    datesw = 0;
                    continue;
  
!               case INPLSW: 
                    inplace++;
                    continue;
!               case NINPLSW: 
                    inplace = 0;
                    continue;
  
!               case TEXTSW: 
                    if (text)
                        adios (NULL, "only one body at a time!");
--- 152,170 ----
                    continue;
  
!               case DATESW:
                    datesw++;
                    continue;
!               case NDATESW:
                    datesw = 0;
                    continue;
  
!               case INPLSW:
                    inplace++;
                    continue;
!               case NINPLSW:
                    inplace = 0;
                    continue;
  
!               case TEXTSW:
                    if (text)
                        adios (NULL, "only one body at a time!");
***************
*** 120,123 ****
--- 172,204 ----
                        adios (NULL, "missing argument to %s", argp[-2]);
                    continue;
+ 
+               case DELETESW:          /* delete annotations */
+                   delete = 0;
+                   continue;
+ 
+               case DRFTSW:            /* draft message specified */
+                   draft = "";
+                   continue;
+ 
+               case LISTSW:            /* produce a listing */
+                   list = 1;
+                   continue;
+ 
+               case NUMBERSW:          /* number listing or delete by number */
+                   if (number != 0)
+                       adios (NULL, "only one number at a time!");
+ 
+                   if (argp - arguments == argc - 1 || **argp == '-')
+                       number = 1;
+ 
+                   else if (!(number = atoi(*argp++)))
+                       adios (NULL, "missing argument to %s", argp[-2]);
+ 
+                   delete = number;
+                   continue;
+ 
+               case APPENDSW:          /* append annotations instead of 
default prepend */
+                   append = 1;
+                   continue;
            }
        }
***************
*** 142,145 ****
--- 223,248 ----
      }
  
+     /*
+      *        We're dealing with the draft message instead of message numbers.
+      *        Get the name of the draft and deal with it just as we do with
+      *        message numbers below.
+      */
+ 
+     if (draft != (char *)0) {
+       if (nummsgs != 0)
+           adios(NULL, "can only have message numbers or -draft.");
+ 
+       draft = getcpy(m_draft(folder, (char *)0, 1, &isdf));
+ 
+       make_comp(&comp);
+ 
+       if (list)
+           annolist(draft, comp, text, number);
+       else
+           annotate (draft, comp, text, inplace, datesw, delete, append);
+ 
+       return (done(0));
+     }
+ 
  #ifdef UCI
      if (strcmp(invo_name, "fanno") == 0)      /* ugh! */
***************
*** 174,180 ****
  
      /* annotate all the SELECTED messages */
!     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
!       if (is_selected(mp, msgnum))
!           annotate (m_name (msgnum), comp, text, inplace, datesw);
  
      context_replace (pfolder, folder);        /* update current folder  */
--- 277,288 ----
  
      /* annotate all the SELECTED messages */
!     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
!       if (is_selected(mp, msgnum)) {
!           if (list)
!               annolist(m_name(msgnum), comp, text, number);
!           else
!               annotate (m_name (msgnum), comp, text, inplace, datesw, delete, 
append);
!       }
!     }
  
      context_replace (pfolder, folder);        /* update current folder  */
***************
*** 186,190 ****
  }
  
- 
  static void
  make_comp (char **ap)
--- 294,297 ----
***************
*** 215,217 ****
            adios (NULL, "invalid component name %s", *ap);
  }
- 
--- 322,323 ----

Index: annosbr.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/annosbr.c,v
retrieving revision 1.3
retrieving revision 1.3.2.1
diff -C2 -r1.3 -r1.3.2.1
*** annosbr.c   2 Jul 2002 22:09:14 -0000       1.3
--- annosbr.c   28 Feb 2003 19:08:36 -0000      1.3.2.1
***************
*** 20,28 ****
   * static prototypes
   */
! static int annosbr (int, char *, char *, char *, int, int);
  
  
  int
! annotate (char *file, char *comp, char *text, int inplace, int datesw)
  {
      int i, fd;
--- 20,28 ----
   * static prototypes
   */
! static int annosbr (int, char *, char *, char *, int, int, int, int);
  
  
  int
! annotate (char *file, char *comp, char *text, int inplace, int datesw, int 
delete, int append)
  {
      int i, fd;
***************
*** 31,38 ****
      if ((fd = lkopen (file, O_RDWR, 0)) == NOTOK) {
        switch (errno) {
!           case ENOENT: 
                break;
  
!           default: 
                admonish (file, "unable to lock and open");
                break;
--- 31,38 ----
      if ((fd = lkopen (file, O_RDWR, 0)) == NOTOK) {
        switch (errno) {
!           case ENOENT:
                break;
  
!           default:
                admonish (file, "unable to lock and open");
                break;
***************
*** 41,52 ****
      }
  
!     i = annosbr (fd, file, comp, text, inplace, datesw);
      lkclose (fd, file);
      return i;
  }
  
  
  static int
! annosbr (int fd, char *file, char *comp, char *text, int inplace, int datesw)
  {
      int mode, tmpfd;
--- 41,146 ----
      }
  
!     i = annosbr (fd, file, comp, text, inplace, datesw, delete, append);
      lkclose (fd, file);
      return i;
  }
  
+ /*
+  *  Produce a listing of all header fields (annotations) whose field name 
matches
+  *  comp.  Number the listing if number is set.  Treate the field bodies as 
path
+  *  names and just output the last component unless text is non-NULL.  We 
don't
+  *  care what text is set to.
+  */
+ 
+ void
+ annolist(char *file, char *comp, char *text, int number)
+ {
+     int               c;              /* current character */
+     int               count;          /* header field (annotation) counter */
+     char      *cp;            /* miscellaneous character pointer */
+     char      *field;         /* buffer for header field */
+     int               field_size;     /* size of field buffer */
+     FILE      *fp;            /* file pointer made from locked file 
descriptor */
+     int               length;         /* length of field name */
+     int               n;              /* number of bytes written */
+     char      *sp;            /* another miscellaneous character pointer */
+ 
+     if ((fp = fopen(file, "r")) == (FILE *)0)
+       adios(file, "unable to open");
+ 
+     /*
+      *  Allocate a buffer to hold the header components as they're read in.
+      *  This buffer might need to be quite large, so we grow it as needed.
+      */
+ 
+     if ((field = (char *)malloc(field_size = 256)) == (char *)0)
+       adios(NULL, "can't allocate field buffer.");
+ 
+     /*
+      *  Get the length of the field name since we use it often.
+      */
+ 
+     length = strlen(comp);
+ 
+     count = 0;
+ 
+     do {
+       /*
+        *      Get a line from the input file, growing the field buffer as 
needed.  We do this
+        *      so that we can fit an entire line in the buffer making it easy 
to do a string
+        *      comparison on both the field name and the field body which 
might be a long path
+        *      name.
+        */
+ 
+       for (n = 0, cp = field; (c = getc(fp)) != EOF; *cp++ = c) {
+           if (c == '\n' && (c = getc(fp)) != ' ' && c != '\t') {
+               (void)ungetc(c, fp);
+               c = '\n';
+               break;
+           }
+ 
+           if (++n >= field_size - 1) {
+               if ((field = (char *)realloc((void *)field, field_size += 256)) 
== (char *)0)
+                   adios(NULL, "can't grow field buffer.");
+               
+               cp = field + n - 1;
+           }
+       }
+ 
+       /*
+        *      NUL-terminate the field..
+        */
+ 
+       *cp = '\0';
+ 
+       if (strncasecmp(field, comp, length) == 0 && field[length] == ':') {
+           for (cp = field + length + 1; *cp == ' ' || *cp == '\t'; cp++)
+               ;
+ 
+           if (number)
+               (void)printf("%d\t", ++count);
+ 
+           if (text == (char *)0 && (sp = strrchr(cp, '/')) != (char *)0)
+               cp = sp + 1;
+ 
+           (void)printf("%s\n", cp);
+       }
+ 
+     } while (*field != '\0' && *field != '-');
+ 
+     /*
+      *        Clean up.
+      */
+ 
+     free(field);
+ 
+     (void)fclose(fp);
+ 
+     return;
+ }
+ 
  
  static int
! annosbr (int fd, char *file, char *comp, char *text, int inplace, int datesw, 
int delete, int append)
  {
      int mode, tmpfd;
***************
*** 54,58 ****
      char buffer[BUFSIZ], tmpfil[BUFSIZ];
      struct stat st;
!     FILE *tmp;
  
      mode = fstat (fd, &st) != NOTOK ? (st.st_mode & 0777) : m_gmprot ();
--- 148,159 ----
      char buffer[BUFSIZ], tmpfil[BUFSIZ];
      struct stat st;
!     FILE      *tmp;
!     int               c;              /* current character */
!     int               count;          /* header field (annotation) counter */
!     char      *field;         /* buffer for header field */
!     int               field_size;     /* size of field buffer */
!     FILE      *fp;            /* file pointer made from locked file 
descriptor */
!     int               length;         /* length of field name */
!     int               n;              /* number of bytes written */
  
      mode = fstat (fd, &st) != NOTOK ? (st.st_mode & 0777) : m_gmprot ();
***************
*** 66,85 ****
      chmod (tmpfil, mode);
  
!     if (datesw)
!       fprintf (tmp, "%s: %s\n", comp, dtimenow (0));
!     if ((cp = text)) {
        do {
!           while (*cp == ' ' || *cp == '\t')
!               cp++;
!           sp = cp;
!           while (*cp && *cp++ != '\n')
!               continue;
!           if (cp - sp)
!               fprintf (tmp, "%s: %*.*s", comp, cp - sp, cp - sp, sp);
!       } while (*cp);
!       if (cp[-1] != '\n' && cp != text)
!           putc ('\n', tmp);
      }
      fflush (tmp);
      cpydata (fd, fileno (tmp), file, tmpfil);
      fclose (tmp);
--- 167,364 ----
      chmod (tmpfil, mode);
  
!     /*
!      *  We're going to need to copy some of the message file to the temporary
!      *        file while examining the contents.  Convert the message file 
descriptor
!      *        to a file pointer since it's a lot easier and more efficient to 
use
!      *        stdio for this.  Also allocate a buffer to hold the header 
components
!      *        as they're read in.  This buffer is grown as needed later.
!      */
! 
!     if (delete >= 0 || append != 0) {
!       if ((fp = fdopen(fd, "r")) == (FILE *)0)
!           adios(NULL, "unable to fdopen file.");
! 
!       if ((field = (char *)malloc(field_size = 256)) == (char *)0)
!           adios(NULL, "can't allocate field buffer.");
!     }
! 
!     /*
!      *        We're trying to delete a header field (annotation )if the 
delete flag is
!      *        non-negative.  A  value greater than zero means that we're 
deleting the
!      *        nth header field that matches the field (component) name.  A 
value of
!      *        zero means that we're deleting the first field in which both 
the field
!      *        name matches the component name and the field body matches the 
text.
!      *        The text is matched in its entirety if it begins with a slash; 
otherwise
!      *        the text is matched against whatever portion of the field body 
follows
!      *        the last slash.  This allows matching of both absolute and 
relative path
!      *        names.  This is because this functionality was added to support 
attachments.
!      *        It might be worth having a separate flag to indicate path name 
matching to
!      *        make it more general.
!      */
! 
!     if (delete >= 0) {
!       /*
!        *  Get the length of the field name since we use it often.
!        */
! 
!       length = strlen(comp);
! 
!       /*
!        *  Initialize the field counter.  This is only used if we're deleting 
by
!        *  number.
!        */
! 
!       count = 0;
! 
!       /*
!        *  Copy lines from the input file to the temporary file until we 
either find the one
!        *  that we're looking for (which we don't copy) or we reach the end of 
the headers.
!        *  Both a blank line and a line beginning with a - terminate the 
headers so that we
!        *  can handle both drafts and RFC-2822 format messages.
!        */
! 
        do {
!           /*
!            *  Get a line from the input file, growing the field buffer as 
needed.  We do this
!            *  so that we can fit an entire line in the buffer making it easy 
to do a string
!            *  comparison on both the field name and the field body which 
might be a long path
!            *  name.
!            */
! 
!           for (n = 0, cp = field; (c = getc(fp)) != EOF; *cp++ = c) {
!               if (c == '\n' && (c = getc(fp)) != ' ' && c != '\t') {
!                   (void)ungetc(c, fp);
!                   c = '\n';
!                   break;
!               }
! 
!               if (++n >= field_size - 1) {
!                   if ((field = (char *)realloc((void *)field, field_size *= 
2)) == (char *)0)
!                       adios(NULL, "can't grow field buffer.");
!                   
!                   cp = field + n - 1;
!               }
!           }
! 
!           /*
!            *  NUL-terminate the field..
!            */
! 
!           *cp = '\0';
! 
!           /*
!            *  Check for a match on the field name.  We delete the line by not 
copying it to the
!            *  temporary file if
!            *
!            *   o  The delete flag is 0, meaning that we're going to delete 
the first matching
!            *      field, and the text is NULL meaning that we don't care 
about the field body.
!            *
!            *   o  The delete flag is 0, meaning that we're going to delete 
the first matching
!            *      field, and the text begins with a / meaning that we're 
looking for a full
!            *      path name, and the text matches the field body.
!            *
!            *   o  The delete flag is 0, meaning that we're going to delete 
the first matching
!            *      field, the text does not begin with a / meaning that we're 
looking for the
!            *      last path name component, and the last path name component 
matches the text.
!            *
!            *   o  The delete flag is non-zero meaning that we're going to 
delete the nth field
!            *      with a matching field name, and this is the nth matching 
field name.
!            */
! 
!           if (strncasecmp(field, comp, length) == 0 && field[length] == ':') {
!               if (delete == 0) {
!                   if (text == (char *)0)
!                       break;
! 
!                   for (cp = field + length + 1; *cp == ' ' || *cp == '\t'; 
cp++)
!                       ;
! 
!                   if (*text == '/') {
!                       if (strcmp(cp, text) == 0)
!                               break;
!                   }
!                   else {
!                       if ((sp = strrchr(cp, '/')) != (char *)0)
!                           cp = sp + 1;
! 
!                       if (strcmp(cp, text) == 0)
!                           break;
!                   }
!               }
! 
!               else if (++count == delete)
!                   break;
!           }
! 
!           /*
!            *  This line wasn't a match so copy it to the temporary file.
!            */
! 
!           if ((n = fputs(field, tmp)) == EOF || (c == '\n' && fputc('\n', 
tmp) == EOF))
!               adios(NULL, "unable to write temporary file.");
! 
!       } while (*field != '\0' && *field != '-');
! 
!       /*
!        *  Get rid of the field buffer because we're done with it.
!        */
! 
!       free((void *)field);
!     }
! 
!     else {
!       /*
!        *  Find the end of the headers before adding the annotations if we're
!        *  appending instead of the default prepending.
!        */
! 
!       if (append) {
!           /*
!            *  Copy lines from the input file to the temporary file until we
!            *  reach the end of the headers.
!            */
! 
!           while ((c = getc(fp)) != EOF) {
!               (void)putc(c, tmp);
! 
!               if (c == '\n') {
!                   (void)ungetc(c = getc(fp), fp);
! 
!                   if (c == '\n' || c == '-')
!                       break;
!               }
!           }
!       }
! 
!       if (datesw)
!           fprintf (tmp, "%s: %s\n", comp, dtimenow (0));
!       if ((cp = text)) {
!           do {
!               while (*cp == ' ' || *cp == '\t')
!                   cp++;
!               sp = cp;
!               while (*cp && *cp++ != '\n')
!                   continue;
!               if (cp - sp)
!                   fprintf (tmp, "%s: %*.*s", comp, cp - sp, cp - sp, sp);
!           } while (*cp);
!           if (cp[-1] != '\n' && cp != text)
!               putc ('\n', tmp);
!       }
      }
+ 
      fflush (tmp);
+ 
+     /*
+      *        We've been messing with the input file position.  Move the 
input file
+      *  descriptor to the current place in the file because the stock data
+      *        copying routine uses the descriptor, not the pointer.
+      */
+ 
+     if (append || delete >= 0) {
+       if (lseek(fd, (off_t)ftell(fp), SEEK_SET) == (off_t)-1)
+           adios(NULL, "can't seek.");
+     }
+ 
      cpydata (fd, fileno (tmp), file, tmpfil);
      fclose (tmp);
***************
*** 88,92 ****
--- 367,381 ----
        if ((tmpfd = open (tmpfil, O_RDONLY)) == NOTOK)
            adios (tmpfil, "unable to open for re-reading");
+ 
        lseek (fd, (off_t) 0, SEEK_SET);
+ 
+       /*
+        *  We're making the file shorter if we're deleting a header field
+        *  so the file has to be truncated or it will contain garbage.
+        */
+ 
+       if (delete >= 0 && ftruncate(fd, 0) == -1)
+           adios(tmpfil, "unable to truncate.");
+ 
        cpydata (tmpfd, fd, tmpfil, file);
        close (tmpfd);
***************
*** 111,114 ****
--- 400,412 ----
        }
      }
+ 
+     /*
+      *        Close the delete file so that we don't run out of file pointers 
if
+      *        we're doing piles of files.  Note that this will make the 
close() in
+      *        lkclose() fail, but that failure is ignored so it's not a 
problem.
+      */
+ 
+     if (delete >= 0)
+       (void)fclose(fp);
  
      return 0;

Index: burst.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/burst.c,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -C2 -r1.4 -r1.4.2.1
*** burst.c     2 Jul 2002 22:09:14 -0000       1.4
--- burst.c     28 Feb 2003 19:08:36 -0000      1.4.2.1
***************
*** 43,47 ****
   */
  static int find_delim (int, struct smsg *);
! static void burst (struct msgs **, int, struct smsg *, int, int, int);
  static void cpybrst (FILE *, FILE *, char *, char *, int);
  
--- 43,47 ----
   */
  static int find_delim (int, struct smsg *);
! static void burst (struct msgs **, int, struct smsg *, int, int, int, char *);
  static void cpybrst (FILE *, FILE *, char *, char *, int);
  
***************
*** 157,161 ****
                    printf ("%d message%s exploded from digest %d\n",
                            numburst, numburst > 1 ? "s" : "", msgnum);
!               burst (&mp, msgnum, smsgs, numburst, inplace, verbosw);
            } else {
                if (numburst == 0) {
--- 157,161 ----
                    printf ("%d message%s exploded from digest %d\n",
                            numburst, numburst > 1 ? "s" : "", msgnum);
!               burst (&mp, msgnum, smsgs, numburst, inplace, verbosw, maildir);
            } else {
                if (numburst == 0) {
***************
*** 257,261 ****
  static void
  burst (struct msgs **mpp, int msgnum, struct smsg *smsgs, int numburst,
!       int inplace, int verbosw)
  {
      int i, j, mode;
--- 257,261 ----
  static void
  burst (struct msgs **mpp, int msgnum, struct smsg *smsgs, int numburst,
!       int inplace, int verbosw, char *maildir)
  {
      int i, j, mode;
***************
*** 297,300 ****
--- 297,303 ----
       * source message, to make room for each of the messages
       * contained within the digest.
+      *
+      * This is equivalent to refiling a message from the point
+      * of view of the external hooks.
       */
      if (inplace) {
***************
*** 308,311 ****
--- 311,319 ----
                if (rename (f2, f1) == NOTOK)
                    admonish (f1, "unable to rename %s to", f2);
+ 
+               (void)snprintf(f1, sizeof (f1), "%s/%d", maildir, i);
+               (void)snprintf(f2, sizeof (f2), "%s/%d", maildir, j);
+               ext_hook("ref-hook", f1, f2);
+ 
                copy_msg_flags (mp, i, j);
                clear_msg_flags (mp, j);
***************
*** 317,321 ****
      unset_selected (mp, msgnum);
  
!     /* new hghmsg is hghmsg + numburst */
      i = inplace ? msgnum + numburst : mp->hghmsg;
      for (j = numburst; j >= (inplace ? 0 : 1); i--, j--) {
--- 325,347 ----
      unset_selected (mp, msgnum);
  
!     /* new hghmsg is hghmsg + numburst
!      *
!      * At this point, there is an array of numburst smsgs, each element of
!      * which contains the starting and stopping offsets (seeks) of the message
!      * in the digest.  The inplace flag is set if the original digest is 
replaced
!      * by a message containing the table of contents.  smsgs[0] is that table 
of
!      * contents.  Go through the message numbers in reverse order (high to 
low).
!      *
!      * Set f1 to the name of the destination message, f2 to the name of a 
scratch
!      * file.  Extract a message from the digest to the scratch file.  Move the
!      * original message to a backup file if the destination message number is 
the
!      * same as the number of the original message, which only happens if the
!      * inplace flag is set.  Then move the scratch file to the destination 
message.
!      *
!      * Moving the original message to the backup file is equivalent to 
deleting the
!      * message from the point of view of the external hooks.  And bursting 
each
!      * message is equivalent to adding a new message.
!      */
! 
      i = inplace ? msgnum + numburst : mp->hghmsg;
      for (j = numburst; j >= (inplace ? 0 : 1); i--, j--) {
***************
*** 337,343 ****
--- 363,376 ----
            if (rename (f1, f3) == NOTOK)
                admonish (f3, "unable to rename %s to", f1);
+ 
+           (void)snprintf(f3, sizeof (f3), "%s/%d", maildir, i);
+           ext_hook("del-hook", f3, (char *)0);
        }
        if (rename (f2, f1) == NOTOK)
            admonish (f1, "unable to rename %s to", f2);
+ 
+       (void)snprintf(f3, sizeof (f3), "%s/%d", maildir, i);
+       ext_hook("add-hook", f3, (char *)0);
+ 
        copy_msg_flags (mp, i, msgnum);
        mp->msgflags |= SEQMOD;

Index: flist.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/flist.c,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -C2 -r1.4 -r1.4.2.1
*** flist.c     4 Feb 2000 20:28:23 -0000       1.4
--- flist.c     28 Feb 2003 19:08:36 -0000      1.4.2.1
***************
*** 698,705 ****
      register struct node *np;
  
-     /* sanity check - check that context has been read */
-     if (defpath == NULL)
-       adios (NULL, "oops, context hasn't been read yet");
- 
      snprintf (atrcur, sizeof(atrcur), "atr-%s-", current);
      atrlen = strlen (atrcur);
--- 698,701 ----

Index: folder.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/folder.c,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -C2 -r1.4 -r1.4.2.1
*** folder.c    2 Jul 2002 22:09:14 -0000       1.4
--- folder.c    28 Feb 2003 19:08:36 -0000      1.4.2.1
***************
*** 819,826 ****
      register struct node *np;
  
-     /* sanity check - check that context has been read */
-     if (defpath == NULL)
-       adios (NULL, "oops, context hasn't been read yet");
- 
      snprintf (atrcur, sizeof(atrcur), "atr-%s-", current);
      atrlen = strlen (atrcur);
--- 819,822 ----

Index: inc.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/inc.c,v
retrieving revision 1.13
retrieving revision 1.13.2.1
diff -C2 -r1.13 -r1.13.2.1
*** inc.c       2 Jul 2002 22:09:14 -0000       1.13
--- inc.c       28 Feb 2003 19:08:36 -0000      1.13.2.1
***************
*** 246,249 ****
--- 246,250 ----
      struct stat st, s1;
      FILE *aud = NULL;
+     char      b[MAXPATHLEN + 1];
  
  #ifdef POP
***************
*** 803,807 ****
            newmsg = folder_addmsg(mp, tmpfilenam);
  #endif
- 
            /* create scanline for new message */
            switch (i = scan (in, msgnum + 1, msgnum + 1, nfs, width,
--- 804,807 ----
***************
*** 827,830 ****
--- 827,837 ----
            case SCNMSG:
            case SCNENC:
+               /*
+                *  Run the external program hook on the message.
+                */
+ 
+               (void)snprintf(b, sizeof (b), "%s/%d", maildir, msgnum + 1);
+               (void)ext_hook("add-hook", b, (char *)0);
+ 
                if (aud)
                    fputs (scanl, aud);

Index: install-mh.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/install-mh.c,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -C2 -r1.4 -r1.4.2.1
*** install-mh.c        2 Jul 2002 22:09:14 -0000       1.4
--- install-mh.c        28 Feb 2003 19:08:36 -0000      1.4.2.1
***************
*** 1,3 ****
- 
  /*
   * install-mh.c -- initialize the nmh environment of a new user
--- 1,2 ----
***************
*** 10,15 ****
   */
  
! #include <h/mh.h>
! #include <pwd.h>
  
  static struct swit switches[] = {
--- 9,14 ----
   */
  
! #include <h/mh.h>                             /* mh internals */
! #include <pwd.h>                              /* structure for getpwuid() 
results */
  
  static struct swit switches[] = {
***************
*** 20,35 ****
  #define HELPSW     2
      { "help", 0 },
      { NULL, 0 }
  };
  
- static char *message[] = {
-     "Prior to using nmh, it is necessary to have a file in your login",
-     "directory (%s) named %s which contains information",
-     "to direct certain nmh operations.  The only item which is required",
-     "is the path to use for all nmh folder operations.  The suggested nmh",
-     "path for you is %s/Mail...",
-     NULL
- };
- 
  /*
   * static prototypes
--- 19,27 ----
  #define HELPSW     2
      { "help", 0 },
+ #define CHECKSW     3
+     { "check", 1 },
      { NULL, 0 }
  };
  
  /*
   * static prototypes
***************
*** 41,46 ****
  main (int argc, char **argv)
  {
!     int i, autof = 0;
!     char *cp, *path, buf[BUFSIZ];
      char *dp, **arguments, **argp;
      struct node *np;
--- 33,38 ----
  main (int argc, char **argv)
  {
!     int autof = 0;
!     char *cp, *pathname, buf[BUFSIZ];
      char *dp, **arguments, **argp;
      struct node *np;
***************
*** 48,51 ****
--- 40,44 ----
      struct stat st;
      FILE *in, *out;
+     int               check;
  
  #ifdef LOCALE
***************
*** 56,59 ****
--- 49,54 ----
      argp = arguments;
  
+     check = 0;
+ 
      while ((dp = *argp++)) {
        if (*dp == '-') {
***************
*** 76,79 ****
--- 71,78 ----
                    autof++;
                    continue;
+ 
+               case CHECKSW:
+                   check = 1;
+                   continue;
            }
        } else {
***************
*** 82,117 ****
      }
  
!     /* straight from context_read ... */
!     if (mypath == NULL) {
!       if ((mypath = getenv ("HOME"))) {
!           mypath = getcpy (mypath);
!       } else {
!           if ((pw = getpwuid (getuid ())) == NULL
!                   || pw->pw_dir == NULL
!                   || *pw->pw_dir == 0)
!               adios (NULL, "no HOME envariable");
!           else
!               mypath = getcpy (pw->pw_dir);
!       }
!       if ((cp = mypath + strlen (mypath) - 1) > mypath && *cp == '/')
!           *cp = 0;
      }
!     defpath = concat (mypath, "/", mh_profile, NULL);
  
      if (stat (defpath, &st) != NOTOK) {
!       if (autof)
            adios (NULL, "invocation error");
        else
!           adios (NULL,
!                   "You already have an nmh profile, use an editor to modify 
it");
      }
  
      if (!autof && gans ("Do you want help? ", anoyes)) {
!       putchar ('\n');
!       for (i = 0; message[i]; i++) {
!           printf (message[i], mypath, mh_profile);
!           putchar ('\n');
!       }
!       putchar ('\n');
      }
  
--- 81,136 ----
      }
  
!     /*
!      *        Find user's home directory.  Try the HOME environment variable 
first,
!      *        the home directory field in the password file if that's not 
found.
!      */
! 
!     if ((mypath = getenv("HOME")) == (char *)0) {
!       if ((pw = getpwuid(getuid())) == (struct passwd *)0 || *pw->pw_dir == 
'\0')
!           adios(NULL, "cannot determine your home directory");
!       else
!           mypath = pw->pw_dir;
      }
! 
!     /*
!      *        Find the user's profile.  Check for the existence of an MH 
environment
!      *        variable first with non-empty contents.  Convert any relative 
path name
!      *        found there to an absolute one.  Look for the profile in the 
user's home
!      *        directory if the MH environment variable isn't set.
!      */
! 
!     if ((cp = getenv("MH")) && *cp != '\0')
!       defpath = path(cp, TFILE);
!     else
!       defpath = concat(mypath, "/", mh_profile, NULL);
! 
!     /*
!      *        Check for the existence of the profile file.  It's an error if 
it exists and
!      *        this isn't an installation check.  An installation check fails 
if it does not
!      *        exist, succeeds if it does.
!      */
  
      if (stat (defpath, &st) != NOTOK) {
!       if (check)
!           done(0);
! 
!       else if (autof)
            adios (NULL, "invocation error");
        else
!           adios (NULL, "You already have an nmh profile, use an editor to 
modify it");
!     }
!     else if (check) {
!       done(1);
      }
  
      if (!autof && gans ("Do you want help? ", anoyes)) {
!       (void)printf(
!        "\n"
!        "Prior to using nmh, it is necessary to have a file in your login\n"
!        "directory (%s) named %s which contains information\n"
!        "to direct certain nmh operations.  The only item which is required\n"
!        "is the path to use for all nmh folder operations.  The suggested 
nmh\n"
!        "path for you is %s/Mail...\n"
!        "\n", mypath, mh_profile, mypath);
      }
  
***************
*** 122,126 ****
                    cp, "\".\nDo you want to use it for nmh? ", NULL);
            if (gans (cp, anoyes))
!               path = "Mail";
            else
                goto query;
--- 141,145 ----
                    cp, "\".\nDo you want to use it for nmh? ", NULL);
            if (gans (cp, anoyes))
!               pathname = "Mail";
            else
                goto query;
***************
*** 135,139 ****
                    mypath, "/", "Mail\"? ", NULL);
        if (autof || gans (cp, anoyes))
!           path = "Mail";
        else {
  query:
--- 154,158 ----
                    mypath, "/", "Mail\"? ", NULL);
        if (autof || gans (cp, anoyes))
!           pathname = "Mail";
        else {
  query:
***************
*** 141,148 ****
                        anoyes)) {
                printf ("What is the path?  %s/", mypath);
!               path = geta ();
            } else {
                printf ("What is the whole path?  /");
!               path = concat ("/", geta (), NULL);
            }
        }
--- 160,167 ----
                        anoyes)) {
                printf ("What is the path?  %s/", mypath);
!               pathname = geta ();
            } else {
                printf ("What is the whole path?  /");
!               pathname = concat ("/", geta (), NULL);
            }
        }
***************
*** 150,158 ****
  
      chdir (mypath);
!     if (chdir (path) == NOTOK) {
!       cp = concat ("\"", path, "\" doesn't exist; Create it? ", NULL);
        if (autof || gans (cp, anoyes))
!           if (makedir (path) == 0)
!               adios (NULL, "unable to create %s", path);
      } else {
        printf ("[Using existing directory]\n");
--- 169,177 ----
  
      chdir (mypath);
!     if (chdir (pathname) == NOTOK) {
!       cp = concat ("\"", pathname, "\" doesn't exist; Create it? ", NULL);
        if (autof || gans (cp, anoyes))
!           if (makedir (pathname) == 0)
!               adios (NULL, "unable to create %s", pathname);
      } else {
        printf ("[Using existing directory]\n");
***************
*** 166,170 ****
      np = m_defs;
      np->n_name = getcpy ("Path");
!     np->n_field = getcpy (path);
      np->n_context = 0;
      np->n_next = NULL;
--- 185,189 ----
      np = m_defs;
      np->n_name = getcpy ("Path");
!     np->n_field = getcpy (pathname);
      np->n_context = 0;
      np->n_next = NULL;

Index: mhstoresbr.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/mhstoresbr.c,v
retrieving revision 1.7
retrieving revision 1.7.2.1
diff -C2 -r1.7 -r1.7.2.1
*** mhstoresbr.c        2 Jul 2002 22:09:14 -0000       1.7
--- mhstoresbr.c        28 Feb 2003 19:08:36 -0000      1.7.2.1
***************
*** 922,926 ****
      if ((mp = folder_read (folder))) {
        /* Link file into folder */
!       msgnum = folder_addmsg (&mp, filename, 0, 0, 0);
      } else {
        advise (NULL, "unable to read folder %s", folder);
--- 922,926 ----
      if ((mp = folder_read (folder))) {
        /* Link file into folder */
!       msgnum = folder_addmsg (&mp, filename, 0, 0, 0, 0);
      } else {
        advise (NULL, "unable to read folder %s", folder);

Index: rcvstore.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/rcvstore.c,v
retrieving revision 1.7
retrieving revision 1.7.2.1
diff -C2 -r1.7 -r1.7.2.1
*** rcvstore.c  2 Jul 2002 22:09:15 -0000       1.7
--- rcvstore.c  28 Feb 2003 19:08:36 -0000      1.7.2.1
***************
*** 204,208 ****
       * to the Unseen-Sequence's.
       */
!     if ((msgnum = folder_addmsg (&mp, tmpfilenam, 0, unseensw, 0)) == -1)
        done (1);
  
--- 204,208 ----
       * to the Unseen-Sequence's.
       */
!     if ((msgnum = folder_addmsg (&mp, tmpfilenam, 0, unseensw, 0, 0)) == -1)
        done (1);
  

Index: refile.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/refile.c,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -C2 -r1.4 -r1.4.2.1
*** refile.c    2 Jul 2002 22:09:15 -0000       1.4
--- refile.c    28 Feb 2003 19:08:36 -0000      1.4.2.1
***************
*** 67,71 ****
  static void clsfolds (struct st_fold *, int);
  static void remove_files (int, char **);
! static int m_file (char *, struct st_fold *, int, int);
  
  
--- 67,71 ----
  static void clsfolds (struct st_fold *, int);
  static void remove_files (int, char **);
! static int m_file (char *, struct st_fold *, int, int, int);
  
  
***************
*** 214,218 ****
        opnfolds (folders, foldp);
        for (i = 0; i < filep; i++)
!           if (m_file (files[i], folders, foldp, preserve))
                done (1);
        /* If -nolink, then "remove" files */
--- 214,218 ----
        opnfolds (folders, foldp);
        for (i = 0; i < filep; i++)
!           if (m_file (files[i], folders, foldp, preserve, 0))
                done (1);
        /* If -nolink, then "remove" files */
***************
*** 248,256 ****
      opnfolds (folders, foldp);
  
!     /* Link all the selected messages into destination folders */
      for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
        if (is_selected (mp, msgnum)) {
            cp = getcpy (m_name (msgnum));
!           if (m_file (cp, folders, foldp, preserve))
                done (1);
            free (cp);
--- 248,261 ----
      opnfolds (folders, foldp);
  
!     /* Link all the selected messages into destination folders.
!      *
!      * This causes the add hook to be run for messages that are
!      * linked into another folder.  The refile hook is run for
!      * messages that are moved to another folder.
!      */
      for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
        if (is_selected (mp, msgnum)) {
            cp = getcpy (m_name (msgnum));
!           if (m_file (cp, folders, foldp, preserve, !linkf))
                done (1);
            free (cp);
***************
*** 270,276 ****
      }
  
!     /* If -nolink, then "remove" messages from source folder */
      if (!linkf) {
!       folder_delmsgs (mp, unlink_msgs);
      }
  
--- 275,285 ----
      }
  
!     /* If -nolink, then "remove" messages from source folder.
!      *
!      * Note that folder_delmsgs does not call the delete hook
!      * because the message has already been handled above.
!      */
      if (!linkf) {
!       folder_delmsgs (mp, unlink_msgs, 1);
      }
  
***************
*** 388,392 ****
  
  static int
! m_file (char *msgfile, struct st_fold *folders, int nfolders, int preserve)
  {
      int msgnum;
--- 397,401 ----
  
  static int
! m_file (char *msgfile, struct st_fold *folders, int nfolders, int preserve, 
int refile)
  {
      int msgnum;
***************
*** 394,398 ****
  
      for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
!       if ((msgnum = folder_addmsg (&fp->f_mp, msgfile, 1, 0, preserve)) == -1)
            return 1;
      }
--- 403,407 ----
  
      for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
!       if ((msgnum = folder_addmsg (&fp->f_mp, msgfile, 1, 0, preserve, 
nfolders == 1 && refile)) == -1)
            return 1;
      }

Index: rmf.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/rmf.c,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -C2 -r1.4 -r1.4.2.1
*** rmf.c       2 Jul 2002 22:09:15 -0000       1.4
--- rmf.c       28 Feb 2003 19:08:36 -0000      1.4.2.1
***************
*** 153,156 ****
--- 153,162 ----
      others = 0;
  
+     /*
+      *        Run the external delete hook program.
+      */
+ 
+     (void)ext_hook("del-hook", maildir, (char *)0);
+ 
      j = strlen(BACKUP_PREFIX);
      while ((dp = readdir (dd))) {
***************
*** 217,224 ****
      register char *cp;
      register struct node *np, *pp;
- 
-     /* sanity check - check that context has been read */
-     if (defpath == NULL)
-       adios (NULL, "oops, context hasn't been read yet");
  
      alen = strlen ("atr-");
--- 223,226 ----

Index: rmm.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/rmm.c,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -C2 -r1.4 -r1.4.2.1
*** rmm.c       2 Jul 2002 22:09:15 -0000       1.4
--- rmm.c       28 Feb 2003 19:08:36 -0000      1.4.2.1
***************
*** 146,150 ****
  
      /* "remove" the SELECTED messages */
!     folder_delmsgs (mp, unlink_msgs);
  
      seq_save (mp);            /* synchronize message sequences  */
--- 146,150 ----
  
      /* "remove" the SELECTED messages */
!     folder_delmsgs (mp, unlink_msgs, 0);
  
      seq_save (mp);            /* synchronize message sequences  */

Index: scan.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/scan.c,v
retrieving revision 1.8.2.1
retrieving revision 1.8.2.2
diff -C2 -r1.8.2.1 -r1.8.2.2

Index: send.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/send.c,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -C2 -r1.5 -r1.5.2.1
*** send.c      2 Jul 2002 22:09:15 -0000       1.5
--- send.c      28 Feb 2003 19:08:36 -0000      1.5.2.1
***************
*** 103,106 ****
--- 103,108 ----
  #define USERSW                39
      { "user", SASLminc(-4) },
+ #define ATTACHSW              40
+     { "attach", 6 },
      { NULL, 0 }
  };
***************
*** 141,144 ****
--- 143,147 ----
      struct msgs *mp;
      struct stat st;
+     char      *attach = (char *)0;    /* header field name for attachments */
  #ifdef UCI
      FILE *fp;
***************
*** 274,277 ****
--- 277,285 ----
                    vec[vecp++] = cp;
                    continue;
+               
+               case ATTACHSW:
+                   if (!(attach = *argp++) || *attach == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
            }
        } else {
***************
*** 437,441 ****
  
      for (msgnum = 0; msgnum < msgp; msgnum++) {
!       switch (sendsbr (vec, vecp, msgs[msgnum], &st, 1)) {
            case DONE: 
                done (++status);
--- 445,449 ----
  
      for (msgnum = 0; msgnum < msgp; msgnum++) {
!       switch (sendsbr (vec, vecp, msgs[msgnum], &st, 1, attach)) {
            case DONE: 
                done (++status);

Index: sendsbr.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/sendsbr.c,v
retrieving revision 1.4
retrieving revision 1.4.2.1
diff -C2 -r1.4 -r1.4.2.1
*** sendsbr.c   2 Jul 2002 22:09:15 -0000       1.4
--- sendsbr.c   28 Feb 2003 19:08:36 -0000      1.4.2.1
***************
*** 43,50 ****
  static jmp_buf env;
  
  /*
   * external prototypes
   */
! int sendsbr (char **, int, char *, struct stat *, int);
  int done (int);
  char *getusername (void);
--- 43,58 ----
  static jmp_buf env;
  
+ static        char    body_file_name[MAXPATHLEN + 1];         /* name of 
temporary file for body content */
+ static        char    composition_file_name[MAXPATHLEN + 1];  /* name of 
mhbuild composition temporary file */
+ static        int     field_size;                             /* size of 
header field buffer */
+ static        char    *field;                                 /* header field 
buffer */
+ static        FILE    *draft_file;                            /* draft file 
pointer */
+ static        FILE    *body_file;                             /* body file 
pointer */
+ static        FILE    *composition_file;                      /* composition 
file pointer */
+ 
  /*
   * external prototypes
   */
! int sendsbr (char **, int, char *, struct stat *, int, char *);
  int done (int);
  char *getusername (void);
***************
*** 60,63 ****
--- 68,76 ----
  static int sendaux (char **, int, char *, struct stat *);
  
+ static        int     attach(char *, char *);
+ static        void    clean_up_temporary_files(void);
+ static        int     get_line(void);
+ static        void    make_mime_composition_file_entry(char *);
+ 
  
  /*
***************
*** 66,74 ****
  
  int
! sendsbr (char **vec, int vecp, char *drft, struct stat *st, int rename_drft)
  {
      int status;
      char buffer[BUFSIZ], file[BUFSIZ];
      struct stat sts;
  
      armed++;
--- 79,119 ----
  
  int
! sendsbr (char **vec, int vecp, char *drft, struct stat *st, int rename_drft, 
char *attachment_header_field_name)
  {
      int status;
      char buffer[BUFSIZ], file[BUFSIZ];
      struct stat sts;
+     char      *original_draft;                /* name of original draft file 
*/
+     char      *p;                             /* string pointer for building 
file name */
+ 
+     /*
+      *        Save the original name of the draft file.  The name of the 
draft file is changed
+      *        to a temporary file containing the built MIME message if there 
are attachments.
+      *        We need the original name so that it can be renamed after the 
message is sent.
+      */
+ 
+     original_draft = drft;
+ 
+     /*
+      *        There might be attachments if a header field name for 
attachments is supplied.
+      *        Convert the draft to a MIME message.  Use the mhbuild 
composition file for the
+      *        draft if there was a successful conversion because that now 
contains the MIME
+      *        message.  A nice side effect of this is that it leaves the 
original draft file
+      *        untouched so that it can be retrieved and modified if desired.
+      */
+ 
+     if (attachment_header_field_name != (char *)0) {
+       switch (attach(attachment_header_field_name, drft)) {
+       case OK:
+           drft = composition_file_name;
+           break;
+ 
+       case NOTOK:
+           return (NOTOK);
+ 
+       case DONE:
+           break;
+       }
+     }
  
      armed++;
***************
*** 99,103 ****
        /* rename the original draft */
        if (rename_drft && status == OK &&
!               rename (drft, strncpy (buffer, m_backup (drft), 
sizeof(buffer))) == NOTOK)
            advise (buffer, "unable to rename %s to", drft);
        break;
--- 144,148 ----
        /* rename the original draft */
        if (rename_drft && status == OK &&
!               rename (original_draft, strncpy (buffer, m_backup 
(original_draft), sizeof(buffer))) == NOTOK)
            advise (buffer, "unable to rename %s to", drft);
        break;
***************
*** 112,118 ****
--- 157,494 ----
        unlink (distfile);
  
+     /*
+      *        Get rid of any temporary files that we created for attachments. 
 Also get rid of
+      *        the renamed composition file that mhbuild leaves as a turd.  It 
looks confusing,
+      *        but we use the body file name to help build the renamed 
composition file name.
+      */
+ 
+     if (drft == composition_file_name) {
+       clean_up_temporary_files();
+ 
+       if (strlen(composition_file_name) >= sizeof (composition_file_name) - 6)
+           advise((char *)0, "unable to remove original composition file.");
+ 
+       else {
+           if ((p = strrchr(composition_file_name, '/')) == (char *)0)
+               p = composition_file_name;
+           else
+               p++;
+           
+           (void)strcpy(body_file_name, p);
+           *p++ = ',';
+           (void)strcpy(p, body_file_name);
+           (void)strcat(p, ".orig");
+           
+           (void)unlink(composition_file_name);
+       }
+     }
+ 
      return status;
  }
  
+ static        int
+ attach(char *attachment_header_field_name, char *draft_file_name)
+ {
+     char              buf[MAXPATHLEN + 6];    /* miscellaneous buffer */
+     int                       c;                      /* current character 
for body copy */
+     int                       has_attachment;         /* draft has at least 
one attachment */
+     int                       has_body;               /* draft has a message 
body */
+     int                       length;                 /* length of attachment 
header field name */
+     char              *p;                     /* miscellaneous string pointer 
*/
+ 
+     /*
+      *        Open up the draft file.
+      */
+ 
+     if ((draft_file = fopen(draft_file_name, "r")) == (FILE *)0)
+       adios((char *)0, "can't open draft file `%s'.", draft_file_name);
+ 
+     /*
+      *  Allocate a buffer to hold the header components as they're read in.
+      *  This buffer might need to be quite large, so we grow it as needed.
+      */
+ 
+     if ((field = (char *)malloc(field_size = 256)) == (char *)0)
+       adios(NULL, "can't allocate field buffer.");
+ 
+     /*
+      *        Scan the draft file for a header field name that matches the 
-attach
+      *        argument.  The existence of one indicates that the draft has 
attachments.
+      *        Bail out if there are no attachments because we're done.  Read 
to the
+      *        end of the headers even if we have no attachments.
+      */
+ 
+     length = strlen(attachment_header_field_name);
+ 
+     has_attachment = 0;
+ 
+     while (get_line() != EOF && *field != '\0' && *field != '-')
+       if (strncasecmp(field, attachment_header_field_name, length) == 0 && 
field[length] == ':')
+           has_attachment = 1;
+ 
+     if (has_attachment == 0)
+       return (DONE);
+ 
+     /*
+      *        We have at least one attachment.  Look for at least one 
non-blank line
+      *        in the body of the message which indicates content in the body.
+      */
+ 
+     has_body = 0;
+ 
+     while (get_line() != EOF) {
+       for (p = field; *p != '\0'; p++) {
+           if (*p != ' ' && *p != '\t') {
+               has_body = 1;
+               break;
+           }
+       }
+ 
+       if (has_body)
+           break;
+     }
+ 
+     /*
+      *        Make names for the temporary files.
+      */
+ 
+     (void)strncpy(body_file_name, m_scratch("", m_maildir(invo_name)), sizeof 
(body_file_name));
+     (void)strncpy(composition_file_name, m_scratch("", m_maildir(invo_name)), 
sizeof (composition_file_name));
+ 
+     if (has_body)
+       body_file = fopen(body_file_name, "w");
+ 
+     composition_file = fopen(composition_file_name, "w");
+ 
+     if ((has_body && body_file == (FILE *)0) || composition_file == (FILE 
*)0) {
+       clean_up_temporary_files();
+       adios((char *)0, "unable to open all of the temporary files.");
+     }
+ 
+     /*
+      *        Start at the beginning of the draft file.  Copy all 
non-attachment header fields
+      *        to the temporary composition file.  Then add the dashed line 
separator.
+      */
+ 
+     rewind(draft_file);
+ 
+     while (get_line() != EOF && *field != '\0' && *field != '-')
+       if (strncasecmp(field, attachment_header_field_name, length) != 0 || 
field[length] != ':')
+           (void)fprintf(composition_file, "%s\n", field);
+ 
+     (void)fputs("--------\n", composition_file);
+ 
+     /*
+      *        Copy the message body to a temporary file.
+      */
+ 
+     if (has_body) {
+       while ((c = getc(draft_file)) != EOF)
+               putc(c, body_file);
+ 
+       (void)fclose(body_file);
+     }
+ 
+     /*
+      *        Add a mhbuild MIME composition file line for the body if there 
was one.
+      */
+ 
+     if (has_body)
+       make_mime_composition_file_entry(body_file_name);
+ 
+     /*
+      *        Now, go back to the beginning of the draft file and look for 
header fields
+      *        that specify attachments.  Add a mhbuild MIME composition file 
for each.
+      */
+ 
+     rewind(draft_file);
+ 
+     while (get_line() != EOF && *field != '\0' && *field != '-') {
+       if (strncasecmp(field, attachment_header_field_name, length) == 0 && 
field[length] == ':') {
+           for (p = field + length + 1; *p == ' ' || *p == '\t'; p++)
+               ;
+ 
+           make_mime_composition_file_entry(p);
+       }
+     }
+ 
+     (void)fclose(composition_file);
+ 
+     /*
+      *        We're ready to roll!  Run mhbuild on the composition file.  
Note that mhbuild
+      *        is in the context as buildmimeproc.
+      */
+ 
+     (void)sprintf(buf, "%s %s", buildmimeproc, composition_file_name);
+ 
+     if (system(buf) != 0) {
+       clean_up_temporary_files();
+       return (NOTOK);
+     }
+ 
+     return (OK);
+ }
+ 
+ static        void
+ clean_up_temporary_files(void)
+ {
+     (void)unlink(body_file_name);
+     (void)unlink(composition_file_name);
+ 
+     return;
+ }
+ 
+ static        int
+ get_line(void)
+ {
+     int               c;      /* current character */
+     int               n;      /* number of bytes in buffer */
+     char      *p;     /* buffer pointer */
+ 
+     /*
+      *        Get a line from the input file, growing the field buffer as 
needed.  We do this
+      *        so that we can fit an entire line in the buffer making it easy 
to do a string
+      *        comparison on both the field name and the field body which 
might be a long path
+      *        name.
+      */
+ 
+     for (n = 0, p = field; (c = getc(draft_file)) != EOF; *p++ = c) {
+       if (c == '\n' && (c = getc(draft_file)) != ' ' && c != '\t') {
+           (void)ungetc(c, draft_file);
+           c = '\n';
+           break;
+       }
+ 
+       if (++n >= field_size - 1) {
+           if ((field = (char *)realloc((void *)field, field_size += 256)) == 
(char *)0)
+               adios(NULL, "can't grow field buffer.");
+ 
+           p = field + n - 1;
+       }
+     }
+ 
+     /*
+      *        NUL-terminate the field..
+      */
+ 
+     *p = '\0';
+ 
+     return (c);
+ }
+ 
+ static        void
+ make_mime_composition_file_entry(char *file_name)
+ {
+     int                       binary;                 /* binary character 
found flag */
+     int                       c;                      /* current character */
+     char              cmd[MAXPATHLEN + 6];    /* file command buffer */
+     char              *content_type;          /* mime content type */
+     FILE              *fp;                    /* content and pipe file 
pointer */
+     struct    node    *np;                    /* context scan node pointer */
+     char              *p;                     /* miscellaneous string pointer 
*/
+     struct    stat    st;                     /* file status buffer */
+ 
+     content_type = (char *)0;
+ 
+     /*
+      *        Check the file name for a suffix.  Scan the context for that 
suffix on a
+      *        mhshow-suffix- entry.  We use these entries to be compatible 
with mhnshow,
+      *        and there's no reason to make the user specify each suffix 
twice.  Context
+      *        entries of the form "mhshow-suffix-contenttype" in the name 
have the suffix
+      *        in the field, including the dot.
+      */
+ 
+     if ((p = strrchr(file_name, '.')) != (char *)0) {
+       for (np = m_defs; np; np = np->n_next) {
+           if (strncasecmp(np->n_name, "mhshow-suffix-", 14) == 0 && 
strcasecmp(p, np->n_field) == 0) {
+               content_type = np->n_name + 14;
+               break;
+           }
+       }
+     }
+ 
+     /*
+      *        No content type was found, either because there was no matching 
entry in the
+      *        context or because the file name has no suffix.  Open the file 
and check for
+      *        non-ASCII characters.  Choose the content type based on this 
check.
+      */
+ 
+     if (content_type == (char *)0) {
+       if ((fp = fopen(file_name, "r")) == (FILE *)0) {
+           clean_up_temporary_files();
+           adios((char *)0, "unable to access file \"%s\"", file_name);
+       }
+ 
+       binary = 0;
+ 
+       while ((c = getc(fp)) != EOF) {
+           if (c > 127 || c < 0) {
+               binary = 1;
+               break;
+           }
+       }
+ 
+       (void)fclose(fp);
+ 
+       content_type = binary ? "application/octet-stream" : "text/plain";
+     }
+ 
+     /*
+      *        Make sure that the attachment file exists and is readable.  
Append a mhbuild
+      *        directive to the draft file.  This starts with the content 
type.  Append a
+      *        file name attribute and a private x-unix-mode attribute.  Also 
append a
+      *        description obtained (if possible) by running the "file" 
command on the file.
+      */
+ 
+     if (stat(file_name, &st) == -1 || access(file_name, R_OK) != 0) {
+       clean_up_temporary_files();
+       adios((char *)0, "unable to access file \"%s\"", file_name);
+     }
+ 
+     (void)fprintf(composition_file, "#%s; name=\"%s\"; x-unix-mode=0%.3ho",
+      content_type, ((p = strrchr(file_name, '/')) == (char *)0) ? file_name : 
p + 1, (unsigned short)(st.st_mode & 0777));
+ 
+     if (strlen(file_name) > MAXPATHLEN) {
+       clean_up_temporary_files();
+       adios((char *)0, "attachment file name `%s' too long.", file_name);
+     }
+ 
+     (void)sprintf(cmd, "file '%s'", file_name);
+ 
+     if ((fp = popen(cmd, "r")) != (FILE *)0 && fgets(cmd, sizeof (cmd), fp) 
!= (char *)0) {
+       *strchr(cmd, '\n') = '\0';
+ 
+       /*
+        *  The output of the "file" command is of the form
+        *
+        *      file:   description
+        *
+        *  Strip off the "file:" and subsequent white space.
+        */
+ 
+       for (p = cmd; *p != '\0'; p++) {
+           if (*p == ':') {
+               for (p++; *p != '\0'; p++) {
+                   if (*p != '\t')
+                       break;
+               }
+               break;
+           }
+       }
+ 
+       if (*p != '\0')
+           (void)fprintf(composition_file, " [ %s ]", p);
+ 
+       (void)pclose(fp);
+     }
+ 
+     /*
+      *        Finish up with the file name.
+      */
+ 
+     (void)fprintf(composition_file, " %s\n", file_name);
+ 
+     return;
+ }
  
  /*
***************
*** 645,649 ****
            if (debugsw)
                advise (NULL, "annotate message %d", msgnum);
!           annotate (m_name (msgnum), annotext, cp, inplace, 1);
        }
      }
--- 1021,1025 ----
            if (debugsw)
                advise (NULL, "annotate message %d", msgnum);
!           annotate (m_name (msgnum), annotext, cp, inplace, 1, 0, 0);
        }
      }

Index: sortm.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/sortm.c,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -C2 -r1.5 -r1.5.2.1
*** sortm.c     2 Jul 2002 22:09:15 -0000       1.5
--- sortm.c     28 Feb 2003 19:08:36 -0000      1.5.2.1
***************
*** 233,237 ****
      if (verbose) {    /* announce what we're doing */
        if (subjsort)
!           printf ("sorting by %s-major %s-minor\n", 
                submajor ? subjsort : datesw,
                submajor ? datesw : subjsort);
--- 233,237 ----
      if (verbose) {    /* announce what we're doing */
        if (subjsort)
!           printf ("sorting by %s-major %s-minor\n",
                submajor ? subjsort : datesw,
                submajor ? datesw : subjsort);
***************
*** 241,245 ****
  
      /* first sort by date, or by subject-major, date-minor */
!     qsort ((char *) dlist, nmsgs, sizeof(*dlist), 
            (qsort_comp) (submajor && subjsort ? txtsort : dsort));
  
--- 241,245 ----
  
      /* first sort by date, or by subject-major, date-minor */
!     qsort ((char *) dlist, nmsgs, sizeof(*dlist),
            (qsort_comp) (submajor && subjsort ? txtsort : dsort));
  
***************
*** 293,297 ****
            while (*s && (*s)->s_subj[0] &&
                   strcmp((*s)->s_subj, s[-1]->s_subj) == 0 &&
!                  (datelimit == 0 || 
                   (*s)->s_clock - s[-1]->s_clock <= datelimit)) {
                il[(*s)->s_msg] = 0;
--- 293,297 ----
            while (*s && (*s)->s_subj[0] &&
                   strcmp((*s)->s_subj, s[-1]->s_subj) == 0 &&
!                  (datelimit == 0 ||
                   (*s)->s_clock - s[-1]->s_clock <= datelimit)) {
                il[(*s)->s_msg] = 0;
***************
*** 304,307 ****
--- 304,313 ----
        dlist = flist;
      }
+ 
+     /*
+      * At this point, dlist is a sorted array of pointers to smsg structures,
+      * each of which contains a message number.
+      */
+ 
      rename_msgs (mp, dlist);
  
***************
*** 313,317 ****
  }
  
! static int 
  read_hdrs (struct msgs *mp, char *datesw)
  {
--- 319,323 ----
  }
  
! static int
  read_hdrs (struct msgs *mp, char *datesw)
  {
***************
*** 432,436 ****
             * try to make the subject "canonical": delete
             * leading "re:", everything but letters & smash
!            * letters to lower case. 
             */
            register char  *cp, *cp2, c;
--- 438,442 ----
             * try to make the subject "canonical": delete
             * leading "re:", everything but letters & smash
!            * letters to lower case.
             */
            register char  *cp, *cp2, c;
***************
*** 473,477 ****
   * sort on dates.
   */
! static int 
  dsort (struct smsg **a, struct smsg **b)
  {
--- 479,483 ----
   * sort on dates.
   */
! static int
  dsort (struct smsg **a, struct smsg **b)
  {
***************
*** 489,493 ****
   * sort on subjects.
   */
! static int 
  subsort (struct smsg **a, struct smsg **b)
  {
--- 495,499 ----
   * sort on subjects.
   */
! static int
  subsort (struct smsg **a, struct smsg **b)
  {
***************
*** 500,504 ****
  }
  
! static int 
  txtsort (struct smsg **a, struct smsg **b)
  {
--- 506,510 ----
  }
  
! static int
  txtsort (struct smsg **a, struct smsg **b)
  {
***************
*** 518,521 ****
--- 524,528 ----
      int nxt, old, new;
      char *newname, oldname[BUFSIZ];
+     char newbuf[MAXPATHLEN + 1];
  
      for (;;) {
***************
*** 532,540 ****
            adios (newname, "unable to rename %s to", oldname);
  
        copy_msg_flags (mp, new, old);
        if (mp->curmsg == old)
            seq_setcur (mp, new);
  
!       if (nxt == endmsg) 
            break;
  
--- 539,551 ----
            adios (newname, "unable to rename %s to", oldname);
  
+       (void)snprintf(oldname, sizeof (oldname), "%s/%d", mp->foldpath, old);
+       (void)snprintf(newbuf, sizeof (newbuf), "%s/%d", mp->foldpath, new);
+       ext_hook("ref-hook", oldname, newbuf);
+ 
        copy_msg_flags (mp, new, old);
        if (mp->curmsg == old)
            seq_setcur (mp, new);
  
!       if (nxt == endmsg)
            break;
  
***************
*** 551,554 ****
--- 562,566 ----
      seqset_t tmpset;
      char f1[BUFSIZ], tmpfil[BUFSIZ];
+     char newbuf[MAXPATHLEN + 1];
      struct smsg *sp;
  
***************
*** 556,560 ****
  
      for (i = 0; i < nmsgs; i++) {
!       if (! (sp = mlist[i])) 
            continue;   /* did this one */
  
--- 568,572 ----
  
      for (i = 0; i < nmsgs; i++) {
!       if (! (sp = mlist[i]))
            continue;   /* did this one */
  
***************
*** 577,580 ****
--- 589,603 ----
        if (rename (f1, tmpfil) == NOTOK)
            adios (tmpfil, "unable to rename %s to ", f1);
+ 
+       /*
+        *      Run the external hook to refile the old message as message
+        *      number 2147483647.  This is our way of making a temporary
+        *      message number.  I don't really like this.
+        */
+ 
+       (void)snprintf(f1, sizeof (f1), "%s/%d", mp->foldpath, old);
+       (void)snprintf(newbuf, sizeof (newbuf), "%s/2147483647", mp->foldpath);
+       ext_hook("ref-hook", f1, newbuf);
+ 
        get_msg_flags (mp, &tmpset, old);
  
***************
*** 582,585 ****
--- 605,616 ----
        if (rename (tmpfil, m_name(new)) == NOTOK)
            adios (m_name(new), "unable to rename %s to", tmpfil);
+ 
+       /*
+        *      Run the external hook to refile the temorary message number
+        *      to the real place.
+        */
+ 
+       (void)snprintf(f1, sizeof (f1), "%s/%d", mp->foldpath, new);
+       ext_hook("ref-hook", newbuf, f1);
  
        set_msg_flags (mp, &tmpset, new);

Index: viamail.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/viamail.c,v
retrieving revision 1.7
retrieving revision 1.7.2.1
diff -C2 -r1.7 -r1.7.2.1
*** viamail.c   2 Jul 2002 22:09:15 -0000       1.7
--- viamail.c   28 Feb 2003 19:08:36 -0000      1.7.2.1
***************
*** 238,242 ****
        vec[vecp++] = "-verbose";
  
!     switch (sendsbr (vec, vecp, tmpfil, &st, 0)) {
        case DONE:
        case NOTOK:
--- 238,242 ----
        vec[vecp++] = "-verbose";
  
!     switch (sendsbr (vec, vecp, tmpfil, &st, 0, (char *)0)) {
        case DONE:
        case NOTOK:

Index: whatnowsbr.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/whatnowsbr.c,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -C2 -r1.5 -r1.5.2.1
*** whatnowsbr.c        2 Jul 2002 22:09:15 -0000       1.5
--- whatnowsbr.c        28 Feb 2003 19:08:36 -0000      1.5.2.1
***************
*** 8,11 ****
--- 8,42 ----
   * COPYRIGHT file in the root directory of the nmh distribution for
   * complete copyright information.
+  *
+  *  Several options have been added to ease the inclusion of attachments
+  *  using the header field name mechanism added to anno and send.  The
+  *  -attach option is used to specify the header field name for attachments.
+  *
+  *  Several commands have been added at the whatnow prompt:
+  *
+  *    cd [ directory ]        This option works just like the shell's
+  *                            cd command and lets the user change the
+  *                            directory from which attachments are
+  *                            taken so that long path names are not
+  *                            needed with every file.
+  *
+  *    ls [ ls-options ]       This option works just like the normal
+  *                            ls command and exists to allow the user
+  *                            to verify file names in the directory.
+  *
+  *    pwd                     This option works just like the normal
+  *                            pwd command and exists to allow the user
+  *                            to verify the directory.
+  *
+  *    attach files            This option attaches the named files to
+  *                            the draft.
+  *
+  *    alist [-ln]             This option lists the attachments on the
+  *                            draft.  -l gets long listings, -n gets
+  *                            numbered listings.
+  *
+  *    detach files            This option removes attachments from the
+  *    detach -n numbers       draft.  This can be done by file name or
+  *                            by attachment number.
   */
  
***************
*** 32,35 ****
--- 63,68 ----
  #define       HELPSW                  7
      { "help", 0 },
+ #define       ATTACHSW                8
+     { "attach header-field-name", 0 },
      { NULL, 0 }
  };
***************
*** 59,62 ****
--- 92,107 ----
  #define DELETESW                       9
      { "delete", 0 },
+ #define       CDCMDSW                       10
+     { "cd [directory]", 0 },
+ #define       PWDCMDSW                      11
+     { "pwd", 0 },
+ #define       LSCMDSW                       12
+     { "ls", 0 },
+ #define       ATTACHCMDSW                   13
+     { "attach", 0 },
+ #define       DETACHCMDSW                   14
+     { "detach [-n]", 2 },
+ #define       ALISTCMDSW                    15
+     { "alist [-ln] ", 2 },
      { NULL, 0 }
  };
***************
*** 90,93 ****
--- 135,145 ----
      char **argp, **arguments;
      struct stat st;
+     char      *attach = (char *)0;    /* attachment header field name */
+     char      cwd[MAXPATHLEN + 1];    /* current working directory */
+     char      file[MAXPATHLEN + 1];   /* file name buffer */
+     char      shell[MAXPATHLEN + 1];  /* shell response buffer */
+     FILE      *f;                     /* read pointer for bgnd proc */
+     char      *l;                     /* set on -l to alist  command */
+     int               n;                      /* set on -n to alist command */
  
      invo_name = r1bindex (argv[0], '/');
***************
*** 99,112 ****
      argp = arguments;
  
      while ((cp = *argp++)) {
        if (*cp == '-') {
            switch (smatch (++cp, whatnowswitches)) {
!           case AMBIGSW: 
                ambigsw (cp, whatnowswitches);
                done (1);
!           case UNKWNSW: 
                adios (NULL, "-%s unknown", cp);
  
!           case HELPSW: 
                snprintf (buf, sizeof(buf), "%s [switches] [file]", invo_name);
                print_help (buf, whatnowswitches, 1);
--- 151,172 ----
      argp = arguments;
  
+     /*
+      *        Get the initial current working directory.
+      */
+ 
+     if (getcwd(cwd, sizeof (cwd)) == (char *)0) {
+       adios("getcwd", "could not get working directory");
+     }
+ 
      while ((cp = *argp++)) {
        if (*cp == '-') {
            switch (smatch (++cp, whatnowswitches)) {
!           case AMBIGSW:
                ambigsw (cp, whatnowswitches);
                done (1);
!           case UNKWNSW:
                adios (NULL, "-%s unknown", cp);
  
!           case HELPSW:
                snprintf (buf, sizeof(buf), "%s [switches] [file]", invo_name);
                print_help (buf, whatnowswitches, 1);
***************
*** 116,120 ****
                done (1);
  
!           case DFOLDSW: 
                if (dfolder)
                    adios (NULL, "only one draft folder at a time!");
--- 176,180 ----
                done (1);
  
!           case DFOLDSW:
                if (dfolder)
                    adios (NULL, "only one draft folder at a time!");
***************
*** 124,128 ****
                                *cp != '@' ? TFOLDER : TSUBCWF);
                continue;
!           case DMSGSW: 
                if (dmsg)
                    adios (NULL, "only one draft message at a time!");
--- 184,188 ----
                                *cp != '@' ? TFOLDER : TSUBCWF);
                continue;
!           case DMSGSW:
                if (dmsg)
                    adios (NULL, "only one draft message at a time!");
***************
*** 130,144 ****
                    adios (NULL, "missing argument to %s", argp[-2]);
                continue;
!           case NDFLDSW: 
                dfolder = NULL;
                isdf = NOTOK;
                continue;
  
!           case EDITRSW: 
                if (!(ed = *argp++) || *ed == '-')
                    adios (NULL, "missing argument to %s", argp[-2]);
                nedit = 0;
                continue;
!           case NEDITSW: 
                nedit++;
                continue;
--- 190,204 ----
                    adios (NULL, "missing argument to %s", argp[-2]);
                continue;
!           case NDFLDSW:
                dfolder = NULL;
                isdf = NOTOK;
                continue;
  
!           case EDITRSW:
                if (!(ed = *argp++) || *ed == '-')
                    adios (NULL, "missing argument to %s", argp[-2]);
                nedit = 0;
                continue;
!           case NEDITSW:
                nedit++;
                continue;
***************
*** 148,151 ****
--- 208,218 ----
                    adios (NULL, "missing argument to %s", argp[-2]);
                continue;
+ 
+           case ATTACHSW:
+               if (attach != (char *)0)
+                   adios(NULL, "only one attachment header field name at a 
time!");
+               if (!(attach = *argp++) || *attach == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
            }
        }
***************
*** 201,205 ****
            break;
  
!       case LISTSW: 
            /* display the draft file */
            showfile (++argp, drft);
--- 268,272 ----
            break;
  
!       case LISTSW:
            /* display the draft file */
            showfile (++argp, drft);
***************
*** 233,242 ****
            break;
  
!       case SENDSW: 
            /* Send draft */
            sendfile (++argp, drft, 0);
            break;
  
!       case REFILEOPT: 
            /* Refile the draft */
            if (refile (++argp, drft) == 0)
--- 300,309 ----
            break;
  
!       case SENDSW:
            /* Send draft */
            sendfile (++argp, drft, 0);
            break;
  
!       case REFILEOPT:
            /* Refile the draft */
            if (refile (++argp, drft) == 0)
***************
*** 244,248 ****
            break;
  
!       default: 
            /* Unknown command */
            advise (NULL, "say what?");
--- 311,555 ----
            break;
  
!       case CDCMDSW:
!           /* Change the working directory for attachments
!            *
!            *  Run the directory through the user's shell so that
!            *  we can take advantage of any syntax that the user
!            *  is accustomed to.  Read back the absolute path.
!            */
! 
!           if (*++argp == (char *)0) {
!               (void)sprintf(buf, "$SHELL -c \"cd;pwd\"");
!           }
!           else if (strlen(*argp) >= BUFSIZ) {
!               adios((char *)0, "arguments too long");
!           }
!           else {
!               (void)sprintf(buf, "$SHELL -c \"cd %s;cd %s;pwd\"", cwd, *argp);
!           }
!           if ((f = popen(buf, "r")) != (FILE *)0) {
!               fgets(cwd, sizeof (cwd), f);
! 
!               if (strchr(cwd, '\n') != (char *)0)
!                       *strchr(cwd, '\n') = '\0';
! 
!               pclose(f);
!           }
!           else {
!               advise("popen", "could not get directory");
!           }
! 
!           break;
! 
!       case PWDCMDSW:
!           /* Print the working directory for attachments */
!           printf("%s\n", cwd);
!           break;
! 
!       case LSCMDSW:
!           /* List files in the current attachment working directory
!            *
!            *  Use the user's shell so that we can take advantage of any
!            *  syntax that the user is accustomed to.
!            */
! 
!           cp = buf + sprintf(buf, "$SHELL -c \" cd %s;ls", cwd);
! 
!           while (*++argp != (char *)0) {
!               if (cp + strlen(*argp) + 2 >= buf + BUFSIZ)
!                   adios((char *)0, "arguments too long");
! 
!               cp += sprintf(cp, " %s", *argp);
!           }
! 
!           (void)strcat(cp, "\"");
!           (void)system(buf);
!           break;
! 
!       case ALISTCMDSW:
!           /*
!            *  List attachments on current draft.  Options are:
!            *
!            *   -l     long listing (full path names)
!            *   -n     numbers listing
!            */
! 
!           if (attach == (char *)0) {
!               advise((char *)0, "can't list because no header field name was 
given.");
!               break;
!           }
! 
!           l = (char *)0;
!           n = 0;
! 
!           while (*++argp != (char *)0) {
!               if (strcmp(*argp, "-l") == 0)
!                   l = "/";
! 
!               else if (strcmp(*argp, "-n") == 0)
!                   n = 1;
! 
!               else if (strcmp(*argp, "-ln") == 0 || strcmp(*argp, "-nl") == 
0) {
!                   l = "/";
!                   n = 1;
!               }
! 
!               else {
!                   n = -1;
!                   break;
!               }
!           }
! 
!           if (n == -1)
!               advise((char *)0, "usage is alist [-ln].");
! 
!           else
!               annolist(drft, attach, l, n);
! 
!           break;
! 
!       case ATTACHCMDSW:
!           /*
!            *  Attach files to current draft.
!            */
! 
!           if (attach == (char *)0) {
!               advise((char *)0, "can't attach because no header field name 
was given.");
!               break;
!           }
! 
!           /*
!            *  Build a command line that causes the user's shell to list the 
file name
!            *  arguments.  This handles and wildcard expansion, tilde 
expansion, etc.
!            */
! 
!           cp = buf + sprintf(buf, "$SHELL -c \" cd %s;ls", cwd);
! 
!           while (*++argp != (char *)0) {
!               if (cp + strlen(*argp) + 3 >= buf + BUFSIZ)
!                   adios((char *)0, "arguments too long");
! 
!               cp += sprintf(cp, " %s", *argp);
!           }
! 
!           (void)strcat(cp, "\"");
! 
!           /*
!            *  Read back the response from the shell, which contains a number 
of lines
!            *  with one file name per line.  Remove off the newline.  
Determine whether
!            *  we have an absolute or relative path name.  Prepend the current 
working
!            *  directory to relative path names.  Add the attachment 
annotation to the
!            *  draft.
!            */
! 
!           if ((f = popen(buf, "r")) != (FILE *)0) {
!               while (fgets(shell, sizeof (shell), f) != (char *)0) {
!                   *(strchr(shell, '\n')) = '\0';
! 
!                   if (*shell == '/')
!                       (void)annotate(drft, attach, shell, 1, 0, -1, 1);
!                   else {
!                       (void)sprintf(file, "%s/%s", cwd, shell);
!                       (void)annotate(drft, attach, file, 1, 0, -1, 1);
!                   }
!               }
! 
!               pclose(f);
!           }
!           else {
!               advise("popen", "could not get file from shell");
!           }
! 
!           break;
! 
!       case DETACHCMDSW:
!           /*
!            *  Detach files from current draft.
!            */
! 
!           if (attach == (char *)0) {
!               advise((char *)0, "can't detach because no header field name 
was given.");
!               break;
!           }
! 
!           /*
!            *  Scan the arguments for a -n.  Mixed file names and numbers 
aren't allowed,
!            *  so this catches a -n anywhere in the argument list.
!            */
! 
!           for (n = 0, arguments = argp + 1; *arguments != (char *)0; 
arguments++) {
!               if (strcmp(*arguments, "-n") == 0) {
!                       n = 1;
!                       break;
!               }
!           }
! 
!           /*
!            *  A -n was found so interpret the arguments as attachment numbers.
!            *  Decrement any remaining argument number that is greater than 
the one
!            *  just processed after processing each one so that the numbering 
stays
!            *  correct.
!            */
! 
!           if (n == 1) {
!               for (arguments = argp + 1; *arguments != (char *)0; 
arguments++) {
!                   if (strcmp(*arguments, "-n") == 0)
!                       continue;
! 
!                   if (**arguments != '\0') {
!                       n = atoi(*arguments);
!                       (void)annotate(drft, attach, (char *)0, 1, 0, n, 1);
! 
!                       for (argp = arguments + 1; *argp != (char *)0; argp++) {
!                           if (atoi(*argp) > n) {
!                               if (atoi(*argp) == 1)
!                                   *argp = "";
!                               else
!                                   (void)sprintf(*argp, "%d", atoi(*argp) - 1);
!                           }
!                       }
!                   }
!               }
!           }
! 
!           /*
!            *  The arguments are interpreted as file names.  Run them through 
the
!            *  user's shell for wildcard expansion and other goodies.  Do this 
from
!            *  the current working directory if the argument is not an 
absolute path
!            *  name (does not begin with a /).
!            */
! 
!           else {
!               for (arguments = argp + 1; *arguments != (char *)0; 
arguments++) {
!                   if (**arguments == '/') {
!                       if (strlen(*arguments) + sizeof ("$SHELL -c \"ls \"") 
>= sizeof (buf))
!                           adios((char *)0, "arguments too long");
! 
!                       (void)sprintf(buf, "$SHELL -c \"ls %s\"", *arguments);
!                   }
!                   else {
!                       if (strlen(cwd) + strlen(*arguments) + sizeof ("$SHELL 
-c \" cd ; ls \"") >= sizeof (buf))
!                           adios((char *)0, "arguments too long");
! 
!                       (void)sprintf(buf, "$SHELL -c \" cd %s;ls %s\"", cwd, 
*arguments);
!                   }
! 
!                   if ((f = popen(buf, "r")) != (FILE *)0) {
!                       while (fgets(shell, sizeof (cwd), f) != (char *)0) {
!                           *(strchr(shell, '\n')) = '\0';
!                           (void)annotate(drft, attach, shell, 1, 0, 0, 1);
!                       }
! 
!                       pclose(f);
!                   }
!                   else {
!                       advise("popen", "could not get file from shell");
!                   }
!               }
!           }
! 
!           break;
! 
!       default:
            /* Unknown command */
            advise (NULL, "say what?");
***************
*** 331,340 ****
  
      switch (pid = vfork ()) {
!       case NOTOK: 
            advise ("fork", "unable to");
            status = NOTOK;
            break;
  
!       case OK: 
            if (cwd)
                chdir (cwd);
--- 638,647 ----
  
      switch (pid = vfork ()) {
!       case NOTOK:
            advise ("fork", "unable to");
            status = NOTOK;
            break;
  
!       case OK:
            if (cwd)
                chdir (cwd);
***************
*** 358,362 ****
            _exit (-1);
  
!       default: 
            if ((status = pidwait (pid, NOTOK))) {
  #ifdef ATTVIBUG
--- 665,669 ----
            _exit (-1);
  
!       default:
            if ((status = pidwait (pid, NOTOK))) {
  #ifdef ATTVIBUG
***************
*** 493,499 ****
        sleep (5);
      switch (child_id) {
!       case NOTOK: 
            advise (NULL, "unable to fork, so sending directly...");
!       case OK: 
            vecp = 0;
            vec[vecp++] = invo_name;
--- 800,806 ----
        sleep (5);
      switch (child_id) {
!       case NOTOK:
            advise (NULL, "unable to fork, so sending directly...");
!       case OK:
            vecp = 0;
            vec[vecp++] = invo_name;
***************
*** 511,515 ****
            _exit (-1);
  
!       default: 
            if (pidwait(child_id, OK) == 0)
                done (0);
--- 818,822 ----
            _exit (-1);
  
!       default:
            if (pidwait(child_id, OK) == 0)
                done (0);
***************
*** 697,700 ****
--- 1004,1009 ----
  #define USERSW           38
      { "user", SASLminc(-4) },
+ #define SNDATTACHSW       39
+     { "attach file", 6 },
      { NULL, 0 }
  };
***************
*** 721,724 ****
--- 1030,1034 ----
      char **arguments, *vec[MAXARGS];
      struct stat st;
+     char      *attach = (char *)0;    /* attachment header field name */
  
  #ifndef       lint
***************
*** 778,789 ****
        if (*cp == '-') {
            switch (smatch (++cp, sendswitches)) {
!               case AMBIGSW: 
                    ambigsw (cp, sendswitches);
                    return;
!               case UNKWNSW: 
                    advise (NULL, "-%s unknown\n", cp);
                    return;
  
!               case SHELPSW: 
                    snprintf (buf, sizeof(buf), "%s [switches]", sp);
                    print_help (buf, sendswitches, 1);
--- 1088,1099 ----
        if (*cp == '-') {
            switch (smatch (++cp, sendswitches)) {
!               case AMBIGSW:
                    ambigsw (cp, sendswitches);
                    return;
!               case UNKWNSW:
                    advise (NULL, "-%s unknown\n", cp);
                    return;
  
!               case SHELPSW:
                    snprintf (buf, sizeof(buf), "%s [switches]", sp);
                    print_help (buf, sendswitches, 1);
***************
*** 793,804 ****
                    return;
  
!               case SPSHSW: 
                    pushed++;
                    continue;
!               case NSPSHSW: 
                    pushed = 0;
                    continue;
  
!               case SPLITSW: 
                    if (!(cp = *argp++) || sscanf (cp, "%d", &splitsw) != 1) {
                        advise (NULL, "missing argument to %s", argp[-2]);
--- 1103,1114 ----
                    return;
  
!               case SPSHSW:
                    pushed++;
                    continue;
!               case NSPSHSW:
                    pushed = 0;
                    continue;
  
!               case SPLITSW:
                    if (!(cp = *argp++) || sscanf (cp, "%d", &splitsw) != 1) {
                        advise (NULL, "missing argument to %s", argp[-2]);
***************
*** 807,824 ****
                    continue;
  
!               case UNIQSW: 
                    unique++;
                    continue;
!               case NUNIQSW: 
                    unique = 0;
                    continue;
!               case FORWSW: 
                    forwsw++;
                    continue;
!               case NFORWSW: 
                    forwsw = 0;
                    continue;
  
!               case VERBSW: 
                    verbsw++;
                    vec[vecp++] = --cp;
--- 1117,1134 ----
                    continue;
  
!               case UNIQSW:
                    unique++;
                    continue;
!               case NUNIQSW:
                    unique = 0;
                    continue;
!               case FORWSW:
                    forwsw++;
                    continue;
!               case NFORWSW:
                    forwsw = 0;
                    continue;
  
!               case VERBSW:
                    verbsw++;
                    vec[vecp++] = --cp;
***************
*** 829,859 ****
                    continue;
  
!               case DEBUGSW: 
                    debugsw++;  /* fall */
!               case NFILTSW: 
!               case FRMTSW: 
!               case NFRMTSW: 
                case BITSTUFFSW:
                case NBITSTUFFSW:
!               case MIMESW: 
!               case NMIMESW: 
!               case MSGDSW: 
!               case NMSGDSW: 
!               case WATCSW: 
!               case NWATCSW: 
!               case MAILSW: 
!               case SAMLSW: 
!               case SSNDSW: 
!               case SOMLSW: 
!               case SNOOPSW: 
                case SASLSW:
                    vec[vecp++] = --cp;
                    continue;
  
!               case ALIASW: 
!               case FILTSW: 
!               case WIDTHSW: 
!               case CLIESW: 
!               case SERVSW: 
                case SASLMECHSW:
                case USERSW:
--- 1139,1169 ----
                    continue;
  
!               case DEBUGSW:
                    debugsw++;  /* fall */
!               case NFILTSW:
!               case FRMTSW:
!               case NFRMTSW:
                case BITSTUFFSW:
                case NBITSTUFFSW:
!               case MIMESW:
!               case NMIMESW:
!               case MSGDSW:
!               case NMSGDSW:
!               case WATCSW:
!               case NWATCSW:
!               case MAILSW:
!               case SAMLSW:
!               case SSNDSW:
!               case SOMLSW:
!               case SNOOPSW:
                case SASLSW:
                    vec[vecp++] = --cp;
                    continue;
  
!               case ALIASW:
!               case FILTSW:
!               case WIDTHSW:
!               case CLIESW:
!               case SERVSW:
                case SASLMECHSW:
                case USERSW:
***************
*** 866,876 ****
                    continue;
  
!               case SDRFSW: 
!               case SDRMSW: 
                    if (!(cp = *argp++) || *cp == '-') {
                        advise (NULL, "missing argument to %s", argp[-2]);
                        return;
                    }
!               case SNDRFSW: 
                    continue;
            }
--- 1176,1193 ----
                    continue;
  
!               case SDRFSW:
!               case SDRMSW:
                    if (!(cp = *argp++) || *cp == '-') {
                        advise (NULL, "missing argument to %s", argp[-2]);
                        return;
                    }
!               case SNDRFSW:
!                   continue;
! 
!               case SNDATTACHSW:
!                   if (!(attach = *argp++) || *attach == '-') {
!                       advise (NULL, "missing argument to %s", argp[-2]);
!                       return;
!                   }
                    continue;
            }
***************
*** 939,943 ****
      closefds (3);
  
!     if (sendsbr (vec, vecp, file, &st, 1) == OK)
        done (0);
  }
--- 1256,1260 ----
      closefds (3);
  
!     if (sendsbr (vec, vecp, file, &st, 1, attach) == OK)
        done (0);
  }
***************
*** 958,966 ****
  
      switch (pid = vfork ()) {
!       case NOTOK: 
            advise ("fork", "unable to");
            return 1;
  
!       case OK: 
            vecp = 0;
            vec[vecp++] = r1bindex (whomproc, '/');
--- 1275,1283 ----
  
      switch (pid = vfork ()) {
!       case NOTOK:
            advise ("fork", "unable to");
            return 1;
  
!       case OK:
            vecp = 0;
            vec[vecp++] = r1bindex (whomproc, '/');
***************
*** 976,980 ****
            _exit (-1);         /* NOTREACHED */
  
!       default: 
            return (pidwait (pid, NOTOK) & 0377 ? 1 : 0);
      }
--- 1293,1297 ----
            _exit (-1);         /* NOTREACHED */
  
!       default:
            return (pidwait (pid, NOTOK) & 0377 ? 1 : 0);
      }





reply via email to

[Prev in Thread] Current Thread [Next in Thread]