// exec-family regression for the GNO libc (punch-list #5 + #7).
// Exercises access() over Kstat and the exec* wrappers over K_execve.
//
// Two halves, run in order so one boot can cover both:
//   1. Negative / no-2nd-program tests (always run, fully deterministic):
//        - access(".", F_OK) succeeds on the login cwd (a directory).
//        - access("/nonesuch/no", F_OK) fails (-1) on a missing path.
//        - execvp("nosuchprog", ...) fails (-1, errno set): buildPath's
//          $PATH search finds no candidate and returns before ever reaching
//          the kernel, so this validates buildCmd/buildPath/access without
//          needing a second executable.
//   2. exec-into (needs GNO_PROG2=demos/gnoExeced.omf placed on /bin): the
//        LAST action execvp's the tiny 2nd program "gnoexeced", which sets
//        its own bank-2 marker and the reached-end word.  If GNO_PROG2 is
//        not supplied the exec-into fails, prog1 sets the fallback reached-
//        end word, and only the half-1 checks are meaningful.
//
// Bank-2 absolute uint16 markers (DBR-independent), gnoHeapProbe convention:
//   0x025000  C0DE  reached end (set by gnoExeced on success, else by prog1)
//   0x025002  0001  execvp("nosuchprog") returned -1, else DEAD
//   0x025004  0001  errno was set nonzero by the failed execvp, else DEAD
//   0x025008  0001  access(".", F_OK) == 0 (existing dir), else DEAD
//   0x02500A  0001  access("/nonesuch/no", F_OK) == -1 (missing), else DEAD
//   0x025010  E0EC  gnoExeced ran (exec-into transferred control), else DEAD
#include <stdint.h>

extern int access(const char *path, int mode);
extern int execvp(const char *file, char *const *argv);
extern int errno;

#define M(a) (*(volatile uint16_t *)(a))
#define OK   0x0001
#define BAD  0xDEAD
#define F_OK 0


int main(void) {
    char *nope[2];
    char *prog2[2];
    int   r;

    M(0x025000UL) = 0x0000;
    M(0x025002UL) = BAD;
    M(0x025004UL) = BAD;
    M(0x025008UL) = BAD;
    M(0x02500AUL) = BAD;
    M(0x025010UL) = BAD;

    // access() over Kstat: existing directory vs. missing path.
    if (access(".", F_OK) == 0) {
        M(0x025008UL) = OK;
    }
    if (access("/nonesuch/no", F_OK) == -1) {
        M(0x02500AUL) = OK;
    }

    // Failed exec: no such program on $PATH -> -1 with errno set.  (This
    // fails in buildPath, before K_execve, so it is safe and deterministic.)
    nope[0] = "nosuchprog";
    nope[1] = 0;
    errno   = 0;
    r = execvp("nosuchprog", nope);
    if (r == -1) {
        M(0x025002UL) = OK;
    }
    if (errno != 0) {
        M(0x025004UL) = OK;
    }

    // exec-into the 2nd program (GNO_PROG2).  On success this never returns;
    // gnoExeced sets 0x025010 and the reached-end word in the same image.
    prog2[0] = "gnoexeced";
    prog2[1] = 0;
    execvp("gnoexeced", prog2);

    // Only reached if the exec-into failed (no GNO_PROG2 on /bin).
    M(0x025000UL) = 0xC0DE;
    for (volatile unsigned long j = 0; j < 300000UL; j++) {
    }
    return 0;
}
