size_t mwCountLinesInFile(FILE* f)
{
    int c;
    size_t lineCount = 0;

    while ((c = fgetc(f)) != EOF)
    {
        if (c == '\n')
        {
            ++lineCount;
        }
    }

    if (!feof(f))
    {
        mwPerror("Error counting file lines");
        return 0;
    }

    if (fseek(f, 0L, SEEK_SET) < 0)
    {
        mwPerror("Error seeking file for counting");
        return 0;
    }

    return lineCount;
}
int writeCheckpoint(EvaluationState* es)
{
    FILE* f;

    /* Avoid corrupting the checkpoint file by writing to a temporary file, and moving that */
    f = mw_fopen(CHECKPOINT_FILE_TMP, "wb");
    if (!f)
    {
        mwPerror("Opening checkpoint '%s'", CHECKPOINT_FILE_TMP);
        return 1;
    }

    es->lastCheckpointNuStep = es->nu_step;
    writeState(f, es);
    fclose(f);

    if (mw_rename(CHECKPOINT_FILE_TMP, resolvedCheckpointPath))
    {
        mwPerror("Failed to update checkpoint file ('%s' to '%s')",
                 CHECKPOINT_FILE_TMP,
                 resolvedCheckpointPath
            );
        return 1;
    }

    return 0;
}
int mwWriteFile(const char* filename, const char* str)
{
    FILE* f;
    int rc;

    if (!str || !filename)
    {
        return 1;
    }

    f = mw_fopen(filename, "wb");
    if (!f)
    {
        mwPerror("Writing file '%s'", filename);
        return 1;
    }

    rc = fputs(str, f);
    if (rc == EOF)
    {
        mwPerror("Error writing file '%s'", filename);
    }

    fcloseVerbose(f, filename, "Closing write file");
    return rc;
}
int nbSetupCursesOutput(void)
{
    int stderrTmp;
    int redirect; /* Will become stderr, let it "leak" */

    /* Since we use stderr for everything, use stdout for curses stuff.

       We don't want the printed stuff to disappear at the end after
       endwin() clears. Redirect stderr to a temporary file and print
       that at the end.

       Ideally we would also prevent the initial clearing, but I can't
       figure out how to do that. Internet says you can't without
       dropping to a lower level of terminal controls.
     */

    snprintf(stderrTmpFile, sizeof(stderrTmpFile), "stderr_tmp_%d", getpid());

    fflush(stderr);
    stderrBack = dup(fileno(stderr));
    if (stderrBack < 0)
    {
        mwPerror("dup() error on STDERR_FILENO");
        return errno;
    }

    stderrTmp = open(stderrTmpFile, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (stderrTmp < 0)
    {
        mwPerror("Failed to get stderr dump file '%s'", stderrTmpFile);
        close(stderrBack);
        return errno;
    }

    redirect = dup2(stderrTmp, fileno(stderr));
    if (redirect < 0)
    {
        mwPerror("dup2() error to stderr redirect");
        close(stderrBack);
        return errno;
    }

    if (close(stderrTmp) < 0)
    {
        printf("Error closing stderrTmp: %s\n", strerror(errno));
        /* Try to restore stderr to console */
        dup2(stderrBack, fileno(stderr));

        close(stderrBack);
        return errno;
    }

    initscr();
    cursesSetup = TRUE;

    return 0;
}
static scene_t* nbglConnectSharedScene(int instanceId)
{
    int shmId;
    const int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    struct stat sb;
    char name[128];
    scene_t* scene = NULL;

    if (snprintf(name, sizeof(name), "/milkyway_nbody_%d", instanceId) == sizeof(name))
    {
        mw_panic("name buffer too small for shared memory name\n");
    }

    shmId = shm_open(name, O_RDWR, mode);
    if (shmId < 0)
    {
        mwPerror("Error getting shared memory");
        return NULL;
    }

    if (fstat(shmId, &sb) < 0)
    {
        mwPerror("shmem fstat");
        shm_unlink(name);
        return NULL;
    }

    scene = (scene_t*) mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmId, 0);
    if (scene == MAP_FAILED)
    {
        mwPerror("mmap: Failed to mmap shared memory");
        if (shm_unlink(name) < 0)
        {
            mwPerror("Unlink shared memory");
        }

        return NULL;
    }

    if (sb.st_size < (ssize_t) sizeof(scene_t) || sb.st_size < (ssize_t) nbFindShmemSize(scene->nbody))
    {
        mw_printf("Shared memory segment is impossibly small ("ZU")\n", (size_t) sb.st_size);
        if (shm_unlink(name) < 0)
        {
            mwPerror("Unlink shared memory");
        }

        return NULL;
    }

    return scene;
}
/* Take a program binary from clGetPropramInfo() and a replacement IL source as a string.
   Replace the .amdil section in the ELF image and return a new copy of the binary.
 */
unsigned char* getModifiedAMDBinary(unsigned char* bin, size_t binSize, int nStream, MWCALtargetEnum target, size_t* newBinSizeOut)
{
    int fd = -1;
    int rc = 0;
    char tmpBinFile[128];
    unsigned char* newBin = NULL;

    if (!bin)
        return NULL;

    getTmpBinaryName(tmpBinFile, sizeof(tmpBinFile));

    /* Write binary to a temporary file since we need a file descriptor for libelf */
    fd = open(tmpBinFile, openMode, openPermMode);
    if (fd < 0)
    {
        mwPerror("Failed to open AMD binary file '%s", tmpBinFile);
        return NULL;
    }

    if (write(fd, bin, binSize) <= 0)
    {
        mwPerror("Failed to write temporary binary file '%s'", tmpBinFile);
        return NULL;
    }

    rc = processElf(fd, nStream, target);
    if (rc == 0)
    {
        if (lseek(fd, 0, SEEK_SET) != 0)
        {
            mwPerror("Failed to seek temporary binary file '%s'", tmpBinFile);
            return NULL;
        }

        newBin = (unsigned char*) readFD(fd, newBinSizeOut);
    }

    if (close(fd) < 0)
    {
        mwPerror("Failed to close binary file '%s'", tmpBinFile);
        free(newBin);
        return NULL;
    }

    mw_remove(tmpBinFile);

    return newBin;
}
/* Open a lua_State, bind run information such as server arguments and
 * BOINC status, and evaluate input script. */
static lua_State* separationOpenLuaStateWithScript(const SeparationFlags* sf)
{
    int failed;
    char* script;
    lua_State* luaSt;

    luaSt = separationLuaOpen(FALSE);
    if (!luaSt)
        return NULL;

    script = mwReadFileResolved(sf->ap_file);
    if (!script)
    {
        mwPerror("Opening Lua script '%s'", sf->ap_file);
        lua_close(luaSt);
        return NULL;
    }

    failed = tryEvaluateScript(luaSt, script, sf);
    free(script);

    if (failed)
    {
        lua_close(luaSt);
        return NULL;
    }

    return luaSt;
}
int nbDetachSharedScene(NBodyState* st)
{
  #if USE_POSIX_SHMEM
    if (st->scene)
    {
        if (shm_unlink(st->scene->shmemName) < 0)
        {
            mwPerror("Closing shared scene memory '%s'", st->scene->shmemName);
            return 1;
        }
    }
  #elif USE_WIN32_SHARED_MAP
    if (st->scene)
    {
        if (!UnmapViewOfFile((LPCVOID) st->scene))
        {
            mwPerrorW32("Error unmapping shared scene memory");
            return 1;
        }
    }
  #endif /* USE_POSIX_SHMEM */

    st->scene = NULL;
    return 0;
}
void nbCleanupCursesOutput(void)
{
    FILE* f;
    size_t readSize;
    char buf[4096];

    if (!cursesSetup)
    {
        return;
    }

    if (endwin() != OK)
    {
        mw_printf("endwin() error\n");
    }

    /* Restore stderr to the console */
    fflush(stderr);
    if (dup2(stderrBack, fileno(stderr)) < 0)
    {
        printf("Error restoring stderr: %s\n", strerror(errno));
    }

    if (close(stderrBack) < 0)
    {
        mwPerror("Error closing stderrBack");
    }

    /* You can't seek on the redirected fd, so reopen the stderr tmp
     * file so we can print it. */
    f = fopen(stderrTmpFile, "r");
    if (!f)
    {
        mwPerror("Error reopening stderr log '%s'", stderrTmpFile);
        return;
    }

    /* Copy file contents to stderr */
    while (!feof(f))
    {
        readSize = fread(buf, 1, sizeof(buf), f);
        fwrite(buf, 1, readSize, stderr);
    }

    fclose(f);
    remove(stderrTmpFile);
}
static char* readFD(int fd, size_t* lenOut)
{
    char* strBuf = NULL;
    struct stat props;
    size_t len = 0;

    if (fd < 0)
    {
        return NULL;
    }

    if (fstat(fd, &props) < 0)
    {
        mwPerror("fstat on temporary AMD binary file");
    }

    if (props.st_size <= 0)
    {
        mw_printf("Modified binary file is empty\n");
        return NULL;
    }

    len = props.st_size + 1;

    strBuf = (char*) malloc(len);
    if (!strBuf)
    {
        mwPerror("Failed to allocate "ZU" for AMD binary file", len);
        return NULL;
    }
    strBuf[props.st_size] = '\0';

    if (read(fd, strBuf, props.st_size) < 0)
    {
        mwPerror("Error reading from AMD Binary file");
        free(strBuf);
        strBuf = NULL;
    }

    if (lenOut)
    {
        *lenOut = props.st_size;
    }

    return strBuf;
}
static char* fcloseVerbose(FILE* f, const char* name, const char* err)
{
    if (fclose(f))
    {
        mwPerror("Error closing file '%s' while %s", name, err);
    }

    return NULL;
}
int mwOSHasAVXSupport(void)
{
    size_t len;
    char* kernelVersion;
    int major = 0;
    int minor = 0;
    int patchLevel = 0;
    static int mib[2] = { CTL_KERN, KERN_OSRELEASE };

    if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0)
    {
        mwPerror("sysctl kernel version size");
        return FALSE;
    }

    kernelVersion = malloc(len * sizeof(char));
    if (!kernelVersion)
    {
        mwPerror("malloc");
        return FALSE;
    }

    if (sysctl(mib, 2, kernelVersion, &len, NULL, 0) < 0)
    {
        mwPerror("sysctl kernel version");
        free(kernelVersion);
        return FALSE;
    }

    if (sscanf(kernelVersion, "%d.%d.%d", &major, &minor, &patchLevel) != 3)
    {
        major = minor = patchLevel = 0;
        /* Old examples seem to say it was 2 digits */
        if (sscanf(kernelVersion, "%d.%d", &major, &minor) != 2)
        {
            major = minor = patchLevel = 0;
        }
    }

    free(kernelVersion);

   /* AVX support added in 10.6.8, which has kernel 10.8 */
    return (major >= 10 && minor >= 8);
}
int mwSetProcessPriority(MWPriority priority)
{
    int value = mwPriorityToNice(priority);
    if (setpriority(PRIO_PROCESS, getpid(), value))
    {
        mwPerror("Setting process priority to %d", value);
        return 1;
    }

    return 0;
}
char* mwFreadFileWithSize(FILE* f, const char* filename, size_t* sizeOut)
{
    long fsize;
    size_t readSize;
    char* buf;

    if (!f)
    {
        return NULL;
    }

     /* Find size of file */
    if (fseek(f, 0, SEEK_END) == -1)
    {
        return fcloseVerbose(f, filename, "seeking file end");
    }

    fsize = ftell(f);
    if (fsize == -1)
    {
        return fcloseVerbose(f, filename, "getting file size");
    }

    fseek(f, 0, SEEK_SET);

    buf = (char*) malloc((fsize + 1) * sizeof(char));
    if (!buf)
    {
        return fcloseVerbose(f, filename, "closing read file");
    }

    buf[fsize] = '\0';

    readSize = fread(buf, sizeof(char), fsize, f);
    if (readSize != (size_t) fsize)
    {
        mwPerror("Failed to read file '%s': Expected to read %ld, but got "ZU,
                 filename, fsize, readSize);
        free(buf);
        buf = NULL;
    }

    fcloseVerbose(f, filename, "closing read file");

    if (sizeOut)
        *sizeOut = readSize;

    return buf;
}
int mwGetHighResTime_RealTime(MWHighResTime* t)
{
    struct timespec ts;

    if (clock_gettime(CLOCK_REALTIME, &ts))
    {
        mwPerror("Error getting CLOCK_REALTIME");
        return 1;
    }

    t->sec = (uint64_t) ts.tv_sec;
    t->nSec = (uint64_t) ts.tv_nsec;

    return 0;
}
SeparationResults* readReferenceResults(const char* refFile, unsigned int nStream)
{
    FILE* f;
    SeparationResults* refResults;

    f = fopen(refFile, "r");
    if (!f)
    {
        mwPerror("Opening reference results '%s'", refFile);
        return NULL;
    }

    refResults = freadReferenceResults(f, nStream);

    fclose(f);

    return refResults;
}
int nbDetachSharedScene(NBodyState* st)
{
    (void) st;

  #if USE_SHMEM
    if (st->scene)
    {
        if (shm_unlink(st->scene->shmemName) < 0)
        {
            mwPerror("Closing shared scene memory '%s'", st->scene->shmemName);
            return 1;
        }

        st->scene = NULL;
    }
  #endif /* USE_SHMEM */

    return 0;
}
int readCheckpoint(EvaluationState* es)
{
    int rc;
    FILE* f;

    f = mwOpenResolved(CHECKPOINT_FILE, "rb");
    if (!f)
    {
        mwPerror("Opening checkpoint '%s'", CHECKPOINT_FILE);
        return 1;
    }

    rc = readState(f, es);
    if (rc)
        mw_printf("Failed to read state\n");

    fclose(f);

    addTmpCheckpointSums(es);

    return rc;
}
/* output: Print bodies */
static int nbOutputBodies(FILE* f, const NBodyCtx* ctx, const NBodyState* st, const NBodyFlags* nbf)
{
    Body* p;
    mwvector lbr;
    const Body* endp = st->bodytab + st->nbody;

    nbPrintBodyOutputHeader(f, nbf->outputCartesian);

    for (p = st->bodytab; p < endp; p++)
    {
        fprintf(f, "%8d,", ignoreBody(p));  /* Print if model it belongs to is ignored */
        if (nbf->outputCartesian)
        {
            fprintf(f,
                    " %22.15f, %22.15f, %22.15f, %22.15f, %22.15f, %22.15f\n",
                    X(Pos(p)), Y(Pos(p)), Z(Pos(p)),
                    X(Vel(p)), Y(Vel(p)), Z(Vel(p)));
        }
        else
        {
            lbr = cartesianToLbr(Pos(p), ctx->sunGCDist);
            fprintf(f,
                    " %22.15f, %22.15f, %22.15f, %22.15f, %22.15f, %22.15f\n",
                    L(lbr), B(lbr), R(lbr),
                    X(Vel(p)), Y(Vel(p)), Z(Vel(p)));
        }
    }

    if (fflush(f))
    {
        mwPerror("Body output flush");
        return TRUE;
    }

    return FALSE;
}
/* From the extra parameters, read them as doubles */
real* mwReadRestArgs(const char** rest, unsigned int n)
{
    unsigned int i;
    real* parameters = NULL;

    if (!rest)
        return NULL;

    parameters = (real*) mwMalloc(n * sizeof(real));

    errno = 0;
    for (i = 0; i < n; ++i)
    {
        parameters[i] = (real) strtod(rest[i], NULL);
        if (errno)
        {
            mwPerror("Error parsing command line fit parameters at '%s'", rest[i]);
            free(parameters);
            return NULL;
        }
    }

    return parameters;
}
int nbWriteBodies(const NBodyCtx* ctx, const NBodyState* st, const NBodyFlags* nbf)
{
    FILE* f;
    int rc = 0;

    if (!nbf->outFileName)
    {
        return 1;
    }

    f = mwOpenResolved(nbf->outFileName, nbf->outputBinary ? "wb" : "w");
    if (!f)
    {
        mw_printf("Failed to open output file '%s'\n", nbf->outFileName);
        return 1;
    }

    if (nbf->outputBinary)
    {
        mw_printf("Binary output unimplemented\n");
        return 1;
    }
    else
    {
        mw_boinc_print(f, "<bodies>\n");
        rc = nbOutputBodies(f, ctx, st, nbf);
        mw_boinc_print(f, "</bodies>\n");
    }

    if (fclose(f) < 0)
    {
        mwPerror("Error closing output file '%s'", nbf->outFileName);
    }

    return rc;
}
static scene_t* nbglLoadStaticSceneFromFile(const char* filename)
{
    FILE* f;
    size_t lnCount; /* ~= nbody */
    size_t line = 0;
    int nbody = 0;
    char lnBuf[4096];
    int rc = 0;
    int ignore;
    double x, y, z;
    double vx, vy, vz;
    double lambda;
    FloatPos* r;
    int hasError = FALSE;
    scene_t* scene = NULL;

    f = fopen(filename, "r");
    if (!f)
    {
        mwPerror("Failed to open file '%s'", filename);
        return NULL;
    }

    lnCount = mwCountLinesInFile(f);
    if (lnCount == 0)
    {
        mw_printf("Error counting lines from file '%s'\n", filename);
        fclose(f);
        return NULL;
    }

    scene = mwCalloc(sizeof(scene_t) + 1 * (lnCount * sizeof(FloatPos)), sizeof(char));

    /* We don't need the buffering features so just use first buffer slot */
    r = &scene->queue.bodyData[0];
    scene->hasInfo = FALSE;
    scene->staticScene = TRUE;

    /* Make read data fake that we have 1 element in the queue */
    OPA_store_int(&scene->queue.head, 0);
    OPA_store_int(&scene->queue.tail, 1);


    readCoordinateSystem = FALSE;
    readHasGalaxy = FALSE;
    readCenterOfMass = FALSE;

    while (fgets(lnBuf, (int) sizeof(lnBuf), f) && line < lnCount)
    {
        ++line;

        if (strlen(lnBuf) + 1 >= sizeof(lnBuf))
        {
            mw_printf("Error reading histogram line "ZU" (Line buffer too small): %s", line, lnBuf);
            hasError = TRUE;
            break;
        }

        /* Skip comments and blank lines */
        if (lnBuf[0] == '#' || lnBuf[0] == '\n')
            continue;

        if (nbglTryReadSceneItems(lnBuf, scene))
            continue;

        rc = sscanf(lnBuf,
                    "%d , %lf , %lf , %lf , %lf , %lf , %lf ",
                    &ignore,
                    &x, &y, &z,
                    &vx, &vy, &vz);
        if (rc == 7)
        {
            /* May or may not be there */
            rc = sscanf(lnBuf, " , %lf \n", &lambda);
            if (rc != 1)
            {
                sscanf(lnBuf, " \n");
            }

            r[nbody].x = (float) x;
            r[nbody].y = (float) y;
            r[nbody].z = (float) z;

            ++nbody;
        }
        else
        {
            hasError = TRUE;
        }
    }

    if (fclose(f))
    {
        mwPerror("Failed to close file '%s'", filename);
    }

    if (!readCoordinateSystem || !readHasGalaxy || !readCenterOfMass)
    {
        mw_printf("Warning: Failed to read some scene info items\n");
    }

    scene->nbody = nbody;
    if (hasError)
    {
        free(scene);
        return NULL;
    }

    return scene;
}
/* Create the next available segment of the form /milkyway_nbody_n n = 0 .. 127*/
int nbCreateSharedScene(NBodyState* st, const NBodyCtx* ctx)
{
    size_t size = sizeof(scene_t) + st->nbody * sizeof(FloatPos);
    int shmId = -1;
    const int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    void* p = NULL;
    int instanceId = -1;
    char name[128];

    /* Try looking for the next available segment of the form /milkyway_nbody_<n> */
    while (shmId < 0 && instanceId < MAX_INSTANCES)
    {
        ++instanceId;

        if (snprintf(name, sizeof(name), "/milkyway_nbody_%d", instanceId) == sizeof(name))
            mw_panic("Buffer too small for scared memory name\n");

        shmId = shm_open(name, O_CREAT | O_RDWR | O_EXCL, mode); /* Try to open exclusively */
        if (shmId < 0 && errno != EEXIST) /* Only failed if */
        {
            mwPerror("Error creating shared memory '%s'", name);
            return 1;
        }
    }

    if (instanceId >= MAX_INSTANCES)
    {
        mw_printf("Could not open new shm segment in %d tries\n", MAX_INSTANCES);
        return 1;
    }

    if (ftruncate(shmId, size) < 0) /* Make the segment the correct size */
    {
        mwPerror("Error ftruncate() shared memory");
        if (shm_unlink(name) < 0)
        {
            mwPerror("Error unlinking shared memory '%s'", name);
        }

        return 1;
    }

    p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmId, 0);
    if (p == MAP_FAILED)
    {
        mwPerror("mmap: Failed to mmap shared memory");
        if (shm_unlink(name) < 0)
        {
            mwPerror("Error unlinking shared memory '%s'", name);
        }

        return 1;
    }

    st->scene = (scene_t*) p;
    st->shmId = shmId;
    st->scene->instanceId = instanceId;
    strncpy(st->scene->shmemName, name, sizeof(st->scene->shmemName));
    nbPrepareSceneFromState(ctx, st);

    return 0;
}
void nbLaunchVisualizer(NBodyState* st, const char* visArgs)
{
    pid_t pid;
    const char* path = NULL;
    char* newPath = NULL;
    int argc = 0;
    char* buf = NULL;
    char* p = NULL;
    char** argv = NULL;
    size_t argvSize = 0;
    size_t visArgsLen = 0;
    char idArg[128];

    if (!st->scene) /* If there's no scene to share, there's no point */
        return;

    if (st->usesExact)
    {
        mw_printf("Visualizer broken with Exact\n");
        return;
    }

    pid = fork();
    if (pid != 0)  /* Parent */
        return;

    /* Child */

    /* Put places convenient for testing. Not essential, failure of
     * any of these is OK */
    path = getenv("PATH");
    if (!path)
    {
        mwPerror("Error getting PATH");
    }
    else
    {
        if (asprintf(&newPath, ".:../bin/:%s", path) < 0)
        {
            mwPerror("Appending to path");
        }
        else
        {
            if (setenv("PATH", newPath, TRUE) < 0)
            {
                mwPerror("Error setting PATH");
            }
            free(newPath);
        }
    }

    if (snprintf(idArg, sizeof(idArg), "--instance-id=%d ", st->scene->instanceId) == sizeof(idArg))
        mw_panic("Buffer too small for --instance-id visualizer argument\n");

    /* Stick the program name at the head of the arguments passed in */
    visArgsLen = visArgs ? strlen(visArgs) : 0;
    argvSize = visArgsLen + sizeof(idArg) + sizeof(nbodyGraphicsName) + 2; /* arguments + program name + space + null */
    buf = mwCalloc(argvSize, sizeof(char));

    p = stpcpy(buf, nbodyGraphicsName);
    p = stpcpy(p, " ");
    p = stpcpy(p, idArg);
    if (visArgs)
    {
        stpcpy(p, visArgs);
    }

    if (poptParseArgvString(buf, &argc, (const char***) &argv))
    {
        mw_printf("Error parsing arguments for visualizer '%s'\n", visArgs);
        free(buf);
        return;
    }

    if (execvp(argv[0], argv) < 0)
    {
        mwPerror("Failed to launch visualizer '%s'", argv[0]);
    }

    free(buf);
    free(argv);
    mw_finish(EXIT_SUCCESS);  /* Unnecessary */
}