/* cmd.c - (C) Copyright 1984, 1985, 1986 STSC, Inc. */ /* CMD provides a fast and efficient way to execute UNIX commands * and to capture their output in APL. It provides two advantages * over )SH -- it is considerably faster, and the output produced is * captured and returned to APL as an explicit result. LLG Nov 84 */ /* "varmac.h" contains definitions of the internal structure of variables in the APL*PLUS/UNX System. The macros included are used to reference and set the important data fields in an APL variable */ #include "varmac.h" /* "errmacro.h" defines the error numbers used internally by the APL*PLUS/UNX System. The routine error(), included here, can be used by an external process to signal a specific error in the APL enviroment. */ #include "errmacro.h" #include #include #include #define STDIN 0 #define STDOUT 1 #define BUFSIZE 10000 #define BUFOUT 40000 char bufl[BUFSIZE]; /* Left arg Input buffer */ char bufr[BUFSIZE]; /* Right arg Input buffer */ char bufz[BUFOUT]; /* Output buffer */ int done() /* Exit routine: shutdown of external process */ { exit(0); } main(argc, argv) /* Main entry point for test routine */ int argc; /* argument count and */ char *argv[]; /* ... vector passed down from main's caller */ { long initbufz(); /* Set up result object framework */ unsigned getarg(); /* Fetch object from input stream */ int pdb[2]; /* Pipe between this process and executed command */ long len, i, n; /* Assorted temporaries */ char *bufp; /* Pointer into result buffer */ /* Set trap routine for termination signal from APL. This signal triggers a clean shutdown of the external process */ signal(SIGTERM, done); for(;;) { /* Loop until SIGTERM signal received */ /* Process one UNIX command per trip around the loop */ /* fprintf(stderr, "cmd waiting for something to do\r\n"); */ /* Read the two arguments to quadXP into bufl (left argument) and bufr (right argument). */ getarg(bufl); /* Read (and then ignore) left argument */ len = getarg(bufr); /* Read right argument (UNIX command) */ if (len == -1) { /* Too much data for buffer */ error(LIMIT_ERROR); continue; } if (*desc(bufr) != CHAR) { error(DOMAIN_ERROR); continue; } if (*rank(bufr) != 1) { error(RANK_ERROR); continue; } /* Set up Pipes for interprocess communication */ if (pipe(pdb) == -1) { /* Result pipe */ error(FTQE_ERROR); /* File tie quota exceeded */ continue; } /* --------------------------------------------------------------- Execute the UNIX command by forking a new process. The new process uses the system call execlp() to run the standard UNIX shell "sh", with the right argument of quadXPn as the command to process. */ if ( 0 == fork() ) { /* Start up child process */ /* Only the child process executes this code. The output is redirected to the pipe created before the fork. Then the child will execute the UNIX command provided in the argument to quadXP; the parent external process reads the output and returns it to APL as a character variable. */ close(STDOUT); /* Turn off Standard output */ dup(pdb[1]); /* Pipe is Output now */ close(pdb[1]); /* Turn off old pipe */ /* Make the XP right argument a null-terminated string */ *(values(bufr) + *nelm(bufr)) = '\0'; /** The Child process executes the command */ execlp("sh", "sh", "-c", values(bufr), NULL); /* If execlp() succeeds, child process will be overlaid with the shell; if this point is reached, execlp() failed */ fprintf(stderr, "child process execlp() failed\r\n"); exit(0); } /* ------------------------------------------------------------ */ /* Parent process closes output descriptor of the pipe. This is necessary so that an EOF will be generated on the pipe when the child process terminates */ close ( pdb[1] ); /* Close output end of pipe */ /* Read the result of the UNIX command into the output buffer */ initbufz(CHAR, 1, 16L); /* Set up for values macro */ bufp = valuesv(bufz); len = 0; do { n = read(pdb[0], bufp, 4000); /* Read pipe input */ len += n; bufp += n; if (len > BUFOUT) { fprintf(stderr, "cmd> too much input data %ld\r\n",len); error(LIMIT_ERROR); close(pdb[0]); /* Clean up for next request */ continue; } } while ( n > 0 ); if (initbufz(CHAR, 1, len) == -1) { /* Now overlay real data */ error(LIMIT_ERROR); continue; } *shape(bufz) = len; /* Must always set the shape (vector) */ bufp = valuesv(bufz); for (n=0; n < len; n++, bufp++) /* APL likes Quad-TCNL */ if (*bufp == '\n') /* Replace all linefeeds */ *bufp = '\r'; /* with carriage returns */ close(pdb[0]); /* Clean up for next request */ /* Send result back to APL */ len = *length(bufz); /* fprintf(stderr, "writing %ld bytes to pipe\r\n", len); */ write(STDOUT, bufz, (unsigned) len); wait((int *) 0); /* Take care of children */ } } error(type) /* Return Error response to APL */ int type; { /* Users can define their own errors with an error type between * 500 and 32000. APL will echo the error as Quad-XP ERROR n, where * n = type - 500; */ long errtyp[2]; errtyp[0] = -1; /* Flag Error response */ errtyp[1] = (long) type; /* Error type */ write(STDOUT, errtyp, 8); fprintf(stderr, "error %d in external process\r\n", type); return; } long initbufz(d, r, n) /* Define result buffer for return object */ register char d; /* descriptor byte */ register char r; /* rank byte */ register long n; /* nelm */ { long varsize(); register long len; /** Prepare result space **/ len = varsize(d, r, n); len += WSMGROH; /* Length and refcount too */ /* fprintf(stderr, "initbufz> result %d, d %d, r %d, n %d\r\n", len, d, r, n);*/ if (len > BUFOUT) { fprintf(stderr, "initbufz> error result length %d\r\n", len); return(-1); } *length(bufz) = len; /* Leading byte length of result */ *tlength(bufz) = len; /* Trailing byte length of result */ *refcount(bufz)= 1L; /* Initialize reference count */ *desc(bufz) = d; /* Result Descriptor */ *rank(bufz) = r; /* Result rank */ *nelm(bufz) = n; /* Number of Elements */ /* fprintf(stderr, "initbufz> result length %ld\r\n", len); */ return(len); } long varsize(d,r,n) /* Computes size needed for a variable */ register char d; /* descriptor byte */ register char r; /* rank byte */ register long n; /* nelm */ { register long z = 4+4; /* desc,rank word + nelm word */ z += 4 * r; /* one fullword for each coordinate */ switch (d) { case CHAR: z += n; break; case BOOL: z += ( n >> 3 ) + ((n & 7 ) ? 1 : 0); break; case INT : z += n*4; break; case FLOAT: z += n*8; break; case HET: case PTR: /* Not supported for external process results */ default: return(0); } /* Round up to fullword if needed */ z = 0xfffffffc & (z+3); return(z); } unsigned int getarg(buffer) /* Read input pipe into local buffer */ register char *buffer; /* Return length of data object, or -1 for error */ { register unsigned int len, bytes, n; register char *bufp; n = read(STDIN, buffer, 4); /* Read length of Input */ bytes = *(long *) buffer; /* Length of input data */ len = bytes - 4; /* Remainder to read */ if (bytes > BUFSIZE) { /* Too much, clean up, WS full */ while (len > 0) { /* Pipe read transfers up to 5120 bytes per read */ n = read(STDIN, buffer, len); /* Purge rest of Input */ fprintf(stderr, "too much data, ask %u, got %u\r\n", len, n); len -= n; } return(-1); } bufp = buffer + 4; while (len > 0) { n = read(STDIN, bufp, len); /* Read rest of Input */ len -= n; bufp += n; } /* fprintf(stderr, "getarg> got %u bytes, nelm %ld, desc %d, rank %d\r\n", bytes, *nelm(buffer), *desc(buffer), *rank(buffer)); */ return(bytes); }