Subversion Repositories SvarDOS

Rev

Rev 964 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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