Ejemplo n.º 1
0
static int JsiFreeWaitInfoTable(Jsi_Interp *interp, void *privData)
{
    struct WaitInfoTable *table = privData;

    Jsi_Free(table->info);
    Jsi_Free(table);
    return JSI_OK;
}
Ejemplo n.º 2
0
void jsi_PstateFree(jsi_Pstate *ps)
{
    /* TODO: when do we free opcodes */
    jsi_PstateClear(ps);
    Jsi_Free(ps->lexer);
    if (ps->opcodes)
        jsi_FreeOpcodes(ps->opcodes);
    if (ps->hPtr)
        Jsi_HashEntryDelete(ps->hPtr);
    Jsi_HashDelete(ps->argsTbl);
    Jsi_HashDelete(ps->strTbl);
    Jsi_HashDelete(ps->fastVarTbl);
    MEMCLEAR(ps);
    Jsi_Free(ps);
}
Ejemplo n.º 3
0
static int JsiOpenForWrite(Jsi_Interp *interp, const char *filename, int append)
{
    char *fn = Jsi_FileRealpathStr(interp, filename, NULL);
    int rc = open(fn, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
    Jsi_Free(fn);
    return rc;
}
Ejemplo n.º 4
0
static int JsiOpenForRead(Jsi_Interp *interp, const char *filename)
{
    char *fn = Jsi_FileRealpathStr(interp, filename, NULL);
    int rc = open(fn, O_RDONLY, 0);
    Jsi_Free(fn);
    return rc;
}
Ejemplo n.º 5
0
static int JsiCleanupChildren(Jsi_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId, Jsi_DString *dStr, Jsi_DString *cStr, int *code)
{
    struct WaitInfoTable *table = jsi_ExecCmdData(interp);
    int result = JSI_OK;
    int i, exitCode = 256;
    char buf[1000];
    Jsi_DString sStr;
    Jsi_DSInit(&sStr);
    for (i = 0; i < numPids; i++) {
        int waitStatus = 0;
        if (cStr) {
            if (i==0)
                Jsi_DSAppend(cStr, (Jsi_DSLength(cStr)>1?", ":""), "children: [", NULL);
            else
                Jsi_DSAppend(&sStr, ", ", NULL);
        }
        if (JsiWaitForProcess(table, pidPtr[i], &waitStatus) != JSI_BAD_PID) {
            //  if (JsiCheckWaitStatus(interp, pidPtr[i], waitStatus, dStr?&sStr:0) != JSI_OK) 
            //result = JSI_ERROR;  // TODO: we don't error out on non-zero return code. Find way to return it.
            int es = WEXITSTATUS(waitStatus);
            if (WIFEXITED(waitStatus)) {
                if (i==0)
                    exitCode = es;
                else if (es>exitCode)
                    exitCode = es;
            }
            if (cStr) {
                pidtype pid = pidPtr[i];
                if (WIFEXITED(waitStatus))
                    sprintf(buf, "{type:\"exit\", pid:%ld, exitCode:%d}", (long)pid, es);
                else 
                    sprintf(buf, "{type:\"%s\", pid:%ld, signal: %d}",
                        (WIFSIGNALED(waitStatus) ? "killed" : "suspended"), (long)pid, WTERMSIG(waitStatus));
                Jsi_DSAppend(&sStr, buf, NULL);
            }
        }
    }
    if (i>0 && cStr) {
        if (exitCode != 256)
            sprintf(buf, ", exitCode: %d", exitCode);
        Jsi_DSAppend(cStr, Jsi_DSValue(&sStr), "]", buf, NULL);
    }
    Jsi_DSFree(&sStr);
    Jsi_Free(pidPtr);

    /*
     * Read the standard error file.  If there's anything there,
     * then add the file's contents to the result
     * string.
     */
    if (errorId != JSI_BAD_FD) {
        JsiRewindFd(errorId);
        if (JsiAppendStreamToString(interp, errorId, dStr) != JSI_OK) {
            result = JSI_ERROR;
        }
    }
    *code = exitCode;
    return result;
}
Ejemplo n.º 6
0
static fdtype JsiOpenForWrite(Jsi_Interp *interp, const char *filename, int append)
{
    char *fn = Jsi_FileRealpathStr(interp, filename, NULL);
    int rc = CreateFile(fn, append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
        JsiStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, 0, (HANDLE) NULL);
    Jsi_Free(fn);
    return rc;
}
Ejemplo n.º 7
0
static fdtype JsiOpenForRead(Jsi_Interp *interp, const char *filename)
{
    char *fn = Jsi_FileRealpathStr(interp, filename, NULL);
    int rc = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
        JsiStdSecAttrs(), OPEN_EXISTING, 0, NULL);
    Jsi_Free(fn);
    return rc;
}
Ejemplo n.º 8
0
static void FileNormalize(Jsi_Interp *interp, Jsi_Value *path) {
    char *np, *cp = Jsi_ValueString(interp, path, 0);
    if (!cp)
        return;
    np = Jsi_FileRealpath(interp, path, NULL);
    if (np && Jsi_Strcmp(cp, np))
        Jsi_ValueMakeString(interp, path, np);
    else if (np)
        Jsi_Free(np);
}
Ejemplo n.º 9
0
/* 
 * Frees any allocated space and sets the DString back to empty such that it is safe to exit the scope.
 * Alternatively, the DString may be immediately reused. However in that case a call to Jsi_DSReset might
 * be more appropriate. 
 */
void
Jsi_DSFree(Jsi_DString *dsPtr)
{
    if (!dsPtr->str)
        return;
    if (dsPtr->str != dsPtr->staticSpace && dsPtr->spaceAvl>0)
        Jsi_Free(dsPtr->str);
    dsPtr->str = NULL;
    dsPtr->staticSpace[0] = 0;
    dsPtr->spaceAvl = dsPtr->staticSize;
}
Ejemplo n.º 10
0
int Jsi_UserObjUnregister(Jsi_Interp *interp, Jsi_UserObjReg *udreg)
{
    Jsi_HashEntry *hPtr;
    if (interp->deleting) return JSI_ERROR;
    hPtr = Jsi_HashEntryFind(interp->userdataTbl, udreg->name);
    if (hPtr == NULL)
        return JSI_ERROR;
    
    Jsi_HashEntryDelete(hPtr);
    UserObjReg* ptr = Jsi_HashValueGet(hPtr);
    SIGASSERT(ptr, USER_REG);
    Jsi_Free(ptr);
    return JSI_OK;
}
Ejemplo n.º 11
0
void jsi_UserObjFree(Jsi_Interp *interp, Jsi_UserObj *uobj)
{
    Jsi_UserObjReg *udr =uobj->reg;
    if (interp != uobj->interp) {
        Jsi_LogError("UDID bad interp");
        return;
    }
    if (uobj->hPtr)
        Jsi_HashEntryDelete(uobj->hPtr);
    if (udr->freefun) {
        udr->freefun(interp, uobj->data);
    }
    MEMCLEAR(uobj);
    Jsi_Free(uobj);
}
Ejemplo n.º 12
0
int jsi_UserObjDelete(Jsi_Interp *interp, void *data)
{
    UserObjReg* ptr = data;
    SIGASSERT(ptr, USER_REG);
    Jsi_Hash *tblPtr = ptr->hashPtr;
    Jsi_HashEntry *hPtr;
    Jsi_HashSearch search;
    for (hPtr = Jsi_HashEntryFirst(tblPtr, &search);
        hPtr != NULL; hPtr = Jsi_HashEntryNext(&search)) {
        void *dptr;
        Jsi_Obj *obj = Jsi_HashValueGet(hPtr);
        assert(obj && obj->ot == JSI_OT_USEROBJ);
        dptr = obj->d.uobj->data;
        Jsi_HashEntryDelete(hPtr);
        if (!dptr) continue;
        if (ptr->reg->freefun)
            ptr->reg->freefun(interp, dptr);
    }
    Jsi_HashDelete(tblPtr);
    Jsi_Free(ptr);
    return JSI_OK;
}
Ejemplo n.º 13
0
static int TestSqlite(Jsi_Interp *interp, int level)
{
    /* SETUP AND DB CREATION */
    Jsi_Db *jdb;
    if (!interp)
        jdb = Jsi_DbNew("~/mytest.db", 0);
    else {
#ifndef JSI_LITE_ONLY
        if (JSI_OK != Jsi_EvalString(interp, "var mydb = new Sqlite('/tmp/mytest.db');", 0))
            return JSI_ERROR;
        jdb = Jsi_UserObjDataFromVar(interp, "mydb");
        if (!jdb)
            return JSI_ERROR;
#endif
    }
    sqlite3 *db = Jsi_DbHandle(interp, jdb);
    sqlite3_exec(db, "DROP TABLE IF EXISTS mytable;", 0, 0, 0);
    sqlite3_exec(db, "CREATE TABLE mytable (max FLOAT, name, desc, id INT, myTime INT, mark, markSet);", 0, 0, 0);

    /* NOW STAGE DATA TO AND FROM STRUCT. */
    int i, n, res = JSI_OK;
    MyData mydata = {.id=99, .max=100.0, .mark=MARK_A, .markSet=6, .name="maryjane"};
    mydata.myTime = time(NULL)*1000LL; // or use Jsi_DateTime() for milliseconds;
    Jsi_DSSet(&mydata.desc, "Some stuff");

    /* Quickly store 10 rows into the database */
    sqlite3_exec(db, "BEGIN", 0, 0, 0);
    for (i=0; i<10; i++) {
        mydata.id++;
        mydata.max--;
        n = Jsi_DbQuery(jdb, MyOptions, &mydata, 1, "INSERT INTO mytable %s", 0); assert(n==1);
    }
    sqlite3_exec(db, "COMMIT", 0, 0, 0);

    /* Read the last inserted row into mydata2. */
    MyData mydata2 = {};
    mydata2.rowid = mydata.rowid;
    n = Jsi_DbQuery(jdb, MyOptions, &mydata2, 1, "SELECT id,name FROM mytable WHERE rowid=:rowid", 0); assert(n==1);
    n = Jsi_DbQuery(jdb, MyOptions, &mydata2, 1, "SELECT %s FROM mytable WHERE rowid=:rowid", 0); assert(n==1);

    /* Modify all fields of last inserted row. */
    mydata.max = -1;
    mydata.myTime = Jsi_DateTime();
    strcpy(mydata.name, "billybob");
    n = Jsi_DbQuery(jdb, MyOptions, &mydata, 1, "UPDATE mytable SET %s WHERE id=:id", 0); assert(n==1);

    /* Modify specific columns for half of inserted rows. */
    mydata.id = 105;
    n = Jsi_DbQuery(jdb, MyOptions, &mydata, 1, "UPDATE mytable SET name=:name, max=:max WHERE id<:id", 0); assert(n==5);

    /* Delete item id 105. */
    n = Jsi_DbQuery(jdb, MyOptions, &mydata, 1, "DELETE FROM mytable WHERE id=:id", 0); assert(n==1);

    /* ARRAY OF STRUCTS. */
    int num = 10, cnt;
    MyData mydatas[10] = {};
    cnt = Jsi_DbQuery(jdb, MyOptions, mydatas, num, "SELECT %s FROM mytable", 0); assert(cnt==9);
    
    for (i=0; i<cnt; i++)
        mydatas[i].id += i;
    n = Jsi_DbQuery(jdb, MyOptions, mydatas, cnt, "UPDATE mytable SET %s WHERE rowid = :rowid", 0); assert(n==9);
    
    /* Update only the dirty rows. */
    for (i=1; i<=3; i++) {
        mydatas[i].isdirty = 1;
        mydatas[i].id += 100*i;
    }
    n = Jsi_DbQuery(jdb, MyOptions, mydatas, cnt, "UPDATE mytable SET %s WHERE rowid = :rowid", JSI_DB_DIRTY_ONLY); assert(n==3);
 
    /* ARRAY OF POINTERS TO STRUCTS. */
    num = 3;
    static MyData *mdPtr[3] = {};  /* Fixed length */
    n = Jsi_DbQuery(jdb, MyOptions, mdPtr, num, "SELECT %s FROM mytable", JSI_DB_PTRS); assert(n==3);
    printf("%f\n", mdPtr[0]->max);

    MyData **dynPtr = NULL;  /* Variable length */
    n = Jsi_DbQuery(jdb, MyOptions, &dynPtr, 0, "SELECT %s FROM mytable WHERE rowid < 5", JSI_DB_PTR_PTRS); assert(n==4);
    n = Jsi_DbQuery(jdb, MyOptions, &dynPtr, n, "SELECT %s FROM mytable LIMIT 1000", JSI_DB_PTR_PTRS);  assert(n==9);
    n = Jsi_DbQuery(jdb, MyOptions, &dynPtr, n, NULL, JSI_DB_PTR_PTRS|JSI_DB_MEMFREE);
    assert(!dynPtr);
    
    /* Multi-bind interface, to bind vars from other struct(s). */
    Jsi_DbMultipleBind binds[] = {
        { ':', MyOptions, mdPtr, num },
        { '$', MyOptions, &mydata },
        {}
    };
    mydata.max = -1;
    n = Jsi_DbQuery(jdb, NULL, binds, 0, "SELECT %s FROM mytable WHERE max=$max", JSI_DB_PTRS); assert(n==3);

    /* Make mdPtr available as "mydata" to Javascript info.cdata() */
#ifndef JSI_LITE_ONLY
    Jsi_CDataRegister(interp, "mydata", MyOptions, mdPtr, num, JSI_DB_PTRS);
#endif
    jdbPtr = jdb;
    if (level==2) {
        ExecMyData(mydatas, n, "SELECT %s FROM mytable;", 0);
        ExecMySemi(mdPtr,   n, "SELECT %s FROM mytable;", 0);
        ExecMyDyn(&dynPtr, n, "SELECT %s FROM mytable;", 0);
    }
    
    /* Load test: insert/select/update 1,000,000 rows. */
    if (level==1) {
        Jsi_Wide stim, etim;
        int bnum = 1000000;
        MyData *big = Jsi_Calloc(bnum, sizeof(MyData)), *b = big;
        //b->max = 2; b->id=99; b->max=100.0; b->mark=MARK_A; b->markSet=6;
        *b = mydata;
        printf("TESTING %d ROWS\n", bnum);

        stim = Jsi_DateTime();
        b[0].id = 0;
        for (i=1; i<bnum; i++) {
            big[i] = *b;
            big[i].id = i;
        }
        etim = Jsi_DateTime();
        printf("INIT C: %8.3f secs\n", ((etim-stim)/1000.0));
        sqlite3_exec(db, "DELETE FROM mytable", 0, 0, 0);

        stim=etim;
        n = ExecMyData(big, bnum, "INSERT INTO mytable %s", 0); assert(n==bnum);
        etim = Jsi_DateTime();
        i=(int)(etim-stim);
        printf("%8.3f sec, %8d rows/sec    INSERT %d ROWS\n", i/1000.0, bnum*1000/i, n);

        stim=etim;
        memset(big, 0, num*sizeof(MyData));
        n = ExecMyData(big, bnum, "SELECT %s FROM mytable", 0); assert(n==bnum);
        etim = Jsi_DateTime();
        i=(int)(etim-stim);
        printf("%8.3f sec, %8d rows/sec    SELECT %d ROWS \n", i/1000.0, bnum*1000/i, bnum);
        for (i=0; i<bnum; i++) {
            if (b[i].id != i)
                printf("FAILED: Data[%d].id: %d\n", i, b[i].id);
            b[i].id = i+1;
        }

        stim=etim;
        n = ExecMyData(big, bnum, "UPDATE mytable SET id=:id where rowid=:rowid", 0); assert(n==bnum);
        etim = Jsi_DateTime();
        i=(int)(etim-stim);
        printf("%8.3f sec, %8d rows/sec    UPDATE %d ROWS, 1 FIELD\n", i/1000.0, bnum*1000/i, n);
        for (i=0; i<bnum; i++) {
            if (b[i].id != (i+1))
                printf("FAILED: Data[%d].id: %d\n", i, b[i].id);
            b[i].id = i+2;
            if ((i%10)==0)
                b[i].isdirty = 1;
        }

        stim=etim;
        n = ExecMyData(big, bnum, "UPDATE mytable SET %s where rowid=:rowid", 0); assert(n==bnum);
        etim = Jsi_DateTime();
        i=(int)(etim-stim);
        printf("%8.3f sec, %8d rows/sec    UPDATE %d ROWS, ALL FIELDS\n", i/1000.0, n*1000/i, n);

        stim=etim;
        n = ExecMyData(big, bnum, "UPDATE mytable SET %s where rowid=:rowid", JSI_DB_DIRTY_ONLY); assert(n==(bnum/10));
        etim = Jsi_DateTime();
        i=(int)(etim-stim);
        printf("%8.3f sec, %8d rows/sec    UPDATE %d DIRTY ROWS\n", i/1000.0, (int)(n*1000.0/i), n);

        stim=etim;
        for (i=0; i<bnum; i++) {
            if ((i%1000)==0)
                b[i].isdirty = 1;
        }
        n = ExecMyData(big, bnum, "UPDATE mytable SET %s where rowid=:rowid", JSI_DB_DIRTY_ONLY); assert(n==(bnum/1000));
        etim = Jsi_DateTime();
        i=(int)(etim-stim);
        printf("%8.3f sec, %8d rows/sec    UPDATE %d DIRTY ROWS\n", i/1000.0, n*1000/i, n);

        stim=etim;
        for (i=0; i<bnum; i++) {
            if ((i%100000)==0)
                b[i].isdirty = 1;
        }
        n = ExecMyData(big, bnum, "UPDATE mytable SET %s where rowid=:rowid", JSI_DB_DIRTY_ONLY); assert(n==(bnum/100000));
        etim = Jsi_DateTime();
        i=(int)(etim-stim);
        printf("%8.3f sec, %8d rows/sec    UPDATE %d DIRTY ROWS\n", i/1000.0, n*1000/i, n);

        Jsi_Free(big);
    }
    // Why we need wrappers: following is an error that the compiler does not detect!
    if (0) Jsi_DbQuery(jdb, MyOptions, "a bad struct", n, "", 0); 

    return res;
}

int main(int argc, char *argv[]) {
#ifdef JSI_LITE_ONLY
    TestSqlite(NULL, argc>1?atoi(argv[1]):0);
#else
    Jsi_Interp *interp = Jsi_InterpCreate(NULL, argc, argv, 0);
    TestSqlite(interp, argc>1?atoi(argv[1]):0);
    Jsi_Interactive(interp, JSI_OUTPUT_QUOTE|JSI_OUTPUT_NEWLINES);
#endif
    return 0;
}
Ejemplo n.º 14
0
void jsi_ScopeStrsFree(Jsi_ScopeStrs *ss)
{
    if (!ss) return;
    Jsi_Free(ss->strings);
    Jsi_Free(ss);
}
Ejemplo n.º 15
0
/*
 *----------------------------------------------------------------------
 *
 * JsiCreatePipeline --
 *
 *  Given an argc/argv array, instantiate a pipeline of processes
 *  as described by the argv.
 *
 * Results:
 *  The return value is a count of the number of new processes
 *  created, or -1 if an error occurred while creating the pipeline.
 *  *pidArrayPtr is filled in with the address of a dynamically
 *  allocated array giving the ids of all of the processes.  It
 *  is up to the caller to free this array when it isn't needed
 *  anymore.  If inPipePtr is non-NULL, *inPipePtr is filled in
 *  with the file id for the input pipe for the pipeline (if any):
 *  the caller must eventually close this file.  If outPipePtr
 *  isn't NULL, then *outPipePtr is filled in with the file id
 *  for the output pipe from the pipeline:  the caller must close
 *  this file.  If errFilePtr isn't NULL, then *errFilePtr is filled
 *  with a file id that may be used to read error output after the
 *  pipeline completes.
 *
 * Side effects:
 *  Processes and pipes are created.
 *
 *----------------------------------------------------------------------
 */
static int
JsiCreatePipeline(Jsi_Interp *interp, Jsi_Value* args, int argc, pidtype **pidArrayPtr,
    fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
{
    pidtype *pidPtr = NULL;         /* Points to malloc-ed array holding all
                                 * the pids of child processes. */
    int numPids = 0;            /* Actual number of processes that exist
                                 * at *pidPtr right now. */
    int cmdCount;               /* Count of number of distinct commands
                                 * found in argc/argv. */
    const char *input = NULL;   /* Describes input for pipeline, depending
                                 * on "inputFile".  NULL means take input
                                 * from stdin/pipe. */

#define FILE_NAME   0           /* input/output: filename */
#define FILE_APPEND 1           /* output only:  filename, append */
#define FILE_HANDLE 2           /* input/output: filehandle */
#define FILE_TEXT   3           /* input only:   input is actual text */

    int inputFile = FILE_NAME;  /* 1 means input is name of input file.
                                 * 2 means input is filehandle name.
                                 * 0 means input holds actual
                                 * text to be input to command. */

    int outputFile = FILE_NAME; /* 0 means output is the name of output file.
                                 * 1 means output is the name of output file, and append.
                                 * 2 means output is filehandle name.
                                 * All this is ignored if output is NULL
                                 */
    int errorFile = FILE_NAME;  /* 0 means error is the name of error file.
                                 * 1 means error is the name of error file, and append.
                                 * 2 means error is filehandle name.
                                 * All this is ignored if error is NULL
                                 */
    const char *output = NULL;  /* Holds name of output file to pipe to,
                                 * or NULL if output goes to stdout/pipe. */
    const char *error = NULL;   /* Holds name of stderr file to pipe to,
                                 * or NULL if stderr goes to stderr/pipe. */
    fdtype inputId = JSI_BAD_FD;
                                 /* Readable file id input to current command in
                                 * pipeline (could be file or pipe).  JSI_BAD_FD
                                 * means use stdin. */
    fdtype outputId = JSI_BAD_FD;
                                 /* Writable file id for output from current
                                 * command in pipeline (could be file or pipe).
                                 * JSI_BAD_FD means use stdout. */
    fdtype errorId = JSI_BAD_FD;
                                 /* Writable file id for all standard error
                                 * output from all commands in pipeline.  JSI_BAD_FD
                                 * means use stderr. */
    fdtype lastOutputId = JSI_BAD_FD;
                                 /* Write file id for output from last command
                                 * in pipeline (could be file or pipe).
                                 * -1 means use stdout. */
    fdtype pipeIds[2];           /* File ids for pipe that's being created. */
    int firstArg, lastArg;      /* Indexes of first and last arguments in
                                 * current command. */
    int lastBar;
    int i;
    pidtype pid;
    char **save_environ;
    struct WaitInfoTable *table = jsi_ExecCmdData(interp);  /* TODO: mutex??? */
    /*int argc = Jsi_ValueGetLength(interp, args);*/

    /* Holds the args which will be used to exec */
    char **arg_array = Jsi_Calloc((argc + 1), sizeof(*arg_array));
    int arg_count = 0;

    JsiReapDetachedPids(table);

    if (inPipePtr != NULL) {
        *inPipePtr = JSI_BAD_FD;
    }
    if (outPipePtr != NULL) {
        *outPipePtr = JSI_BAD_FD;
    }
    if (errFilePtr != NULL) {
        *errFilePtr = JSI_BAD_FD;
    }
    pipeIds[0] = pipeIds[1] = JSI_BAD_FD;

    /*
     * First, scan through all the arguments to figure out the structure
     * of the pipeline.  Count the number of distinct processes (it's the
     * number of "|" arguments).  If there are "<", "<<", or ">" arguments
     * then make note of input and output redirection and remove these
     * arguments and the arguments that follow them.
     */
    cmdCount = 1;
    lastBar = -1;
    for (i = 0; i < argc; i++) {
        if (i == 0) {
            FileNormalize(interp,  Jsi_ValueArrayIndex(interp, args, 0));
        }
        const char *arg = Jsi_ValueArrayIndexToStr(interp, args, i, NULL);
        
        if (arg[0] == '<') {
            inputFile = FILE_NAME;
            input = arg + 1;
            if (*input == '<') {
                inputFile = FILE_TEXT; // TODO: make this or @ from a var
                input++;
            }
            else if (*input == '@') {
                inputFile = FILE_HANDLE;
                input++;
            }

            if (!*input && ++i < argc) {
                input = Jsi_ValueArrayIndexToStr(interp, args, i, NULL);
            }
        }
        else if (arg[0] == '>') {
            int dup_error = 0;

            outputFile = FILE_NAME;

            output = arg + 1;
            if (*output == '>') {
                outputFile = FILE_APPEND;
                output++;
            }
            if (*output == '&') {
                /* Redirect stderr too */
                output++;
                dup_error = 1;
            }
            if (*output == '@') {
                outputFile = FILE_HANDLE;
                output++;
            }
            if (!*output && ++i < argc) {
                output = Jsi_ValueArrayIndexToStr(interp, args, i, NULL);
            }
            if (dup_error) {
                errorFile = outputFile;
                error = output;
            }
        }
        else if (arg[0] == '2' && arg[1] == '>') {
            error = arg + 2;
            errorFile = FILE_NAME;

            if (*error == '@' || (*error == '&' && error[1] == '1')) {
                errorFile = FILE_HANDLE;
                error++;
            }
            else if (*error == '>') {
                errorFile = FILE_APPEND;
                error++;
            }
            if (!*error && ++i < argc) {
                error = Jsi_ValueArrayIndexToStr(interp, args, i, NULL);
            }
        }
        else {
            if (Jsi_Strcmp(arg, "|") == 0 || Jsi_Strcmp(arg, "|&") == 0) {
                if (i == lastBar + 1 || i == argc - 1) {
                    Jsi_LogError("illegal use of | or |& in command");
                    goto badargs;
                }
                lastBar = i;
                cmdCount++;
            }
            /* Either |, |& or a "normal" arg, so store it in the arg array */
            arg_array[arg_count++] = (char *)arg;
            continue;
        }

        if (i >= argc) {
            Jsi_LogError("can't specify \"%s\" as last word in command", arg);
            goto badargs;
        }
    }

    if (arg_count == 0) {
        Jsi_LogError("didn't specify command to execute");
badargs:
        Jsi_Free(arg_array);
        return -1;
    }

    /* Must do this before vfork(), so do it now */
    save_environ = JsiSaveEnv(JsiBuildEnv(interp));

    /*
     * Set up the redirected input source for the pipeline, if
     * so requested.
     */
    if (input != NULL) {
        if (inputFile == FILE_TEXT) {
            /*
             * Immediate data in command.  Create temporary file and
             * put data into file.
             */
            inputId = JsiCreateTemp(interp, input);
            if (inputId == JSI_BAD_FD) {
                goto error;
            }
        }
        else if (inputFile == FILE_HANDLE) {
            /* Should be a file descriptor */
            FILE *fh = JsiGetAioFilehandle(interp, input);

            if (fh == NULL) {
                goto error;
            }
            inputId = JsiDupFd(JsiFileno(fh));
        }
        else {
            /*
             * File redirection.  Just open the file.
             */
            inputId = JsiOpenForRead(interp, input);
            if (inputId == JSI_BAD_FD) {
                Jsi_LogError("couldn't read file \"%s\": %s", input, JsiStrError());
                goto error;
            }
        }
    }
    else if (inPipePtr != NULL) {
        if (JsiPipe(pipeIds) != 0) {
            Jsi_LogError("couldn't create input pipe for command");
            goto error;
        }
        inputId = pipeIds[0];
        *inPipePtr = pipeIds[1];
        pipeIds[0] = pipeIds[1] = JSI_BAD_FD;
    }

    /*
     * Set up the redirected output sink for the pipeline from one
     * of two places, if requested.
     */
    if (output != NULL) {
        if (outputFile == FILE_HANDLE) {
            FILE *fh = JsiGetAioFilehandle(interp, output);
            if (fh == NULL) {
                goto error;
            }
            fflush(fh);
            lastOutputId = JsiDupFd(JsiFileno(fh));
        }
        else {
            /*
             * Output is to go to a file.
             */
            lastOutputId = JsiOpenForWrite(interp, output, outputFile == FILE_APPEND);
            if (lastOutputId == JSI_BAD_FD) {
                Jsi_LogError("couldn't write file \"%s\": %s", output, JsiStrError());
                goto error;
            }
        }
    }
    else if (outPipePtr != NULL) {
        /*
         * Output is to go to a pipe.
         */
        if (JsiPipe(pipeIds) != 0) {
            Jsi_LogError("couldn't create output pipe");
            goto error;
        }
        lastOutputId = pipeIds[1];
        *outPipePtr = pipeIds[0];
        pipeIds[0] = pipeIds[1] = JSI_BAD_FD;
    }
    /* If we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */
    if (error != NULL) {
        if (errorFile == FILE_HANDLE) {
            if (Jsi_Strcmp(error, "1") == 0) {
                /* Special 2>@1 */
                if (lastOutputId != JSI_BAD_FD) {
                    errorId = JsiDupFd(lastOutputId);
                }
                else {
                    /* No redirection of stdout, so just use 2>@stdout */
                    error = "stdout";
                }
            }
            if (errorId == JSI_BAD_FD) {
                FILE *fh = JsiGetAioFilehandle(interp, error);
                if (fh == NULL) {
                    goto error;
                }
                fflush(fh);
                errorId = JsiDupFd(JsiFileno(fh));
            }
        }
        else {
            /*
             * Output is to go to a file.
             */
            errorId = JsiOpenForWrite(interp, error, errorFile == FILE_APPEND);
            if (errorId == JSI_BAD_FD) {
                Jsi_LogError("couldn't write file \"%s\": %s", error, JsiStrError());
                goto error;
            }
        }
    }
    else if (errFilePtr != NULL) {
        /*
         * Set up the standard error output sink for the pipeline, if
         * requested.  Use a temporary file which is opened, then deleted.
         * Could potentially just use pipe, but if it filled up it could
         * cause the pipeline to deadlock:  we'd be waiting for processes
         * to complete before reading stderr, and processes couldn't complete
         * because stderr was backed up.
         */
        errorId = JsiCreateTemp(interp, NULL);
        if (errorId == JSI_BAD_FD) {
            goto error;
        }
        *errFilePtr = JsiDupFd(errorId);
    }

    /*
     * Scan through the argc array, forking off a process for each
     * group of arguments between "|" arguments.
     */

    pidPtr = Jsi_Calloc(cmdCount, sizeof(*pidPtr));
    for (i = 0; i < numPids; i++) {
        pidPtr[i] = JSI_BAD_PID;
    }
    for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
        int pipe_dup_err = 0;
        fdtype origErrorId = errorId;

        for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
            if (arg_array[lastArg][0] == '|') {
                if (arg_array[lastArg][1] == '&') {
                    pipe_dup_err = 1;
                }
                break;
            }
        }
        /* Replace | with NULL for execv() */
        arg_array[lastArg] = NULL;
        if (lastArg == arg_count) {
            outputId = lastOutputId;
        }
        else {
            if (JsiPipe(pipeIds) != 0) {
                Jsi_LogError("couldn't create pipe");
                goto error;
            }
            outputId = pipeIds[1];
        }

        /* Now fork the child */

#ifdef __MINGW32__
        pid = JsiStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId);
        if (pid == JSI_BAD_PID) {
            Jsi_LogError("couldn't exec \"%s\"", arg_array[firstArg]);
            goto error;
        }
#else
        /*
         * Disable SIGPIPE signals:  if they were allowed, this process
         * might go away unexpectedly if children misbehave.  This code
         * can potentially interfere with other application code that
         * expects to handle SIGPIPEs;  what's really needed is an
         * arbiter for signals to allow them to be "shared".
         */
        if (table->info == NULL) {
            (void)signal(SIGPIPE, SIG_IGN);
        }

        /* Need to do this befor vfork() */
        if (pipe_dup_err) {
            errorId = outputId;
        }

        /*
         * Make a new process and enter it into the table if the fork
         * is successful.
         */
        pid = vfork();
        if (pid < 0) {
            Jsi_LogError("couldn't fork child process");
            goto error;
        }
        if (pid == 0) {
            /* Child */

            if (inputId != -1) dup2(inputId, 0);
            if (outputId != -1) dup2(outputId, 1);
            if (errorId != -1) dup2(errorId, 2);

            for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) {
                close(i);
            }

            /* TODO: execvpe not using last arg! */
            execvpe(arg_array[firstArg], &arg_array[firstArg], Jsi_GetEnviron());

            /* Need to prep an error message before vfork(), just in case */
            fprintf(stderr, "couldn't exec \"%s\"", arg_array[firstArg]);
            _exit(127);
        }
#endif

        /* parent */

        /*
         * Enlarge the wait table if there isn't enough space for a new
         * entry.
         */
        if (table->used == table->size) {
            table->size += WAIT_TABLE_GROW_BY;
            table->info = Jsi_Realloc(table->info, table->size * sizeof(*table->info));
        }

        table->info[table->used].pid = pid;
        table->info[table->used].flags = 0;
        table->used++;

        pidPtr[numPids] = pid;

        /* Restore in case of pipe_dup_err */
        errorId = origErrorId;

        /*
         * Close off our copies of file descriptors that were set up for
         * this child, then set up the input for the next child.
         */

        if (inputId != JSI_BAD_FD) {
            JsiCloseFd(inputId);
        }
        if (outputId != JSI_BAD_FD) {
            JsiCloseFd(outputId);
        }
        inputId = pipeIds[0];
        pipeIds[0] = pipeIds[1] = JSI_BAD_FD;
    }
    *pidArrayPtr = pidPtr;

    /*
     * All done.  Cleanup open files lying around and then return.
     */

  cleanup:
    if (inputId != JSI_BAD_FD) {
        JsiCloseFd(inputId);
    }
    if (lastOutputId != JSI_BAD_FD) {
        JsiCloseFd(lastOutputId);
    }
    if (errorId != JSI_BAD_FD) {
        JsiCloseFd(errorId);
    }
    Jsi_Free(arg_array);

    if (save_environ)
        JsiRestoreEnv(save_environ);

    return numPids;

    /*
     * An error occurred.  There could have been extra files open, such
     * as pipes between children.  Clean them all up.  Detach any child
     * processes that have been created.
     */

  error:
    if ((inPipePtr != NULL) && (*inPipePtr != JSI_BAD_FD)) {
        JsiCloseFd(*inPipePtr);
        *inPipePtr = JSI_BAD_FD;
    }
    if ((outPipePtr != NULL) && (*outPipePtr != JSI_BAD_FD)) {
        JsiCloseFd(*outPipePtr);
        *outPipePtr = JSI_BAD_FD;
    }
    if ((errFilePtr != NULL) && (*errFilePtr != JSI_BAD_FD)) {
        JsiCloseFd(*errFilePtr);
        *errFilePtr = JSI_BAD_FD;
    }
    if (pipeIds[0] != JSI_BAD_FD) {
        JsiCloseFd(pipeIds[0]);
    }
    if (pipeIds[1] != JSI_BAD_FD) {
        JsiCloseFd(pipeIds[1]);
    }
    if (pidPtr != NULL) {
        for (i = 0; i < numPids; i++) {
            if (pidPtr[i] != JSI_BAD_PID) {
                JsiDetachPids(interp, 1, &pidPtr[i]);
            }
        }
        Jsi_Free(pidPtr);
    }
    numPids = -1;
    goto cleanup;
}
Ejemplo n.º 16
0
/**
 * Apply the printf-like format in fmtObjPtr with the given arguments.
 *
 * Returns a new object with zero reference count if OK, or NULL on error.
 */
int Jsi_FormatString(Jsi_Interp *interp, Jsi_Value *args, Jsi_DString *dStr)
{
    const char *span, *format, *formatEnd, *msg;
    int numBytes = 0, argIndex = 1, gotXpg = 0, gotSequential = 0, argCnt;
    static const char * const mixedXPG =
        "cannot mix \"%\" and \"%n$\" conversion specifiers";
    static const char * const badIndex[2] = {
        "not enough arguments for all format specifiers",
        "\"%n$\" argument index out of range"
    };
    int formatLen;
    Jsi_Value *v;
    
    /* A single buffer is used to store numeric fields (with sprintf())
     * This buffer is allocated/reallocated as necessary
     */
    char stat_buf[100], *num_buffer = stat_buf;
    int num_buffer_size = sizeof(stat_buf);
    argCnt = Jsi_ValueGetLength(interp, args);

    if (argCnt<1) {
        msg = "missing format";
        goto errorMsg;
    }
    format = Jsi_ValueArrayIndexToStr(interp, args,0, &formatLen);
    
    span = format;
    formatEnd = format + formatLen;
    Jsi_DSInit(dStr);

    while (format != formatEnd) {
        char *end;
        int gotMinus, sawFlag;
        int gotPrecision, useShort, useLong;
        long width, precision;
        int newXpg;
        int ch;
        int step;
        int doubleType;
        char pad = ' ';
        char spec[2*JSI_INTEGER_SPACE + 12];
        char *p;

        int formatted_chars;
        int formatted_bytes;
        const char *formatted_buf = NULL;

        step = jsi_utf8_tounicode(format, &ch);
        format += step;
        if (ch != '%') {
            numBytes += step;
            continue;
        }
        if (numBytes) {
            Jsi_DSAppendLen(dStr, span, numBytes);
            numBytes = 0;
        }

        /*
         * Saw a % : process the format specifier.
         *
         * Step 0. Handle special case of escaped format marker (i.e., %%).
         */

        step = jsi_utf8_tounicode(format, &ch);
        if (ch == '%') {
            span = format;
            numBytes = step;
            format += step;
            continue;
        }

        /*
         * Step 1. XPG3 position specifier
         */

        newXpg = 0;
        if (isdigit(ch)) {
            int position = strtoul(format, &end, 10);
            if (*end == '$') {
                newXpg = 1;
                argIndex = position - 1;
                format = end + 1;
                step = jsi_utf8_tounicode(format, &ch);
            }
        }
        if (newXpg) {
            if (gotSequential) {
                msg = mixedXPG;
                goto errorMsg;
            }
            gotXpg = 1;
        } else {
            if (gotXpg) {
                msg = mixedXPG;
                goto errorMsg;
            }
            gotSequential = 1;
        }
        if ((argIndex < 0) || (argIndex >= argCnt)) {
            msg = badIndex[gotXpg];
            goto errorMsg;
        }

        /*
         * Step 2. Set of flags. Also build up the sprintf spec.
         */
        p = spec;
        *p++ = '%';

        gotMinus = 0;
        sawFlag = 1;
        do {
            switch (ch) {
            case '-':
                gotMinus = 1;
                break;
            case '0':
                pad = ch;
                break;
            case ' ':
            case '+':
            case '#':
                break;
            default:
                sawFlag = 0;
                continue;
            }
            *p++ = ch;
            format += step;
            step = jsi_utf8_tounicode(format, &ch);
        } while (sawFlag);

        /*
         * Step 3. Minimum field width.
         */

        width = 0;
        if (isdigit(ch)) {
            width = strtoul(format, &end, 10);
            format = end;
            step = jsi_utf8_tounicode(format, &ch);
        } else if (ch == '*') {
            if (argIndex >= argCnt - 1) {
                msg = badIndex[gotXpg];
                goto errorMsg;
            }
            v = Jsi_ValueArrayIndex(interp, args, argIndex);
            if (Jsi_GetLongFromValue(interp, v, &width) != JSI_OK) {
                goto error;
            }
            if (width < 0) {
                width = -width;
                if (!gotMinus) {
                    *p++ = '-';
                    gotMinus = 1;
                }
            }
            argIndex++;
            format += step;
            step = jsi_utf8_tounicode(format, &ch);
        }

        /*
         * Step 4. Precision.
         */

        gotPrecision = precision = 0;
        if (ch == '.') {
            gotPrecision = 1;
            format += step;
            step = jsi_utf8_tounicode(format, &ch);
        }
        if (isdigit(ch)) {
            precision = strtoul(format, &end, 10);
            format = end;
            step = jsi_utf8_tounicode(format, &ch);
        } else if (ch == '*') {
            if (argIndex >= argCnt - 1) {
                msg = badIndex[gotXpg];
                goto errorMsg;
            }
            v = Jsi_ValueArrayIndex(interp, args, argIndex);
            if (Jsi_GetLongFromValue(interp, v, &precision) != JSI_OK) {
                goto error;
            }

            /*
             * TODO: Check this truncation logic.
             */

            if (precision < 0) {
                precision = 0;
            }
            argIndex++;
            format += step;
            step = jsi_utf8_tounicode(format, &ch);
        }

        /*
         * Step 5. Length modifier.
         */

        useShort = 0;
        useLong = 0;
        if (ch == 'h') {
            useShort = 1;
            format += step;
            step = jsi_utf8_tounicode(format, &ch);
        } else if (ch == 'l') {
            useLong = 1;
            format += step;
            step = jsi_utf8_tounicode(format, &ch);
            if (ch == 'l') {
                format += step;
                step = jsi_utf8_tounicode(format, &ch);
            }
        }

        format += step;
        span = format;

        /*
         * Step 6. The actual conversion character.
         */

        if (ch == 'i') {
            ch = 'd';
        }

        doubleType = 0;

        /* Each valid conversion will set:
         * formatted_buf   - the result to be added
         * formatted_chars - the length of formatted_buf in characters
         * formatted_bytes - the length of formatted_buf in bytes
         */
        switch (ch) {
        case '\0':
            msg = "format string ended in middle of field specifier";
            goto errorMsg;
        case 's': {
            v = Jsi_ValueArrayIndex(interp, args, argIndex);
            if (Jsi_GetStringFromValue(interp, v, &formatted_buf) != JSI_OK)
                goto error;
            formatted_bytes = 
            formatted_chars = Jsi_Strlen(formatted_buf);
            if (gotPrecision && (precision < formatted_chars)) {
                /* Need to build a (null terminated) truncated string */
                formatted_chars = precision;
                formatted_bytes = jsi_utf8_index(formatted_buf, precision);
            }
            break;
        }
        case 'c': {
            Jsi_Wide code;

            v = Jsi_ValueArrayIndex(interp, args, argIndex);
            if (Jsi_GetWideFromValue(interp, v, &code) != JSI_OK) {
                goto error;
            }
            /* Just store the value in the 'spec' buffer */
            formatted_bytes = jsi_utf8_fromunicode(spec, code);
            formatted_buf = spec;
            formatted_chars = 1;
            break;
        }

        case 'e':
        case 'E':
        case 'f':
        case 'g':
        case 'G':
            doubleType = 1;
            /* fall through */
        case 'd':
        case 'u':
        case 'o':
        case 'x':
        case 'X': {
            Jsi_Wide w;
            Jsi_Number d;
            int length;

            /* Fill in the width and precision */
            if (width) {
                p += sprintf(p, "%ld", width);
            }
            if (gotPrecision) {
                p += sprintf(p, ".%ld", precision);
            }

            /* Now the modifier, and get the actual value here */
            if (doubleType) {
                v = Jsi_ValueArrayIndex(interp, args, argIndex);
                if (Jsi_GetDoubleFromValue(interp, v, &d) != JSI_OK) {
                    goto error;
                }
                length = MAX_FLOAT_WIDTH;
            }
            else {
                v = Jsi_ValueArrayIndex(interp, args, argIndex);
                if (Jsi_GetWideFromValue(interp, v, &w) != JSI_OK) {
                    goto error;
                }
                length = JSI_INTEGER_SPACE;
                if (useShort) {
                    *p++ = 'h';
                    if (ch == 'd') {
                        w = (short)w;
                    }
                    else {
                        w = (unsigned short)w;
                    }
                }
                else {
                    *p++ = 'l';
#ifdef HAVE_LONG_LONG
                    if (useLong && sizeof(long long) == sizeof(Jsi_Wide)) {
                        *p++ = 'l';
                    }
#endif
                }
            }

            *p++ = (char) ch;
            *p = '\0';

            /* Adjust length for width and precision */
            if (width > length) {
                length = width;
            }
            if (gotPrecision) {
                length += precision;
            }

            /* Increase the size of the buffer if needed */
            if (num_buffer_size < length + 1) {
                num_buffer_size = length + 1;
                num_buffer = Jsi_Realloc((num_buffer==stat_buf?NULL:num_buffer), num_buffer_size);
            }

            if (doubleType) {
                snprintf(num_buffer, length + 1, spec, d);
            }
            else {
                formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
            }
            formatted_chars = formatted_bytes = strlen(num_buffer);
            formatted_buf = num_buffer;
            break;
        }

        default: {
            /* Just reuse the 'spec' buffer */
            spec[0] = ch;
            spec[1] = '\0';
            Jsi_LogError("bad field specifier \"%s\"", spec);
            goto error;
        }
        }

        if (!gotMinus) {
            while (formatted_chars < width) {
                Jsi_DSAppendLen(dStr, &pad, 1);
                formatted_chars++;
            }
        }

        Jsi_DSAppendLen(dStr, formatted_buf, formatted_bytes);

        while (formatted_chars < width) {
            Jsi_DSAppendLen(dStr, &pad, 1);
            formatted_chars++;
        }

        argIndex += gotSequential;
    }
    if (numBytes) {
        Jsi_DSAppendLen(dStr, span, numBytes);
    }

    if (num_buffer!=stat_buf)
        Jsi_Free(num_buffer);
    return JSI_OK;

errorMsg:
    Jsi_LogError("%s", msg);
error:
    Jsi_DSFree(dStr);
    if (num_buffer!=stat_buf)
        Jsi_Free(num_buffer);
    return JSI_ERROR;
}
Ejemplo n.º 17
0
/*
 * The main exec command
 */
int jsi_execCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_DString *dStr, Jsi_DString *cStr, int *code)
{
    fdtype outputId;               /* File id for output pipe.  -1
                                 * means command overrode. */
    fdtype errorId;                /* File id for temporary file
                                 * containing error output. */
    pidtype *pidPtr;
    int numPids, result, argc;
    
    argc = Jsi_ValueGetLength(interp, args);
    /*
     * See if the command is to be run in background;  if so, create
     * the command, detach it, and return.
     */

    if (argc > 1 && Jsi_Strcmp(Jsi_ValueArrayIndexToStr(interp, args, argc-1, NULL), "&") == 0) {
        int i;

        argc--;
        numPids = JsiCreatePipeline(interp, args, argc, &pidPtr, NULL, NULL, NULL);
        if (numPids < 0) {
            return JSI_ERROR;
        }
        if (cStr) {
            /* The return value is a list of the pids */
            Jsi_DSAppend(cStr,(Jsi_DSLength(cStr)>1?", ":""), "pids : [", NULL);
            for (i = 0; i < numPids; i++) {
                char ibuf[100];
                sprintf(ibuf, "%s%ld", (i?",":""), (long)pidPtr[i]);
                Jsi_DSAppend(cStr, ibuf, NULL);
            }
            Jsi_DSAppend(cStr, "]", NULL);
        }
        JsiDetachPids(interp, numPids, pidPtr);
        Jsi_Free(pidPtr);
        return JSI_OK;
    }
    /*
     * Create the command's pipeline.
     */
    numPids =
        JsiCreatePipeline(interp, args, argc, &pidPtr, NULL, &outputId, &errorId);

    if (numPids < 0) {
        return JSI_ERROR;
    }

    /*
     * Read the child's output (if any) and put it into the result.
     */

    result = JSI_OK;
    if (outputId != JSI_BAD_FD) {
        result = JsiAppendStreamToString(interp, outputId, dStr);
        if (result != JSI_OK && cStr)
            Jsi_DSAppend(cStr, (Jsi_DSLength(cStr)>1?", ":""), "errstr: \"error reading from output pipe\"", NULL);
    }

    if (JsiCleanupChildren(interp, numPids, pidPtr, errorId, dStr, cStr, code) != JSI_OK) {
        result = JSI_ERROR;
    }
    return result;
}