/*
 * FDNPKG16 - FreeDOS Network Package manager
 * Copyright (C) 2012-2017 Mateusz Viste
 * Copyright (C) 2025-2026 Victoria Crenshaw aka sparky4 && Katheryn Northwood
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */


#include <ctype.h>    /* tolower() */
#include <stdio.h>    /* printf() */
#include <stdlib.h>   /* free() */
#include <string.h>   /* strcasecmp() */
#include <strings.h>  /* strcasecmp() */
#include <unistd.h>   /* unlink() */
#include <conio.h>    /* getch() */
#include <malloc.h>   /* _heapmin() */

#include "fdnpkg16.h" /* this file */
#include "helpers.h"  /* various helper functions */
#ifdef USE_INTERNAL_WATTCP
#include "http.h"     /* http_get() */
#endif
#include "kitten.h"   /* required for kitten subsystem */
#include "kprintf.h"  /* kitten_printf() */
#include "libgz.h"    /* ungz() */
#include "libunzip.h" /* zip_freelist()... */
#include "loadconf.h" /* loadconf() */
#ifdef USE_INTERNAL_WATTCP
#include "net.h"      /* net_init() */
#endif
#include "pkgdb.h"    /* createdb(), findpkg()... */
#include "pkgsrch.h"  /* pkgsrch() */
#include "pkginst.h"  /* pkginstall() */
#include "pkgrem.h"   /* pkgrem() */
#include "readenv.h"  /* readenv() */
#include "showinst.h" /* showinstalledpkgs() */
#include "memcore.h"  /* farcoreleft() & coreleft() */
#include "version.h"

/* #define DEBUG */ /* uncomment this to enable debug mode */


//unsigned _stklen = /*512*/24 * 1024; /* I need 512K of stack space */ //not doable in 16 bit lets give it 24k

// sparky4: may not be used
// sparky4: just some program naming here for fdnpkg16 and fdnpkg86
#ifdef USE_INTERNAL_WATTCP
#define EXECNAME "86"
extern char *wattcpVersion(); /* provided by wattcp to poll its version */
#else
#define EXECNAME "16"
#endif

// sparky4: for possible short and long help header and such
static void printhelpheader(void) {
 puts("FDNPKG" EXECNAME " v" PVER " Copyright (C) " POLDDATE " Mateusz Viste &\r\n"
 "Copyright (C) " PNEWDATE " Victoria Crenshaw & Katheryn Northwood");
 kitten_puts(1, 0, "This is a network package manager for FreeDOS.");
}

static void printhelp(void) {
  //puts("FDNPKG" EXECNAME " v" PVER " (C) " PDATE " Mateusz Viste & Victoria Crenshaw");
  //kitten_puts(1, 0, "This is a network package manager for FreeDOS."); puts("");
  printhelpheader();
  kitten_printf(1,1,"Usage: FDNPKG%s action [parameters]", EXECNAME);
  puts("");
  puts("");
  kitten_puts(1, 2,  "Where action is one of the following:");
  printf(" search [str]      - "); kitten_puts(1, 3,  "search net repositories for package containing 'str'");
  printf(" vsearch [str]     - "); kitten_puts(1, 4,  "same as 'search', but prints also source repositories");
  printf(" install pkg       - "); kitten_puts(1, 5,  "install the package 'pkg' (or local zip file)");
  printf(" install-nosrc pkg - "); kitten_puts(1, 10, "install the package 'pkg' (or local zip file) w/o sources");
  printf(" install-wsrc pkg  - "); kitten_puts(1, 11, "install the package 'pkg' (or local zip file) with sources");
  printf(" reinstall pkg     - "); kitten_puts(1, 20, "reinstall the package 'pkg' (or local zip file)");
  printf(" remove pkg        - "); kitten_puts(1, 6,  "remove the package 'pkg'");
  printf(" listlocal [str]   - "); kitten_puts(1, 16, "list all local (installed) packages containing 'str'");
  printf(" listfiles pkg     - "); kitten_puts(1, 18, "list files owned by the package 'pkg'");
  printf(" checkupdates      - "); kitten_puts(1, 13, "check for available updates of packages and display them");
  printf(" update [pkg]      - "); kitten_puts(1, 15, "update 'pkg' to last version (or all packages if no arg)");
  printf(" holdlist [str]    - "); kitten_puts(1, 22, "list held all packages or packages containing 'str'");
  printf(" hold pkg          - "); kitten_puts(1, 23, "hold 'pkg' at current version");
  printf(" unhold pkg        - "); kitten_puts(1, 24, "unhold 'pkg' for updates");
  printf(" dumpcfg           - "); kitten_puts(1, 7,  "print out the configuration loaded from the cfg file");
  printf(" clearcache        - "); kitten_printf(1,19,"clear FDNPKG%s's local cache", EXECNAME); puts("");
  printf(" license           - "); kitten_puts(1, 8,  "print out the license of this program");
#ifdef DEBUG
#if defined(__WATCOMC__)
#if (__WATCOMC__ >= 1200)
  printf ("FDNPKG%s is Compiled with OpenWatcom %d.%d\n", EXECNAME,
                  (__WATCOMC__/100) - 11, (__WATCOMC__ % 100) / 10);
#else
  printf ("FDNPKG%s is Compiled with Watcom C %d.%d\n", EXECNAME, __WATCOMC__/100, __WATCOMC__ % 100);
#endif /* #if (__WATCOMC__ >= 1200) */
#endif /* #if defined(__WATCOMC__) */
  puts("");
#endif /* #ifdef DEBUG */
#ifdef USE_INTERNAL_WATTCP
  kitten_printf(1, 9, "FDNPKG%s is linked against the Watt-32 version below:", EXECNAME);
  puts("");
  puts(wattcpVersion());
#else
  //kitten_printf(1, 21, "FDNPKG%s is using HTTPGET.EXE", EXECNAME);
#endif
}
static void printhelpshort(void) {
  //puts("FDNPKG" EXECNAME " v" PVER " (C) " PDATE " Mateusz Viste & Victoria Crenshaw");
  //kitten_puts(1, 0, "This is a network package manager for FreeDOS."); puts("");
  printhelpheader();
  kitten_printf(1,1,"Usage: FDNPKG%s action [parameters]", EXECNAME);
  puts("");
  puts("");
  kitten_puts(1, 2,  "Where action is one of the following:");
  printf(" se [str]          - "); kitten_puts(1, 3,  "search net repositories for package containing 'str'");
  printf(" vs [str]          - "); kitten_puts(1, 4,  "same as 'search', but prints also source repositories");
  printf(" in pkg            - "); kitten_puts(1, 5,  "install the package 'pkg' (or local zip file)");
  printf(" in-nosrc pkg      - "); kitten_puts(1, 10, "install the package 'pkg' (or local zip file) w/o sources");
  printf(" in-wsrc pkg       - "); kitten_puts(1, 11, "install the package 'pkg' (or local zip file) with sources");
  printf(" ri pkg            - "); kitten_puts(1, 20, "reinstall the package 'pkg' (or local zip file)");
  printf(" rm pkg            - "); kitten_puts(1, 6,  "remove the package 'pkg'");
  printf(" ll [str]          - "); kitten_puts(1, 16, "list all local (installed) packages containing 'str'");
  printf(" lf pkg            - "); kitten_puts(1, 18, "list files owned by the package 'pkg'");
  printf(" cu                - "); kitten_puts(1, 13, "check for available updates of packages and display them");
  printf(" up [pkg]          - "); kitten_puts(1, 15, "update 'pkg' to last version (or all packages if no arg)");
  printf(" hl [str]          - "); kitten_puts(1, 22, "list held all packages or packages containing 'str'");
  printf(" ho pkg            - "); kitten_puts(1, 23, "hold 'pkg' at current version");
  printf(" uh pkg            - "); kitten_puts(1, 24, "unhold 'pkg' for updates");
  printf(" dc                - "); kitten_puts(1, 7,  "print out the configuration loaded from the cfg file");
  printf(" cc                - "); kitten_printf(1,19,"clear FDNPKG%s's local cache", EXECNAME); puts("");
  printf(" li                - "); kitten_puts(1, 8,  "print out the license of this program");
#ifdef DEBUG
#if defined(__WATCOMC__)
#if (__WATCOMC__ >= 1200)
  printf ("FDNPKG%s is Compiled with OpenWatcom %d.%d\n", EXECNAME,
                  (__WATCOMC__/100) - 11, (__WATCOMC__ % 100) / 10);
#else
  printf ("FDNPKG%s is Compiled with Watcom C %d.%d\n", EXECNAME, __WATCOMC__/100, __WATCOMC__ % 100);
#endif /* #if (__WATCOMC__ >= 1200) */
#endif /* #if defined(__WATCOMC__) */
  puts("");
#endif /* #ifdef DEBUG */
#ifdef USE_INTERNAL_WATTCP
  kitten_printf(1, 9, "FDNPKG%s is linked against the Watt-32 version below:", EXECNAME);
  puts("");
  puts(wattcpVersion());
#else
  kitten_printf(1, 21, "FDNPKG%s is using HTTPGET.EXE", EXECNAME);
#endif
}
static void printlic(void) {
  puts("FDNPKG" EXECNAME " v" PVER " - FreeDOS Network Package manager 16-bit\r\n"
       "Copyright (C) " POLDDATE " Mateusz Viste\r\n"
       "Copyright (C) " PNEWDATE " Victoria Crenshaw & Katheryn Northwood\r\n");

  puts("Permission is hereby granted, free of charge, to any person obtaining a copy\r\n"
       "of this software and associated documentation files (the \"Software\"), to deal\r\n"
       "in the Software without restriction, including without limitation the rights\r\n"
       "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n"
       "copies of the Software, and to permit persons to whom the Software is\r\n"
       "furnished to do so, subject to the following conditions:\r\n");

  puts("The above copyright notice and this permission notice shall be included in\r\n"
       "all copies or substantial portions of the Software.\r\n");

  puts("THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n"
       "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n"
       "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n"
       "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n"
       "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r\n"
       "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\r\n"
       "IN THE SOFTWARE.");

  puts("If you want to contribute, let me know! https://discord.com/invite/qBH9W7fXHJ\n"
       "or join my irc and ping me irc://4ch.mooo.com/#fdnpkg16");
}

// sparky4: actions the program can do
enum actions {
  ACTION_HELP,
  ACTION_SEARCH,
  ACTION_INSTALL,
  ACTION_INSTALL_LOCALFILE,
  ACTION_UPGRADE,
  ACTION_REMOVE,
  ACTION_DUMPCFG,
  ACTION_LICENSE,
  ACTION_LISTLOCAL,
  ACTION_LISTFILES,
  ACTION_CHECKUPDATES,
  ACTION_UPDATE,
  ACTION_CLEARCACHE,
  ACTION_REINSTALL,
  ACTION_REINSTALL_LOCALFILE,
  ACTION_HOLDLIST,
  ACTION_HOLD,
  ACTION_UNHOLD,
  ACTION_NOTSEARCH
};


/* try to create a temp file in the 'dirname' directory and write garbage to
 * it. returns 0 on success, non-zero on failure. */
static int trycreatefileindir(char *dirname) {
  FILE *fd;
  char filename[512];
  int writeres;
  if (dirname == NULL) return(-1);
  sprintf(filename, "%s\\fdnpkg16.tmp", dirname);
  fd = fopen(filename, "wb");
  if (fd == NULL) return(-1);
  writeres = fputs("FDNPKG16", fd);
  fclose(fd);
  if (writeres < 0) return(-1);
  return(0);
}


#define QUIT(x) \
  kittenclose(); \
  return(x);

#define PRESS_KEY() \
  kitten_puts(2, 21, "Press any key but Q to continue..."); \
  while (tolower(getch()) == 'q') { QUIT(0) }


int main(int argc, char **argv) {
  long htgetres;
  char *tempdir;
  char cfgfile[256];
  char *dosdir;
  #define MAXREPS 64
  char *repolist[MAXREPS];
  int repolistcount;
  int verbosemode = 0;
  int flags = 0;
  long maxcachetime = 7200;
  char *proxy = NULL;
  char downloadingstring[64];
  int proxyport = 8080;
  int x;
  char *mapdrv = "";
  unsigned long cfgfilecrc;
  struct pkgdb *pkgdb = NULL;
  struct customdirs *dirlist = NULL;
  enum actions action = ACTION_HELP;
  FILE *zipfilefd;
  struct ziplist *zipfileidx;

  //sparky4: new variables from me! <3
  int i, y;                 // sparky4: used in for loops... you know.. counting
  int netinitres;           // sparky4: for netowrking initialization for internal networking (external for now)
  int arglen;               // sparky4: pkgname length
  int argci;                // sparky4: argument variable for number of variables. for multi packages
  char actionarg[18] = "";  // sparky4: this gotta be long enough for the commands

  // sparky4: this is for various action flags to prevent networking on local files or no repository action for local files
  //short action_flags; // sparky4: this is for not doing repository stuff. specifically not require to load content of repositories!

  #ifdef DEBUG
  puts("DEBUG BUILD " __DATE__ " " __TIME__);
  #endif

  /* First init KITTEN */
  kittenopen("FDNPKG16");

  /* fetch the 'downloading...' kitten string, we will need to pass it to http_get later, eventually */
  sprintf(downloadingstring, "%s", kittengets(11, 0, "Downloading %s... %ld bytes"));

  /* read all required environment variables */
  if (readenv(&dosdir, &tempdir, cfgfile, sizeof(cfgfile), argv) != 0) {
    QUIT(0)
  }

  /* check the available memory and display a warning if too low */
  // sparky4: farcoreleft() function from: https://forum.vcfed.org/index.php?threads/ibm-5160-memory-management-c-code-compiling-with-open-watcom.1247002/post-1369076
  /* 192k */
  /*196608L*/ //sparky4: old value. did some recalculating and 256k is needed for calling httpget.exe
  /* 256k */ // sparky4: new value of 256k
#ifdef DEBUG
  printf("farcoreleft() == %ld\n", farcoreleft());
  printf("coreleft() == %u\n", coreleft());
  //getch();
#endif
  if (farcoreleft() < 262144L) {
    kitten_printf(2, 17, "WARNING: Virtual memory too low. FDNPKG%s might behave unreliably.", EXECNAME); puts("");
    getch(); // sparky4: warn user of low memory, and this getch() will let them see it!
  } // sparky4: seems to work now with the new farcoreleft function from vfed! Thanks guys! <3

  /* Load the list of package repositories */
  repolistcount = loadconf(cfgfile, repolist, MAXREPS, &cfgfilecrc, &maxcachetime, &dirlist, &flags, &proxy, &proxyport, &mapdrv);
  if (repolistcount < 0) return(5);

  // sparky4: check for / at front of action argument 1
  if (argc > 1) { // Ensure argv[1] exists
    if (argv[1][0] == '/') { // Check if the first character is '/'
      // Shift the pointer to effectively remove the first character
      // This modifies what actionarg points to, but not the underlying string data
      strcpy(actionarg, (++argv[1])); // sparky4: if there is /, copy the argument with out /.
    } else {
      strcpy(actionarg, argv[1]);     // sparky4: copy the argument directly if there is no /
    }
  }

  // sparky4: for now initiate this variable as the same value as argc we will change it later
  argci = argc;

  // sparky4: start of that huge for loop. This loop manages the packages in the argument list! :D
  for (i = 0; i < argci; i++) {
    //action_flags = 0; // sparky4: old may remove

//0000    printf("Starting  value(hex): 0x%X\n", flags);
    // sparky4 flag resetter for 2 bits
    flags &= (~((1 << 3) | (1 << 4)));  // sparky4: disable 3rd and 4th bit position in the flags for the 2 new bits to be reseted
    // 2 bits are for FDNPKG16_NETINIT && FDNPKG16_NOREPOA
//0000    printf("Resulting value(hex): 0x%X\n", flags);

    /* parse cli parameters */
    if (argc > 1) { /* fdnpkg16 action [param] */
      if ((argc > 2) && (argv[2] != NULL)) {  /* fdnpkg16 action package(s) */
        argci = argc - 2; // sparky4: get the for loop condition number ready for more than 1 argument in package area! :D
      } else argci--;     // sparky4: this prevent looping twice for these functions (this only happens if argc == 2)
      if ((strcasecmp(actionarg, "search") && strcasecmp(actionarg, "se")) == 0) {
        action = ACTION_SEARCH;
      } else if ((strcasecmp(actionarg, "vsearch") && strcasecmp(actionarg, "vs")) == 0) {
        action = ACTION_SEARCH;
        verbosemode = 1;
      } else if (((strcasecmp(actionarg, "install") && strcasecmp(actionarg, "in")) == 0) || ((strcasecmp(actionarg, "install-nosrc") && strcasecmp(actionarg, "in-nosrc")) == 0) || ((strcasecmp(actionarg, "install-wsrc") && strcasecmp(actionarg, "in-wsrc")) == 0)) {
        if ((strcasecmp(actionarg, "install-nosrc") && strcasecmp(actionarg, "in-nosrc")) == 0) flags |= PKGINST_NOSOURCE;
        if ((strcasecmp(actionarg, "install-wsrc") && strcasecmp(actionarg, "in-wsrc")) == 0) flags &= ~(PKGINST_NOSOURCE);
        if (argc < 3) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        } else {
          arglen = strlen(argv[i+2]);
          action = ACTION_INSTALL;
          if (arglen > 4) {
            if ((argv[i+2][arglen - 4] == '.') && (tolower(argv[i+2][arglen - 3]) == 'z') && (tolower(argv[i+2][arglen - 2]) == 'i')) { /* if argument ends with '.zi?' (zip/zib), then it's a local package file */
              action = ACTION_INSTALL_LOCALFILE;
              /*action_*/flags |= (FDNPKG16_NETINIT | FDNPKG16_NOREPOA);
            }
          }
        }
      } else if ((strcasecmp(actionarg, "remove") && strcasecmp(actionarg, "rm")) == 0) {
        if (argc < 3) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        } else {
          action = ACTION_REMOVE;
          pkgrem(argv[i+2], dosdir, mapdrv);
          /*action_*/flags |= (FDNPKG16_NETINIT | FDNPKG16_NOREPOA);
        }
      // sparky4: this is a loop now so we can do this now! :D
      } else if ((strcasecmp(actionarg, "update") && strcasecmp(actionarg, "up")) == 0) {
        if (argc >= 3) {
          action = ACTION_UPDATE;
        } else if (argc == 2) {
          action = ACTION_UPGRADE;
        } else {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        }
      } else if (((strcasecmp(actionarg, "listlocal") && strcasecmp(actionarg, "ll")) == 0) || ((strcasecmp(actionarg, "showinstalled") && strcasecmp(actionarg, "si")) == 0)) { /* 'showinstalled' is the old name for 'listlocal' - retained for backward compatibility, but to be removed in some futur */
          action = ACTION_LISTLOCAL;
          /*action_*/flags |= (FDNPKG16_NETINIT | FDNPKG16_NOREPOA);
      } else if ((strcasecmp(actionarg, "listfiles") && strcasecmp(actionarg, "lf")) == 0) {
        if (argc < 3) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        } else {
          action = ACTION_LISTFILES;
          /*action_*/flags |= (FDNPKG16_NOREPOA);
        }
      } else if ((strcasecmp(actionarg, "dumpcfg") && strcasecmp(actionarg, "dc")) == 0) {
        if (argc != 2) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        } else {
          action = ACTION_DUMPCFG;
          /*action_*/flags |= (FDNPKG16_NETINIT);
        }
      } else if ((strcasecmp(actionarg, "license") && strcasecmp(actionarg, "li")) == 0) {
        if (argc != 2) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
        } else {
          printlic();
        }
        QUIT(0)
      } else if ((strcasecmp(actionarg, "checkupdates") && strcasecmp(actionarg, "cu")) == 0) {
        if (argc != 2) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        } else {
          action = ACTION_CHECKUPDATES;
        }
      } else if ((strcasecmp(actionarg, "clearcache") && strcasecmp(actionarg, "cc")) == 0) {
        if (argc != 2) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        } else {
          action = ACTION_CLEARCACHE;
          /*action_*/flags |= (FDNPKG16_NETINIT);
        }
      } else if ((strcasecmp(actionarg, "reinstall") && strcasecmp(actionarg, "ri")) == 0) {
        if (argc < 3) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        } else {
          arglen = strlen(argv[i+2]);
          action = ACTION_REINSTALL;
          if (arglen > 4) {
            if ((argv[i+2][arglen - 4] == '.') && (tolower(argv[i+2][arglen - 3]) == 'z') && (tolower(argv[i+2][arglen - 2]) == 'i')) { /* if argument ends with '.zi?' (zip/zib), then it's a local package file */
              action = ACTION_REINSTALL_LOCALFILE;
              /*action_*/flags |= (FDNPKG16_NETINIT | FDNPKG16_NOREPOA);
            }
          }
        }
      } else if ((strcasecmp(actionarg, "holdlist") && strcasecmp(actionarg, "hl")) == 0) {
          action = ACTION_HOLDLIST;
          /*action_*/flags |= (FDNPKG16_NETINIT | FDNPKG16_NOREPOA);
      } else if ((strcasecmp(actionarg, "hold") && strcasecmp(actionarg, "ho")) == 0) {
        if (argc < 3) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        } else {
          action = ACTION_HOLD;
          /*action_*/flags |= (FDNPKG16_NETINIT | FDNPKG16_NOREPOA);
        }
      } else if ((strcasecmp(actionarg, "unhold") && strcasecmp(actionarg, "uh")) == 0) {
        if (argc < 3) {
          kitten_printf(2, 4, "Invalid number of arguments. Run FDNPKG%s without any parameter for help.", EXECNAME); puts("");
          QUIT(0)
        } else {
          action = ACTION_UNHOLD;
          /*action_*/flags |= (FDNPKG16_NETINIT | FDNPKG16_NOREPOA);
        }
      } else if ((strcasecmp(actionarg, "notinstalled") && strcasecmp(actionarg, "ni")) == 0) {
        action = ACTION_NOTSEARCH;
      } else if ((strcasecmp(actionarg, "vnotinstalled") && strcasecmp(actionarg, "vn")) == 0) {
        action = ACTION_NOTSEARCH;
        verbosemode = 1;
        // sparky4: <3
      } else if ((strcasecmp(actionarg, "bibabo")) == 0) {
        printf("ビバボ！ｗ");
        QUIT(0)
      } else if ((strcasecmp(actionarg, "poi")) == 0) {
        printf("ぽい！ｗ");
        QUIT(0)
      } else if ((strcasecmp(actionarg, "fcl")) == 0) {
        printf("farcoreleft() == %ld Byte(s) Free\n", farcoreleft());
        printf("coreleft() == %ld Byte(s) Free\n", coreleft());
        QUIT(0)
      }
    }

    //sparky4: switches are faster
    switch (action) {
      /* sparky4: Dual help! this runs both helps! :D */
      case ACTION_HELP:
        printhelp();
        PRESS_KEY()
        printhelpshort();
        puts("");
        QUIT(0)
      break;
    }

    /* test if the %TEMP% directory is writeable */
    if (trycreatefileindir(tempdir) != 0) {
      kitten_printf(2, 18, "ERROR: Unable to write in the '%s' directory. Check your %%TEMP%% variable.", tempdir);
      puts("");
      QUIT(0)
    }

    //sparky4: switches are faster
    switch (action) {
      /* listing local packages need no special preparation - do it now and quit */
      case ACTION_LISTLOCAL:
      {
        char *filterstr = NULL;
        if (argc >= 3) filterstr = argv[i+2];  // sparky4: again this is to stop it from listing all files 2x ...
        showinstalledpkgs(filterstr, dosdir);
      }
      break;
      /* listing local files need no special preparation - do it now and quit */
      case ACTION_LISTFILES:
        listfilesofpkg(argv[i+2], dosdir);
      break;
      /* locally held at version package listings */
      case ACTION_HOLDLIST:
      {
        char *filterstr = NULL;
        if (argc >= 3) filterstr = argv[i+2];  // sparky4: again this is to stop it from listing all files 2x ...
        showheldedpkgs(filterstr, dosdir);
      }
      break;
      /* hold a package and dont change it */
      case ACTION_HOLD:
      {
        char *filterstr = NULL;
        if (argc >= 3) filterstr = argv[i+2];  // sparky4: again this is to stop it from listing all files 2x ...
        holdpkg(filterstr, dosdir);
      }
      break;
      /* unhold a package and do change it */
      case ACTION_UNHOLD:
      {
        char *filterstr = NULL;
        if (argc >= 3) filterstr = argv[i+2];  // sparky4: again this is to stop it from listing all files 2x ...
        unholdpkg(filterstr, dosdir);
      }
      break;
      /* if we install from a local file, do it and quit */
      case ACTION_INSTALL_LOCALFILE:
      {
        char pkgname[64];
        char buffmem1k[1024];
        int t, lastpathdelim = -1, u = 0;
        for (t = 0; argv[i+2][t] != 0; t++) {
          if ((argv[i+2][t] == '/') || (argv[i+2][t] == '\\')) lastpathdelim = t;
        }
        /* copy the filename into pkgname (without path elements) */
        for (t = lastpathdelim + 1; argv[i+2][t] != 0; t++) pkgname[u++] = argv[i+2][t];
        pkgname[u] = 0; /* terminate the string */
        /* truncate the file's extension (.zip) */
        for (t = u; t > 0; t--) {
          if (pkgname[t] == '.') {
            pkgname[t] = 0;
            break;
          }
        }

        /* prepare the zip file and install it */
        zipfileidx = pkginstall_preparepackage(pkgdb, pkgname, tempdir, argv[i+2], flags, repolist, &zipfilefd, proxy, proxyport, downloadingstring, dosdir, dirlist, buffmem1k, mapdrv);
        if (zipfileidx != NULL) {
          pkginstall_installpackage(pkgname, dosdir, dirlist, zipfileidx, zipfilefd, mapdrv);
          fclose(zipfilefd);
        }
      }
      break;
      /* sparky4: Reinstall a local file! */
      case ACTION_REINSTALL_LOCALFILE:
      {
        char pkgname[64];
        char buffmem1k[1024];
        int t, lastpathdelim = -1, u = 0;
        for (t = 0; argv[i+2][t] != 0; t++) {
          if ((argv[i+2][t] == '/') || (argv[i+2][t] == '\\')) lastpathdelim = t;
        }
        /* copy the filename into pkgname (without path elements) */
        for (t = lastpathdelim + 1; argv[i+2][t] != 0; t++) pkgname[u++] = argv[i+2][t];
        pkgname[u] = 0; /* terminate the string */
        /* truncate the file's extension (.zip) */
        for (t = u; t > 0; t--) {
          if (pkgname[t] == '.') {
            pkgname[t] = 0;
            break;
          }
        }

        /* prepare the zip file */
        zipfileidx = pkginstall_preparepackage(pkgdb, pkgname, tempdir, argv[i+2], flags | PKGINST_UPDATE, repolist, &zipfilefd, proxy, proxyport, downloadingstring, dosdir, dirlist, buffmem1k, mapdrv);
        /* if the zip file is ok, remove the old package and install our zip file */
        if (zipfileidx != NULL) {
          if (pkgrem(pkgname, dosdir, mapdrv) == -2) { /* mayday! removal failed for some reason */
            zip_freelist(&zipfileidx);
          } else {
            pkginstall_installpackage(pkgname, dosdir, dirlist, zipfileidx, zipfilefd, mapdrv);
          }
          fclose(zipfilefd);
        }
      }
      break;
      /* clear cache? */
      case ACTION_CLEARCACHE:
      {
        char tempfile[512];
        sprintf(tempfile, "%s\\fdnpkg16.db", tempdir);
        unlink(tempfile);
        kitten_puts(2, 19, "Cache cleared.");
        QUIT(0)
      }
      break;
    } //sparky4: end of big switch function

    /* if we want to play, check if any repo is configured */
    if (repolistcount == 0) {
      kitten_puts(2, 5, "No repository is configured. You need at least one.");
      kitten_puts(2, 6, "You should place in your configuration file at least one entry of such form:");
      puts("REPO http://www.freedos.org/repo");
      puts("");
      QUIT(0)
    }

    /* sparky4: check arg2 for a . if there is one in existance then skip networking initiation */ // sparky4: also dont do networking when we are removing a package or i > 0 (for the loop)
    if ((/*action_*/flags & FDNPKG16_NETINIT) == 0) {
      if ((!((argv[i+2][arglen - 4] == '.') && (tolower(argv[i+2][arglen - 3]) == 'z') && (tolower(argv[i+2][arglen - 2]) == 'i')))) { /* if argument ends with '.zi?' (zip/zib), then it's a local package file */
        /* if there is at least one online repo, init the Watt32 stack */
        for (x = 0; x < repolistcount; x++) {
          if (detect_localpath(repolist[x]) == 0) {
            #ifndef USE_INTERNAL_WATTCP
            #ifdef DEBUG
            printf("mTCP is used\n");
            #endif /* #ifdef DEBUG */
            #ifdef USE_MTCP
            netinitres = system("dhcp");
            #else
            netinitres = 0; // use dhcp in httpget this is currently used
            #endif
            #else /* #ifndef USE_INTERNAL_WATTCP */
            #ifdef DEBUG
            printf("watt32 is used\n");
            #endif /* #ifdef DEBUG */
            netinitres = net_init();
            #endif /* #ifndef USE_INTERNAL_WATTCP */
            if (netinitres != 0) {
              kitten_puts(2, 15, "Error: TCP/IP initialization failed!");
              QUIT(0)
            }
  #ifdef USE_INTERNAL_WATTCP
            puts("");
  #endif
            /*action_*/flags |= (FDNPKG16_NETINIT);
            break;
          }
        }
      }
    } //sparky4: end of ((/*action_*/flags & FDNPKG16_NETINIT) == 0)

    if (action == ACTION_DUMPCFG) { /* if all we wanted was to print out repositories... */
      short dci;
      struct customdirs *dircursor;
      dci = 4;

      PRESS_KEY()
      printf("maxcachetime: %ld seconds\n", maxcachetime);
      printf("installsources: %d\n", (flags & PKGINST_NOSOURCE) == 0); // sparky4: found a bug here with comparison. == not != it inverted it.
      printf("mapdrives: %s\n", mapdrv);
      puts("");

      for (dircursor = dirlist; dircursor != NULL; dircursor = dircursor->next) {
        switch (dci) {
          case 24:
            getch();
            dci = 0;
          break;
          default:
            printf("%s -> %s\n", dircursor->name, dircursor->location);
            dci++;
          break;
        }
      }
      if (dirlist != NULL) puts("");
      kitten_printf(2, 8, "The list of configured FDNPKG%s repositories follows:", EXECNAME); puts(""); dci++;
      for (x = 0; x < repolistcount; x++) {
        switch (dci) {
          case 24:
            getch();
            dci = 0;
          break;
          default:
            puts(repolist[x]);
            dci++;
          break;
        }
      }
      puts("");
    } else if ((/*action_*/flags & FDNPKG16_NOREPOA) == 0) { /* other actions: search, install, checkupdates, update - all that require to load content of repositories */
      pkgdb = createdb();
      if (pkgdb != NULL) {
        char tempfilegz[512];
        char tempfile[512];
        char repoindex[512];
        int ungzres;
#ifndef USE_INTERNAL_WATTCP
        char command[512];// sparky4: for the download command for external programs
        FILE *batch_file;
        char commandforbatch[512];
#endif
        sprintf(tempfile, "%s\\fdnpkg16.db", tempdir);
        if (loaddb_fromcache(pkgdb, tempfile, cfgfilecrc, maxcachetime) == 0) { /* load db from cache (if not older than 2h) */
          // sparky4: I added this ==== line so the user know the loop is going on! :D and not have to be spammed with the kitten message below.
          if (i == 0) kitten_puts(2, 13, "Package database loaded from local cache.");
        } else {
          freedb(&pkgdb);      /* recreate the db from scratch, because after */
          pkgdb = createdb();  /* a cache attempt it might contain garbage */
          /* download every repo index into %TEMP% and load them in RAM */
          for (x = 0; x < repolistcount; x++) {
            kitten_printf(2, 16, "Loading %s...", repolist[x]);
            puts("");
            sprintf(repoindex, "%sindex.gz", repolist[x]);
            if (detect_localpath(repolist[x]) != 0) { /* if it's an ondisk repo, use the file as-is */
              strcpy(tempfilegz, repoindex);
              htgetres = 100; /* whatever > 0 would be fine, too */
            } else { /* else it's a network resource, let's download it */
              sprintf(tempfilegz, "%s\\fdnpkg_z.tmp", tempdir);
              #ifdef DEBUG
              puts("DEBUG: download start");
              #endif
              htgetres = -1;
              #ifndef USE_INTERNAL_WATTCP
              #ifdef USE_MTCP
              sprintf(command, "@echo off\nhtget -quiet -o %s %s", tempfilegz, repoindex);
              #else
              sprintf(command, "@httpget.exe %s %s /q", repoindex, tempfilegz);
              #endif
              #endif
              for (y = 0; y < MAXINDEXRETRIES; y++) {
                sprintf(repoindex, "%sindex.gz", repolist[x]);  // refresh the index variable
                #ifdef DEBUG
                printf("\trepoindex: %s\n", repoindex);
                #endif
                #ifdef USE_INTERNAL_WATTCP
                if (htgetres > 0) break;
                _nheapgrow();
                _fheapgrow();
                htgetres = http_get(repoindex, tempfilegz, proxy, proxyport, NULL, 1);
                _fheapshrink();
                _nheapshrink();
                #else
  //              if (htgetres == 21) break;
                if (htgetres >= 0) break;
                // lets try this
                sprintf(commandforbatch, "%s\\fdnpkg16.bat", tempdir);
                batch_file = fopen(commandforbatch, "w");
                if (batch_file == NULL) {
                  kitten_printf(3, 10, "Error: Could not create %s!");
                  htgetres = -1;
                } else {
                  fprintf(batch_file, "%s", command);
                  fclose(batch_file);
                  _nheapmin();
                    //_nheapshrink(); // sparky4: these 2 functions are for heap management to make it smaller so we can call the batch file with the commands
                  _fheapmin();
                    //_fheapshrink(); // sparky4: these 4 functions are for heap management to make it smaller so we can call the batch file with the commands
                  htgetres = system(commandforbatch);
                }
                #endif
                #ifdef DEBUG
                printf("htgetres returned: %d\n", htgetres);
                #endif
  //              #ifdef USE_INTERNAL_WATTCP
  //              if (htgetres <= 0) putchar('.');
  //              #else
  //              if (htgetres != 21) putchar('.');
  //              #endif
              }
              #ifdef DEBUG
              puts("DEBUG: download stop");
              #endif
            }
  //          #ifdef USE_INTERNAL_WATTCP
  //          if (htgetres <= 0) {
  //          #else
  //          if (htgetres != 21) {
  //              #endif
            if (htgetres < 0) {
              kitten_puts(2, 10, "Repository download failed!");
              #ifndef ERRCACHE
              maxcachetime = 0; /* disable cache writing this time */
              #endif
            } else {
              char *dbmsg;
              /* uncompress and load the index file */
              sprintf(tempfile, "%s\\fdnpkg16.tmp", tempdir);
              ungzres = ungz(tempfilegz, tempfile);
              if ((ungzres != 0) || (loaddb(pkgdb, tempfile, x, &dbmsg) != 0)) {
                kitten_puts(2, 11, "An error occured while trying to load repository from tmp file...");
                printf("Error code: %d\n", ungzres);
                maxcachetime = 0; /* disable cache writing this time */
              }
              /* if there's a message to display from the db provider, display it now */
              if (dbmsg != NULL) {
                puts("");
                kitten_printf(6, 5, "Message from %s:", repolist[x]);
                printf("\n%s\n", dbmsg);
                free(dbmsg);
              }
//0000              if (htgetres > 0) puts("ok");  // sparky4: just let the user know the file was downloaded and installed
            }
          }
          /* save results into the (new) cache file db */
          if (maxcachetime > 0) {
            sprintf(tempfile, "%s\\fdnpkg16.db", tempdir);
            dumpdb(pkgdb, tempfile, cfgfilecrc);
          }
        }
        //sparky4: switches are faster
        switch (action) {
          case ACTION_SEARCH: /* for search: iterate through the sorted db, and print out all packages that match the pattern */
            if (argc >= 3) { /* a search term has been provided */
              pkgsearch(pkgdb, argv[i+2], verbosemode, repolist);
            } else {
              pkgsearch(pkgdb, NULL, verbosemode, repolist);
            }
          break;
          case ACTION_NOTSEARCH:
            {
              char *filterstr = NULL;
              if (argc >= 3) filterstr = argv[i+2];  // sparky4: again this is to stop it from listing all files 2x ...
              shownotinstalledpkgs(filterstr, dosdir, pkgdb, verbosemode, repolist);//000000
            }
          break;
          case ACTION_INSTALL: /* install remote package */
            if (validate_package_not_installed(argv[i+2], dosdir, mapdrv) == 0) { /* check that package is not already installed first */
              char membuff1k[1024];
              zipfileidx = pkginstall_preparepackage(pkgdb, argv[i+2], tempdir, NULL, flags, repolist, &zipfilefd, proxy, proxyport, downloadingstring, dosdir, dirlist, membuff1k, mapdrv);
              if (zipfileidx != NULL) {
                pkginstall_installpackage(argv[i+2], dosdir, dirlist, zipfileidx, zipfilefd, mapdrv);
                fclose(zipfilefd);
              }
            }
          break;
          case ACTION_UPDATE: /* UPDATE, but only for a SINGLE package */
            if (is_package_installed(argv[i+2], dosdir, mapdrv) == 0) { /* is this package installed at all? */
              kitten_printf(10, 6, "Package %s is not installed.", argv[i+2]);
              puts("");
            } else if (checkupdates(dosdir, pkgdb, repolist, argv[i+2], tempdir, 0, dirlist, proxy, proxyport, downloadingstring, mapdrv) != 0) { /* no update available */
              kitten_printf(10, 2, "No update found for the '%s' package.", argv[i+2]);
              puts("");
            } else { /* the package is locally installed, and an update have been found - let's proceed */
              char membuff1k[1024];
              /* prepare the zip file */
              zipfileidx = pkginstall_preparepackage(pkgdb, argv[i+2], tempdir, NULL, flags | PKGINST_UPDATE, repolist, &zipfilefd, proxy, proxyport, downloadingstring, dosdir, dirlist, membuff1k, mapdrv);
              /* if the zip file is ok, remove the old package and install our zip file */
              if (zipfileidx != NULL) {
                if (pkgrem(argv[i+2], dosdir, mapdrv) != 0) { /* mayday! removal failed for some reason */
                  zip_freelist(&zipfileidx);
                } else {
                  pkginstall_installpackage(argv[i+2], dosdir, dirlist, zipfileidx, zipfilefd, mapdrv);
                }
                fclose(zipfilefd);
              }
            }
          break;
          case ACTION_UPGRADE: /* recursive UPDATE for the whole system */
            checkupdates(dosdir, pkgdb, repolist, NULL, tempdir, flags | PKGINST_UPDATE, dirlist, proxy, proxyport, downloadingstring, mapdrv);
          break;
          case ACTION_CHECKUPDATES: /* checkupdates */
            checkupdates(dosdir, pkgdb, repolist, NULL, tempdir, 0, dirlist, proxy, proxyport, downloadingstring, mapdrv);
          break;
          case ACTION_REINSTALL: /* REINSTALL, but only for a SINGLE package */
            {
              char membuff1k[1024];
              /* prepare the zip file */
              zipfileidx = pkginstall_preparepackage(pkgdb, argv[i+2], tempdir, NULL, flags | PKGINST_UPDATE, repolist, &zipfilefd, proxy, proxyport, downloadingstring, dosdir, dirlist, membuff1k, mapdrv);
              /* if the zip file is ok, remove the old package and install our zip file */
              if (zipfileidx != NULL) {
                if (pkgrem(argv[i+2], dosdir, mapdrv) == -2) { /* mayday! removal failed for some reason */
                  zip_freelist(&zipfileidx);
                } else {
                  pkginstall_installpackage(argv[i+2], dosdir, dirlist, zipfileidx, zipfilefd, mapdrv);
                }
                fclose(zipfilefd);
              }
              break;
            }
          break;
        } //sparky4: end of action switch
        /* free memory of the pkg database */
        freedb(&pkgdb);
      } /* pkgdb != NULL */
    } /* action == ACTION_LISTREP */
    if (i+1 < argci) puts("========================================");  // sparky4: for formatting and splitting the lines between packages!
  } /* sparky4: end of that huge for loop. This loop manages the packages in the argument list! :D */
  freeconf(repolist, repolistcount, &dirlist);
  QUIT(0)
}
