Subversion Repositories SvarDOS

Rev

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

Rev 990 Rev 1736
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-2024 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
 * copy
26
 * copy
27
 */
27
 */
28
 
28
 
29
/* /A - Used to copy ASCII files. Applies to the filename preceding it and to
29
/* /A - Used to copy ASCII files. Applies to the filename preceding it and to
30
 * all following filenames. Files will be copied until an end-of-file mark is
30
 * all following filenames. Files will be copied until an end-of-file mark is
31
 * encountered in the file being copied. If an end-of-file mark is encountered
31
 * encountered in the file being copied. If an end-of-file mark is encountered
32
 * in the file, the rest of the file is not copied. DOS will append an EOF
32
 * in the file, the rest of the file is not copied. DOS will append an EOF
33
 * mark at the end of the copied file.
33
 * mark at the end of the copied file.
34
 *
34
 *
35
 * /B - Used to copy binary files. Applies to the filename preceding it and to
35
 * /B - Used to copy binary files. Applies to the filename preceding it and to
36
 * all following filenames. Copied files will be read by size (according to
36
 * all following filenames. Copied files will be read by size (according to
37
 * the number of bytes indicated in the file`s directory listing). An EOF mark
37
 * the number of bytes indicated in the file`s directory listing). An EOF mark
38
 * is not placed at the end of the copied file.
38
 * is not placed at the end of the copied file.
39
 *
39
 *
40
 * /V - Checks after the copy to assure that a file was copied correctly. If
40
 * /V - Checks after the copy to assure that a file was copied correctly. If
41
 * the copy cannot be verified, the program will display an error message.
41
 * the copy cannot be verified, the program will display an error message.
42
 * Using this option will result in a slower copying process.
42
 * Using this option will result in a slower copying process.
43
 *
43
 *
44
 * special case: "COPY A+B+C+D" means "append B, C and D files to the A file"
44
 * special case: "COPY A+B+C+D" means "append B, C and D files to the A file"
45
 * if A does not exist, then "append C and D to B", etc.
45
 * if A does not exist, then "append C and D to B", etc.
46
 */
46
 */
47
 
47
 
48
struct copy_setup {
48
struct copy_setup {
49
  const char *src[64];
49
  const char *src[64];
50
  unsigned short src_count; /* how many sources are declared */
50
  unsigned short src_count; /* how many sources are declared */
51
  char cursrc[256];         /* buffer for currently processed src */
51
  char cursrc[256];         /* buffer for currently processed src */
52
  char dst[256];
52
  char dst[256];
53
  unsigned short dstlen;
53
  unsigned short dstlen;
54
  char src_asciimode[64];
54
  char src_asciimode[64];
55
  char dst_asciimode;
55
  char dst_asciimode;
56
  char last_asciimode; /* /A or /B impacts the file preceding it and becomes the new default for all files that follow */
56
  char last_asciimode; /* /A or /B impacts the file preceding it and becomes the new default for all files that follow */
57
  char verifyflag;
57
  char verifyflag;
58
  char lastitemwasplus;
58
  char lastitemwasplus;
59
  unsigned short databufsz;
59
  unsigned short databufsz;
60
  char databuf[1];
60
  char databuf[1];
61
};
61
};
62
 
62
 
63
 
63
 
64
/* copies src to dst, overwriting or appending to the destination.
64
/* copies src to dst, overwriting or appending to the destination.
65
 * - copy is performed in ASCII mode if asciiflag set (stop at first EOF in src
65
 * - copy is performed in ASCII mode if asciiflag set (stop at first EOF in src
66
 *   and append an EOF in dst).
66
 *   and append an EOF in dst).
67
 * - returns zero on success, DOS error code on error */
67
 * - returns zero on success, DOS error code on error */
68
static unsigned short cmd_copy_internal(const char *dst, char dstascii, const char *src, char srcascii, unsigned char appendflag, void *buff, unsigned short buffsz) {
68
static unsigned short cmd_copy_internal(const char *dst, char dstascii, const char *src, char srcascii, unsigned char appendflag, void *buff, unsigned short buffsz) {
69
  unsigned short errcode = 0;
69
  unsigned short errcode = 0;
70
  unsigned short srch = 0xffff, dsth = 0xffff;
70
  unsigned short srch = 0xffff, dsth = 0xffff;
71
  _asm {
71
  _asm {
72
 
72
 
73
    /* open src */
73
    /* open src */
74
    OPENSRC:
74
    OPENSRC:
75
    mov ax, 0x3d00 /* DOS 2+ -- open an existing file, read access mode */
75
    mov ax, 0x3d00 /* DOS 2+ -- open an existing file, read access mode */
76
    mov dx, src    /* ASCIIZ fname */
76
    mov dx, src    /* ASCIIZ fname */
77
    int 0x21       /* CF clear on success, handle in AX */
77
    int 0x21       /* CF clear on success, handle in AX */
78
    jc FAIL
78
    jc FAIL
79
    mov [srch], ax /* store src handle in memory */
79
    mov [srch], ax /* store src handle in memory */
80
 
80
 
81
    /* check appendflag so I know if I have to try opening dst for append */
81
    /* check appendflag so I know if I have to try opening dst for append */
82
    xor al, al
82
    xor al, al
83
    or al, [appendflag]
83
    or al, [appendflag]
84
    jz CREATEDST
84
    jz CREATEDST
85
 
85
 
86
    /* try opening dst first if appendflag set */
86
    /* try opening dst first if appendflag set */
87
    mov ax, 0x3d01 /* DOS 2+ -- open an existing file, write access mode */
87
    mov ax, 0x3d01 /* DOS 2+ -- open an existing file, write access mode */
88
    mov dx, dst    /* ASCIIZ fname */
88
    mov dx, dst    /* ASCIIZ fname */
89
    int 0x21       /* CF clear on success, handle in AX */
89
    int 0x21       /* CF clear on success, handle in AX */
90
    jc CREATEDST   /* failed to open file (file does not exist) */
90
    jc CREATEDST   /* failed to open file (file does not exist) */
91
    mov [dsth], ax /* store dst handle in memory */
91
    mov [dsth], ax /* store dst handle in memory */
92
 
92
 
93
    /* got file open, LSEEK to end of it now so future data is appended */
93
    /* got file open, LSEEK to end of it now so future data is appended */
94
    mov bx, ax     /* file handle in BX (was still in AX) */
94
    mov bx, ax     /* file handle in BX (was still in AX) */
95
    mov ax, 0x4202 /* DOS 2+ -- set file pointer to end of file + CX:DX */
95
    mov ax, 0x4202 /* DOS 2+ -- set file pointer to end of file + CX:DX */
96
    xor cx, cx     /* offset zero */
96
    xor cx, cx     /* offset zero */
97
    xor dx, dx     /* offset zero */
97
    xor dx, dx     /* offset zero */
98
    int 0x21       /* CF set on error */
98
    int 0x21       /* CF set on error */
99
    jc FAIL
99
    jc FAIL
100
    jmp COPY
100
    jmp COPY
101
 
101
 
102
    /* create dst */
102
    /* create dst */
103
    CREATEDST:
103
    CREATEDST:
104
    mov ah, 0x3c   /* DOS 2+ -- create a file */
104
    mov ah, 0x3c   /* DOS 2+ -- create a file */
105
    mov dx, dst
105
    mov dx, dst
106
    xor cx, cx     /* zero out attributes */
106
    xor cx, cx     /* zero out attributes */
107
    int 0x21       /* handle in AX on success, CF set on error */
107
    int 0x21       /* handle in AX on success, CF set on error */
108
    jc FAIL
108
    jc FAIL
109
    mov [dsth], ax /* store dst handle in memory */
109
    mov [dsth], ax /* store dst handle in memory */
110
 
110
 
111
    /* perform actual copy */
111
    /* perform actual copy */
112
    COPY:
112
    COPY:
113
    /* read a block from src */
113
    /* read a block from src */
114
    mov ah, 0x3f   /* DOS 2+ -- read from file */
114
    mov ah, 0x3f   /* DOS 2+ -- read from file */
115
    mov bx, [srch]
115
    mov bx, [srch]
116
    mov cx, [buffsz]
116
    mov cx, [buffsz]
117
    mov dx, [buff] /* DX points to buffer */
117
    mov dx, [buff] /* DX points to buffer */
118
    int 0x21       /* CF set on error, bytes read in AX (0=EOF) */
118
    int 0x21       /* CF set on error, bytes read in AX (0=EOF) */
119
    jc FAIL        /* abort on error */
119
    jc FAIL        /* abort on error */
120
    /* EOF? (ax == 0) */
120
    /* EOF? (ax == 0) */
121
    test ax, ax
121
    test ax, ax
122
    jz ENDOFFILE
122
    jz ENDOFFILE
123
    /* write block of AX bytes to dst */
123
    /* write block of AX bytes to dst */
124
    mov cx, ax     /* block length */
124
    mov cx, ax     /* block length */
125
    mov ah, 0x40   /* DOS 2+ -- write to file (CX bytes from DS:DX) */
125
    mov ah, 0x40   /* DOS 2+ -- write to file (CX bytes from DS:DX) */
126
    mov bx, [dsth] /* file handle */
126
    mov bx, [dsth] /* file handle */
127
    /* mov dx, [buff] */ /* DX points to buffer already */
127
    /* mov dx, [buff] */ /* DX points to buffer already */
128
    int 0x21       /* CF clear and AX=CX on success */
128
    int 0x21       /* CF clear and AX=CX on success */
129
    jc FAIL
129
    jc FAIL
130
    cmp ax, cx     /* sould be equal, otherwise failed */
130
    cmp ax, cx     /* should be equal, otherwise failed */
131
    mov ax, 0x08   /* preset to DOS error "Insufficient memory" */
131
    mov ax, 0x08   /* preset to DOS error "Insufficient memory" */
132
    jne FAIL
132
    jne FAIL
133
    jmp COPY
133
    jmp COPY
134
 
134
 
135
    ENDOFFILE:
135
    ENDOFFILE:
136
    /* if dst ascii mode -> add an EOF (ASCII mode not supported for the time being) */
136
    /* if dst ascii mode -> add an EOF (ASCII mode not supported for the time being) */
137
 
137
 
138
    jmp CLOSESRC
138
    jmp CLOSESRC
139
 
139
 
140
    FAIL:
140
    FAIL:
141
    mov [errcode], ax
141
    mov [errcode], ax
142
 
142
 
-
 
143
    /* close src and dst, but first take care to clone the timestamp to dst */
143
    CLOSESRC:
144
    CLOSESRC:
144
    /* close src and dst */
-
 
145
    mov bx, [srch]
145
    mov bx, [srch]
146
    cmp bx, 0xffff
146
    cmp bx, 0xffff /* skip if not a file */
147
    je CLOSEDST
147
    je CLOSEDST
-
 
148
    mov ax, 0x5700 /* DOS 2+ - GET FILE'S LAST-WRITTEN DATE AND TIME */
-
 
149
    int 0x21  /* time and date are in CX and DX now */
-
 
150
    /* proceed with closing the file */
-
 
151
    /* mov bx, [srch] */
148
    mov ah, 0x3e   /* DOS 2+ -- close a file handle */
152
    mov ah, 0x3e   /* DOS 2+ -- close a file handle */
149
    int 0x21
153
    int 0x21
150
 
154
 
151
    CLOSEDST:
155
    CLOSEDST:
-
 
156
    mov ax, bx     /* save src handle, because I'll need it in a moment */
152
    mov bx, [dsth]
157
    mov bx, [dsth]
153
    cmp bx, 0xffff
158
    cmp bx, 0xffff
154
    je DONE
159
    je DONE
-
 
160
    /* set timestamp, unless src was in error or operation was appending */
-
 
161
    cmp ax, 0xffff /* skip date/time setting if source was not open */
-
 
162
    je SKIPDATESAVE
-
 
163
    /* skip timesetting also if appending */
-
 
164
    xor al, al
-
 
165
    cmp [appendflag], al
-
 
166
    jne SKIPDATESAVE
-
 
167
    /* do the job */
-
 
168
    mov ax, 0x5701 /* DOS 2+ - SET FILE'S LAST-WRITTEN DATE AND TIME */
-
 
169
    /* BX=file handle  CX=TIME  DX=DATE */
-
 
170
    int 0x21
-
 
171
    SKIPDATESAVE:
155
    mov ah, 0x3e   /* DOS 2+ -- close a file handle */
172
    mov ah, 0x3e   /* DOS 2+ -- close a file handle */
156
    int 0x21
173
    int 0x21
157
 
174
 
158
    DONE:
175
    DONE:
159
  }
176
  }
160
  return(errcode);
177
  return(errcode);
161
}
178
}
162
 
179
 
163
 
180
 
164
static enum cmd_result cmd_copy(struct cmd_funcparam *p) {
181
static enum cmd_result cmd_copy(struct cmd_funcparam *p) {
165
  struct copy_setup *setup = (void *)(p->BUFFER);
182
  struct copy_setup *setup = (void *)(p->BUFFER);
166
  unsigned short i;
183
  unsigned short i;
167
  unsigned short copiedcount_in = 0, copiedcount_out = 0; /* number of input/output copied files */
184
  unsigned short copiedcount_in = 0, copiedcount_out = 0; /* number of input/output copied files */
168
  struct DTA *dta = (void *)0x80; /* use DTA at default location in PSP */
185
  struct DTA *dta = (void *)0x80; /* use DTA at default location in PSP */
169
 
186
 
170
  if (cmd_ishlp(p)) {
187
  if (cmd_ishlp(p)) {
171
    nls_outputnl(38,0); /* "Copies one or more files to another location." */
188
    nls_outputnl(38,0); /* "Copies one or more files to another location." */
172
    outputnl("");
189
    outputnl("");
173
    nls_outputnl(38,1); /* "COPY [/A|/B] source [/A|/B] [+source [/A|/B] [+...]] [destination [/A|/B]] [/V]" */
190
    nls_outputnl(38,1); /* "COPY [/A|/B] source [/A|/B] [+source [/A|/B] [+...]] [destination [/A|/B]] [/V]" */
174
    outputnl("");
191
    outputnl("");
175
    nls_outputnl(38,2); /* "source       Specifies the file or files to be copied" */
192
    nls_outputnl(38,2); /* "source       Specifies the file or files to be copied" */
176
    nls_outputnl(38,3); /* "/A           Indicates an ASCII text file" */
193
    nls_outputnl(38,3); /* "/A           Indicates an ASCII text file" */
177
    nls_outputnl(38,4); /* "/B           Indicates a binary file" */
194
    nls_outputnl(38,4); /* "/B           Indicates a binary file" */
178
    nls_outputnl(38,5); /* "destination  Specifies the directory and/or filename for the new file(s)" */
195
    nls_outputnl(38,5); /* "destination  Specifies the directory and/or filename for the new file(s)" */
179
    nls_outputnl(38,6); /* "/V           Verifies that new files are written correctly" */
196
    nls_outputnl(38,6); /* "/V           Verifies that new files are written correctly" */
180
    outputnl("");
197
    outputnl("");
181
    nls_outputnl(38,7); /* "To append files, specify a single file for destination, but multiple (...)" */
198
    nls_outputnl(38,7); /* "To append files, specify a single file for destination, but multiple (...)" */
182
    outputnl("");
199
    outputnl("");
183
    nls_outputnl(38,8); /* "NOTE: /A and /B are no-ops, provided only for compatibility reasons" */
200
    nls_outputnl(38,8); /* "NOTE: /A and /B are no-ops, provided only for compatibility reasons" */
184
    return(CMD_OK);
201
    return(CMD_OK);
185
  }
202
  }
186
 
203
 
187
  /* parse cmdline and fill the setup struct accordingly */
204
  /* parse cmdline and fill the setup struct accordingly */
188
 
205
 
189
  memset(setup, 0, sizeof(*setup));
206
  memset(setup, 0, sizeof(*setup));
190
  setup->databufsz = p->BUFFERSZ - sizeof(*setup);
207
  setup->databufsz = p->BUFFERSZ - sizeof(*setup);
191
 
208
 
192
  for (i = 0; i < p->argc; i++) {
209
  for (i = 0; i < p->argc; i++) {
193
 
210
 
194
    /* switch? */
211
    /* switch? */
195
    if (p->argv[i][0] == '/') {
212
    if (p->argv[i][0] == '/') {
196
      if ((imatch(p->argv[i], "/a")) || (imatch(p->argv[i], "/b"))) {
213
      if ((imatch(p->argv[i], "/a")) || (imatch(p->argv[i], "/b"))) {
197
        setup->last_asciimode = 'b';
214
        setup->last_asciimode = 'b';
198
        if (imatch(p->argv[i], "/a")) setup->last_asciimode = 'a';
215
        if (imatch(p->argv[i], "/a")) setup->last_asciimode = 'a';
199
        /* */
216
        /* */
200
        if (setup->dst[0] != 0) {
217
        if (setup->dst[0] != 0) {
201
          setup->dst_asciimode = setup->last_asciimode;
218
          setup->dst_asciimode = setup->last_asciimode;
202
        } else if (setup->src_count != 0) {
219
        } else if (setup->src_count != 0) {
203
          setup->src_asciimode[setup->src_count - 1] = setup->last_asciimode;
220
          setup->src_asciimode[setup->src_count - 1] = setup->last_asciimode;
204
        }
221
        }
205
      } else if (imatch(p->argv[i], "/v")) {
222
      } else if (imatch(p->argv[i], "/v")) {
206
        setup->verifyflag = 1;
223
        setup->verifyflag = 1;
207
      } else {
224
      } else {
208
        nls_outputnl(0,2); /* "Invalid switch" */
225
        nls_outputnl(0,2); /* "Invalid switch" */
209
        return(CMD_FAIL);
226
        return(CMD_FAIL);
210
      }
227
      }
211
      continue;
228
      continue;
212
    }
229
    }
213
 
230
 
214
    /* not a switch - must be either a source, a destination or a + */
231
    /* not a switch - must be either a source, a destination or a + */
215
    if (p->argv[i][0] == '+') {
232
    if (p->argv[i][0] == '+') {
216
      /* a plus cannot appear after destination or before first source */
233
      /* a plus cannot appear after destination or before first source */
217
      if ((setup->dst[0] != 0) || (setup->src_count == 0)) {
234
      if ((setup->dst[0] != 0) || (setup->src_count == 0)) {
218
        nls_outputnl(0,1); /* "Invalid syntax" */
235
        nls_outputnl(0,1); /* "Invalid syntax" */
219
        return(CMD_FAIL);
236
        return(CMD_FAIL);
220
      }
237
      }
221
      setup->lastitemwasplus = 1;
238
      setup->lastitemwasplus = 1;
222
      /* a plus may be immediately followed by a filename - if so, emulate
239
      /* a plus may be immediately followed by a filename - if so, emulate
223
       * a new argument */
240
       * a new argument */
224
      if (p->argv[i][1] != 0) {
241
      if (p->argv[i][1] != 0) {
225
        p->argv[i] += 1;
242
        p->argv[i] += 1;
226
        i--;
243
        i--;
227
      }
244
      }
228
      continue;
245
      continue;
229
    }
246
    }
230
 
247
 
231
    /* src? (first non-switch or something that follows a +) */
248
    /* src? (first non-switch or something that follows a +) */
232
    if ((setup->lastitemwasplus) || (setup->src_count == 0)) {
249
    if ((setup->lastitemwasplus) || (setup->src_count == 0)) {
233
      setup->src[setup->src_count] = p->argv[i];
250
      setup->src[setup->src_count] = p->argv[i];
234
      setup->src_asciimode[setup->src_count] = setup->last_asciimode;
251
      setup->src_asciimode[setup->src_count] = setup->last_asciimode;
235
      setup->src_count++;
252
      setup->src_count++;
236
      setup->lastitemwasplus = 0;
253
      setup->lastitemwasplus = 0;
237
      continue;
254
      continue;
238
    }
255
    }
239
 
256
 
240
    /* must be a dst then */
257
    /* must be a dst then */
241
    if (setup->dst[0] != 0) {
258
    if (setup->dst[0] != 0) {
242
      nls_outputnl(0,1); /* "Invalid syntax" */
259
      nls_outputnl(0,1); /* "Invalid syntax" */
243
      return(CMD_FAIL);
260
      return(CMD_FAIL);
244
    }
261
    }
245
    if (file_truename(p->argv[i], setup->dst) != 0) {
262
    if (file_truename(p->argv[i], setup->dst) != 0) {
246
      nls_outputnl(0,8); /* "Invalid destination" */
263
      nls_outputnl(0,8); /* "Invalid destination" */
247
      return(CMD_FAIL);
264
      return(CMD_FAIL);
248
    }
265
    }
249
    setup->dst_asciimode = setup->last_asciimode;
266
    setup->dst_asciimode = setup->last_asciimode;
250
    /* if dst is a directory then append a backslash */
267
    /* if dst is a directory then append a backslash */
251
    setup->dstlen = path_appendbkslash_if_dir(setup->dst);
268
    setup->dstlen = path_appendbkslash_if_dir(setup->dst);
252
  }
269
  }
253
 
270
 
254
  /* DEBUG: output setup content ("if 1" to enable) */
271
  /* DEBUG: output setup content ("if 1" to enable) */
255
  #if 0
272
  #if 0
256
  printf("src: ");
273
  printf("src: ");
257
  for (i = 0; i < setup->src_count; i++) {
274
  for (i = 0; i < setup->src_count; i++) {
258
    if (i != 0) printf(", ");
275
    if (i != 0) printf(", ");
259
    printf("%s [%c]", setup->src[i], setup->src_asciimode[i]);
276
    printf("%s [%c]", setup->src[i], setup->src_asciimode[i]);
260
  }
277
  }
261
  printf("\r\n");
278
  printf("\r\n");
262
  printf("dst: %s [%c]\r\n", setup->dst, setup->dst_asciimode);
279
  printf("dst: %s [%c]\r\n", setup->dst, setup->dst_asciimode);
263
  printf("verify: %s\r\n", (setup->verifyflag)?"ON":"OFF");
280
  printf("verify: %s\r\n", (setup->verifyflag)?"ON":"OFF");
264
  #endif
281
  #endif
265
 
282
 
266
  /* must have at least one source */
283
  /* must have at least one source */
267
  if (setup->src_count == 0) {
284
  if (setup->src_count == 0) {
268
    nls_outputnl(0,7); /* "Required parameter missing" */
285
    nls_outputnl(0,7); /* "Required parameter missing" */
269
    return(CMD_FAIL);
286
    return(CMD_FAIL);
270
  }
287
  }
271
 
288
 
272
  /* perform the operation based on setup directives:
289
  /* perform the operation based on setup directives:
273
   * iterate over every source and copy it to dest */
290
   * iterate over every source and copy it to dest */
274
 
291
 
275
  for (i = 0; i < setup->src_count; i++) {
292
  for (i = 0; i < setup->src_count; i++) {
276
    unsigned short t;
293
    unsigned short t;
277
    unsigned short cursrclen;
294
    unsigned short cursrclen;
278
    unsigned short pathendoffset;
295
    unsigned short pathendoffset;
279
 
296
 
280
    /* resolve truename of src and write it to buffer */
297
    /* resolve truename of src and write it to buffer */
281
    t = file_truename(setup->src[i], setup->cursrc);
298
    t = file_truename(setup->src[i], setup->cursrc);
282
    if (t != 0) {
299
    if (t != 0) {
283
      output(setup->src[i]);
300
      output(setup->src[i]);
284
      output(" - ");
301
      output(" - ");
285
      nls_outputnl_doserr(t);
302
      nls_outputnl_doserr(t);
286
      continue;
303
      continue;
287
    }
304
    }
288
    cursrclen = strlen(setup->cursrc); /* remember cursrc length */
305
    cursrclen = strlen(setup->cursrc); /* remember cursrc length */
289
 
306
 
290
    /* if length zero, skip (not sure why this would be possible, though) */
307
    /* if length zero, skip (not sure why this would be possible, though) */
291
    if (cursrclen == 0) continue;
308
    if (cursrclen == 0) continue;
292
 
309
 
293
    /* if src does not end with a backslash AND it is a directory then append a backslash */
310
    /* if src does not end with a backslash AND it is a directory then append a backslash */
294
    cursrclen = path_appendbkslash_if_dir(setup->cursrc);
311
    cursrclen = path_appendbkslash_if_dir(setup->cursrc);
295
 
312
 
296
    /* if src ends with a '\' then append *.* */
313
    /* if src ends with a '\' then append *.* */
297
    if (setup->cursrc[cursrclen - 1] == '\\') {
314
    if (setup->cursrc[cursrclen - 1] == '\\') {
298
      strcat(setup->cursrc, "*.*");
315
      strcat(setup->cursrc, "*.*");
299
    }
316
    }
300
 
317
 
301
    /* remember where the path in cursrc ends */
318
    /* remember where the path in cursrc ends */
302
    for (t = 0; setup->cursrc[t] != 0; t++) {
319
    for (t = 0; setup->cursrc[t] != 0; t++) {
303
      if (setup->cursrc[t] == '\\') pathendoffset = t + 1;
320
      if (setup->cursrc[t] == '\\') pathendoffset = t + 1;
304
    }
321
    }
305
 
322
 
306
    /* */
323
    /* */
307
    if (findfirst(dta, setup->cursrc, 0) != 0) {
324
    if (findfirst(dta, setup->cursrc, 0) != 0) {
308
      continue;
325
      continue;
309
    }
326
    }
310
 
327
 
311
    do {
328
    do {
312
      char appendflag;
329
      char appendflag;
313
      if (dta->attr & DOS_ATTR_DIR) continue; /* skip directories */
330
      if (dta->attr & DOS_ATTR_DIR) continue; /* skip directories */
314
 
331
 
315
      /* compute full path/name of the file */
332
      /* compute full path/name of the file */
316
      strcpy(setup->cursrc + pathendoffset, dta->fname);
333
      strcpy(setup->cursrc + pathendoffset, dta->fname);
317
 
334
 
318
      /* if there was no destination, then YOU are the destination now!
335
      /* if there was no destination, then YOU are the destination now!
319
       * this handles situations like COPY a.txt+b.txt+c.txt */
336
       * this handles situations like COPY a.txt+b.txt+c.txt */
320
      if (setup->dst[0] == NULL) {
337
      if (setup->dst[0] == NULL) {
321
        strcpy(setup->dst, setup->cursrc);
338
        strcpy(setup->dst, setup->cursrc);
322
        setup->dstlen = strlen(setup->dst);
339
        setup->dstlen = strlen(setup->dst);
323
        copiedcount_in++;
340
        copiedcount_in++;
324
        copiedcount_out++;
341
        copiedcount_out++;
325
        continue;
342
        continue;
326
      }
343
      }
327
 
344
 
328
      /* is dst ending with a backslash? then append fname to it */
345
      /* is dst ending with a backslash? then append fname to it */
329
      if (setup->dst[setup->dstlen - 1] == '\\') strcpy(setup->dst + setup->dstlen, dta->fname);
346
      if (setup->dst[setup->dstlen - 1] == '\\') strcpy(setup->dst + setup->dstlen, dta->fname);
330
 
347
 
331
      /* now cursrc contains the full source and dst contains the full dest... COPY TIME! */
348
      /* now cursrc contains the full source and dst contains the full dest... COPY TIME! */
332
 
349
 
333
      /* if dst file exists already -> overwrite it or append?
350
      /* if dst file exists already -> overwrite it or append?
334
          - if dst is a dir (dstlen-1 points at a \\) -> overwrite
351
          - if dst is a dir (dstlen-1 points at a \\) -> overwrite
335
          - otherwise: if copiedcount_in==0 overwrite, else append */
352
          - otherwise: if copiedcount_in==0 overwrite, else append */
336
      output(setup->cursrc);
353
      output(setup->cursrc);
337
      if ((setup->dst[setup->dstlen - 1] == '\\') || (copiedcount_in == 0)) {
354
      if ((setup->dst[setup->dstlen - 1] == '\\') || (copiedcount_in == 0)) {
338
        appendflag = 0;
355
        appendflag = 0;
339
        output(" > ");
356
        output(" > ");
340
        copiedcount_out++;
357
        copiedcount_out++;
341
      } else {
358
      } else {
342
        appendflag = 1;
359
        appendflag = 1;
343
        output(" >> ");
360
        output(" >> ");
344
      }
361
      }
345
      outputnl(setup->dst);
362
      outputnl(setup->dst);
346
 
363
 
347
      t = cmd_copy_internal(setup->dst, 0, setup->cursrc, 0, appendflag, setup->databuf, setup->databufsz);
364
      t = cmd_copy_internal(setup->dst, 0, setup->cursrc, 0, appendflag, setup->databuf, setup->databufsz);
348
      if (t != 0) {
365
      if (t != 0) {
349
        nls_outputnl_doserr(t);
366
        nls_outputnl_doserr(t);
350
        return(CMD_FAIL);
367
        return(CMD_FAIL);
351
      }
368
      }
352
 
369
 
353
      copiedcount_in++;
370
      copiedcount_in++;
354
    } while (findnext(dta) == 0);
371
    } while (findnext(dta) == 0);
355
 
372
 
356
  }
373
  }
357
 
374
 
358
  sprintf(setup->databuf, svarlang_str(38,9)/*"%u file(s) copied"*/, copiedcount_out);
375
  sprintf(setup->databuf, svarlang_str(38,9)/*"%u file(s) copied"*/, copiedcount_out);
359
  outputnl(setup->databuf);
376
  outputnl(setup->databuf);
360
 
377
 
361
  return(CMD_OK);
378
  return(CMD_OK);
362
}
379
}
363
 
380