コード例 #1
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * gr - get record attributes
 */
static NuError
GetRecordFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    const NuRecord* pRecord;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 2);

    err = NuGetRecord(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], nil, 0), &pRecord);
    if (err == kNuErrNone) {
        printf("Exerciser: success, call returned:\n");
        printf("\tfileSysID   : %d\n", pRecord->recFileSysID);
        printf("\tfileSysInfo : 0x%04x ('%c')\n", pRecord->recFileSysInfo,
            NuGetSepFromSysInfo(pRecord->recFileSysInfo));
        printf("\taccess      : 0x%02lx\n", pRecord->recAccess);
        printf("\tfileType    : 0x%04lx\n", pRecord->recFileType);
        printf("\textraType   : 0x%04lx\n", pRecord->recExtraType);
        printf("\tcreateWhen  : ...\n");
        printf("\tmodWhen     : ...\n");        /* too lazy */
        printf("\tarchiveWhen : ...\n");
    }
    return err;
}
コード例 #2
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * ar - add an empty record
 */
static NuError
AddRecordFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    NuRecordIdx recordIdx;
    NuFileDetails nuFileDetails;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 2);

    memset(&nuFileDetails, 0, sizeof(nuFileDetails));
    nuFileDetails.threadID = 0;     /* irrelevant */
    nuFileDetails.storageName = argv[1];
    nuFileDetails.fileSysID = kNuFileSysUnknown;
    nuFileDetails.fileSysInfo = (short) kFssep;
    nuFileDetails.access = kUnlocked;
    /* fileType, extraType, storageType, dates */

    err = NuAddRecord(ExerciserState_GetNuArchive(pState),
            &nuFileDetails, &recordIdx);
    if (err == kNuErrNone)
        printf("Exerciser: success, new recordIdx=%ld\n", recordIdx);
    return err;
}
コード例 #3
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * et - extract thread
 */
static NuError
ExtractThreadFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    NuDataSink* pDataSink = nil;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 3);

    err = NuCreateDataSinkForFile(true, kNuConvertOff, argv[2], kFssep,
            &pDataSink);
    if (err != kNuErrNone) {
        fprintf(stderr, "Exerciser: data sink create failed\n");
        goto bail;
    }

    err = NuExtractThread(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], nil, 0), pDataSink);
    /* fall through with err */

bail:
    NuFreeDataSink(pDataSink);
    return err;
}
コード例 #4
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * p - print
 */
static NuError PrintFunc(ExerciserState* pState, int argc, char** argv)
{
    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 1);

    return NuContents(ExerciserState_GetNuArchive(pState), PrintEntry);
}
コード例 #5
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * t - test archive
 */
static NuError TestFunc(ExerciserState* pState, int argc, char** argv)
{
    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 1);

    return NuTest(ExerciserState_GetNuArchive(pState));
}
コード例 #6
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * pd - print debug
 */
static NuError
PrintDebugFunc(ExerciserState* pState, int argc, char** argv)
{
    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 1);

    return NuDebugDumpArchive(ExerciserState_GetNuArchive(pState));
}
コード例 #7
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * sev - set value
 *
 * Currently takes numeric arguments.
 */
static NuError SetValueFunc(ExerciserState* pState, int argc, char** argv)
{
    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 3);

    return NuSetValue(ExerciserState_GetNuArchive(pState),
            (NuValueID) strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0));
}
コード例 #8
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * tr - test record
 */
static NuError TestRecordFunc(ExerciserState* pState, int argc, char** argv)
{
    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 2);

    return NuTestRecord(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], NULL, 0));
}
コード例 #9
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * re - rename record
 */
static NuError RenameFunc(ExerciserState* pState, int argc, char** argv)
{
    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 4);

    return NuRename(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], NULL, 0), argv[2], argv[3][0]);
}
コード例 #10
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * sec - set error callback
 *
 * Use an error handler callback.
 */
static NuError
SetErrorCallbackFunc(ExerciserState* pState, int argc, char** argv)
{
    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 1);

    NuSetErrorHandler(ExerciserState_GetNuArchive(pState), ErrorHandler);
    return kNuErrNone;
}
コード例 #11
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * e - extract all files (selection-filtered)
 */
static NuError ExtractFunc(ExerciserState* pState, int argc, char** argv)
{
    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 1);

    NuSetSelectionFilter(ExerciserState_GetNuArchive(pState), SelectionFilter);

    return NuExtract(ExerciserState_GetNuArchive(pState));
}
コード例 #12
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * dt - delete thread
 */
static NuError
DeleteThreadFunc(ExerciserState* pState, int argc, char** argv)
{
    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 2);

    return NuDeleteThread(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], nil, 0));
}
コード例 #13
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * fl - flush changes to archive
 */
static NuError FlushFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    uint32_t flushStatus;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 1);

    err = NuFlush(ExerciserState_GetNuArchive(pState), &flushStatus);
    if (err != kNuErrNone)
        printf("Exerciser: flush failed, status flags=0x%04x\n", flushStatus);
    return err;
}
コード例 #14
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * gev - get value
 *
 * Currently takes numeric arguments.  We could be nice and accept the
 * things like "IgnoreCRC" for kNuValueIgnoreCRC, but not yet.
 */
static NuError GetValueFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    NuValue value;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 2);

    err = NuGetValue(ExerciserState_GetNuArchive(pState),
            (NuValueID) strtol(argv[1], NULL, 0), &value);
    if (err == kNuErrNone)
        printf("  --> %u\n", value);
    return err;
}
コード例 #15
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * grip - get record idx by position
 */
static NuError
GetRecordIdxByPositionFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    NuRecordIdx recIdx;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 2);

    err = NuGetRecordIdxByPosition(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], nil, 0), &recIdx);
    if (err == kNuErrNone)
        printf("Exerciser: success, returned recordIdx=%ld\n", recIdx);
    return err;
}
コード例 #16
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * cl - close archive
 */
static NuError CloseFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 1);

    err = NuClose(ExerciserState_GetNuArchive(pState));
    if (err == kNuErrNone) {
        ExerciserState_SetNuArchive(pState, NULL);
        ExerciserState_SetArchivePath(pState, NULL);
    }

    return err;
}
コード例 #17
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * ors - open streaming read-only
 */
static NuError
OpenStreamingReadOnlyFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    NuArchive* pArchive;
    FILE* fp = nil;

    assert(ExerciserState_GetNuArchive(pState) == nil);
    assert(argc == 2);

    if ((fp = fopen(argv[1], kNuFileOpenReadOnly)) == nil) {
        err = errno ? (NuError)errno : kNuErrGeneric;
        fprintf(stderr, "Exerciser: unable to open '%s'\n", argv[1]);
    } else {
        err = NuStreamOpenRO(fp, &pArchive);
        if (err == kNuErrNone) {
            ExerciserState_SetNuArchive(pState, pArchive);
            ExerciserState_SetArchivePath(pState, argv[1]);
            fp = nil;
        }
    }

    if (fp != nil)
        fclose(fp);

    return err;
}
コード例 #18
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * grin - get record idx by name
 */
static NuError GetRecordIdxByNameFunc(ExerciserState* pState, int argc,
    char** argv)
{
    NuError err;
    NuRecordIdx recIdx;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 2);

    err = NuGetRecordIdxByName(ExerciserState_GetNuArchive(pState),
            argv[1], &recIdx);
    if (err == kNuErrNone)
        printf("Exerciser: success, returned recordIdx=%u\n", recIdx);
    return err;
}
コード例 #19
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * upt - update pre-sized thread
 */
static NuError UpdatePresizedThreadFunc(ExerciserState* pState, int argc,
    char** argv)
{
    NuError err;
    NuDataSource* pDataSource = NULL;
    char* lineBuf = NULL;
    long ourLen;
    int32_t maxLen;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 2);

    lineBuf = (char*)malloc(kNiceLineLen);
    assert(lineBuf != NULL);
    err = GetLine("Enter data for thread", lineBuf, kNiceLineLen);
    if (err != kNuErrNone)
        goto bail;

    ourLen = strlen(lineBuf);

    /* use "ourLen" for both buffer len and data len */
    err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed,
            ourLen, (uint8_t*)lineBuf, 0, ourLen, FreeCallback,
            &pDataSource);
    if (err != kNuErrNone) {
        fprintf(stderr, "Exerciser: data source create failed (err=%d)\n",
            err);
        goto bail;
    }
    lineBuf = NULL;  /* now owned by the library */

    err = NuUpdatePresizedThread(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], NULL, 0), pDataSource, &maxLen);
    if (err == kNuErrNone)
        printf("Exerciser: success; function returned maxLen=%d\n", maxLen);

bail:
    NuFreeDataSource(pDataSource);
    if (lineBuf != NULL)
        free(lineBuf);
    return err;
}
コード例 #20
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * af - add file to archive
 */
static NuError AddFileFunc(ExerciserState* pState, int argc, char** argv)
{
    NuFileDetails nuFileDetails;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 2);

    memset(&nuFileDetails, 0, sizeof(nuFileDetails));
    nuFileDetails.threadID = kNuThreadIDDataFork;
    nuFileDetails.storageNameMOR = argv[1];
    nuFileDetails.fileSysID = kNuFileSysUnknown;
    nuFileDetails.fileSysInfo = (short) kFssep;
    nuFileDetails.access = kUnlocked;
    /* fileType, extraType, storageType, dates */

    return NuAddFile(ExerciserState_GetNuArchive(pState), argv[1],
            &nuFileDetails, false, NULL);
}
コード例 #21
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * gmh - get master header
 */
static NuError
GetMasterHeaderFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    const NuMasterHeader* pMasterHeader;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 1);

    err = NuGetMasterHeader(ExerciserState_GetNuArchive(pState),
            &pMasterHeader);
    if (err == kNuErrNone) {
        printf("Exerciser: success (version=%u, totalRecords=%lu, EOF=%lu)\n",
            pMasterHeader->mhMasterVersion, pMasterHeader->mhTotalRecords,
            pMasterHeader->mhMasterEOF);
    }
    return err;
}
コード例 #22
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * sra - set record attributes
 *
 * Right now I'm only allowing changes to file type and aux type.  This
 * could be adapted to do more easily, but the command handler has a
 * rigid notion of how many arguments each function should have, so
 * you'd need to list all of them every time.
 */
static NuError SetRecordAttrFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    const NuRecord* pRecord;
    NuRecordAttr recordAttr;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != NULL);
    assert(argc == 4);

    err = NuGetRecord(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], NULL, 0), &pRecord);
    if (err != kNuErrNone)
        return err;
    printf("Exerciser: NuGetRecord succeeded, calling NuSetRecordAttr\n");
    NuRecordCopyAttr(&recordAttr, pRecord);
    recordAttr.fileType = strtol(argv[2], NULL, 0);
    recordAttr.extraType = strtol(argv[3], NULL, 0);
    /*recordAttr.fileSysInfo = ':';*/
    return NuSetRecordAttr(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], NULL, 0), &recordAttr);
}
コード例 #23
0
ファイル: Exerciser.c プロジェクト: salfter/ciderpress
/*
 * orw - open read-write
 */
static NuError OpenReadWriteFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    NuArchive* pArchive;

    assert(ExerciserState_GetNuArchive(pState) == NULL);
    assert(argc == 2);

    err = NuOpenRW(argv[1], kTempFile, 0, &pArchive);
    if (err == kNuErrNone) {
        ExerciserState_SetNuArchive(pState, pArchive);
        ExerciserState_SetArchivePath(pState, argv[1]);
    }

    return err;
}
コード例 #24
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * oro - open read-only
 */
static NuError
OpenReadOnlyFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    NuArchive* pArchive;

    assert(ExerciserState_GetNuArchive(pState) == nil);
    assert(argc == 2);

    err = NuOpenRO(argv[1], &pArchive);
    if (err == kNuErrNone) {
        ExerciserState_SetNuArchive(pState, pArchive);
        ExerciserState_SetArchivePath(pState, argv[1]);
    }

    return err;
}
コード例 #25
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * Parse a command from the user.
 *
 * "lineBuf" will be mangled.  On success, "pFunc", "pArgc", and "pArgv"
 * will receive the results.
 */
static NuError
ParseLine(char* lineBuf, ExerciserState* pState, CommandFunc* pFunc, int* pArgc,
    char*** pArgv)
{
    NuError err = kNuErrSyntax;
    char* command;
    char* cp;
    int i;

    /*
     * Parse the strings.
     */

    command = strtok(lineBuf, kWhitespace);
    if (command == nil) {
        /* no command; the user probably just hit "enter" on a blank line */
        *pFunc = NothingFunc;
        *pArgc = 0;
        *pArgv = nil;
        err = kNuErrNone;
        goto bail;
    }

    /* no real need to be flexible; add 1 for command and one for nil */
    *pArgv = (char**) malloc(sizeof(char*) * (kMaxArgs+2));
    (*pArgv)[0] = command;
    *pArgc = 1;

    cp = strtok(nil, kWhitespace);
    while (cp != nil) {
        if (*pArgc >= kMaxArgs+1) {
            printf("ERROR: too many arguments\n");
            goto bail;
        }
        (*pArgv)[*pArgc] = cp;
        (*pArgc)++;

        cp = strtok(nil, kWhitespace);
    }
    assert(*pArgc < kMaxArgs+2);
    (*pArgv)[*pArgc] = nil;

    /*
     * Look up the command.
     */
    for (i = 0; i < (int)NELEM(gCommandTable); i++) {
        if (strcmp(command, gCommandTable[i].commandStr) == 0)
            break;
    }
    if (i == NELEM(gCommandTable)) {
        printf("ERROR: unrecognized command\n");
        goto bail;
    }

    *pFunc = gCommandTable[i].func;

    /*
     * Check arguments and flags.
     */
    if (*pArgc -1 != gCommandTable[i].expectedArgCount) {
        printf("ERROR: expected %d args, found %d\n",
            gCommandTable[i].expectedArgCount, *pArgc -1);
        goto bail;
    }

    if (gCommandTable[i].flags & kFlagArchiveReq) {
        if (ExerciserState_GetNuArchive(pState) == nil) {
            printf("ERROR: must have an archive open\n");
            goto bail;
        }
    }
    if (gCommandTable[i].flags & kFlagNoArchiveReq) {
        if (ExerciserState_GetNuArchive(pState) != nil) {
            printf("ERROR: an archive is already open\n");
            goto bail;
        }
    }

    /*
     * Looks good!
     */
    err = kNuErrNone;

bail:
    return err;
}
コード例 #26
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * Interpret commands, do clever things.
 */
static NuError
CommandLoop(void)
{
    NuError err = kNuErrNone;
    ExerciserState* pState = ExerciserState_New();
    CommandFunc func;
    char lineBuf[128];
    int argc;
    char** argv = nil;

    while (1) {
        printf("\nEnter command (%s)> ", ExerciserState_GetArchiveFile(pState));
        fflush(stdout);

        if (fgets(lineBuf, sizeof(lineBuf), stdin) == nil) {
            printf("\n");
            break;
        }

        if (argv != nil) {
            free(argv);
            argv = nil;
        }

        func = nil; /* sanity check */

        err = ParseLine(lineBuf, pState, &func, &argc, &argv);
        if (err != kNuErrNone)
            continue;

        assert(func != nil);
        if (func == QuitFunc)
            break;

        err = (*func)(pState, argc, argv);

        if (err < 0)
            printf("Exerciser: received error %d (%s)\n", err, NuStrError(err));
        else if (err > 0)
            printf("Exerciser: received error %d\n", err);

        if (argv != nil) {
            free(argv);
            argv = nil;
        }
    }

    if (ExerciserState_GetNuArchive(pState) != nil) {
        /* ought to query the archive before saying something like this... */
        printf("Exerciser: aborting any un-flushed changes in archive %s\n",
            ExerciserState_GetArchivePath(pState));
        (void) NuAbort(ExerciserState_GetNuArchive(pState));
        err = NuClose(ExerciserState_GetNuArchive(pState));
        if (err != kNuErrNone)
            printf("Exerciser: got error %d closing archive\n", err);
        ExerciserState_SetNuArchive(pState, nil);
    }

    if (pState != nil)
        ExerciserState_Free(pState);
    if (argv != nil)
        free(argv);
    return kNuErrNone;
}
コード例 #27
0
ファイル: Exerciser.c プロジェクト: rostamn739/ciderpress
/*
 * at - add thread to record
 */
static NuError
AddThreadFunc(ExerciserState* pState, int argc, char** argv)
{
    NuError err;
    NuDataSource* pDataSource = nil;
    char* lineBuf = nil;
    long ourLen, maxLen;
    NuThreadID threadID;
    NuThreadIdx threadIdx;

    (void) pState, (void) argc, (void) argv;    /* shut up, gcc */
    assert(ExerciserState_GetNuArchive(pState) != nil);
    assert(argc == 3);

    lineBuf = (char*)malloc(kNiceLineLen);
    assert(lineBuf != nil);

    threadID = strtol(argv[2], nil, 0);
    if (NuThreadIDGetClass(threadID) == kNuThreadClassData) {
        /* load data from a file on disk */
        maxLen = 0;
        err = GetLine("Enter filename", lineBuf, kNiceLineLen);
        if (err != kNuErrNone)
            goto bail;
        if (!lineBuf[0]) {
            fprintf(stderr, "Invalid filename\n");
            err = kNuErrInvalidArg;
            goto bail;
        }

        err = NuCreateDataSourceForFile(kNuThreadFormatUncompressed,
                0, lineBuf, false, &pDataSource);
        if (err != kNuErrNone) {
            fprintf(stderr,
                "Exerciser: file data source create failed (err=%d)\n", err);
            goto bail;
        }
    } else {
        if (threadID == kNuThreadIDFilename || threadID == kNuThreadIDComment) {
            /* select the buffer pre-size */
            err = GetLine("Enter max buffer size", lineBuf, kNiceLineLen);
            if (err != kNuErrNone)
                goto bail;
            maxLen = strtol(lineBuf, nil, 0);
            if (maxLen <= 0) {
                fprintf(stderr, "Bad length\n");
                err = kNuErrInvalidArg;
                goto bail;
            }
        } else {
            maxLen = 0;
        }

        err = GetLine("Enter the thread contents", lineBuf, kNiceLineLen);
        if (err != kNuErrNone)
            goto bail;
        ourLen = strlen(lineBuf);

        /* create a data source from the buffer */
        err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed,
                maxLen, (unsigned char*)lineBuf, 0, ourLen, FreeCallback,
                &pDataSource);
        if (err != kNuErrNone) {
            fprintf(stderr,
                "Exerciser: buffer data source create failed (err=%d)\n", err);
            goto bail;
        }
        lineBuf = nil;  /* now owned by the library */
    }


    err = NuAddThread(ExerciserState_GetNuArchive(pState),
            strtol(argv[1], nil, 0), threadID, pDataSource, &threadIdx);
    if (err == kNuErrNone) {
        pDataSource = nil;  /* library owns it now */
        printf("Exerciser: success; function returned threadIdx=%ld\n",
            threadIdx);
    }

bail:
    NuFreeDataSource(pDataSource);
    if (lineBuf != nil)
        free(lineBuf);
    return err;
}