* uip/mhlistsbr.c, uip/mhlsbr.c, uip/picksbr.c: cast
[mmh] / uip / whatnowsbr.c
index 53320d3..5fcba33 100644 (file)
@@ -44,6 +44,7 @@
 #include <fcntl.h>
 #include <signal.h>
 #include <h/mime.h>
+#include <h/utils.h>
 
 static struct swit whatnowswitches[] = {
 #define        DFOLDSW                 0
@@ -119,6 +120,11 @@ static int buildfile (char **, char *);
 static int check_draft (char *);
 static int whomfile (char **, char *);
 static int removefile (char *);
+static void writelscmd(char *, int, char **);
+static void writesomecmd(char *buf, int bufsz, char *cmd, char *trailcmd, char **argp);
+static FILE* popen_in_dir(const char *dir, const char *cmd, const char *type);
+static int system_in_dir(const char *dir, const char *cmd);
+
 
 #ifdef HAVE_LSTAT
 static int copyf (char *, char *);
@@ -318,16 +324,13 @@ WhatNow (int argc, char **argv)
             *  is accustomed to.  Read back the absolute path.
             */
 
-           if (*++argp == (char *)0) {
+           if (*(argp+1) == (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);
+               writesomecmd(buf, BUFSIZ, "cd", "pwd", argp);
            }
-           if ((f = popen(buf, "r")) != (FILE *)0) {
+           if ((f = popen_in_dir(cwd, buf, "r")) != (FILE *)0) {
                fgets(cwd, sizeof (cwd), f);
 
                if (strchr(cwd, '\n') != (char *)0)
@@ -352,18 +355,8 @@ WhatNow (int argc, char **argv)
             *  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);
+           writelscmd(buf, sizeof(buf), argp);
+           (void)system_in_dir(cwd, buf);
            break;
 
        case ALISTCMDSW:
@@ -422,17 +415,7 @@ WhatNow (int argc, char **argv)
             *  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, "\"");
+           writelscmd(buf, sizeof(buf), argp);
 
            /*
             *  Read back the response from the shell, which contains a number of lines
@@ -442,15 +425,15 @@ WhatNow (int argc, char **argv)
             *  draft.
             */
 
-           if ((f = popen(buf, "r")) != (FILE *)0) {
+           if ((f = popen_in_dir(cwd, 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);
+                       (void)annotate(drft, attach, shell, 1, 0, -2, 1);
                    else {
                        (void)sprintf(file, "%s/%s", cwd, shell);
-                       (void)annotate(drft, attach, file, 1, 0, -1, 1);
+                       (void)annotate(drft, attach, file, 1, 0, -2, 1);
                    }
                }
 
@@ -517,35 +500,19 @@ WhatNow (int argc, char **argv)
             *  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 /).
+            *
+            * We feed all the file names to the shell at once, otherwise you can't
+            * provide a file name with a space in it.
             */
-
-           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");
-                   }
+           writelscmd(buf, sizeof(buf), argp);
+           if ((f = popen_in_dir(cwd, buf, "r")) != (FILE *)0) {
+               while (fgets(shell, sizeof (shell), 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;
@@ -559,6 +526,96 @@ WhatNow (int argc, char **argv)
     /*NOTREACHED*/
 }
 
+
+
+/* Build a command line of the form $SHELL -c "cd 'cwd'; cmd argp ... ; trailcmd". */
+static void
+writesomecmd(char *buf, int bufsz, char *cmd, char *trailcmd, char **argp)
+{
+    char *cp;
+    /* Note that we do not quote -- the argp from the user
+     * is assumed to be quoted as they desire. (We can't treat
+     * it as pure literal as that would prevent them using ~,
+     * wildcards, etc.) The buffer produced by this function
+     * should be given to popen_in_dir() or system_in_dir() so
+     * that the current working directory is set correctly.
+     */
+    int ln = snprintf(buf, bufsz, "$SHELL -c \"%s", cmd);
+    /* NB that some snprintf() return -1 on overflow rather than the
+     * new C99 mandated 'number of chars that would have been written'
+     */
+    /* length checks here and inside the loop allow for the
+     * trailing ';', trailcmd, '"' and NUL
+     */
+    int trailln = strlen(trailcmd) + 3;
+    if (ln < 0 || ln + trailln > bufsz)
+       adios((char *)0, "arguments too long");
+    
+    cp = buf + ln;
+    
+    while (*++argp != (char *)0) {
+       ln = strlen(*argp);
+       /* +1 for leading space */
+       if (ln + trailln + 1 > bufsz - (cp-buf))
+           adios((char *)0, "arguments too long");
+       *cp++ = ' ';
+       memcpy(cp, *argp, ln+1);
+       cp += ln;
+    }
+    if (*trailcmd) {
+       *cp++ = ';';
+       strcpy(cp, trailcmd);
+       cp += trailln - 3;
+    }
+    *cp++ = '"';
+    *cp = 0;
+}
+
+/*
+ * Build a command line that causes the user's shell to list the file name
+ * arguments.  This handles and wildcard expansion, tilde expansion, etc.
+ */
+static void
+writelscmd(char *buf, int bufsz, char **argp)
+{
+    writesomecmd(buf, bufsz, "ls", "", argp);
+}
+
+/* Like system(), but run the command in directory dir.
+ * This assumes the program is single-threaded!
+ */
+static int
+system_in_dir(const char *dir, const char *cmd)
+{
+    char olddir[BUFSIZ];
+    int r;
+    if (getcwd(olddir, sizeof(olddir)) == 0)
+       adios("getcwd", "could not get working directory");
+    if (chdir(dir) != 0)
+       adios("chdir", "could not change working directory");
+    r = system(cmd);
+    if (chdir(olddir) != 0)
+       adios("chdir", "could not change working directory");
+    return r;
+}
+
+/* ditto for popen() */
+static FILE*
+popen_in_dir(const char *dir, const char *cmd, const char *type)
+{
+    char olddir[BUFSIZ];
+    FILE *f;
+    if (getcwd(olddir, sizeof(olddir)) == 0)
+       adios("getcwd", "could not get working directory");
+    if (chdir(dir) != 0)
+       adios("chdir", "could not change working directory");
+    f = popen(cmd, type);
+    if (chdir(olddir) != 0)
+       adios("chdir", "could not change working directory");
+    return f;
+}
+
+
 /*
  * EDIT
  */
@@ -843,8 +900,7 @@ buildfile (char **argp, char *file)
        while (argp[i])
            i++;
     }
-    if ((args = (char **) malloc((i + 2) * sizeof(char *))) == NULL)
-       adios (NULL, "unable to malloc memory");
+    args = (char **) mh_xmalloc((i + 2) * sizeof(char *));
 
     /*
      * For backward compatibility, we need to add -build
@@ -988,7 +1044,7 @@ static struct swit  sendswitches[] = {
 #define        CLIESW           30
     { "client host", -6 },
 #define        SERVSW           31
-    { "server host", -6 },
+    { "server host", 6 },
 #define        SNOOPSW          32
     { "snoop", -5 },
 #define SDRFSW           33
@@ -1002,9 +1058,13 @@ static struct swit  sendswitches[] = {
 #define SASLMECHSW       37
     { "saslmech", SASLminc(-5) },
 #define USERSW           38
-    { "user", SASLminc(-4) },
+    { "user", SASLminc(4) },
 #define SNDATTACHSW       39
     { "attach file", 6 },
+#define SNDATTACHFORMAT   40
+    { "attachformat", 7 },
+#define PORTSW           41
+    { "port server-port-name/number", 4 },
     { NULL, 0 }
 };
 
@@ -1030,6 +1090,8 @@ sendit (char *sp, char **arg, char *file, int pushed)
     char **arguments, *vec[MAXARGS];
     struct stat st;
     char       *attach = (char *)0;    /* attachment header field name */
+    int                attachformat = 0;       /* mhbuild format specifier for
+                                          attachments */
 
 #ifndef        lint
     int        distsw = 0;
@@ -1167,6 +1229,7 @@ sendit (char *sp, char **arg, char *file, int pushed)
                case SERVSW:
                case SASLMECHSW:
                case USERSW:
+               case PORTSW:
                    vec[vecp++] = --cp;
                    if (!(cp = *argp++) || *cp == '-') {
                        advise (NULL, "missing argument to %s", argp[-2]);
@@ -1190,6 +1253,21 @@ sendit (char *sp, char **arg, char *file, int pushed)
                        return;
                    }
                    continue;
+
+               case SNDATTACHFORMAT:
+                   if (! *argp || **argp == '-')
+                       adios (NULL, "missing argument to %s", argp[-1]);
+                   else {
+                       attachformat = atoi (*argp);
+                       if (attachformat < 0 ||
+                           attachformat > ATTACHFORMATS - 1) {
+                           advise (NULL, "unsupported attachformat %d",
+                                   attachformat);
+                           continue;
+                       }
+                   }
+                   ++argp;
+                   continue;
            }
        }
        advise (NULL, "usage: %s [switches]", sp);
@@ -1255,7 +1333,7 @@ sendit (char *sp, char **arg, char *file, int pushed)
     vec[0] = r1bindex (postproc, '/');
     closefds (3);
 
-    if (sendsbr (vec, vecp, file, &st, 1, attach) == OK)
+    if (sendsbr (vec, vecp, file, &st, 1, attach, attachformat) == OK)
        done (0);
 }