1,7 → 1,7 |
/* This file is part of the SvarCOM project and is published under the terms |
* of the MIT license. |
* |
* Copyright (C) 2021 Mateusz Viste |
* Copyright (C) 2021-2022 Mateusz Viste |
* |
* Permission is hereby granted, free of charge, to any person obtaining a |
* copy of this software and associated documentation files (the "Software"), |
41,30 → 41,221 |
* :LABEL |
* : LABEL |
* |
* A label can also be followed by one or more space or tabs, followed by |
* anything. The label is parsed only to the first space, tab, end of line or |
* end of file. Hence this would be perfectly ok: |
* |
* :LABEL this is a comment |
* |
* Labels are searched in the batch file from top to bottom and first match |
* is jumped to. |
* is jumped to. Matching labels is case-insensitive (ie. LABEL == LaBeL) |
*/ |
|
|
static void goto_close_dos_handle(unsigned short fhandle) { |
_asm { |
push bx |
|
mov ah, 0x3e |
mov bx, fhandle |
int 0x21 |
|
pop bx |
} |
} |
|
|
static enum cmd_result cmd_goto(struct cmd_funcparam *p) { |
char *buff = NULL; |
const char *label; |
unsigned short bufflen = 0; |
unsigned short fhandle = 0; |
unsigned short doserr = 0; |
unsigned short batname_seg; |
unsigned short batname_off; |
unsigned char eof_reached = 0; |
unsigned short i; |
|
/* help? reacts only to /? being passed as FIRST argument */ |
if ((p->argc > 0) && imatch(p->argv[0], "/?")) { |
outputnl("Directs batch processing to a labelled line in the batch program."); |
nls_outputnl(17,0); /* "Directs batch processing to a labelled line in the batch program." */ |
outputnl(""); |
outputnl("GOTO LABEL"); |
nls_outputnl(17,1); /* "GOTO LABEL" */ |
outputnl(""); |
outputnl("LABEL specifies a text string used in the batch program as a label."); |
nls_outputnl(17,2); /* "LABEL specifies a text string used in the batch program as a label." */ |
outputnl(""); |
outputnl("A label is on a line by itself and must be preceded by a colon."); |
nls_outputnl(17,3); /* "A label is on a line by itself and must be preceded by a colon." */ |
return(CMD_OK); |
} |
|
/* not inside a batch file? then do nothing */ |
if (p->rmod->bat == NULL) return(CMD_OK); |
/* not inside a batch file? not given any argument? then do nothing */ |
if ((p->rmod->bat == NULL) || (p->argc == 0)) return(CMD_OK); |
|
/* TODO open batch file and read it line by line until label is found or EOF */ |
/* label is in first arg */ |
label = p->argv[0]; |
|
/* TODO if label not found, display error message and abort all batch scripts */ |
/* open batch file (read-only) */ |
batname_seg = FP_SEG(p->rmod->bat->fname); |
batname_off = FP_OFF(p->rmod->bat->fname); |
_asm { |
push bx |
push dx |
|
return(CMD_OK); |
mov ax, batname_seg |
push ds /* save ds */ |
mov ds, ax |
mov dx, batname_off |
mov ax, 0x3d00 |
int 0x21 /* handle in ax on success */ |
pop ds |
jnc OPEN_SUCCESS |
mov doserr, ax |
|
OPEN_SUCCESS: |
mov fhandle, ax /* save file handle */ |
|
pop dx |
pop bx |
} |
|
/* file open failed? */ |
if (doserr != 0) { |
nls_outputnl_doserr(doserr); |
return(CMD_FAIL); |
} |
|
/* reset the rmod bat counter since I will scan all lines from top to bottom */ |
p->rmod->bat->nextline = 0; /* remember this is a byte offset, not a line number */ |
|
/* read bat file line by line until label is found or EOF */ |
for (;;) { |
|
/* move any existing data to the start of the buffer */ |
if (bufflen > 0) memmove(p->BUFFER, buff, bufflen); |
|
buff = p->BUFFER; /* assumption: must be big enough to hold 2 sectors (2 * 512) */ |
|
/* if buffer has less than 512b then load it with another sector (unless eof) */ |
if ((eof_reached == 0) && (bufflen < 512)) { |
/* load 512b of data into buffer */ |
_asm { |
push ax |
push bx |
push cx |
push dx |
pushf |
|
mov ah, 0x3f /* read from file handle */ |
mov bx, fhandle /* file handle where to read from */ |
mov cx, 512 /* read 512 bytes (one sector) */ |
mov dx, buff /* target buffer */ |
add dx, bufflen /* data must follow existing pending data */ |
int 0x21 /* CF clear on success and AX=number of bytes read */ |
/* error? */ |
jnc READ_OK |
mov doserr, ax |
READ_OK: |
add bufflen, ax |
/* set eof if amount of bytes read is shorter than cx */ |
cmp ax, cx |
je EOF_NOT_REACHED |
mov eof_reached, byte ptr 1 |
EOF_NOT_REACHED: |
|
popf |
pop dx |
pop cx |
pop bx |
pop ax |
} |
|
/* on error close the file and quit */ |
if (doserr != 0) { |
goto_close_dos_handle(fhandle); |
nls_outputnl_doserr(doserr); |
return(CMD_FAIL); |
} |
} |
|
/* advance buffer to first non-space/non-tab/non-CR/non-LF */ |
while (bufflen > 0) { |
if ((*buff != ' ') && (*buff != '\t') && (*buff != '\r') && (*buff != '\n')) break; |
bufflen--; |
buff++; |
p->rmod->bat->nextline++; |
} |
|
/* if the line does not start with a colon, then jump to next line */ |
if ((bufflen > 0) && (*buff != ':')) { |
while ((bufflen > 0) && (*buff != '\n')) { |
bufflen--; |
buff++; |
p->rmod->bat->nextline++; |
} |
} |
|
/* refill buffer if needed */ |
if ((bufflen < 512) && (eof_reached == 0)) continue; |
|
/* eof? */ |
if (bufflen == 0) break; |
|
/* skip the colon */ |
if (*buff == ':') { |
bufflen--; |
buff++; |
p->rmod->bat->nextline++; |
} |
|
/* skip any leading white spaces (space or tab) */ |
while (bufflen > 0) { |
if ((*buff != ' ') && (*buff != '\t')) break; |
bufflen--; |
buff++; |
p->rmod->bat->nextline++; |
} |
|
/* read the in-file label and compare it with what's in the label buff */ |
for (i = 0;; i++) { |
/* if end of label then check if it is also end of in-file label (ends with space, tab, \r or \n) */ |
if ((i == bufflen) || (buff[i] == ' ') || (buff[i] == '\t') || (buff[i] == '\r') || (buff[i] == '\n')) { |
if (label[i] == 0) { |
/* match found -> close file, skip to end of line and quit */ |
while (bufflen > 0) { |
bufflen--; |
buff++; |
p->rmod->bat->nextline++; |
if (*buff == '\n') break; |
} |
goto_close_dos_handle(fhandle); |
return(CMD_OK); |
} |
break; |
} |
/* end of label = mismatch */ |
if (label[i] == 0) break; |
/* case-insensitive comparison */ |
if ((label[i] & 0xDF) != (buff[i] & 0xDF)) break; |
} |
|
/* no match, move forward to end of line and repeat */ |
while ((bufflen > 0) && (*buff != '\n')) { |
bufflen--; |
buff++; |
p->rmod->bat->nextline++; |
} |
} |
|
/* close the batch file handle */ |
goto_close_dos_handle(fhandle); |
|
/* label not found, display error message and abort all batch scripts */ |
nls_outputnl(17, 10); /* "Label not found" */ |
rmod_free_bat_llist(p->rmod); |
|
/* restore echo flag as it was before running the (first) bat file */ |
p->rmod->flags &= ~FLAG_ECHOFLAG; |
if (p->rmod->flags & FLAG_ECHO_BEFORE_BAT) p->rmod->flags |= FLAG_ECHOFLAG; |
|
return(CMD_FAIL); |
} |