962 |
mateusz.vi |
1 |
/* This file is part of the SvarCOM project and is published under the terms
|
|
|
2 |
* of the MIT license.
|
|
|
3 |
*
|
964 |
mateusz.vi |
4 |
* Copyright (C) 2021-2022 Mateusz Viste
|
962 |
mateusz.vi |
5 |
*
|
|
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
7 |
* copy of this software and associated documentation files (the "Software"),
|
|
|
8 |
* to deal in the Software without restriction, including without limitation
|
|
|
9 |
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
10 |
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
11 |
* Software is furnished to do so, subject to the following conditions:
|
|
|
12 |
*
|
|
|
13 |
* The above copyright notice and this permission notice shall be included in
|
|
|
14 |
* all copies or substantial portions of the Software.
|
|
|
15 |
*
|
|
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
19 |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
21 |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
22 |
* DEALINGS IN THE SOFTWARE.
|
|
|
23 |
*/
|
|
|
24 |
|
|
|
25 |
/*
|
|
|
26 |
* goto label
|
|
|
27 |
*
|
|
|
28 |
* if label does not exist in the currently processed batch file, then a error
|
|
|
29 |
* "Label not found" is displayed and the batch file is aborted. Any parent
|
|
|
30 |
* batches are ALSO aborted. Same behavior if called without any parameter.
|
|
|
31 |
*
|
|
|
32 |
* if called outside of a batch file, this command has no effect (and does not
|
|
|
33 |
* display any error, even when called without any argument).
|
|
|
34 |
*
|
|
|
35 |
* it reacts to /? by outputing its help screen
|
|
|
36 |
*
|
|
|
37 |
* only 1st argument is processed, any extra arguments are ignored (even /?).
|
|
|
38 |
*
|
|
|
39 |
* Labels can be written as:
|
|
|
40 |
* :LABEL
|
|
|
41 |
* :LABEL
|
|
|
42 |
* : LABEL
|
|
|
43 |
*
|
964 |
mateusz.vi |
44 |
* A label can also be followed by one or more space or tabs, followed by
|
|
|
45 |
* anything. The label is parsed only to the first space, tab, end of line or
|
|
|
46 |
* end of file. Hence this would be perfectly ok:
|
|
|
47 |
*
|
|
|
48 |
* :LABEL this is a comment
|
|
|
49 |
*
|
962 |
mateusz.vi |
50 |
* Labels are searched in the batch file from top to bottom and first match
|
964 |
mateusz.vi |
51 |
* is jumped to. Matching labels is case-insensitive (ie. LABEL == LaBeL)
|
962 |
mateusz.vi |
52 |
*/
|
|
|
53 |
|
964 |
mateusz.vi |
54 |
|
|
|
55 |
static void goto_close_dos_handle(unsigned short fhandle) {
|
|
|
56 |
_asm {
|
|
|
57 |
push bx
|
|
|
58 |
|
|
|
59 |
mov ah, 0x3e
|
|
|
60 |
mov bx, fhandle
|
|
|
61 |
int 0x21
|
|
|
62 |
|
|
|
63 |
pop bx
|
|
|
64 |
}
|
|
|
65 |
}
|
|
|
66 |
|
|
|
67 |
|
962 |
mateusz.vi |
68 |
static enum cmd_result cmd_goto(struct cmd_funcparam *p) {
|
964 |
mateusz.vi |
69 |
char *buff = NULL;
|
|
|
70 |
const char *label;
|
|
|
71 |
unsigned short bufflen = 0;
|
|
|
72 |
unsigned short fhandle = 0;
|
|
|
73 |
unsigned short doserr = 0;
|
|
|
74 |
unsigned short batname_seg;
|
|
|
75 |
unsigned short batname_off;
|
|
|
76 |
unsigned char eof_reached = 0;
|
|
|
77 |
unsigned short i;
|
962 |
mateusz.vi |
78 |
|
|
|
79 |
/* help? reacts only to /? being passed as FIRST argument */
|
|
|
80 |
if ((p->argc > 0) && imatch(p->argv[0], "/?")) {
|
964 |
mateusz.vi |
81 |
nls_outputnl(17,0); /* "Directs batch processing to a labelled line in the batch program." */
|
962 |
mateusz.vi |
82 |
outputnl("");
|
964 |
mateusz.vi |
83 |
nls_outputnl(17,1); /* "GOTO LABEL" */
|
962 |
mateusz.vi |
84 |
outputnl("");
|
964 |
mateusz.vi |
85 |
nls_outputnl(17,2); /* "LABEL specifies a text string used in the batch program as a label." */
|
962 |
mateusz.vi |
86 |
outputnl("");
|
964 |
mateusz.vi |
87 |
nls_outputnl(17,3); /* "A label is on a line by itself and must be preceded by a colon." */
|
962 |
mateusz.vi |
88 |
return(CMD_OK);
|
|
|
89 |
}
|
|
|
90 |
|
964 |
mateusz.vi |
91 |
/* not inside a batch file? not given any argument? then do nothing */
|
|
|
92 |
if ((p->rmod->bat == NULL) || (p->argc == 0)) return(CMD_OK);
|
962 |
mateusz.vi |
93 |
|
964 |
mateusz.vi |
94 |
/* label is in first arg */
|
|
|
95 |
label = p->argv[0];
|
962 |
mateusz.vi |
96 |
|
964 |
mateusz.vi |
97 |
/* open batch file (read-only) */
|
|
|
98 |
batname_seg = FP_SEG(p->rmod->bat->fname);
|
|
|
99 |
batname_off = FP_OFF(p->rmod->bat->fname);
|
|
|
100 |
_asm {
|
|
|
101 |
push bx
|
|
|
102 |
push dx
|
962 |
mateusz.vi |
103 |
|
964 |
mateusz.vi |
104 |
mov ax, batname_seg
|
|
|
105 |
push ds /* save ds */
|
|
|
106 |
mov ds, ax
|
|
|
107 |
mov dx, batname_off
|
|
|
108 |
mov ax, 0x3d00
|
|
|
109 |
int 0x21 /* handle in ax on success */
|
|
|
110 |
pop ds
|
|
|
111 |
jnc OPEN_SUCCESS
|
|
|
112 |
mov doserr, ax
|
|
|
113 |
|
|
|
114 |
OPEN_SUCCESS:
|
|
|
115 |
mov fhandle, ax /* save file handle */
|
|
|
116 |
|
|
|
117 |
pop dx
|
|
|
118 |
pop bx
|
|
|
119 |
}
|
|
|
120 |
|
|
|
121 |
/* file open failed? */
|
|
|
122 |
if (doserr != 0) {
|
|
|
123 |
nls_outputnl_doserr(doserr);
|
|
|
124 |
return(CMD_FAIL);
|
|
|
125 |
}
|
|
|
126 |
|
|
|
127 |
/* reset the rmod bat counter since I will scan all lines from top to bottom */
|
|
|
128 |
p->rmod->bat->nextline = 0; /* remember this is a byte offset, not a line number */
|
|
|
129 |
|
|
|
130 |
/* read bat file line by line until label is found or EOF */
|
|
|
131 |
for (;;) {
|
|
|
132 |
|
|
|
133 |
/* move any existing data to the start of the buffer */
|
|
|
134 |
if (bufflen > 0) memmove(p->BUFFER, buff, bufflen);
|
|
|
135 |
|
|
|
136 |
buff = p->BUFFER; /* assumption: must be big enough to hold 2 sectors (2 * 512) */
|
|
|
137 |
|
|
|
138 |
/* if buffer has less than 512b then load it with another sector (unless eof) */
|
|
|
139 |
if ((eof_reached == 0) && (bufflen < 512)) {
|
|
|
140 |
/* load 512b of data into buffer */
|
|
|
141 |
_asm {
|
|
|
142 |
push ax
|
|
|
143 |
push bx
|
|
|
144 |
push cx
|
|
|
145 |
push dx
|
|
|
146 |
pushf
|
|
|
147 |
|
|
|
148 |
mov ah, 0x3f /* read from file handle */
|
|
|
149 |
mov bx, fhandle /* file handle where to read from */
|
|
|
150 |
mov cx, 512 /* read 512 bytes (one sector) */
|
|
|
151 |
mov dx, buff /* target buffer */
|
|
|
152 |
add dx, bufflen /* data must follow existing pending data */
|
|
|
153 |
int 0x21 /* CF clear on success and AX=number of bytes read */
|
|
|
154 |
/* error? */
|
|
|
155 |
jnc READ_OK
|
|
|
156 |
mov doserr, ax
|
|
|
157 |
READ_OK:
|
|
|
158 |
add bufflen, ax
|
|
|
159 |
/* set eof if amount of bytes read is shorter than cx */
|
|
|
160 |
cmp ax, cx
|
|
|
161 |
je EOF_NOT_REACHED
|
|
|
162 |
mov eof_reached, byte ptr 1
|
|
|
163 |
EOF_NOT_REACHED:
|
|
|
164 |
|
|
|
165 |
popf
|
|
|
166 |
pop dx
|
|
|
167 |
pop cx
|
|
|
168 |
pop bx
|
|
|
169 |
pop ax
|
|
|
170 |
}
|
|
|
171 |
|
|
|
172 |
/* on error close the file and quit */
|
|
|
173 |
if (doserr != 0) {
|
|
|
174 |
goto_close_dos_handle(fhandle);
|
|
|
175 |
nls_outputnl_doserr(doserr);
|
|
|
176 |
return(CMD_FAIL);
|
|
|
177 |
}
|
|
|
178 |
}
|
|
|
179 |
|
|
|
180 |
/* advance buffer to first non-space/non-tab/non-CR/non-LF */
|
|
|
181 |
while (bufflen > 0) {
|
|
|
182 |
if ((*buff != ' ') && (*buff != '\t') && (*buff != '\r') && (*buff != '\n')) break;
|
|
|
183 |
bufflen--;
|
|
|
184 |
buff++;
|
|
|
185 |
p->rmod->bat->nextline++;
|
|
|
186 |
}
|
|
|
187 |
|
|
|
188 |
/* if the line does not start with a colon, then jump to next line */
|
|
|
189 |
if ((bufflen > 0) && (*buff != ':')) {
|
|
|
190 |
while ((bufflen > 0) && (*buff != '\n')) {
|
|
|
191 |
bufflen--;
|
|
|
192 |
buff++;
|
|
|
193 |
p->rmod->bat->nextline++;
|
|
|
194 |
}
|
|
|
195 |
}
|
|
|
196 |
|
|
|
197 |
/* refill buffer if needed */
|
|
|
198 |
if ((bufflen < 512) && (eof_reached == 0)) continue;
|
|
|
199 |
|
|
|
200 |
/* eof? */
|
|
|
201 |
if (bufflen == 0) break;
|
|
|
202 |
|
|
|
203 |
/* skip the colon */
|
|
|
204 |
if (*buff == ':') {
|
|
|
205 |
bufflen--;
|
|
|
206 |
buff++;
|
|
|
207 |
p->rmod->bat->nextline++;
|
|
|
208 |
}
|
|
|
209 |
|
|
|
210 |
/* skip any leading white spaces (space or tab) */
|
|
|
211 |
while (bufflen > 0) {
|
|
|
212 |
if ((*buff != ' ') && (*buff != '\t')) break;
|
|
|
213 |
bufflen--;
|
|
|
214 |
buff++;
|
|
|
215 |
p->rmod->bat->nextline++;
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
/* read the in-file label and compare it with what's in the label buff */
|
|
|
219 |
for (i = 0;; i++) {
|
|
|
220 |
/* if end of label then check if it is also end of in-file label (ends with space, tab, \r or \n) */
|
|
|
221 |
if ((i == bufflen) || (buff[i] == ' ') || (buff[i] == '\t') || (buff[i] == '\r') || (buff[i] == '\n')) {
|
|
|
222 |
if (label[i] == 0) {
|
|
|
223 |
/* match found -> close file, skip to end of line and quit */
|
|
|
224 |
while (bufflen > 0) {
|
|
|
225 |
bufflen--;
|
|
|
226 |
buff++;
|
|
|
227 |
p->rmod->bat->nextline++;
|
|
|
228 |
if (*buff == '\n') break;
|
|
|
229 |
}
|
|
|
230 |
goto_close_dos_handle(fhandle);
|
|
|
231 |
return(CMD_OK);
|
|
|
232 |
}
|
|
|
233 |
break;
|
|
|
234 |
}
|
|
|
235 |
/* end of label = mismatch */
|
|
|
236 |
if (label[i] == 0) break;
|
|
|
237 |
/* case-insensitive comparison */
|
|
|
238 |
if ((label[i] & 0xDF) != (buff[i] & 0xDF)) break;
|
|
|
239 |
}
|
|
|
240 |
|
|
|
241 |
/* no match, move forward to end of line and repeat */
|
|
|
242 |
while ((bufflen > 0) && (*buff != '\n')) {
|
|
|
243 |
bufflen--;
|
|
|
244 |
buff++;
|
|
|
245 |
p->rmod->bat->nextline++;
|
|
|
246 |
}
|
|
|
247 |
}
|
|
|
248 |
|
|
|
249 |
/* close the batch file handle */
|
|
|
250 |
goto_close_dos_handle(fhandle);
|
|
|
251 |
|
|
|
252 |
/* label not found, display error message and abort all batch scripts */
|
|
|
253 |
nls_outputnl(17, 10); /* "Label not found" */
|
|
|
254 |
rmod_free_bat_llist(p->rmod);
|
|
|
255 |
|
|
|
256 |
/* restore echo flag as it was before running the (first) bat file */
|
|
|
257 |
p->rmod->flags &= ~FLAG_ECHOFLAG;
|
|
|
258 |
if (p->rmod->flags & FLAG_ECHO_BEFORE_BAT) p->rmod->flags |= FLAG_ECHOFLAG;
|
|
|
259 |
|
|
|
260 |
return(CMD_FAIL);
|
962 |
mateusz.vi |
261 |
}
|