Subversion Repositories SvarDOS

Rev

Rev 962 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
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
}