/*
 * mars-nwe-dosutils - NetWare/DOS utility tools.
 *
 * Copyright (C) 2026 Mario Fetka
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Purpose: Small DOS delayed-start helper used by the utility set.
 * Depends on: DOS/Open Watcom runtime headers only; it is independent from the NetWare helper modules.
 */
/* dlystrt.c - maintainer-only delayed DOS command starter.
 *
 * This is a tiny TSR helper for login/logout tests.  It waits a short
 * time, then stuffs a command line into the BIOS keyboard buffer.  The
 * command is therefore executed by the command processor after the batch
 * file that installed DLYSTRT has already returned to the DOS prompt.
 *
 * Example:
 *   DLYSTRT /T:2 C:\\LGNTC.BAT
 *
 * The tool is intended for MAINTAINER_BUILD only and is not installed in
 * normal builds.
 */

#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <i86.h>

#ifndef MAINTAINER_BUILD
int main(void)
{
  puts("DLYSTRT is only available in maintainer builds.");
  return 1;
}
#else

#define MAX_CMD 126
#define BIOS_SEG 0x0040
#define KB_HEAD  0x001a
#define KB_TAIL  0x001c
#define KB_BUF   0x001e
#define KB_END   0x003e

typedef void (__interrupt __far *intr_fn)(void);

static intr_fn old_int1c;
static volatile unsigned wait_ticks;
static volatile unsigned pos;
static volatile unsigned done;
static char command[MAX_CMD + 3];

static int strnicmp_local(const char *a, const char *b, int n)
{
  int ca, cb;
  while (n-- > 0) {
    ca = toupper((unsigned char)*a++);
    cb = toupper((unsigned char)*b++);
    if (ca != cb || ca == 0 || cb == 0) return ca - cb;
  }
  return 0;
}

static int kb_put_char(char ch)
{
  unsigned far *headp = (unsigned far *)MK_FP(BIOS_SEG, KB_HEAD);
  unsigned far *tailp = (unsigned far *)MK_FP(BIOS_SEG, KB_TAIL);
  unsigned far *bufp;
  unsigned head = *headp;
  unsigned tail = *tailp;
  unsigned next = tail + 2;

  if (next >= KB_END) next = KB_BUF;
  if (next == head) return 0;          /* keyboard buffer full */

  bufp = (unsigned far *)MK_FP(BIOS_SEG, tail);
  *bufp = (unsigned)((unsigned char)ch); /* scan code 0, ASCII ch */
  *tailp = next;
  return 1;
}

static void raw_restore_vector(void)
{
  intr_fn __far *vecp;

  if (!old_int1c) return;

  /* Do not call DOS from the timer interrupt.  Restore the IVT entry
   * directly, then leave the TSR out of the interrupt path before the
   * delayed command is executed.
   */
  _disable();
  vecp = (intr_fn __far *)MK_FP(0x0000, 0x1c * 4);
  *vecp = old_int1c;
  _enable();
}

static void __interrupt __far dly_int1c(void)
{
  intr_fn prev = old_int1c;

  if (!done) {
    if (wait_ticks) {
      wait_ticks--;
    } else {
      if (command[pos]) {
        if (kb_put_char(command[pos])) pos++;
      } else {
        /* One-shot: unhook before pressing Enter so the command that gets
         * started by COMMAND.COM runs without DLYSTRT still sitting on INT 1Ch.
         */
        done = 1;
        raw_restore_vector();
        kb_put_char('\r');
      }
    }
  }

  if (prev) prev();
}

static void usage(void)
{
  puts("Usage: DLYSTRT [/T:seconds] command [args]");
  puts("Maintainer helper: delay-start a command after returning to DOS.");
}

static void build_command(int argc, char **argv, int first)
{
  int i;

  command[0] = '\0';
  for (i = first; i < argc; i++) {
    if (command[0]) strncat(command, " ", MAX_CMD - strlen(command));
    strncat(command, argv[i], MAX_CMD - strlen(command));
  }
  command[MAX_CMD] = '\0';
}

int main(int argc, char **argv)
{
  unsigned seconds = 2;
  int first = 1;
  unsigned paras;

  if (argc < 2) {
    usage();
    return 1;
  }

  if (!strnicmp_local(argv[first], "/T:", 3)) {
    seconds = (unsigned)atoi(argv[first] + 3);
    if (seconds == 0) seconds = 1;
    first++;
  }

  if (first >= argc) {
    usage();
    return 1;
  }

  build_command(argc, argv, first);
  wait_ticks = seconds * 18;
  pos = 0;
  done = 0;

  old_int1c = _dos_getvect(0x1c);
  _dos_setvect(0x1c, dly_int1c);

  printf("DLYSTRT installed: %s\n", command);

  /* Keep a small fixed amount resident.  This maintainer helper is tiny;
   * 256 paragraphs = 4 KiB is enough for code, data and the command buffer
   * in the small DOS model used here.
   */
  paras = 256;
  _dos_keep(0, paras);
  return 0;
}

#endif /* MAINTAINER_BUILD */
