nmh-commits
[Top][All Lists]
Advanced

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

[Nmh-commits] CVS: nmh/uip anno.c,1.4,1.5 annosbr.c,1.3,1.4 send.c,1.5,1


From: Jon Steinhart <address@hidden>
Subject: [Nmh-commits] CVS: nmh/uip anno.c,1.4,1.5 annosbr.c,1.3,1.4 send.c,1.5,1.6 sendsbr.c,1.4,1.5 viamail.c,1.7,1.8 whatnowsbr.c,1.5,1.6
Date: Mon, 19 Aug 2002 16:50:45 -0400

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

Modified Files:
        anno.c annosbr.c send.c sendsbr.c viamail.c whatnowsbr.c 
Log Message:
Added an improved user interface for sending messages with attachments.


Index: anno.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/anno.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -r1.4 -r1.5
*** anno.c      2 Jul 2002 22:09:14 -0000       1.4
--- anno.c      19 Aug 2002 20:50:42 -0000      1.5
***************
*** 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.4
diff -C2 -r1.3 -r1.4
*** annosbr.c   2 Jul 2002 22:09:14 -0000       1.3
--- annosbr.c   19 Aug 2002 20:50:42 -0000      1.4
***************
*** 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: send.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/send.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -r1.5 -r1.6
*** send.c      2 Jul 2002 22:09:15 -0000       1.5
--- send.c      19 Aug 2002 20:50:42 -0000      1.6
***************
*** 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.5
diff -C2 -r1.4 -r1.5
*** sendsbr.c   2 Jul 2002 22:09:15 -0000       1.4
--- sendsbr.c   19 Aug 2002 20:50:42 -0000      1.5
***************
*** 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: viamail.c
===================================================================
RCS file: /cvsroot/nmh/nmh/uip/viamail.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -r1.7 -r1.8
*** viamail.c   2 Jul 2002 22:09:15 -0000       1.7
--- viamail.c   19 Aug 2002 20:50:42 -0000      1.8
***************
*** 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.6
diff -C2 -r1.5 -r1.6
*** whatnowsbr.c        2 Jul 2002 22:09:15 -0000       1.5
--- whatnowsbr.c        19 Aug 2002 20:50:42 -0000      1.6
***************
*** 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]