/* 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 */