Subversion Repositories SvarDOS

Rev

Rev 409 | Rev 413 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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