/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2022 Justine Alexandra Roberts Tunney                              │
│                                                                              │
│ Permission to use, copy, modify, and/or distribute this software for         │
│ any purpose with or without fee is hereby granted, provided that the         │
│ above copyright notice and this permission notice appear in all copies.      │
│                                                                              │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             │
│ PERFORMANCE OF THIS SOFTWARE.                                                │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/mem/mem.h"
#include "libc/mem/gc.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/x/x.h"
#include "tool/args/args.h"

STATIC_YOINK("zip_uri_support");

static struct ZipArgs {
  bool initialized;
  bool loaded;
  int oldargc;
  char *data;
  char **args;
  char **oldargv;
} g_zipargs;

static void AddZipArg(int *argc, char ***argv, char *arg) {
  *argv = xrealloc(*argv, (++(*argc) + 1) * sizeof(*(*argv)));
  (*argv)[*argc - 1] = arg;
  (*argv)[*argc - 0] = 0;
}

void FreeZipArgs(void) {
  if (g_zipargs.loaded) {
    free(g_zipargs.data);
    free(g_zipargs.args);
    __argc = g_zipargs.oldargc;
    __argv = g_zipargs.oldargv;
    g_zipargs.loaded = false;
  }
}

int LoadZipArgsImpl(int *argc, char ***argv, char *data) {
  int i, n, fd;
  bool founddots;
  char *arg, **args, *state, *start;
  assert(!g_zipargs.loaded);
  if (_chomp(data)) {
    n = 0;
    args = 0;
    start = data;
    founddots = false;
    AddZipArg(&n, &args, (*argv)[0]);
    while ((arg = strtok_r(start, "\r\n", &state))) {
      if (!strcmp(arg, "...")) {
        founddots = true;
        for (i = 1; i < *argc; ++i) {
          AddZipArg(&n, &args, (*argv)[i]);
        }
      } else {
        AddZipArg(&n, &args, arg);
      }
      start = 0;
    }
    if (founddots || *argc <= 1) {
      if (!g_zipargs.initialized) {
        atexit(FreeZipArgs);
        g_zipargs.oldargc = __argc;
        g_zipargs.oldargv = __argv;
        g_zipargs.initialized = true;
      }
      g_zipargs.loaded = true;
      g_zipargs.data = data;
      g_zipargs.args = args;
      *argc = n;
      *argv = args;
      __argc = n;
      __argv = args;
    } else {
      free(data);
      free(args);
    }
  }
  return 0;
}

/**
 * Replaces argument list with `/zip/.args` contents if it exists.
 *
 * Your `.args` file should have one argument per line.
 *
 * If the special argument `...` is *not* encountered, then the
 * replacement will only happen if *no* CLI args are specified.
 *
 * If the special argument `...` *is* encountered, then it'll be
 * replaced with whatever CLI args were specified by the user.
 *
 * @return 0 on success, or -1 if not found w/o errno clobber
 */
int LoadZipArgs(int *argc, char ***argv) {
  int e;
  char *p;
  e = errno;
  if ((p = xslurp("/zip/.args", 0))) {
    return LoadZipArgsImpl(argc, argv, p);
  } else {
    errno = e;
    return -1;
  }
}