/*
* 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 .
*/
/*
* 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
#include
#include
#include
#include
#include
#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 */