blob: 4a0f00427a561c9e728192317f8b1ab6de4c4e46 [file] [log] [blame]
Alistair Delvabeaee832021-02-24 11:27:23 -08001/* Copyright 1997-2002,2005-2009 Alain Knaff.
2 * This file is part of mtools.
3 *
4 * Mtools is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Mtools is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * mainloop.c
18 * Iterating over all the command line parameters, and matching patterns
19 * where needed
20 */
21
22#include "sysincludes.h"
23#include "msdos.h"
24#include "mtools.h"
25#include "vfat.h"
26#include "fs.h"
27#include "mainloop.h"
28#include "plain_io.h"
29#include "file.h"
30#include "file_name.h"
31
32
33/* Fix the info in the MCWD file to be a proper directory name.
34 * Always has a leading separator. Never has a trailing separator
35 * (unless it is the path itself). */
36
37static const char *fix_mcwd(char *ans)
38{
39 FILE *fp;
40 char *s;
41 char buf[MAX_PATH];
42
43 fp = open_mcwd("r");
44 if(!fp || !fgets(buf, MAX_PATH, fp)) {
45 if(fp)
46 fclose(fp);
47 ans[0] = get_default_drive();
48 strcpy(ans+1, ":/");
49 return ans;
50 }
51
52 buf[strlen(buf) -1] = '\0';
53 fclose(fp);
54 /* drive letter present? */
55 s = buf;
56 if (buf[0] && buf[1] == ':') {
57 memcpy(ans, buf, 2);
58 ans[2] = '\0';
59 s = &buf[2];
60 } else {
61 ans[0] = get_default_drive();
62 strcpy(ans+1, ":");
63 }
64 /* add a leading separator */
65 if (*s != '/' && *s != '\\') {
66 strcat(ans, "/");
67 strcat(ans, s);
68 } else
69 strcat(ans, s);
70
71#if 0
72 /* translate to upper case */
73 for (s = ans; *s; ++s) {
74 *s = ch_toupper(*s);
75 if (*s == '\\')
76 *s = '/';
77 }
78#endif
79 /* if only drive, colon, & separator */
80 if (strlen(ans) == 3)
81 return(ans);
82 /* zap the trailing separator */
83 if (*--s == '/')
84 *s = '\0';
85 return ans;
86}
87
Yi Kong39bbd962022-01-09 19:41:38 +080088int unix_dir_loop(Stream_t *Stream, MainParam_t *mp);
Alistair Delvabeaee832021-02-24 11:27:23 -080089int unix_loop(Stream_t *Stream UNUSEDP, MainParam_t *mp, char *arg,
90 int follow_dir_link);
91
92static int _unix_loop(Stream_t *Dir, MainParam_t *mp,
93 const char *filename UNUSEDP)
94{
95 return unix_dir_loop(Dir, mp);
96}
97
98int unix_loop(Stream_t *Stream UNUSEDP, MainParam_t *mp,
99 char *arg, int follow_dir_link)
100{
101 int ret;
102 int isdir=0;
Yi Kong39bbd962022-01-09 19:41:38 +0800103 size_t unixNameLength;
Alistair Delvabeaee832021-02-24 11:27:23 -0800104
105 mp->File = NULL;
106 mp->direntry = NULL;
107 unixNameLength = strlen(arg);
108 if(unixNameLength > 1 && arg[unixNameLength-1] == '/') {
109 /* names ending in slash, and having at least two characters */
110 char *name = strdup(arg);
111 name[unixNameLength-1]='\0';
112 mp->unixSourceName = name;
113 } else {
114 mp->unixSourceName = arg;
115 }
116 /* mp->dir.attr = ATTR_ARCHIVE;*/
117 mp->loop = _unix_loop;
118 if((mp->lookupflags & DO_OPEN)){
119 mp->File = SimpleFileOpen(0, 0, arg, O_RDONLY, 0, 0, 0, 0);
120 if(!mp->File){
121 perror(arg);
122#if 0
123 tmp = _basename(arg);
124 strncpy(mp->filename, tmp, VBUFSIZE);
125 mp->filename[VBUFSIZE-1] = '\0';
126#endif
127 return ERROR_ONE;
128 }
129 GET_DATA(mp->File, 0, 0, &isdir, 0);
130 if(isdir) {
131#if !defined(__EMX__) && !defined(OS_mingw32msvc)
132 struct MT_STAT buf;
133#endif
134
135 FREE(&mp->File);
136#if !defined(__EMX__) && !defined(OS_mingw32msvc)
137 if(!follow_dir_link &&
138 MT_LSTAT(arg, &buf) == 0 &&
139 S_ISLNK(buf.st_mode)) {
140 /* skip links to directories in order to avoid
141 * infinite loops */
142 fprintf(stderr,
143 "skipping directory symlink %s\n",
144 arg);
Yi Kong39bbd962022-01-09 19:41:38 +0800145 return 0;
Alistair Delvabeaee832021-02-24 11:27:23 -0800146 }
147#endif
148 if(! (mp->lookupflags & ACCEPT_DIR))
149 return 0;
150 mp->File = OpenDir(arg);
151 }
152 }
153
154 if(isdir)
155 ret = mp->dirCallback(0, mp);
156 else
157 ret = mp->unixcallback(mp);
158 FREE(&mp->File);
159 return ret;
160}
161
162
163int isSpecial(const char *name)
164{
165 if(name[0] == '\0')
166 return 1;
167 if(!strcmp(name,"."))
168 return 1;
169 if(!strcmp(name,".."))
170 return 1;
Yi Kong39bbd962022-01-09 19:41:38 +0800171 return 0;
Alistair Delvabeaee832021-02-24 11:27:23 -0800172}
173
174#ifdef HAVE_WCHAR_H
175int isSpecialW(const wchar_t *name)
176{
177 if(name[0] == '\0')
178 return 1;
179 if(!wcscmp(name,L"."))
180 return 1;
181 if(!wcscmp(name,L".."))
182 return 1;
Yi Kong39bbd962022-01-09 19:41:38 +0800183 return 0;
Alistair Delvabeaee832021-02-24 11:27:23 -0800184}
185#endif
186
187static int checkForDot(int lookupflags, const wchar_t *name)
188{
189 return (lookupflags & NO_DOTS) && isSpecialW(name);
190}
191
192
193typedef struct lookupState_t {
194 Stream_t *container;
195 int nbContainers;
196 Stream_t *Dir;
197 int nbDirs;
198 const char *filename;
199} lookupState_t;
200
201static int isUniqueTarget(const char *name)
202{
203 return name && strcmp(name, "-");
204}
205
206static int handle_leaf(direntry_t *direntry, MainParam_t *mp,
207 lookupState_t *lookupState)
208{
209 Stream_t *MyFile=0;
210 int ret;
211
212 if(got_signal)
213 return ERROR_ONE;
214 if(lookupState) {
215 /* we are looking for a "target" file */
216 switch(lookupState->nbDirs) {
217 case 0: /* no directory yet, open it */
218 lookupState->Dir = OpenFileByDirentry(direntry);
219 lookupState->nbDirs++;
220 /* dump the container, we have
221 * better now */
222 FREE(&lookupState->container);
223 return 0;
224 case 1: /* we have already a directory */
225 FREE(&lookupState->Dir);
226 fprintf(stderr,"Ambiguous\n");
227 return STOP_NOW | ERROR_ONE;
228 default:
229 return STOP_NOW | ERROR_ONE;
230 }
231 }
232
233 mp->direntry = direntry;
234 if(IS_DIR(direntry)) {
235 if(mp->lookupflags & (DO_OPEN | DO_OPEN_DIRS))
236 MyFile = mp->File = OpenFileByDirentry(direntry);
237 ret = mp->dirCallback(direntry, mp);
238 } else {
239 if(mp->lookupflags & DO_OPEN)
240 MyFile = mp->File = OpenFileByDirentry(direntry);
241 ret = mp->callback(direntry, mp);
242 }
243 FREE(&MyFile);
244 if(isUniqueTarget(mp->targetName))
245 ret |= STOP_NOW;
246 return ret;
247}
248
249static int _dos_loop(Stream_t *Dir, MainParam_t *mp, const char *filename)
Yi Kong39bbd962022-01-09 19:41:38 +0800250{
Alistair Delvabeaee832021-02-24 11:27:23 -0800251 Stream_t *MyFile=0;
252 direntry_t entry;
253 int ret;
254 int r;
255
256 ret = 0;
257 r=0;
258 initializeDirentry(&entry, Dir);
259 while(!got_signal &&
Yi Kong39bbd962022-01-09 19:41:38 +0800260 (r=vfat_lookup_zt(&entry, filename,
261 mp->lookupflags,
262 mp->shortname.data, mp->shortname.len,
263 mp->longname.data, mp->longname.len)) == 0 ){
Alistair Delvabeaee832021-02-24 11:27:23 -0800264 mp->File = NULL;
265 if(!checkForDot(mp->lookupflags,entry.name)) {
266 MyFile = 0;
267 if((mp->lookupflags & DO_OPEN) ||
268 (IS_DIR(&entry) &&
269 (mp->lookupflags & DO_OPEN_DIRS))) {
270 MyFile = mp->File = OpenFileByDirentry(&entry);
271 }
272 if(got_signal)
273 break;
274 mp->direntry = &entry;
275 if(IS_DIR(&entry))
276 ret |= mp->dirCallback(&entry,mp);
277 else
278 ret |= mp->callback(&entry, mp);
279 FREE(&MyFile);
280 }
281 if (fat_error(Dir))
282 ret |= ERROR_ONE;
283 if(mp->fast_quit && (ret & ERROR_ONE))
284 break;
285 }
286 if (r == -2)
287 return ERROR_ONE;
288 if(got_signal)
289 ret |= ERROR_ONE;
290 return ret;
291}
292
293static int recurs_dos_loop(MainParam_t *mp, const char *filename0,
294 const char *filename1,
295 lookupState_t *lookupState)
296{
297 /* Dir is de-allocated by the same entity which allocated it */
298 const char *ptr;
299 direntry_t entry;
Yi Kong39bbd962022-01-09 19:41:38 +0800300 size_t length;
Alistair Delvabeaee832021-02-24 11:27:23 -0800301 int lookupflags;
302 int ret;
303 int have_one;
304 int doing_mcwd;
305 int r;
306
307 while(1) {
308 /* strip dots and / */
309 if(!strncmp(filename0,"./", 2)) {
310 filename0 += 2;
311 continue;
312 }
313 if(!strcmp(filename0,".") && filename1) {
314 filename0 ++;
315 continue;
316 }
317 if(filename0[0] == '/') {
318 filename0++;
319 continue;
320 }
321 if(!filename0[0]) {
322 if(!filename1)
323 break;
324 filename0 = filename1;
325 filename1 = 0;
326 continue;
327 }
328 break;
329 }
330
331 if(!strncmp(filename0,"../", 3) ||
332 (!strcmp(filename0, "..") && filename1)) {
333 /* up one level */
334 mp->File = getDirentry(mp->File)->Dir;
335 return recurs_dos_loop(mp, filename0+2, filename1, lookupState);
336 }
337
338 doing_mcwd = !!filename1;
339
340 ptr = strchr(filename0, '/');
Yi Kong39bbd962022-01-09 19:41:38 +0800341 if(!ptr) {
342 length = strlen(filename0);
Alistair Delvabeaee832021-02-24 11:27:23 -0800343 ptr = filename1;
344 filename1 = 0;
345 } else {
Yi Kong39bbd962022-01-09 19:41:38 +0800346 length = ptrdiff(ptr, filename0);
Alistair Delvabeaee832021-02-24 11:27:23 -0800347 ptr++;
348 }
349 if(!ptr) {
350 if(mp->lookupflags & OPEN_PARENT) {
351 mp->targetName = filename0;
352 ret = handle_leaf(getDirentry(mp->File), mp,
353 lookupState);
354 mp->targetName = 0;
355 return ret;
356 }
Yi Kong39bbd962022-01-09 19:41:38 +0800357
Alistair Delvabeaee832021-02-24 11:27:23 -0800358 if(!strcmp(filename0, ".") || !filename0[0]) {
359 return handle_leaf(getDirentry(mp->File),
360 mp, lookupState);
361 }
362
363 if(!strcmp(filename0, "..")) {
364 return handle_leaf(getParent(getDirentry(mp->File)), mp,
365 lookupState);
366 }
367
368 lookupflags = mp->lookupflags;
Yi Kong39bbd962022-01-09 19:41:38 +0800369
Alistair Delvabeaee832021-02-24 11:27:23 -0800370 if(lookupState) {
371 lookupState->filename = filename0;
372 if(lookupState->nbContainers + lookupState->nbDirs > 0){
373 /* we have already one target, don't bother
374 * with this one. */
375 FREE(&lookupState->container);
376 } else {
377 /* no match yet. Remember this container for
378 * later use */
379 lookupState->container = COPY(mp->File);
380 }
381 lookupState->nbContainers++;
382 }
383 } else
384 lookupflags = ACCEPT_DIR | DO_OPEN | NO_DOTS;
385
386 ret = 0;
387 r = 0;
388 have_one = 0;
389 initializeDirentry(&entry, mp->File);
390 while(!(ret & STOP_NOW) &&
391 !got_signal &&
392 (r=vfat_lookup(&entry, filename0, length,
393 lookupflags | NO_MSG,
394 mp->shortname.data, mp->shortname.len,
395 mp->longname.data, mp->longname.len)) == 0 ){
396 if(checkForDot(lookupflags, entry.name))
397 /* while following the path, ignore the
398 * special entries if they were not
399 * explicitly given */
400 continue;
401 have_one = 1;
402 if(ptr) {
403 Stream_t *SubDir;
404 SubDir = mp->File = OpenFileByDirentry(&entry);
405 ret |= recurs_dos_loop(mp, ptr, filename1, lookupState);
406 FREE(&SubDir);
407 } else {
408 ret |= handle_leaf(&entry, mp, lookupState);
409 if(isUniqueTarget(mp->targetName))
410 return ret | STOP_NOW;
411 }
412 if(doing_mcwd)
413 break;
414 }
415 if (r == -2)
416 return ERROR_ONE;
417 if(got_signal)
418 return ret | ERROR_ONE;
419 if(doing_mcwd && !have_one)
420 return NO_CWD;
421 return ret;
422}
423
424static int common_dos_loop(MainParam_t *mp, const char *pathname,
425 lookupState_t *lookupState, int open_mode)
426
427{
428 Stream_t *RootDir;
429 const char *cwd;
430 char drive;
431
432 int ret;
433 mp->loop = _dos_loop;
Yi Kong39bbd962022-01-09 19:41:38 +0800434
Alistair Delvabeaee832021-02-24 11:27:23 -0800435 drive='\0';
436 cwd = "";
437 if(*pathname && pathname[1] == ':') {
438 drive = ch_toupper(*pathname);
439 pathname += 2;
440 if(mp->mcwd[0] == drive)
441 cwd = mp->mcwd+2;
442 } else if(mp->mcwd[0]) {
443 drive = mp->mcwd[0];
444 cwd = mp->mcwd+2;
445 } else {
446 drive = get_default_drive();
447 }
448
449 if(*pathname=='/') /* absolute path name */
450 cwd = "";
451
452 RootDir = mp->File = open_root_dir(drive, open_mode, NULL);
453 if(!mp->File)
454 return ERROR_ONE;
455
456 ret = recurs_dos_loop(mp, cwd, pathname, lookupState);
457 if(ret & NO_CWD) {
458 /* no CWD */
459 *mp->mcwd = '\0';
460 unlink_mcwd();
461 ret = recurs_dos_loop(mp, "", pathname, lookupState);
462 }
463 FREE(&RootDir);
464 return ret;
465}
466
467static int dos_loop(MainParam_t *mp, const char *arg)
468{
469 return common_dos_loop(mp, arg, 0, mp->openflags);
470}
471
472
473static int dos_target_lookup(MainParam_t *mp, const char *arg)
474{
475 lookupState_t lookupState;
476 int ret;
477 int lookupflags;
478
479 lookupState.nbDirs = 0;
480 lookupState.Dir = 0;
481 lookupState.nbContainers = 0;
482 lookupState.container = 0;
483
484 lookupflags = mp->lookupflags;
485 mp->lookupflags = DO_OPEN | ACCEPT_DIR;
486 ret = common_dos_loop(mp, arg, &lookupState, O_RDWR);
487 mp->lookupflags = lookupflags;
488 if(ret & ERROR_ONE)
489 return ret;
490
491 if(lookupState.nbDirs) {
492 mp->targetName = 0;
493 mp->targetDir = lookupState.Dir;
494 FREE(&lookupState.container); /* container no longer needed */
495 return ret;
496 }
497
498 switch(lookupState.nbContainers) {
499 case 0:
500 /* no match */
501 fprintf(stderr,"%s: no match for target\n", arg);
502 return MISSED_ONE;
503 case 1:
504 mp->targetName = strdup(lookupState.filename);
505 mp->targetDir = lookupState.container;
506 return ret;
507 default:
508 /* too much */
509 fprintf(stderr, "Ambiguous %s\n", arg);
Yi Kong39bbd962022-01-09 19:41:38 +0800510 return ERROR_ONE;
Alistair Delvabeaee832021-02-24 11:27:23 -0800511 }
512}
513
Yi Kong39bbd962022-01-09 19:41:38 +0800514/*
515 * Is target a Unix directory
516 * -1 error occured
517 * 0 regular file
518 * 1 directory
519 */
520static int unix_is_dir(const char *name)
521{
522 struct stat buf;
523 if(stat(name, &buf) < 0)
524 return -1;
525 else
526 return 1 && S_ISDIR(buf.st_mode);
527}
528
Alistair Delvabeaee832021-02-24 11:27:23 -0800529static int unix_target_lookup(MainParam_t *mp, const char *arg)
530{
531 char *ptr;
532 mp->unixTarget = strdup(arg);
533 /* try complete filename */
Yi Kong39bbd962022-01-09 19:41:38 +0800534 if(access(mp->unixTarget, F_OK) == 0) {
535 switch(unix_is_dir(mp->unixTarget)) {
536 case -1:
537 return ERROR_ONE;
538 case 0:
539 mp->targetName="";
540 break;
541 }
Alistair Delvabeaee832021-02-24 11:27:23 -0800542 return GOT_ONE;
Yi Kong39bbd962022-01-09 19:41:38 +0800543 }
Alistair Delvabeaee832021-02-24 11:27:23 -0800544 ptr = strrchr(mp->unixTarget, '/');
545 if(!ptr) {
546 mp->targetName = mp->unixTarget;
547 mp->unixTarget = strdup(".");
548 return GOT_ONE;
549 } else {
550 *ptr = '\0';
551 mp->targetName = ptr+1;
552 return GOT_ONE;
553 }
554}
555
556int target_lookup(MainParam_t *mp, const char *arg)
557{
558 if((mp->lookupflags & NO_UNIX) || (arg[0] && arg[1] == ':' ))
559 return dos_target_lookup(mp, arg);
560 else
561 return unix_target_lookup(mp, arg);
562}
563
564int main_loop(MainParam_t *mp, char **argv, int argc)
565{
566 int i;
567 int ret, Bret;
Yi Kong39bbd962022-01-09 19:41:38 +0800568
Alistair Delvabeaee832021-02-24 11:27:23 -0800569 Bret = 0;
570
571 if(argc != 1 && mp->targetName) {
572 fprintf(stderr,
573 "Several file names given, but last argument (%s) not a directory\n", mp->targetName);
Yi Kong39bbd962022-01-09 19:41:38 +0800574 FREE(&mp->targetDir);
575 return 1;
Alistair Delvabeaee832021-02-24 11:27:23 -0800576 }
577
578 for (i = 0; i < argc; i++) {
579 if ( got_signal )
580 break;
581 mp->originalArg = argv[i];
582 mp->basenameHasWildcard = strpbrk(_basename(mp->originalArg),
583 "*[?") != 0;
584 if (mp->unixcallback && (!argv[i][0]
585#ifdef OS_mingw32msvc
586/* On Windows, support only the command-line image drive. */
587 || argv[i][0] != ':'
588#endif
589 || argv[i][1] != ':' ))
590 ret = unix_loop(0, mp, argv[i], 1);
591 else
592 ret = dos_loop(mp, argv[i]);
Yi Kong39bbd962022-01-09 19:41:38 +0800593
Alistair Delvabeaee832021-02-24 11:27:23 -0800594 if (! (ret & (GOT_ONE | ERROR_ONE)) ) {
595 /* one argument was unmatched */
596 fprintf(stderr, "%s: File \"%s\" not found\n",
597 progname, argv[i]);
598 ret |= ERROR_ONE;
599 }
600 Bret |= ret;
601 if(mp->fast_quit && (Bret & (MISSED_ONE | ERROR_ONE)))
602 break;
603 }
604 FREE(&mp->targetDir);
605 if(Bret & ERROR_ONE)
606 return 1;
607 if ((Bret & GOT_ONE) && ( Bret & MISSED_ONE))
608 return 2;
609 if (Bret & MISSED_ONE)
610 return 1;
611 return 0;
612}
613
614static int dispatchToFile(direntry_t *entry, MainParam_t *mp)
615{
616 if(entry)
617 return mp->callback(entry, mp);
618 else
619 return mp->unixcallback(mp);
620}
621
622
623void init_mp(MainParam_t *mp)
624{
625 fix_mcwd(mp->mcwd);
626 mp->openflags = O_RDONLY;
627 mp->targetName = 0;
628 mp->targetDir = 0;
629 mp->unixTarget = 0;
630 mp->dirCallback = dispatchToFile;
631 mp->unixcallback = NULL;
632 mp->shortname.data = mp->longname.data = 0;
633 mp->shortname.len = mp->longname.len = 0;
634 mp->File = 0;
635 mp->fast_quit = 0;
636}
637
638const char *mpGetBasename(MainParam_t *mp)
639{
640 if(mp->direntry) {
641 wchar_to_native(mp->direntry->name, mp->targetBuffer,
642 MAX_VNAMELEN+1, sizeof(mp->targetBuffer));
643 return mp->targetBuffer;
644 } else
645 return _basename(mp->unixSourceName);
646}
647
648void mpPrintFilename(FILE *fp, MainParam_t *mp)
649{
650 if(mp->direntry)
651 fprintPwd(fp, mp->direntry, 0);
652 else
653 fprintf(fp,"%s",mp->originalArg);
654}
655
656const char *mpPickTargetName(MainParam_t *mp)
657{
658 /* picks the target name: either the one explicitly given by the
659 * user, or the same as the source */
660 if(mp->targetName)
661 return mp->targetName;
662 else
663 return mpGetBasename(mp);
664}
665
666char *mpBuildUnixFilename(MainParam_t *mp)
667{
668 const char *target;
669 char *ret;
670 char *tmp;
671
672 target = mpPickTargetName(mp);
673 ret = malloc(strlen(mp->unixTarget) + 2 + strlen(target));
674 if(!ret)
675 return 0;
676 strcpy(ret, mp->unixTarget);
677 if(*target) {
Yi Kong39bbd962022-01-09 19:41:38 +0800678 /* fix for 'mcopy -n x:file existingfile' -- H. Lermen 980816 */
679 if(!mp->targetName && !mp->targetDir && !unix_is_dir(ret))
680 return ret;
Alistair Delvabeaee832021-02-24 11:27:23 -0800681 strcat(ret, "/");
682 if(!strcmp(target, ".")) {
683 target="DOT";
684 } else if(!strcmp(target, "..")) {
685 target="DOTDOT";
686 }
687 while( (tmp=strchr(target, '/')) ) {
Yi Kong39bbd962022-01-09 19:41:38 +0800688 strncat(ret, target, ptrdiff(tmp,target));
Alistair Delvabeaee832021-02-24 11:27:23 -0800689 strcat(ret, "\\");
690 target=tmp+1;
691 }
692 strcat(ret, target);
693 }
694 return ret;
695}