text: /*************************************************************************
   *									 *
   *	 YAP Prolog 							 *
   *									 *
   *	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
   *									 *
   * Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
   *									 *
   **************************************************************************
   *									 *
   * File:		sysbits.c *
   * Last rev:	4/03/88							 *
   * mods: *
   * comments:	very much machine dependent routines			 *
   *									 *
   *************************************************************************/
  #ifdef SCCS
  static char SccsId[] = "%W% %G%";
  #endif
  
  #include "sysbits.h"
  
  /// File Error Handler
  static void FileError(yap_error_number type, Term where, const char *format,
                        ...) {
  
    if (trueLocalPrologFlag(FILEERRORS_FLAG)) {
      va_list ap;
  
      va_start(ap, format);
      /* now build the error string */
      Yap_Error(type, TermNil, format, ap);
      va_end(ap);
    }
  }
  
  static Int p_sh(USES_REGS1);
  static Int p_shell(USES_REGS1);
  static Int p_system(USES_REGS1);
  static Int p_mv(USES_REGS1);
  static Int p_dir_sp(USES_REGS1);
  static Int p_getenv(USES_REGS1);
  static Int p_putenv(USES_REGS1);
  static Term do_glob(const char *spec, bool ok_to);
  #ifdef MACYAP
  
  static int chdir(char *);
  /* #define signal	skel_signal */
  #endif /* MACYAP */
  static const char *expandVars(const char *spec, char *u);
  
  void exit(int);
  
  static void freeBuffer(const void *ptr) {
    CACHE_REGS
    if (ptr == NULL || ptr == LOCAL_FileNameBuf || ptr == LOCAL_FileNameBuf2)
      return;
    free((void *)ptr);
  }
  
  #ifdef _WIN32
  void Yap_WinError(char *yap_error) {
    char msg[256];
    /* Error, we could not read time */
    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                  NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  msg, 256, NULL);
    Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, TermNil, "%s at %s", msg, yap_error);
  }
  #endif /* __WINDOWS__ */
  
  #define is_valid_env_char(C)                                                   \
    (((C) >= 'a' && (C) <= 'z') || ((C) >= 'A' && (C) <= 'Z') || (C) == '_')
  
  #if __ANDROID__
  
  AAssetManager *Yap_assetManager;
  
  void *Yap_openAssetFile(const char *path) {
    const char *p = path + 8;
    AAsset *asset = AAssetManager_open(Yap_assetManager, p, AASSET_MODE_UNKNOWN);
    return asset;
  }
  
  bool Yap_isAsset(const char *path) {
    if (Yap_assetManager == NULL)
      return false;
    return path[0] == '/' && path[1] == 'a' && path[2] == 's' && path[3] == 's' &&
           path[4] == 'e' && path[5] == 't' && path[6] == 's' &&
           (path[7] == '/' || path[7] == '\0');
  }
  
  bool Yap_AccessAsset(const char *name, int mode) {
    AAssetManager *mgr = Yap_assetManager;
    const char *bufp = name + 7;
  
    if (bufp[0] == '/')
      bufp++;
    if ((mode & W_OK) == W_OK) {
      return false;
    }
    // directory works if file exists
    AAssetDir *assetDir = AAssetManager_openDir(mgr, bufp);
    if (assetDir) {
      AAssetDir_close(assetDir);
      return true;
    }
    return false;
  }
  
  bool Yap_AssetIsFile(const char *name) {
    AAssetManager *mgr = Yap_assetManager;
    const char *bufp = name + 7;
    if (bufp[0] == '/')
      bufp++;
    // check if file is a directory.
    AAsset *asset = AAssetManager_open(mgr, bufp, AASSET_MODE_UNKNOWN);
    if (!asset)
      return false;
    AAsset_close(asset);
    return true;
  }
  
  bool Yap_AssetIsDir(const char *name) {
    AAssetManager *mgr = Yap_assetManager;
    const char *bufp = name + 7;
    if (bufp[0] == '/')
      bufp++;
    // check if file is a directory.
    AAssetDir *assetDir = AAssetManager_openDir(mgr, bufp);
    if (!assetDir) {
      return false;
    }
    AAssetDir_close(assetDir);
    AAsset *asset = AAssetManager_open(mgr, bufp, AASSET_MODE_UNKNOWN);
    if (!asset)
      return true;
    AAsset_close(asset);
    return false;
  }
  
  int64_t Yap_AssetSize(const char *name) {
    AAssetManager *mgr = Yap_assetManager;
    const char *bufp = name + 7;
    if (bufp[0] == '/')
      bufp++;
    AAsset *asset = AAssetManager_open(mgr, bufp, AASSET_MODE_UNKNOWN);
    if (!asset)
      return -1;
    off64_t len = AAsset_getLength64(asset);
    AAsset_close(asset);
    return len;
  }
  #endif
  
  /// is_directory: verifies whether an expanded file name
  /// points at a readable directory
  static bool is_directory(const char *FileName) {
  #ifdef __ANDROID__
    if (Yap_isAsset(FileName)) {
      return Yap_AssetIsDir(FileName);
    }
  
  #endif
  
  #ifdef __WINDOWS__
    DWORD dwAtts = GetFileAttributes(FileName);
    if (dwAtts == INVALID_FILE_ATTRIBUTES)
      return false;
    return (dwAtts & FILE_ATTRIBUTE_DIRECTORY);
  #elif HAVE_LSTAT
    struct stat buf;
  
    if (lstat(FileName, &buf) == -1) {
      /* return an error number */
      return false;
    }
    return S_ISDIR(buf.st_mode);
  #else
    Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil,
              "stat not available in this configuration");
    return false;
  #endif
  }
  
  bool Yap_Exists(const char *f) {
  #if _WIN32
    if (_access(f, 0) == 0)
      return true;
    if (errno == EINVAL) {
      Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil, "bad flags to access");
    }
    return false;
  #elif HAVE_ACCESS
    if (access(f, F_OK) == 0)
      return true;
    if (errno == EINVAL) {
      Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil, "bad flags to access");
    }
    return false;
  #else
    Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil,
              "access not available in this configuration");
    return false;
  #endif
  }
  
  static int dir_separator(int ch) {
  #ifdef MAC
    return (ch == ':');
  #elif ATARI || _MSC_VER
    return (ch == '\\');
  #elif defined(__MINGW32__) || defined(__CYGWIN__)
    return (ch == '\\' || ch == '/');
  #else
    return (ch == '/');
  #endif
  }
  
  int Yap_dir_separator(int ch) { return dir_separator(ch); }
  
  #if __WINDOWS__
  #include <psapi.h>
  
  char *libdir = NULL;
  #endif
  
  bool Yap_IsAbsolutePath(const char *p0) {
    // verify first if expansion is needed: ~/ or $HOME/
    const char *p = expandVars(p0, LOCAL_FileNameBuf);
    bool nrc;
  #if _WIN32 || __MINGW32__
    nrc = !PathIsRelative(p);
  #else
    nrc = (p[0] == '/');
  #endif
    return nrc;
  }
  
  #define isValidEnvChar(C)                                                      \
    (((C) >= 'a' && (C) <= 'z') || ((C) >= 'A' && (C) <= 'Z') || (C) == '_')
  
  // this is necessary because
  // support for ~expansion at the beginning
  // systems like Android do not do this.
  static const char *PlExpandVars(const char *source, const char *root,
                                  char *result) {
    CACHE_REGS
    const char *src = source;
    if (!result)
      result = malloc(YAP_FILENAME_MAX + 1);
  
    if (strlen(source) >= YAP_FILENAME_MAX) {
      Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, TermNil,
                "%s in true_file-name is larger than the buffer size (%d bytes)",
                source, strlen(source));
    }
    /* step 1: eating home information */
    if (source[0] == '~') {
      if (dir_separator(source[1]) || source[1] == '\0') {
        char *s;
        src++;
  #if defined(_WIN32)
        s = getenv("HOMEDRIVE");
        if (s != NULL)
          strncpy(result, getenv("HOMEDRIVE"), YAP_FILENAME_MAX);
  // s = getenv("HOMEPATH");
  #else
        s = getenv("HOME");
  #endif
        if (s != NULL)
          strncpy(result, s, YAP_FILENAME_MAX);
        strcat(result, src);
        return result;
      } else {
  #if HAVE_GETPWNAM
        struct passwd *user_passwd;
        char *res = result;
  
        src++;
        while (!dir_separator((*res = *src)) && *res != '\0')
          res++, src++;
        res[0] = '\0';
        if ((user_passwd = getpwnam(result)) == NULL) {
          FileError(SYSTEM_ERROR_OPERATING_SYSTEM,
                    MkAtomTerm(Yap_LookupAtom(source)),
                    "User %s does not exist in %s", result, source);
          return NULL;
        }
        strncpy(result, user_passwd->pw_dir, YAP_FILENAME_MAX);
        strcat(result, src);
  #else
        FileError(
            SYSTEM_ERROR_OPERATING_SYSTEM, MkAtomTerm(Yap_LookupAtom(source)),
            "User %s cannot be found in %s, missing getpwnam", result, source);
        return NULL;
  #endif
      }
      return result;
    }
    // do VARIABLE expansion
    else if (source[0] == '$') {
      /* follow SICStus expansion rules */
      char v[YAP_FILENAME_MAX + 1];
      int ch;
      char *s, *res;
      src = source + 1;
      if (src[0] == '{') {
        res = v;
        src++;
        while ((*res = (ch = *src++)) && isValidEnvChar(ch) && ch != '}') {
          res++;
        }
        if (ch == '}') {
          // {...}
          // done
          res[0] = '\0';
        }
      } else {
        res = v;
        while ((*res = (ch = *src++)) && isValidEnvChar(ch) && ch != '}') {
          res++;
        }
        src--;
        res[0] = '\0';
      }
      if ((s = (char *)getenv(v))) {
        strcpy(result, s);
        strcat(result, src);
      } else
        strcpy(result, src);
    } else {
      size_t tocp = strlen(src);
      if (root) {
        tocp = strlen(root) + 1;
      }
      if (tocp > YAP_FILENAME_MAX) {
        Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, MkStringTerm(src),
                  "path too long");
        return NULL;
      }
      if (root) {
        strncpy(result, root, YAP_FILENAME_MAX);
        strncat(result, "/", YAP_FILENAME_MAX);
        strncat(result, source, YAP_FILENAME_MAX);
      } else {
        strncpy(result, source, strlen(src) + 1);
      }
    }
    return result;
  }
  
  #if _WIN32
  // straightforward conversion from Unix style to WIN style
  // check cygwin path.cc for possible improvements
  static char *unix2win(const char *source, char *target, int max) {
    char *s = target;
    const char *s0 = source;
    char *s1;
    int ch;
  
    if (s == NULL)
      s = malloc(YAP_FILENAME_MAX + 1);
    s1 = s;
    // win32 syntax
    // handle drive notation, eg //a/
    if (s0[0] == '\0') {
      s[0] = '.';
      s[1] = '\0';
      return s;
    }
    if (s0[0] == '/' && s0[1] == '/' && isalpha(s0[2]) && s0[3] == '/') {
      s1[0] = s0[2];
      s1[1] = ':';
      s1[2] = '\\';
      s0 += 4;
      s1 += 3;
    }
    while ((ch = *s1++ = *s0++)) {
      if (ch == '$') {
        s1[-1] = '%';
        ch = *s0;
        // handle $(....)
        if (ch == '{') {
          s0++;
          while ((ch = *s0++) != '}') {
            *s1++ = ch;
            if (ch == '\0')
              return FALSE;
          }
          *s1++ = '%';
        } else {
          while (((ch = *s1++ = *s0++) >= 'A' && ch <= 'Z') ||
                 (ch >= 'a' && ch <= 'z') || (ch == '-') ||
                 (ch >= '0' && ch <= '9') || (ch == '_'))
            ;
          s1[-1] = '%';
          *s1++ = ch;
          if (ch == '\0') {
            s1--;
            s0--;
          }
        }
      } else if (ch == '/')
        s1[-1] = '\\';
    }
    return s;
  }
  #endif
  
  static char *OsPath(const char *p, char *buf) { return (char *)p; }
  
  static char *PrologPath(const char *Y, char *X) { return (char *)Y; }
  
  #if _WIN32
  #define HAVE_BASENAME 1
  #define HAVE_REALPATH 1
  #endif
  
  static bool ChDir(const char *path) {
    bool rc = false;
    char qp[FILENAME_MAX + 1];
    const char *qpath = Yap_AbsoluteFile(path, qp, true);
  
  #ifdef __ANDROID__
    if (GLOBAL_AssetsWD) {
      freeBuffer(GLOBAL_AssetsWD);
      GLOBAL_AssetsWD = NULL;
    }
    if (Yap_isAsset(qpath)) {
      AAssetManager *mgr = Yap_assetManager;
  
      const char *ptr = qpath + 8;
      AAssetDir *d;
      if (ptr[0] == '/')
        ptr++;
      d = AAssetManager_openDir(mgr, ptr);
      if (d) {
        GLOBAL_AssetsWD = malloc(strlen(qpath) + 1);
        strcpy(GLOBAL_AssetsWD, qpath);
        AAssetDir_close(d);
        return true;
      }
      return false;
    } else {
      GLOBAL_AssetsWD = NULL;
    }
  #endif
  #if _WIN32
    rc = true;
    if (qpath != NULL && qpath[0] &&
        (rc = (SetCurrentDirectory(qpath) != 0)) == 0) {
      Yap_WinError("SetCurrentDirectory failed");
    }
  #else
    rc = (chdir(qpath) == 0);
  #endif
    if (qpath != qp && qpath != LOCAL_FileNameBuf && qpath != LOCAL_FileNameBuf2)
      free((char *)qpath);
    return rc;
  }
  
  static const char *myrealpath(const char *path, char *out) {
    if (!out)
      out = LOCAL_FileNameBuf;
  #if _WIN32
    DWORD retval = 0;
  
    // notice that the file does not need to exist
    retval = GetFullPathName(path, YAP_FILENAME_MAX, out, NULL);
    if (retval == 0) {
      Yap_WinError("Generating a full path name for a file");
      return NULL;
    }
    return out;
  #elif HAVE_REALPATH
    {
      char *rc = realpath(path, NULL);
  
      if (rc) {
        return rc;
      }
      // rc = NULL;
      if (errno == ENOENT || errno == EACCES) {
        char base[YAP_FILENAME_MAX + 1];
        strncpy(base, path, YAP_FILENAME_MAX - 1);
        rc = realpath(dirname(base), out);
  
        if (rc) {
          // base may haave been destroyed
          const char *b = basename((char *)path);
          size_t e = strlen(rc);
          size_t bs = strlen(b);
  
          if (rc != out && rc != base) {
            rc = realloc(rc, e + bs + 2);
          }
  #if _WIN32
          if (rc[e - 1] != '\\' && rc[e - 1] != '/') {
            rc[e] = '\\';
            rc[e + 1] = '\0';
          }
  #else
          if (rc[e - 1] != '/') {
            rc[e] = '/';
            rc[e + 1] = '\0';
          }
  #endif
          strcat(rc, b);
          return rc;
        }
      }
    }
  #endif
    out = malloc(strlen(path) + 1);
    strcpy(out, path);
    return out;
  }
  
  static const char *expandVars(const char *spec, char *u) {
    CACHE_REGS
  #if _WIN32 || defined(__MINGW32__)
    char *out;
  
    // first pass, remove Unix style stuff
    if ((out = unix2win(spec, u, YAP_FILENAME_MAX)) == NULL)
      return NULL;
    spec = u;
  #endif
    bool ok_to = LOCAL_PrologMode && !(LOCAL_PrologMode & BootMode);
    if (ok_to) {
      Term t = do_glob(spec, true);
      if (IsPairTerm(t))
        return RepAtom(AtomOfTerm(HeadOfTerm(t)))->StrOfAE;
      return NULL;
    }
    return spec;
  }
  
  /**
   * generate absolute path, if ok first expand SICStus Prolog style
   *
   * @param[in]  spec the file path, including `~` and `$`.
   * @param[in]  ok where to process `~` and `$`.
   *
   * @return tmp, or NULL, in malloced memory
   */
  const char *Yap_AbsoluteFile(const char *spec, char *rc0, bool ok) {
    const char *rc;
    const char *spec1;
    const char *spec2;
    char rc1[YAP_FILENAME_MAX + 1];
  
  /// spec gothe original spec;
  /// rc0 may be an outout buffer
  /// rc1 the internal buffer
  ///
  /// PlExpandVars
  
  #if _WIN32
    char rc2[YAP_FILENAME_MAX];
    if ((rc = unix2win(spec, rc2, YAP_FILENAME_MAX)) == NULL) {
      return NULL;
    }
    spec1 = rc;
  #else
    spec1 = spec;
  #endif
    /// spec gothe original spec;
    /// rc1 the internal buffer
    if (ok) {
      const char *q = PlExpandVars(spec1, NULL, rc1);
      if (!q)
        spec2 = spec1;
      else
        spec2 = q;
    } else {
      spec2 = spec1;
    }
    rc = myrealpath(spec2, rc0);
    return rc;
  }
  
  static Term
      /* Expand the string for the program to run.  */
      do_glob(const char *spec, bool glob_vs_wordexp) {
    CACHE_REGS
    char u[YAP_FILENAME_MAX + 1];
    const char *espec = u;
    if (spec == NULL) {
      return TermNil;
    }
  #if _WIN32
    {
      WIN32_FIND_DATA find;
      HANDLE hFind;
      CELL *dest;
      Term tf;
      char drive[_MAX_DRIVE];
      char dir[_MAX_DIR];
      char fname[_MAX_FNAME];
      char ext[_MAX_EXT];
  
      _splitpath(spec, drive, dir, fname, ext);
      _makepath(u, drive, dir, fname, ext);
  
      // first pass, remove Unix style stuff
      hFind = FindFirstFile(u, &find);
      if (hFind == INVALID_HANDLE_VALUE) {
        return TermNil;
      } else {
        tf = AbsPair(HR);
        _makepath(u, drive, dir, find.cFileName, NULL);
        HR[0] = MkAtomTerm(Yap_LookupAtom(u));
        HR[1] = TermNil;
        dest = HR + 1;
        HR += 2;
        while (FindNextFile(hFind, &find)) {
          *dest = AbsPair(HR);
          _makepath(u, drive, dir, find.cFileName, NULL);
          HR[0] = MkAtomTerm(Yap_LookupAtom(u));
          HR[1] = TermNil;
          dest = HR + 1;
          HR += 2;
        }
        FindClose(hFind);
      }
      return tf;
    }
  #elif HAVE_WORDEXP || HAVE_GLOB
    strncpy(u, spec, sizeof(u));
    /* Expand the string for the program to run.  */
    size_t pathcount;
  #if HAVE_GLOB
    glob_t gresult;
  #endif
  #if HAVE_WORDEXP
    wordexp_t wresult;
  #endif
  #if HAVE_GLOB || HAVE_WORDEXP
    char **ss = NULL;
    int flags = 0, j;
  #endif
    if (glob_vs_wordexp) {
  #if HAVE_GLOB
  #ifdef GLOB_NOCHECK
      flags = GLOB_NOCHECK;
  #else
      flags = 0;
  #endif
  #ifdef GLOB_BRACE
      flags |= GLOB_BRACE | GLOB_TILDE;
  #endif
      switch (glob(espec, flags, NULL, &gresult)) {
      case 0: /* Successful.  */
        ss = gresult.gl_pathv;
        pathcount = gresult.gl_pathc;
        if (pathcount) {
          break;
        }
      case GLOB_NOMATCH:
        globfree(&gresult);
        { return TermNil; }
      case GLOB_ABORTED:
        PlIOError(SYSTEM_ERROR_OPERATING_SYSTEM, ARG1, "glob aborted: %sn",
                  strerror(errno));
        globfree(&gresult);
        return TermNil;
      case GLOB_NOSPACE:
        Yap_Error(RESOURCE_ERROR_HEAP, ARG1, "glob ran out of space: %sn",
                  strerror(errno));
        globfree(&gresult);
        return TermNil;
      /* If the error was WRDE_NOSPACE,
       then perhaps part of the result was allocated.  */
      default: /* Some other error.  */
        return TermNil;
      }
  #endif
    } else {
  #if HAVE_WORDEXP
      int rc;
      memset(&wresult, 0, sizeof(wresult));
      switch ((rc = wordexp(espec, &wresult, flags))) {
      case 0: /* Successful.  */
        ss = wresult.we_wordv;
        pathcount = wresult.we_wordc;
        if (pathcount) {
          break;
        } else {
          Term t;
          t = MkAtomTerm(Yap_LookupAtom(expandVars(espec, NULL)));
          wordfree(&wresult);
          return MkPairTerm(t, TermNil);
        }
      case WRDE_NOSPACE:
        /* If the error was WRDE_NOSPACE,
         then perhaps part of the result was allocated.  */
        Yap_Error(RESOURCE_ERROR_HEAP, ARG1, "wordexp ran out of space: %s",
                  strerror(errno));
        wordfree(&wresult);
        return TermNil;
      default: /* Some other error.  */
        PlIOError(SYSTEM_ERROR_OPERATING_SYSTEM, ARG1, "wordexp failed: %s",
                  strerror(errno));
        wordfree(&wresult);
        return TermNil;
      }
  #endif
    }
    const char *tmp;
    Term tf = TermNil;
    for (j = 0; j < pathcount; j++) {
      const char *s = ss[pathcount - (j + 1)];
      tmp = s;
      // if (!exists(s))
      //  continue;
      Atom a = Yap_LookupAtom(tmp);
      tf = MkPairTerm(MkAtomTerm(a), tf);
    }
  #if HAVE_GLOB
    if (glob_vs_wordexp)
      globfree(&gresult);
  #endif
  #if HAVE_WORDEXP
    if (!glob_vs_wordexp)
      wordfree(&wresult);
  #endif
    return tf;
  #else
    // just use basic
    return MkPairTerm(MkAtomTerm(Yap_LookupAtom(espec)), TermNil);
  #endif
  }
  
  /**
   * @pred prolog_expanded_file_system_path( +PrologPath, +ExpandVars, -OSPath )
   *
   * Apply basic transformations to paths, and conidtionally apply
   * traditional SICStus-style variable expansion.
   *
   * @param PrologPath the source, may be atom or string
   * @param ExpandVars expand initial occurrence of ~ or $
   * @param Prefix add this path before _PrologPath_
   * @param OSPath pathname.
   *
   * @return
   */
  static Int real_path(USES_REGS1) {
    Term t1 = Deref(ARG1);
    const char *cmd, *rc0;
  
    if (IsAtomTerm(t1)) {
      cmd = RepAtom(AtomOfTerm(t1))->StrOfAE;
    } else if (IsStringTerm(t1)) {
      cmd = StringOfTerm(t1);
    } else {
      return false;
    }
  #if _WIN32
    char cmd2[YAP_FILENAME_MAX + 1];
    char *rc;
  
    if ((rc = unix2win(cmd, cmd2, YAP_FILENAME_MAX)) == NULL) {
      return false;
    }
    cmd = rc;
  #endif
  
    rc0 = myrealpath(cmd, NULL);
    if (!rc0) {
      PlIOError(SYSTEM_ERROR_OPERATING_SYSTEM, ARG1, NULL);
    }
    return Yap_unify(MkAtomTerm(Yap_LookupAtom(rc0)), ARG2);
  }
  
  #define EXPAND_FILENAME_DEFS()                                                 \
    PAR("parameter_expansion", isatom, EXPAND_FILENAME_PARAMETER_EXPANSION)      \
    , PAR("commands", booleanFlag, EXPAND_FILENAME_COMMANDS),                    \
        PAR(NULL, ok, EXPAND_FILENAME_END)
  
  #define PAR(x, y, z) z
  
  typedef enum expand_filename_enum_choices {
    EXPAND_FILENAME_DEFS()
  } expand_filename_enum_choices_t;
  
  #undef PAR
  
  #define PAR(x, y, z)                                                           \
    { x, y, z }
  
  static const param_t expand_filename_defs[] = {EXPAND_FILENAME_DEFS()};
  #undef PAR
  
  static Term do_expand_file_name(Term t1, Term opts USES_REGS) {
    xarg *args;
    expand_filename_enum_choices_t i;
    bool use_system_expansion = true;
    char *tmpe = NULL;
    const char *spec;
    Term tf;
  
    if (IsVarTerm(t1)) {
      Yap_Error(INSTANTIATION_ERROR, t1, NULL);
      return TermNil;
    } else if (IsAtomTerm(t1)) {
      spec = AtomTermName(t1);
    } else if (IsStringTerm(t1)) {
      spec = StringOfTerm(t1);
    } else {
      Yap_Error(TYPE_ERROR_ATOM, t1, NULL);
      return TermNil;
    }
  #if _WIN32
    {
      char *rc;
      char cmd2[YAP_FILENAME_MAX + 1];
  
      if ((rc = unix2win(spec, cmd2, YAP_FILENAME_MAX)) == NULL) {
        return false;
      }
      spec = rc;
    }
  #endif
  
    args = Yap_ArgListToVector(opts, expand_filename_defs, EXPAND_FILENAME_END);
    if (args == NULL) {
      return TermNil;
    }
    tmpe = malloc(YAP_FILENAME_MAX + 1);
  
    for (i = 0; i < EXPAND_FILENAME_END; i++) {
      if (args[i].used) {
        Term t = args[i].tvalue;
        switch (i) {
        case EXPAND_FILENAME_PARAMETER_EXPANSION:
          if (t == TermProlog) {
            const char *s = expandVars(spec, LOCAL_FileNameBuf);
            if (s == NULL) {
              return TermNil;
            }
            strcpy(tmpe, s);
          } else if (t == TermTrue) {
            use_system_expansion = true;
          } else if (t == TermFalse) {
            use_system_expansion = false;
          }
          break;
        case EXPAND_FILENAME_COMMANDS:
          if (!use_system_expansion) {
            use_system_expansion = true;
  #if 0 // def WRDE_NOCMD
                      if (t == TermFalse) {
                          flags = WRDE_NOCMD;
                      }
  #endif
          }
        case EXPAND_FILENAME_END:
          break;
        }
      }
    }
  
    if (!use_system_expansion) {
      const char *o = expandVars(spec, NULL);
      if (!o)
        return false;
      return MkPairTerm(MkAtomTerm(Yap_LookupAtom(o)), TermNil);
    }
    tf = do_glob(spec, true);
    return tf;
  }
  
  /* @pred expand_file_name( +Pattern, -ListOfPaths) is det
  
  This builtin receives a pattern and expands it into a list of files.
    In Unix-like systems, YAP applies glob to expand patterns such as '*', '.',
  and '?'. Further variable expansion
  may also happen. glob is shell-dependent: som   Yap_InitCPred
  ("absolute_file_system_path", 2, absolute_file_system_path, 0);
      Yap_InitCPred ("real_path", 2, prolog_realpath, 0);
      Yap_InitCPred ("true_file_name", 2,
                     true_file_name, SyncPredFlag);
      Yap_InitCPred ("true_file_name", 3, true_file_name3, SyncPredFlag);
  e shells allow command execution and brace-expansion.
  
  */
  static Int expand_file_name(USES_REGS1) {
    Term tf = do_expand_file_name(Deref(ARG1), TermNil PASS_REGS);
    if (tf == 0)
      return false;
    return Yap_unify(tf, ARG2);
  }
  
  static Int expand_file_name3(USES_REGS1) {
    Term tf = do_expand_file_name(Deref(ARG1), Deref(ARG2) PASS_REGS);
    return Yap_unify(tf, ARG3);
  }
  
  /*
         static char *canoniseFileName( char *path) {
         #if HAVE_REALPATH && HAVE_BASENAME
         #if _WIN32 || defined(__MINGW32__)
           char *o = malloc(YAP_FILENAME_MAX+1);
           if (!o)
             return NULL;
           // first pass, remove Unix style stuff
           if (unix2win(path, o, YAP_FILENAME_MAX) == NULL)
             return NULL;
           path = o;
         #endif
           char *rc;
           if (tmp == NULL) return NULL;
           rc = myrealpath(path);
         #if _WIN32 || defined(__MINGW32__)
           freeBuffer(o);
  #endif
    return rc;
  #endif
  }
  */
  
  static Int absolute_file_system_path(USES_REGS1) {
    Term t = Deref(ARG1);
    const char *fp;
    bool rc;
    char s[MAXPATHLEN + 1];
    const char *text = Yap_TextTermToText(t, s, MAXPATHLEN, LOCAL_encoding);
  
    if (text == NULL) {
      return false;
    }
    if (!(fp = Yap_AbsoluteFile(RepAtom(AtomOfTerm(t))->StrOfAE, NULL, true)))
      return false;
    rc = Yap_unify(Yap_MkTextTerm(fp, LOCAL_encoding, t), ARG2);
    if (fp != s)
      freeBuffer((void *)fp);
    return rc;
  }
  
  static Int prolog_to_os_filename(USES_REGS1) {
    Term t = Deref(ARG1), t2 = Deref(ARG2);
    char *fp;
    char out[MAXPATHLEN + 1];
  
    if (IsVarTerm(t)) {
  
      if (IsVarTerm(t2)) {
        Yap_Error(INSTANTIATION_ERROR, t, "prolog_to_os_filename");
        return false;
      } else if (IsAtomTerm(t2)) {
        if (!(fp = PrologPath(RepAtom(AtomOfTerm(t2))->StrOfAE, out)))
          return false;
        return Yap_unify(ARG1, MkAtomTerm(Yap_LookupAtom(fp)));
      } else {
        Yap_Error(TYPE_ERROR_ATOM, t2, "prolog_to_os_filename");
        return false;
      }
    } else if (!IsAtomTerm(t)) {
      Yap_Error(TYPE_ERROR_ATOM, t, "prolog_to_os_filename");
      return false;
    }
  
    if (!(fp = OsPath(RepAtom(AtomOfTerm(t))->StrOfAE, out)))
      return false;
    return Yap_unify(MkAtomTerm(Yap_LookupAtom(fp)), ARG2);
  }
  
  Atom Yap_TemporaryFile(const char *prefix, int *fd) {
  #if HAVE_MKSTEMP
    char *tmp = malloc(PATH_MAX);
    int n;
    int f;
    if (tmp == NULL)
      return NIL;
    strncpy(tmp, prefix, PATH_MAX - 1);
    n = strlen(tmp);
    if (n >= 6 && tmp[n - 1] == 'X' && tmp[n - 2] == 'X' && tmp[n - 3] == 'X' &&
        tmp[n - 4] == 'X' && tmp[n - 5] == 'X' && tmp[n - 6] == 'X')
      f = mkstemp(tmp);
    else {
      strncat(tmp, "XXXXXX", PATH_MAX - 1);
      f = mkstemp(tmp);
    }
    if (fd)
      *fd = f;
    return Yap_LookupAtom(tmp);
  #else
    return AtomNil;
  #endif
  }
  
  /** @pred make_directory(+ _Dir_)
  
  Create a directory  _Dir_. The name of the directory must be an atom.
  
  */
  static Int make_directory(USES_REGS1) {
    const char *fd = AtomName(AtomOfTerm(ARG1));
  #if defined(__MINGW32__) || _MSC_VER
    if (_mkdir(fd) == -1) {
  #else
    if (mkdir(fd, 0777) == -1) {
  #endif
      /* return an error number */
      return false; // errno?
    }
    return true;
  }
  
  static Int p_rmdir(USES_REGS1) {
    const char *fd = AtomName(AtomOfTerm(ARG1));
  #if defined(__MINGW32__) || _MSC_VER
    if (_rmdir(fd) == -1) {
  #else
    if (rmdir(fd) == -1) {
  #endif
      /* return an error number */
      return (Yap_unify(ARG2, MkIntTerm(errno)));
    }
    return true;
  }
  
  static bool initSysPath(Term tlib, Term tcommons, bool dir_done,
                          bool commons_done) {
    CACHE_REGS
    int len;
  
  #if __WINDOWS__
    {
      char *dir;
      if ((dir = Yap_RegistryGetString("library")) && is_directory(dir)) {
        if (!Yap_unify(tlib, MkAtomTerm(Yap_LookupAtom(dir))))
          return FALSE;
      }
      dir_done = true;
      if ((dir = Yap_RegistryGetString("prolog_commons")) && is_directory(dir)) {
        if (!Yap_unify(tcommons, MkAtomTerm(Yap_LookupAtom(dir))))
          return FALSE;
      }
      commons_done = true;
    }
    if (dir_done && commons_done)
      return TRUE;
  #endif
    strncpy(LOCAL_FileNameBuf, YAP_SHAREDIR, YAP_FILENAME_MAX);
    strncat(LOCAL_FileNameBuf, "/", YAP_FILENAME_MAX);
    len = strlen(LOCAL_FileNameBuf);
    if (!dir_done) {
      strncat(LOCAL_FileNameBuf, "Yap", YAP_FILENAME_MAX);
      if (is_directory(LOCAL_FileNameBuf)) {
        if (!Yap_unify(tlib, MkAtomTerm(Yap_LookupAtom(LOCAL_FileNameBuf))))
          return FALSE;
        dir_done = true;
      }
    }
    if (!commons_done) {
      LOCAL_FileNameBuf[len] = '\0';
      strncat(LOCAL_FileNameBuf, "PrologCommons", YAP_FILENAME_MAX);
      if (is_directory(LOCAL_FileNameBuf)) {
        if (!Yap_unify(tcommons, MkAtomTerm(Yap_LookupAtom(LOCAL_FileNameBuf))))
          return FALSE;
      }
      commons_done = true;
    }
    if (dir_done && commons_done)
      return TRUE;
  
  #if __WINDOWS__
    {
      size_t buflen;
      char *pt;
      /* couldn't find it where it was supposed to be,
     let's try using the executable */
      if (!GetModuleFileName(NULL, LOCAL_FileNameBuf, YAP_FILENAME_MAX)) {
        Yap_WinError("could not find executable name");
        /* do nothing */
        return FALSE;
      }
      buflen = strlen(LOCAL_FileNameBuf);
      pt = LOCAL_FileNameBuf + buflen;
      while (*--pt != '\\') {
        /* skip executable */
        if (pt == LOCAL_FileNameBuf) {
          FileError(SYSTEM_ERROR_OPERATING_SYSTEM, TermNil,
                    "could not find executable name");
          /* do nothing */
          return FALSE;
        }
      }
      while (*--pt != '\\') {
        /* skip parent directory "bin\\" */
        if (pt == LOCAL_FileNameBuf) {
          FileError(SYSTEM_ERROR_OPERATING_SYSTEM, TermNil,
                    "could not find executable name");
          /* do nothing */
          return FALSE;
        }
      }
      /* now, this is a possible location for the ROOT_DIR, let's look for a share
       * directory here */
      pt[1] = '\0';
      /* grosse */
      strncat(LOCAL_FileNameBuf, "lib\\Yap", YAP_FILENAME_MAX);
      libdir = Yap_AllocCodeSpace(strlen(LOCAL_FileNameBuf) + 1);
      strncpy(libdir, LOCAL_FileNameBuf, strlen(LOCAL_FileNameBuf) + 1);
      pt[1] = '\0';
      strncat(LOCAL_FileNameBuf, "share", YAP_FILENAME_MAX);
    }
    strncat(LOCAL_FileNameBuf, "\\", YAP_FILENAME_MAX);
    len = strlen(LOCAL_FileNameBuf);
    strncat(LOCAL_FileNameBuf, "Yap", YAP_FILENAME_MAX);
    if (!dir_done && is_directory(LOCAL_FileNameBuf)) {
      if (!Yap_unify(tlib, MkAtomTerm(Yap_LookupAtom(LOCAL_FileNameBuf))))
        return FALSE;
    }
    dir_done = true;
    LOCAL_FileNameBuf[len] = '\0';
    strncat(LOCAL_FileNameBuf, "PrologCommons", YAP_FILENAME_MAX);
    if (!commons_done && is_directory(LOCAL_FileNameBuf)) {
      if (!Yap_unify(tcommons, MkAtomTerm(Yap_LookupAtom(LOCAL_FileNameBuf))))
        return FALSE;
    }
    commons_done = true;
  #endif
    return dir_done && commons_done;
  }
  
  static Int libraries_directories(USES_REGS1) {
    return initSysPath(ARG1, ARG2, false, false);
  }
  
  static Int system_library(USES_REGS1) {
    return initSysPath(ARG1, MkVarTerm(), false, true);
  }
  
  static Int commons_library(USES_REGS1) {
    return initSysPath(MkVarTerm(), ARG1, true, false);
  }
  
  static Int p_dir_sp(USES_REGS1) {
  #if ATARI || _MSC_VER || defined(__MINGW32__)
    Term t = MkIntTerm('\\');
    Term t2 = MkIntTerm('/');
  #else
    Term t = MkIntTerm('/');
    Term t2 = MkIntTerm('/');
  #endif
  
    return Yap_unify_constant(ARG1, t) || Yap_unify_constant(ARG1, t2);
  }
  
  size_t Yap_InitPageSize(void) {
  #ifdef _WIN32
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    return si.dwPageSize;
  #elif HAVE_UNISTD_H
  #if defined(__FreeBSD__) || defined(__DragonFly__)
    return getpagesize();
  #elif defined(_AIX)
    return sysconf(_SC_PAGE_SIZE);
  #elif !defined(_SC_PAGESIZE)
    return getpagesize();
  #else
    return sysconf(_SC_PAGESIZE);
  #endif
  #else
    bla bla
  #endif
  }
  
  /* TrueFileName -> Finds the true name of a file */
  
  #ifdef __MINGW32__
  #include <ctype.h>
  #endif
  
  static int volume_header(char *file) {
  #if _MSC_VER || defined(__MINGW32__)
    char *ch = file;
    int c;
  
    while ((c = ch[0]) != '\0') {
      if (isalnum(c))
        ch++;
      else
        return (c == ':');
    }
  #endif
    return (FALSE);
  }
  
  int Yap_volume_header(char *file) { return volume_header(file); }
  
  const char *Yap_getcwd(const char *cwd, size_t cwdlen) {
  #if _WIN32 || defined(__MINGW32__)
    if (GetCurrentDirectory(cwdlen, (char *)cwd) == 0) {
      Yap_WinError("GetCurrentDirectory failed");
      return NULL;
    }
    return (char *)cwd;
  #elif __ANDROID__
    if (GLOBAL_AssetsWD) {
      return strncpy((char *)cwd, (const char *)GLOBAL_AssetsWD, cwdlen);
    }
  
  #endif
    return getcwd((char *)cwd, cwdlen);
  }
  
  static Int working_directory(USES_REGS1) {
    char dir[YAP_FILENAME_MAX + 1];
    Term t1 = Deref(ARG1), t2;
  
    if (!IsVarTerm(t1) && !IsAtomTerm(t1)) {
      Yap_Error(TYPE_ERROR_ATOM, t1, "working_directory");
    }
    if (!Yap_unify(t1,
                   MkAtomTerm(Yap_LookupAtom(Yap_getcwd(dir, YAP_FILENAME_MAX)))))
      return false;
    t2 = Deref(ARG2);
    if (IsVarTerm(t2)) {
      Yap_Error(INSTANTIATION_ERROR, t2, "working_directory");
    }
    if (!IsAtomTerm(t2)) {
      Yap_Error(TYPE_ERROR_ATOM, t2, "working_directory");
    }
    if (t2 == TermEmptyAtom || t2 == TermDot)
      return true;
    return ChDir(RepAtom(AtomOfTerm(t2))->StrOfAE);
  }
  
  /** Yap_findFile(): tries to locate a file, no expansion should be performed/
     *
     *
     * @param isource the proper file
     * @param idef the default name fo rthe file, ie, startup.yss
     * @param root the prefix
     * @param result the output
     * @param access verify whether the file has access permission
     * @param ftype saved state, object, saved file, prolog file
     * @param expand_root expand $ ~, etc
     * @param in_lib library file
     *
     * @return
     */
  const char *Yap_findFile(const char *isource, const char *idef,
                           const char *iroot, char *result, bool access,
                           YAP_file_type_t ftype, bool expand_root, bool in_lib) {
    char save_buffer[YAP_FILENAME_MAX + 1];
    const char *root, *source = isource;
    int rc = FAIL_RESTORE;
    int try
      = 0;
  
    while (rc == FAIL_RESTORE) {
      bool done = false;
      // { CACHE_REGS __android_log_print(ANDROID_LOG_ERROR,  __FUNCTION__,
      // "try=%d %s %s", try, isource, iroot) ; }
      switch (try ++) {
      case 0: // path or file name is given;
        root = iroot;
        if (!root && ftype == YAP_BOOT_PL) {
  	root = YAP_PL_SRCDIR;
        }
        if (idef || isource) {
          source = (isource ? isource : idef);
        }
        break;
      case 1: // library directory is given in command line
        if (in_lib && ftype == YAP_SAVED_STATE) {
          root = iroot;
          source = (isource ? isource : idef);
        } else {
          done = true;
        }
        break;
      case 2: // use environment variable YAPLIBDIR
  #if HAVE_GETENV
        if (in_lib) {
          if (ftype == YAP_SAVED_STATE || ftype == YAP_OBJ) {
  	  root = getenv("YAPLIBDIR");
          } else if (ftype == YAP_BOOT_PL) {
            root = getenv("YAPSHAREDIR");
  	  if (root == NULL) {
  	    continue;	    
  	  } else {
  	    strncpy(save_buffer, root, YAP_FILENAME_MAX);
  	    strncat(save_buffer, "/pl", YAP_FILENAME_MAX);
  	  }
          }
          source = (isource ? isource : idef);
        } else
  #endif
  	done = true;
        break;
      case 3: // use compilation variable YAPLIBDIR
        if (in_lib) {
          source = (isource ? isource : idef);
          if (ftype == YAP_PL) {
            root = YAP_SHAREDIR;
          } else if (ftype == YAP_BOOT_PL) {
            root = YAP_SHAREDIR "/pl";
          } else {
            root = YAP_LIBDIR;
          }
        } else
          done = true;
        break;
  
      case 4: // WIN stuff: registry
  #if __WINDOWS__
        if (in_lib) {
          source = (ftype == YAP_PL || ftype == YAP_QLY ? "library" : "startup");
          source = Yap_RegistryGetString(source);
          root = NULL;
        } else
  #endif
          done = true;
        break;
  
      case 5: // search from the binary
  #ifndef __ANDROID__
        done = true;
        break;
  #endif
        const char *pt = Yap_FindExecutable();
  
        if (pt) {
  	if (ftype == YAP_BOOT_PL) {
  	  root = "../../share/Yap/pl";
  	} else {
  	  root =
              (ftype == YAP_SAVED_STATE
  	     || ftype == YAP_OBJ
  	     ? "../../lib/Yap"
  	     : "../../share/Yap");
  	}
          if (Yap_findFile(source, NULL, root, save_buffer, access, ftype,
                           expand_root, in_lib))
            root = save_buffer;
          else
            done = true;
        } else {
          done = true;
        }
        source = (isource ? isource : idef);
        break;
      case 6: // default, try current directory
        if (!isource && ftype == YAP_SAVED_STATE)
          source = idef;
        root = NULL;
        break;
      default:
        return false;
      }
  
      if (done)
        continue;
      //    { CACHE_REGS __android_log_print(ANDROID_LOG_ERROR,  __FUNCTION__,
      //    "root= %s %s ", root, source) ; }
      const char *work = PlExpandVars(source, root, result);
  
      // expand names in case you have
      // to add a prefix
      if (!access || Yap_Exists(work)) {
        return work; // done
      }
    }
    return NULL;
  }
  
  static Int true_file_name(USES_REGS1) {
    Term t = Deref(ARG1);
    const char *s;
  
    if (IsVarTerm(t)) {
      Yap_Error(INSTANTIATION_ERROR, t, "argument to true_file_name unbound");
      return FALSE;
    }
    if (IsAtomTerm(t)) {
      s = RepAtom(AtomOfTerm(t))->StrOfAE;
    } else if (IsStringTerm(t)) {
      s = StringOfTerm(t);
    } else {
      Yap_Error(TYPE_ERROR_ATOM, t, "argument to true_file_name");
      return FALSE;
    }
    if (!(s = Yap_AbsoluteFile(s, LOCAL_FileNameBuf, true)))
      return false;
    return Yap_unify(ARG2, MkAtomTerm(Yap_LookupAtom(s)));
  }
  
  static Int p_expand_file_name(USES_REGS1) {
    Term t = Deref(ARG1);
    const char *text, *text2;
  
    if (IsVarTerm(t)) {
      Yap_Error(INSTANTIATION_ERROR, t, "argument to true_file_name unbound");
      return FALSE;
    }
    text = Yap_TextTermToText(t, NULL, 0, LOCAL_encoding);
    if (!text)
      return false;
    if (!(text2 = PlExpandVars(text, NULL, NULL)))
      return false;
    freeBuffer(text);
    bool rc = Yap_unify(ARG2, Yap_MkTextTerm(text2, LOCAL_encoding, t));
    freeBuffer(text2);
    return rc;
  }
  
  static Int true_file_name3(USES_REGS1) {
    Term t = Deref(ARG1), t2 = Deref(ARG2);
    char *root = NULL;
  
    if (IsVarTerm(t)) {
      Yap_Error(INSTANTIATION_ERROR, t, "argument to true_file_name unbound");
      return FALSE;
    }
    if (!IsAtomTerm(t)) {
      Yap_Error(TYPE_ERROR_ATOM, t, "argument to true_file_name");
      return FALSE;
    }
    if (!IsVarTerm(t2)) {
      if (!IsAtomTerm(t)) {
        Yap_Error(TYPE_ERROR_ATOM, t2, "argument to true_file_name");
        return FALSE;
      }
      root = RepAtom(AtomOfTerm(t2))->StrOfAE;
    }
    if (!Yap_findFile(RepAtom(AtomOfTerm(t))->StrOfAE, NULL, root,
                      LOCAL_FileNameBuf, false, YAP_PL, false, false))
      return FALSE;
    return Yap_unify(ARG3, MkAtomTerm(Yap_LookupAtom(LOCAL_FileNameBuf)));
  }
  
  /* Executes $SHELL under Prolog */
  /** @pred  sh
  
  
        Creates a new shell interaction.
  
  
    */
  static Int p_sh(USES_REGS1) { /* sh				 */
  #ifdef HAVE_SYSTEM
    char *shell;
    shell = (char *)getenv("SHELL");
    if (shell == NULL)
      shell = "/bin/sh";
    if (system(shell) < 0) {
  #if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, TermNil, "%s in sh/0",
                strerror(errno));
  #else
      Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, TermNil, "in sh/0");
  #endif
      return FALSE;
    }
    return TRUE;
  #else
  #ifdef MSH
    register char *shell;
    shell = "msh -i";
    system(shell);
    return (TRUE);
  #else
    Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil,
              "sh not available in this configuration");
    return (FALSE);
  #endif /* MSH */
  #endif
  }
  
  /** shell(+Command:text, -Status:integer) is det.
  
        Run an external command and wait for its completion.
    */
  static Int p_shell(USES_REGS1) { /* '$shell'(+SystCommand) */
    const char *cmd;
    Term t1 = Deref(ARG1);
    if (IsAtomTerm(t1))
      cmd = RepAtom(AtomOfTerm(t1))->StrOfAE;
    else if (IsStringTerm(t1))
      cmd = StringOfTerm(t1);
    else
      return FALSE;
  #if _MSC_VER || defined(__MINGW32__)
    {
      int rval = system(cmd);
  
      return rval == 0;
    }
  
    return true;
  #else
  #if HAVE_SYSTEM
    char *shell;
    register int bourne = FALSE;
  
    shell = (char *)getenv("SHELL");
    if (!strcmp(shell, "/bin/sh"))
      bourne = TRUE;
    if (shell == NIL)
      bourne = TRUE;
    /* Yap_CloseStreams(TRUE); */
    if (bourne)
      return system(cmd) == 0;
    else {
      int status = -1;
      int child = fork();
  
      if (child == 0) { /* let the children go */
        if (!execl(shell, shell, "-c", cmd, NULL)) {
          exit(-1);
        }
        exit(TRUE);
      }
      { /* put the father on wait */
        int result = child < 0 ||
                     /* vsc:I am not sure this is used, Stevens say wait returns
                  an integer.
                  #if NO_UNION_WAIT
               */
                     wait((&status)) != child ||
                     /*
                 #else
                 wait ((union wait *) (&status)) != child ||
                 #endif
               */
                     status == 0;
        return result;
      }
    }
  #else /* HAVE_SYSTEM */
  #ifdef MSH
    register char *shell;
    shell = "msh -i";
    /* Yap_CloseStreams(); */
    system(shell);
    return TRUE;
  #else
    Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil,
              "shell not available in this configuration");
    return FALSE;
  #endif
  #endif /* HAVE_SYSTEM */
  #endif /* _MSC_VER */
  }
  
  /** system(+Command:text).
  
        Run an external command.
    */
  
  static Int p_system(USES_REGS1) { /* '$system'(+SystCommand)	       */
    const char *cmd;
    Term t1 = Deref(ARG1);
  
    if (IsVarTerm(t1)) {
      Yap_Error(INSTANTIATION_ERROR, t1, "argument to system/1 unbound");
      return FALSE;
    } else if (IsAtomTerm(t1)) {
      cmd = RepAtom(AtomOfTerm(t1))->StrOfAE;
    } else if (IsStringTerm(t1)) {
      cmd = StringOfTerm(t1);
    } else {
      if (!Yap_GetName(LOCAL_FileNameBuf, YAP_FILENAME_MAX, t1)) {
        Yap_Error(TYPE_ERROR_ATOM, t1, "argument to system/1");
        return false;
      }
      cmd = LOCAL_FileNameBuf;
    }
  /* Yap_CloseStreams(TRUE); */
  #if _MSC_VER || defined(__MINGW32__)
  
    {
      STARTUPINFO si;
      PROCESS_INFORMATION pi;
  
      ZeroMemory(&si, sizeof(si));
      si.cb = sizeof(si);
      ZeroMemory(&pi, sizeof(pi));
  
      // Start the child process.
      if (!CreateProcess(NULL,       // No module name (use command line)
                         (LPSTR)cmd, // Command line
                         NULL,       // Process handle not inheritable
                         NULL,       // Thread handle not inheritable
                         FALSE,      // Set handle inheritance to FALSE
                         0,          // No creation flags
                         NULL,       // Use parent's environment block
                         NULL,       // Use parent's starting directory
                         &si,        // Pointer to STARTUPINFO structure
                         &pi)        // Pointer to PROCESS_INFORMATION structure
          ) {
        Yap_Error(SYSTEM_ERROR_INTERNAL, ARG1, "CreateProcess failed (%d).\n",
                  GetLastError());
        return FALSE;
      }
      // Wait until child process exits.
      WaitForSingleObject(pi.hProcess, INFINITE);
  
      // Close process and thread handles.
      CloseHandle(pi.hProcess);
      CloseHandle(pi.hThread);
  
      return TRUE;
    }
  
    return FALSE;
  #elif HAVE_SYSTEM
  #if _MSC_VER
    _flushall();
  #endif
    if (system(cmd)) {
  #if HAVE_STRERROR
      Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, t1, "%s in system(%s)",
                strerror(errno), cmd);
  #else
      Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, t1, "in system(%s)", cmd);
  #endif
      return FALSE;
    }
    return TRUE;
  #else
  #ifdef MSH
    register char *shell;
    shell = "msh -i";
    /* Yap_CloseStreams(); */
    system(shell);
    return (TRUE);
  #undef command
  #else
    Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil, "sh not available in this machine");
    return (FALSE);
  #endif
  #endif /* HAVE_SYSTEM */
  }
  
  static Int p_mv(USES_REGS1) { /* rename(+OldName,+NewName)   */
  #if HAVE_LINK
    int r;
    char *oldname, *newname;
    Term t1 = Deref(ARG1);
    Term t2 = Deref(ARG2);
    if (IsVarTerm(t1)) {
      Yap_Error(INSTANTIATION_ERROR, t1, "first argument to rename/2 unbound");
    } else if (!IsAtomTerm(t1)) {
      Yap_Error(TYPE_ERROR_ATOM, t1, "first argument to rename/2 not atom");
    }
    if (IsVarTerm(t2)) {
      Yap_Error(INSTANTIATION_ERROR, t2, "second argument to rename/2 unbound");
    } else if (!IsAtomTerm(t2)) {
      Yap_Error(TYPE_ERROR_ATOM, t2, "second argument to rename/2 not atom");
    } else {
      oldname = (RepAtom(AtomOfTerm(t1)))->StrOfAE;
      newname = (RepAtom(AtomOfTerm(t2)))->StrOfAE;
      if ((r = link(oldname, newname)) == 0 && (r = unlink(oldname)) != 0)
        unlink(newname);
      if (r != 0) {
  #if HAVE_STRERROR
        Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, t2, "%s in rename(%s,%s)",
                  strerror(errno), oldname, newname);
  #else
        Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, t2, "in rename(%s,%s)", oldname,
                  newname);
  #endif
        return false;
      }
      return true;
    }
  #else
    Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil,
              "rename/2 not available in this machine");
  #endif
    return false;
  }
  
  #ifdef MAC
  
  void Yap_SetTextFile(name) char *name;
  {
  #ifdef MACC
    SetFileType(name, 'TEXT');
    SetFileSignature(name, 'EDIT');
  #else
    FInfo f;
    FInfo *p = &f;
    GetFInfo(name, 0, p);
    p->fdType = 'TEXT';
  #ifdef MPW
    if (mpwshell)
      p->fdCreator = 'MPS\0';
  #endif
  #ifndef LIGHT
    else
      p->fdCreator = 'EDIT';
  #endif
    SetFInfo(name, 0, p);
  #endif
  }
  
  #endif
  
  /* return YAP's environment */
  static Int p_getenv(USES_REGS1) {
  #if HAVE_GETENV
    Term t1 = Deref(ARG1), to;
    char *s, *so;
  
    if (IsVarTerm(t1)) {
      Yap_Error(INSTANTIATION_ERROR, t1, "first arg of getenv/2");
      return (FALSE);
    } else if (!IsAtomTerm(t1)) {
      Yap_Error(TYPE_ERROR_ATOM, t1, "first arg of getenv/2");
      return (FALSE);
    } else
      s = RepAtom(AtomOfTerm(t1))->StrOfAE;
    if ((so = getenv(s)) == NULL)
      return (FALSE);
    to = MkAtomTerm(Yap_LookupAtom(so));
    return (Yap_unify_constant(ARG2, to));
  #else
    Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil,
              "getenv not available in this configuration");
    return (FALSE);
  #endif
  }
  
  /* set a variable in YAP's environment */
  static Int p_putenv(USES_REGS1) {
  #if HAVE_PUTENV
    Term t1 = Deref(ARG1), t2 = Deref(ARG2);
    char *s, *s2, *p0, *p;
  
    if (IsVarTerm(t1)) {
      Yap_Error(INSTANTIATION_ERROR, t1, "first arg to putenv/2");
      return (FALSE);
    } else if (!IsAtomTerm(t1)) {
      Yap_Error(TYPE_ERROR_ATOM, t1, "first arg to putenv/2");
      return (FALSE);
    } else
      s = RepAtom(AtomOfTerm(t1))->StrOfAE;
    if (IsVarTerm(t2)) {
      Yap_Error(INSTANTIATION_ERROR, t1, "second arg to putenv/2");
      return (FALSE);
    } else if (!IsAtomTerm(t2)) {
      Yap_Error(TYPE_ERROR_ATOM, t2, "second arg to putenv/2");
      return (FALSE);
    } else
      s2 = RepAtom(AtomOfTerm(t2))->StrOfAE;
    while (!(p0 = p = Yap_AllocAtomSpace(strlen(s) + strlen(s2) + 3))) {
      if (!Yap_growheap(FALSE, MinHeapGap, NULL)) {
        Yap_Error(RESOURCE_ERROR_HEAP, TermNil, LOCAL_ErrorMessage);
        return FALSE;
      }
    }
    while ((*p++ = *s++) != '\0')
      ;
    p[-1] = '=';
    while ((*p++ = *s2++) != '\0')
      ;
    if (putenv(p0) == 0)
      return TRUE;
  #if HAVE_STRERROR
    Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, TermNil, "in putenv(%s)",
              strerror(errno), p0);
  #else
    Yap_Error(SYSTEM_ERROR_OPERATING_SYSTEM, TermNil, "in putenv(%s)", p0);
  #endif
    return FALSE;
  #else
    Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil,
              "putenv not available in this configuration");
    return FALSE;
  #endif
  }
  
  static Int p_host_type(USES_REGS1) {
    Term out = MkAtomTerm(Yap_LookupAtom(HOST_ALIAS));
    return (Yap_unify(out, ARG1));
  }
  
  static Int p_yap_home(USES_REGS1) {
    Term out = MkAtomTerm(Yap_LookupAtom(YAP_ROOTDIR));
    return (Yap_unify(out, ARG1));
  }
  
  static Int p_yap_paths(USES_REGS1) {
    Term out1, out2, out3;
    const char *env_destdir = getenv("DESTDIR");
    char destdir[YAP_FILENAME_MAX + 1];
  
    if (env_destdir) {
      strncat(destdir, env_destdir, YAP_FILENAME_MAX);
      strncat(destdir, "/" YAP_LIBDIR, YAP_FILENAME_MAX);
      out1 = MkAtomTerm(Yap_LookupAtom(destdir));
    } else {
      out1 = MkAtomTerm(Yap_LookupAtom(YAP_LIBDIR));
    }
    if (env_destdir) {
      strncat(destdir, env_destdir, YAP_FILENAME_MAX);
      strncat(destdir, "/" YAP_SHAREDIR, YAP_FILENAME_MAX);
      out2 = MkAtomTerm(Yap_LookupAtom(destdir));
    } else {
      out2 = MkAtomTerm(Yap_LookupAtom(YAP_SHAREDIR));
    }
    if (env_destdir) {
      strncat(destdir, env_destdir, YAP_FILENAME_MAX);
      strncat(destdir, "/" YAP_BINDIR, YAP_FILENAME_MAX);
      out3 = MkAtomTerm(Yap_LookupAtom(destdir));
    } else {
      out3 = MkAtomTerm(Yap_LookupAtom(YAP_BINDIR));
    }
    return (Yap_unify(out1, ARG1) && Yap_unify(out2, ARG2) &&
            Yap_unify(out3, ARG3));
  }
  
  static Int p_log_event(USES_REGS1) {
    Term in = Deref(ARG1);
    Atom at;
  
    if (IsVarTerm(in))
      return FALSE;
    if (!IsAtomTerm(in))
      return FALSE;
    at = AtomOfTerm(in);
  #if DEBUG
    if (IsWideAtom(at))
      fprintf(stderr, "LOG %S\n", RepAtom(at)->WStrOfAE);
    else if (IsBlob(at))
      return FALSE;
    else
      fprintf(stderr, "LOG %s\n", RepAtom(at)->StrOfAE);
  #endif
    if (IsWideAtom(at) || IsBlob(at))
      return FALSE;
    LOG(" %s ", RepAtom(at)->StrOfAE);
    return TRUE;
  }
  
  static Int p_env_separator(USES_REGS1) {
  #if defined(_WIN32)
    return Yap_unify(MkIntegerTerm(';'), ARG1);
  #else
    return Yap_unify(MkIntegerTerm(':'), ARG1);
  #endif
  }
  
  /*
     * This is responsable for the initialization of all machine dependant
     * predicates
     */
  void Yap_InitSysbits(int wid) {
    CACHE_REGS
  #if __simplescalar__
    {
      char *pwd = getenv("PWD");
      strncpy(GLOBAL_pwd, pwd, YAP_FILENAME_MAX);
    }
  #endif
    Yap_InitWTime();
    Yap_InitRandom();
    /* let the caller control signals as it sees fit */
    Yap_InitOSSignals(worker_id);
  }
  
  static Int p_unix(USES_REGS1) {
  #ifdef unix
    return TRUE;
  #else
  #ifdef __unix__
    return TRUE;
  #else
  #ifdef __APPLE__
    return TRUE;
  #else
    return FALSE;
  #endif
  #endif
  #endif
  }
  
  static Int p_win32(USES_REGS1) {
  #ifdef _WIN32
    return TRUE;
  #else
  #ifdef __CYGWIN__
    return TRUE;
  #else
    return FALSE;
  #endif
  #endif
  }
  
  static Int p_ld_path(USES_REGS1) {
    return Yap_unify(ARG1, MkAtomTerm(Yap_LookupAtom(YAP_LIBDIR)));
  }
  
  static Int p_address_bits(USES_REGS1) {
  #if SIZEOF_INT_P == 4
    return Yap_unify(ARG1, MkIntTerm(32));
  #else
    return Yap_unify(ARG1, MkIntTerm(64));
  #endif
  }
  
  #ifdef _WIN32
  
  /* This code is from SWI-Prolog by Jan Wielemaker */
  
  #define wstreq(s, q) (wcscmp((s), (q)) == 0)
  
  static HKEY reg_open_key(const wchar_t *which, int create) {
    HKEY key = HKEY_CURRENT_USER;
    DWORD disp;
    LONG rval;
  
    while (*which) {
      wchar_t buf[256];
      wchar_t *s;
      HKEY tmp;
  
      for (s = buf; *which && !(*which == '/' || *which == '\\');)
        *s++ = *which++;
      *s = '\0';
      if (*which)
        which++;
  
      if (wstreq(buf, L"HKEY_CLASSES_ROOT")) {
        key = HKEY_CLASSES_ROOT;
        continue;
      } else if (wstreq(buf, L"HKEY_CURRENT_USER")) {
        key = HKEY_CURRENT_USER;
        continue;
      } else if (wstreq(buf, L"HKEY_LOCAL_MACHINE")) {
        key = HKEY_LOCAL_MACHINE;
        continue;
      } else if (wstreq(buf, L"HKEY_USERS")) {
        key = HKEY_USERS;
        continue;
      }
  
      if (RegOpenKeyExW(key, buf, 0L, KEY_READ, &tmp) == ERROR_SUCCESS) {
        RegCloseKey(key);
        key = tmp;
        continue;
      }
  
      if (!create)
        return NULL;
  
      rval =
          RegCreateKeyExW(key, buf, 0, L"", 0, KEY_ALL_ACCESS, NULL, &tmp, &disp);
      RegCloseKey(key);
      if (rval == ERROR_SUCCESS)
        key = tmp;
      else
        return NULL;
    }
  
    return key;
  }
  
  #define MAXREGSTRLEN 1024
  
  static void recover_space(wchar_t *k, Atom At) {
    if (At->WStrOfAE != k)
      Yap_FreeCodeSpace((char *)k);
  }
  
  static wchar_t *WideStringFromAtom(Atom KeyAt USES_REGS) {
    if (IsWideAtom(KeyAt)) {
      return KeyAt->WStrOfAE;
    } else {
      int len = strlen(KeyAt->StrOfAE);
      int sz = sizeof(wchar_t) * (len + 1);
      char *chp = KeyAt->StrOfAE;
      wchar_t *kptr, *k;
  
      k = (wchar_t *)Yap_AllocCodeSpace(sz);
      while (k == NULL) {
        if (!Yap_growheap(false, sz, NULL)) {
          Yap_Error(RESOURCE_ERROR_HEAP, MkIntegerTerm(sz),
                    "generating key in win_registry_get_value/3");
          return false;
        }
        k = (wchar_t *)Yap_AllocCodeSpace(sz);
      }
      kptr = k;
      while ((*kptr++ = *chp++))
        ;
      return k;
    }
  }
  
  static Int p_win_registry_get_value(USES_REGS1) {
    DWORD type;
    BYTE data[MAXREGSTRLEN];
    DWORD len = sizeof(data);
    wchar_t *k, *name;
    HKEY key;
    Term Key = Deref(ARG1);
    Term Name = Deref(ARG2);
    Atom KeyAt, NameAt;
  
    if (IsVarTerm(Key)) {
      Yap_Error(INSTANTIATION_ERROR, Key,
                "argument to win_registry_get_value unbound");
      return FALSE;
    }
    if (!IsAtomTerm(Key)) {
      Yap_Error(TYPE_ERROR_ATOM, Key, "argument to win_registry_get_value");
      return FALSE;
    }
    KeyAt = AtomOfTerm(Key);
    if (IsVarTerm(Name)) {
      Yap_Error(INSTANTIATION_ERROR, Key,
                "argument to win_registry_get_value unbound");
      return FALSE;
    }
    if (!IsAtomTerm(Name)) {
      Yap_Error(TYPE_ERROR_ATOM, Key, "argument to win_registry_get_value");
      return FALSE;
    }
    NameAt = AtomOfTerm(Name);
  
    k = WideStringFromAtom(KeyAt PASS_REGS);
    if (!(key = reg_open_key(k, FALSE))) {
      Yap_Error(EXISTENCE_ERROR_KEY, Key, "argument to win_registry_get_value");
      recover_space(k, KeyAt);
      return FALSE;
    }
    name = WideStringFromAtom(NameAt PASS_REGS);
  
    if (RegQueryValueExW(key, name, NULL, &type, data, &len) == ERROR_SUCCESS) {
      RegCloseKey(key);
      switch (type) {
      case REG_SZ:
        recover_space(k, KeyAt);
        recover_space(name, NameAt);
        ((wchar_t *)data)[len] = '\0';
        return Yap_unify(MkAtomTerm(Yap_LookupMaybeWideAtom((wchar_t *)data)),
                         ARG3);
      case REG_DWORD:
        recover_space(k, KeyAt);
        recover_space(name, NameAt);
        {
          DWORD *d = (DWORD *)data;
          return Yap_unify(MkIntegerTerm((Int)d[0]), ARG3);
        }
      default:
        recover_space(k, KeyAt);
        recover_space(name, NameAt);
        return FALSE;
      }
    }
    recover_space(k, KeyAt);
    recover_space(name, NameAt);
    return FALSE;
  }
  
  char *Yap_RegistryGetString(char *name) {
    DWORD type;
    BYTE data[MAXREGSTRLEN];
    DWORD len = sizeof(data);
    HKEY key;
    char *ptr;
    int i;
  
  #if SIZEOF_INT_P == 8
    if (!(key =
              reg_open_key(L"HKEY_LOCAL_MACHINE/SOFTWARE/YAP/Prolog64", FALSE))) {
      return NULL;
    }
  #else
    if (!(key = reg_open_key(L"HKEY_LOCAL_MACHINE/SOFTWARE/YAP/Prolog", FALSE))) {
      return NULL;
    }
  #endif
    if (RegQueryValueEx(key, name, NULL, &type, data, &len) == ERROR_SUCCESS) {
      RegCloseKey(key);
      switch (type) {
      case REG_SZ:
        ptr = malloc(len + 2);
        if (!ptr)
          return NULL;
        for (i = 0; i <= len; i++)
          ptr[i] = data[i];
        ptr[len + 1] = '\0';
        return ptr;
      default:
        return NULL;
      }
    }
    return NULL;
  }
  
  #endif
  
  void Yap_InitSysPreds(void) {
    Yap_InitCPred("log_event", 1, p_log_event, SafePredFlag | SyncPredFlag);
    Yap_InitCPred("sh", 0, p_sh, SafePredFlag | SyncPredFlag);
    Yap_InitCPred("$shell", 1, p_shell, SafePredFlag | SyncPredFlag);
    Yap_InitCPred("system", 1, p_system,
                  SafePredFlag | SyncPredFlag | UserCPredFlag);
    Yap_InitCPred("$rename", 2, p_mv, SafePredFlag | SyncPredFlag);
    Yap_InitCPred("$yap_home", 1, p_yap_home, SafePredFlag);
    Yap_InitCPred("$yap_paths", 3, p_yap_paths, SafePredFlag);
    Yap_InitCPred("$dir_separator", 1, p_dir_sp, SafePredFlag);
    Yap_InitCPred("libraries_directories", 2, libraries_directories, 0);
    Yap_InitCPred("system_library", 1, system_library, 0);
    Yap_InitCPred("commons_library", 1, commons_library, 0);
    Yap_InitCPred("$getenv", 2, p_getenv, SafePredFlag);
    Yap_InitCPred("$putenv", 2, p_putenv, SafePredFlag | SyncPredFlag);
    Yap_InitCPred("$host_type", 1, p_host_type, SafePredFlag | SyncPredFlag);
    Yap_InitCPred("$env_separator", 1, p_env_separator, SafePredFlag);
    Yap_InitCPred("$unix", 0, p_unix, SafePredFlag);
    Yap_InitCPred("$win32", 0, p_win32, SafePredFlag);
    Yap_InitCPred("$ld_path", 1, p_ld_path, SafePredFlag);
    Yap_InitCPred("$address_bits", 1, p_address_bits, SafePredFlag);
    Yap_InitCPred("$expand_file_name", 2, p_expand_file_name, SyncPredFlag);
    Yap_InitCPred("expand_file_name", 3, expand_file_name3, SyncPredFlag);
    Yap_InitCPred("expand_file_name", 2, expand_file_name, SyncPredFlag);
    Yap_InitCPred("working_directory", 2, working_directory, SyncPredFlag);
    Yap_InitCPred("prolog_to_os_filename", 2, prolog_to_os_filename,
                  SyncPredFlag);
    Yap_InitCPred("absolute_file_system_path", 2, absolute_file_system_path, 0);
    Yap_InitCPred("real_path", 2, real_path, 0);
    Yap_InitCPred("true_file_name", 2, true_file_name, SyncPredFlag);
    Yap_InitCPred("true_file_name", 3, true_file_name3, SyncPredFlag);
  #ifdef _WIN32
    Yap_InitCPred("win_registry_get_value", 3, p_win_registry_get_value, 0);
  #endif
    Yap_InitCPred("rmdir", 2, p_rmdir, SyncPredFlag);
    Yap_InitCPred("make_directory", 1, make_directory, SyncPredFlag);
  }
  