~crc_/retroforth

71993e39252de3c0b844be418ddfe4bf4d393845 — crc 6 months ago 15c2e83
nga-c: split unix device into a separate file

FossilOrigin-Name: 5748427d59ed3bfb9e7697d96ad65d12320cdf430ef60fd7ec9002b08dc58443
2 files changed, 224 insertions(+), 219 deletions(-)

A vm/nga-c/dev-unix.c
M vm/nga-c/retro.c
A vm/nga-c/dev-unix.c => vm/nga-c/dev-unix.c +220 -0
@@ 0,0 1,220 @@
/*---------------------------------------------------------------------
  Copyright (c) 2008 - 2022, Charles Childers

  Portions are based on Ngaro, which was additionally copyright
  by the following:

  Copyright (c) 2009 - 2010, Luke Parrish
  Copyright (c) 2010,        Marc Simpson
  Copyright (c) 2010,        Jay Skeer
  Copyright (c) 2011,        Kenneth Keating
  ---------------------------------------------------------------------*/

#ifdef ENABLE_UNIX
#include <sys/wait.h>
#include <unistd.h>

/*---------------------------------------------------------------------
  `unix_open_pipe()` is like `file_open()`, but for pipes. This pulls
  from the data stack:

  - mode       (number, TOS)
  - executable (string, NOS)

  Modes are:

  | Mode | Corresponds To | Description          |
  | ---- | -------------- | -------------------- |
  |  0   | r              | Open for reading     |
  |  1   | w              | Open for writing     |
  |  3   | r+             | Open for read/update |

  The file name should be a NULL terminated string. This will attempt
  to open the requested file and will return a handle (index number
  into the `OpenFileHandles` array).

  Once opened, you can use the standard file words to read/write to the
  process.
  ---------------------------------------------------------------------*/

void unix_open_pipe(NgaState *vm) {
  CELL slot, mode, name;
  char *request;
  slot = files_get_handle(vm);
  mode = stack_pop(vm);
  name = stack_pop(vm);
  request = string_extract(vm, name);
  if (slot > 0) {
    if (mode == 0)  vm->OpenFileHandles[slot] = popen(request, "r");
    if (mode == 1)  vm->OpenFileHandles[slot] = popen(request, "w");
    if (mode == 3)  vm->OpenFileHandles[slot] = popen(request, "r+");
  }
  if (vm->OpenFileHandles[slot] == NULL) {
    vm->OpenFileHandles[slot] = 0;
    slot = 0;
  }
  stack_push(vm, slot);
}

void unix_close_pipe(NgaState *vm) {
  pclose(vm->OpenFileHandles[TOS]);
  vm->OpenFileHandles[TOS] = 0;
  stack_pop(vm);
}

void unix_system(NgaState *vm) {
  int ignore = 0;
  ignore = system(string_extract(vm, stack_pop(vm)));
}

void unix_fork(NgaState *vm) {
  stack_push(vm, fork());
}

void unix_run_external(NgaState *vm) {
  char *line, *args[128];
  int i, status;
  pid_t pid;

  char **argv = args;
  line = string_extract(vm, stack_pop(vm));

  for(i = 0; i < 128; i++)
    args[i] = 0;

  while (*line != '\0') {
    while (*line == ' ' || *line == '\t' || *line == '\n')
      *line++ = '\0';
    *argv++ = line;
    while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n')
      line++;
  }

  if ((pid = fork()) < 0) {
    printf("*** ERROR: forking child process failed\n");
    exit(1);
  }
  else if (pid == 0) {
    int e = execvp(*args, args);
    if (e < 0) {
      printf("*** ERROR: exec failed with %d\n", e);
      exit(1);
    }
  } else {
  while (wait(&status) != pid)
    ;
  }
}


/*---------------------------------------------------------------------
  UNIX provides `execl` to execute a file, with various forms for
  arguments provided.

  RRE wraps this in several functions, one for each number of passed
  arguments. See the Glossary for details on what each takes from the
  stack. Each of these will return the error code if the execution
  fails.
  ---------------------------------------------------------------------*/

void unix_exec0(NgaState *vm) {
  char path[1025];
  strlcpy(path, string_extract(vm, stack_pop(vm)), 1024);
  execl(path, path, (char *)0);
  stack_push(vm, errno);
}

void unix_exec1(NgaState *vm) {
  char path[1025];
  char arg0[1025];
  strlcpy(arg0, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(path, string_extract(vm, stack_pop(vm)), 1024);
  execl(path, path, arg0, (char *)0);
  stack_push(vm, errno);
}

void unix_exec2(NgaState *vm) {
  char path[1025];
  char arg0[1025], arg1[1025];
  strlcpy(arg1, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(arg0, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(path, string_extract(vm, stack_pop(vm)), 1024);
  execl(path, path, arg0, arg1, (char *)0);
  stack_push(vm, errno);
}

void unix_exec3(NgaState *vm) {
  char path[1025];
  char arg0[1025], arg1[1025], arg2[1025];
  strlcpy(arg2, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(arg1, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(arg0, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(path, string_extract(vm, stack_pop(vm)), 1024);
  execl(path, path, arg0, arg1, arg2, (char *)0);
  stack_push(vm, errno);
}

void unix_exit(NgaState *vm) {
  exit(stack_pop(vm));
}

void unix_getpid(NgaState *vm) {
  stack_push(vm, getpid());
}

void unix_wait(NgaState *vm) {
  int a;
  stack_push(vm, wait(&a));
}

void unix_kill(NgaState *vm) {
  CELL a;
  a = stack_pop(vm);
  kill(stack_pop(vm), a);
}

void unix_write(NgaState *vm) {
  CELL a, b, c;
  ssize_t ignore;
  c = stack_pop(vm);
  b = stack_pop(vm);
  a = stack_pop(vm);
  ignore = write(fileno(vm->OpenFileHandles[c]), string_extract(vm, a), b);
}

void unix_chdir(NgaState *vm) {
  int ignore;
  ignore = chdir(string_extract(vm, stack_pop(vm)));
}

void unix_getenv(NgaState *vm) {
  CELL a, b;
  a = stack_pop(vm);
  b = stack_pop(vm);
  string_inject(vm, getenv(string_extract(vm, b)), a);
}

void unix_putenv(NgaState *vm) {
  putenv(string_extract(vm, stack_pop(vm)));
}

void unix_sleep(NgaState *vm) {
  sleep(stack_pop(vm));
}

Handler UnixActions[] = {
  unix_system,    unix_fork,       unix_exec0,   unix_exec1,   unix_exec2,
  unix_exec3,     unix_exit,       unix_getpid,  unix_wait,    unix_kill,
  unix_open_pipe, unix_close_pipe, unix_write,   unix_chdir,   unix_getenv,
  unix_putenv,    unix_sleep,      unix_run_external
};

void query_unix(NgaState *vm) {
  stack_push(vm, 3);
  stack_push(vm, 8);
}

void io_unix(NgaState *vm) {
  UnixActions[stack_pop(vm)](vm);
}
#endif

M vm/nga-c/retro.c => vm/nga-c/retro.c +4 -219
@@ 32,18 32,6 @@
#include <math.h>
#endif

#ifdef ENABLE_SOCKETS
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#endif

#ifdef ENABLE_UNIX
#include <sys/wait.h>
#include <unistd.h>
#endif

#ifdef ENABLE_FFI
#include <dlfcn.h>
#endif


@@ 964,213 952,6 @@ void io_filesystem(NgaState *vm) {
}


#ifdef ENABLE_UNIX
/*---------------------------------------------------------------------
  `unix_open_pipe()` is like `file_open()`, but for pipes. This pulls
  from the data stack:

  - mode       (number, TOS)
  - executable (string, NOS)

  Modes are:

  | Mode | Corresponds To | Description          |
  | ---- | -------------- | -------------------- |
  |  0   | r              | Open for reading     |
  |  1   | w              | Open for writing     |
  |  3   | r+             | Open for read/update |

  The file name should be a NULL terminated string. This will attempt
  to open the requested file and will return a handle (index number
  into the `OpenFileHandles` array).

  Once opened, you can use the standard file words to read/write to the
  process.
  ---------------------------------------------------------------------*/

void unix_open_pipe(NgaState *vm) {
  CELL slot, mode, name;
  char *request;
  slot = files_get_handle(vm);
  mode = stack_pop(vm);
  name = stack_pop(vm);
  request = string_extract(vm, name);
  if (slot > 0) {
    if (mode == 0)  vm->OpenFileHandles[slot] = popen(request, "r");
    if (mode == 1)  vm->OpenFileHandles[slot] = popen(request, "w");
    if (mode == 3)  vm->OpenFileHandles[slot] = popen(request, "r+");
  }
  if (vm->OpenFileHandles[slot] == NULL) {
    vm->OpenFileHandles[slot] = 0;
    slot = 0;
  }
  stack_push(vm, slot);
}

void unix_close_pipe(NgaState *vm) {
  pclose(vm->OpenFileHandles[TOS]);
  vm->OpenFileHandles[TOS] = 0;
  stack_pop(vm);
}

void unix_system(NgaState *vm) {
  int ignore = 0;
  ignore = system(string_extract(vm, stack_pop(vm)));
}

void unix_fork(NgaState *vm) {
  stack_push(vm, fork());
}

void unix_run_external(NgaState *vm) {
  char *line, *args[128];
  int i, status;
  pid_t pid;

  char **argv = args;
  line = string_extract(vm, stack_pop(vm));

  for(i = 0; i < 128; i++)
    args[i] = 0;

  while (*line != '\0') {
    while (*line == ' ' || *line == '\t' || *line == '\n')
      *line++ = '\0';
    *argv++ = line;
    while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n')
      line++;
  }

  if ((pid = fork()) < 0) {
    printf("*** ERROR: forking child process failed\n");
    exit(1);
  }
  else if (pid == 0) {
    int e = execvp(*args, args);
    if (e < 0) {
      printf("*** ERROR: exec failed with %d\n", e);
      exit(1);
    }
  } else {
  while (wait(&status) != pid)
    ;
  }
}


/*---------------------------------------------------------------------
  UNIX provides `execl` to execute a file, with various forms for
  arguments provided.

  RRE wraps this in several functions, one for each number of passed
  arguments. See the Glossary for details on what each takes from the
  stack. Each of these will return the error code if the execution
  fails.
  ---------------------------------------------------------------------*/

void unix_exec0(NgaState *vm) {
  char path[1025];
  strlcpy(path, string_extract(vm, stack_pop(vm)), 1024);
  execl(path, path, (char *)0);
  stack_push(vm, errno);
}

void unix_exec1(NgaState *vm) {
  char path[1025];
  char arg0[1025];
  strlcpy(arg0, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(path, string_extract(vm, stack_pop(vm)), 1024);
  execl(path, path, arg0, (char *)0);
  stack_push(vm, errno);
}

void unix_exec2(NgaState *vm) {
  char path[1025];
  char arg0[1025], arg1[1025];
  strlcpy(arg1, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(arg0, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(path, string_extract(vm, stack_pop(vm)), 1024);
  execl(path, path, arg0, arg1, (char *)0);
  stack_push(vm, errno);
}

void unix_exec3(NgaState *vm) {
  char path[1025];
  char arg0[1025], arg1[1025], arg2[1025];
  strlcpy(arg2, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(arg1, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(arg0, string_extract(vm, stack_pop(vm)), 1024);
  strlcpy(path, string_extract(vm, stack_pop(vm)), 1024);
  execl(path, path, arg0, arg1, arg2, (char *)0);
  stack_push(vm, errno);
}

void unix_exit(NgaState *vm) {
  exit(stack_pop(vm));
}

void unix_getpid(NgaState *vm) {
  stack_push(vm, getpid());
}

void unix_wait(NgaState *vm) {
  int a;
  stack_push(vm, wait(&a));
}

void unix_kill(NgaState *vm) {
  CELL a;
  a = stack_pop(vm);
  kill(stack_pop(vm), a);
}

void unix_write(NgaState *vm) {
  CELL a, b, c;
  ssize_t ignore;
  c = stack_pop(vm);
  b = stack_pop(vm);
  a = stack_pop(vm);
  ignore = write(fileno(vm->OpenFileHandles[c]), string_extract(vm, a), b);
}

void unix_chdir(NgaState *vm) {
  int ignore;
  ignore = chdir(string_extract(vm, stack_pop(vm)));
}

void unix_getenv(NgaState *vm) {
  CELL a, b;
  a = stack_pop(vm);
  b = stack_pop(vm);
  string_inject(vm, getenv(string_extract(vm, b)), a);
}

void unix_putenv(NgaState *vm) {
  putenv(string_extract(vm, stack_pop(vm)));
}

void unix_sleep(NgaState *vm) {
  sleep(stack_pop(vm));
}

Handler UnixActions[] = {
  unix_system,    unix_fork,       unix_exec0,   unix_exec1,   unix_exec2,
  unix_exec3,     unix_exit,       unix_getpid,  unix_wait,    unix_kill,
  unix_open_pipe, unix_close_pipe, unix_write,   unix_chdir,   unix_getenv,
  unix_putenv,    unix_sleep,      unix_run_external
};

void query_unix(NgaState *vm) {
  stack_push(vm, 3);
  stack_push(vm, 8);
}

void io_unix(NgaState *vm) {
  UnixActions[stack_pop(vm)](vm);
}
#endif


/* Time and Date Functions --------------------------------------------*/
#ifdef ENABLE_CLOCK
void clock_time(NgaState *vm) {


@@ 1288,6 1069,10 @@ void query_rng(NgaState *vm) {
#include "dev-sockets.c"
#endif

#ifdef ENABLE_UNIX
#include "dev-unix.c"
#endif


/*---------------------------------------------------------------------
  Now on to I/O and extensions!