/*
 * Copyright 2021 The Emscripten Authors.  All rights reserved.
 * Emscripten is available under two separate licenses, the MIT license and the
 * University of Illinois/NCSA Open Source License.  Both these licenses can be
 * found in the LICENSE file.
 *
 * Fake/stub implementations of libc functions.
 * See emscripten_syscall_stubs.c for fake/stub implementations of syscalls.
 */

#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <spawn.h>
#include <stdio.h>
#include <sys/times.h>
#include <sys/wait.h>
#include <unistd.h>

#ifndef weak
#define weak __attribute__((__weak__))
#endif

// ==========================================================================
// sys/wait.h
// ==========================================================================

weak int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options) {
  errno = ECHILD;
  return -1;
}

// ==========================================================================
// sys/times.h
// ==========================================================================

clock_t times(struct tms *buf) {
  // clock_t times(struct tms *buffer);
  // http://pubs.opengroup.org/onlinepubs/009695399/functions/times.html
  // NOTE: This is fake, since we can't calculate real CPU time usage in JS.
  if (buf) {
    memset(buf, 0, sizeof(*buf));
  }
  return 0;
}

struct tm *getdate(const char *string) {
  // struct tm *getdate(const char *string);
  // http://pubs.opengroup.org/onlinepubs/009695399/functions/getdate.html
  // TODO: Implement.
  return 0;
}

weak int stime(const time_t *t) {
  errno = EPERM;
  return -1;
}

weak int clock_getcpuclockid(pid_t pid, clockid_t *clockid) {
  if (pid < 0) {
    return ESRCH;
  }
  if (pid != 0 && pid != getpid()) {
    return ENOSYS;
  }
  if (clockid) {
    *clockid = CLOCK_PROCESS_CPUTIME_ID;
  }
  return 0;
}

// ==========================================================================
// pwd.h
// ==========================================================================

struct passwd *getpwnam(const char *name) {
  errno = ENOENT;
  return 0;
}

struct passwd *getpwuid(uid_t uid) {
  errno = ENOENT;
  return 0;
}

weak int getpwnam_r(const char *name, struct passwd *pwd,
               char *buf, size_t buflen, struct passwd **result) {
  return ENOENT;
}

weak int getpwuid_r(uid_t uid, struct passwd *pwd,
                      char *buf, size_t buflen, struct passwd **result) {
  return ENOENT;
}

weak void setpwent(void) {
}

weak void endpwent(void) {
}

struct passwd *getpwent(void) {
  errno = EIO;
  return NULL;
}

// ==========================================================================
// grp.h
// ==========================================================================

weak struct group *getgrnam(const char *name) {
  errno = ENOENT;
  return 0;
}

weak struct group *getgrgid(gid_t gid) {
  errno = ENOENT;
  return 0;
}

weak int getgrnam_r(const char *name, struct group *grp,
               char *buf, size_t buflen, struct group **result) {
  return ENOENT;
}

weak int getgrgid_r(gid_t gid, struct group *grp,
               char *buf, size_t buflen, struct group **result) {
  return ENOENT;
}

weak struct group *getgrent(void) {
  errno = EIO;
  return NULL;
}

weak void endgrent(void) {
}

weak void setgrent(void) {
}

// ==========================================================================
// sys/file.h
// ==========================================================================

weak int flock(int fd, int operation) {
  // Pretend that the locking is successful. These are process-level locks,
  // and Emscripten programs are a single process. If we supported linking a
  // filesystem between programs, we'd need to do more here.
  // See https://github.com/emscripten-core/emscripten/issues/23697
  return 0;
}

weak int chroot(const char *path) {
  // int chroot(const char *path);
  // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chroot.html
  errno = EACCES;
  return -1;
}

weak int execve(const char *pathname, char *const argv[],
           char *const envp[]) {
  // int execve(const char *pathname, char *const argv[],
  //            char *const envp[]);
  // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html
  // We don't support executing external code.
  errno = ENOEXEC;
  return -1;
}

weak pid_t fork(void) {
  // pid_t fork(void);
  // http://pubs.opengroup.org/onlinepubs/000095399/functions/fork.html
  // We don't support multiple processes.
  errno = ENOSYS;
  return -1;
}

weak pid_t vfork(void) {
  errno = ENOSYS;
  return -1;
}

weak int posix_spawn(pid_t *pid, const char *path,
                       const posix_spawn_file_actions_t *file_actions,
                       const posix_spawnattr_t *attrp,
                       char *const argv[], char *const envp[]) {
  errno = ENOSYS;
  return -1;
}

// ==========================================================================
// stdio.h
// ==========================================================================

weak FILE *popen(const char *command, const char *type) {
  errno = ENOSYS;
  return NULL;
}

weak int pclose(FILE *stream) {
  errno = ENOSYS;
  return -1;
}

weak int setgroups(size_t size, const gid_t *list) {
  // int setgroups(int ngroups, const gid_t *gidset);
  // https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/setgroups.2.html
  if (size < 1 || size > sysconf(_SC_NGROUPS_MAX)) {
    errno = EINVAL;
    return -1;
  }
  // We have just one process/user/group, so it makes no sense to set groups.
  errno = EPERM;
  return -1;
}

weak int sigaltstack(const stack_t *restrict ss, stack_t *restrict old_ss) {
  errno = ENOSYS;
  return -1;
}

// ==========================================================================
// dlfcn.h
// ==========================================================================

#ifndef EMSCRIPTEN_DYNAMIC_LINKING
void __dl_seterr(const char*, ...);

weak void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra) {
  __dl_seterr("dynamic linking not enabled");
  return NULL;
}

weak void* dlopen(const char* file, int flags) {
  __dl_seterr("dynamic linking not enabled");
  return NULL;
}
#endif

// ==========================================================================
// stdlib.h
// ==========================================================================

#define MIN(x, y) (((x) < (y)) ? (x) : (y))

weak int getloadavg(double loadavg[], int nelem) {
  // http://linux.die.net/man/3/getloadavg
  int limit = MIN(nelem, 3);
  for (int i = 0; i < limit; i++) {
    loadavg[i] = 0.1;
  }
  return limit;
}
