Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / modules / rosapps / applications / net / ncftp / libncftp / glob.c
1 /* glob.c
2 *
3 * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
4 * All rights reserved.
5 *
6 */
7
8 #include "syshdrs.h"
9
10 static const char *rwx[9] = { "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx", NULL };
11
12
13
14 /* We need to use this because using NLST gives us more stuff than
15 * we want back sometimes. For example, say we have:
16 *
17 * /a (directory)
18 * /a/b (directory)
19 * /a/b/b1
20 * /a/b/b2
21 * /a/b/b3
22 * /a/c (directory)
23 * /a/c/c1
24 * /a/c/c2
25 * /a/c/c3
26 * /a/file
27 *
28 * If you did an "echo /a/<star>" in a normal unix shell, you would expect
29 * to get back /a/b /a/c /a/file. But NLST gives back:
30 *
31 * /a/b/b1
32 * /a/b/b2
33 * /a/b/b3
34 * /a/c/c1
35 * /a/c/c2
36 * /a/c/c3
37 * /a/file
38 *
39 * So we use the following routine to convert into the format I expect.
40 */
41
42 static void
43 RemoteGlobCollapse(const char *pattern, LineListPtr fileList)
44 {
45 LinePtr lp, nextLine;
46 string patPrefix;
47 string cur, prev;
48 char *endp, *cp, *dp;
49 const char *pp;
50 int wasGlobChar;
51 size_t plen;
52
53 /* Copy all characters before the first glob-char. */
54 dp = patPrefix;
55 endp = dp + sizeof(patPrefix) - 1;
56 wasGlobChar = 0;
57 for (cp = (char *) pattern; dp < endp; ) {
58 for (pp=kGlobChars; *pp != '\0'; pp++) {
59 if (*pp == *cp) {
60 wasGlobChar = 1;
61 break;
62 }
63 }
64 if (wasGlobChar)
65 break;
66 *dp++ = *cp++;
67 }
68 *dp = '\0';
69 plen = (size_t) (dp - patPrefix);
70
71 *prev = '\0';
72 for (lp=fileList->first; lp != NULL; lp = nextLine) {
73 nextLine = lp->next;
74 if (strncmp(lp->line, patPrefix, plen) == 0) {
75 (void) STRNCPY(cur, lp->line + plen);
76 cp = strchr(cur, '/');
77 if (cp == NULL)
78 cp = strchr(cur, '\\');
79 if (cp != NULL)
80 *cp = '\0';
81 if ((*prev != '\0') && (STREQ(cur, prev))) {
82 nextLine = RemoveLine(fileList, lp);
83 } else {
84 (void) STRNCPY(prev, cur);
85 /* We are playing with a dynamically
86 * allocated string, but since the
87 * following expression is guaranteed
88 * to be the same or shorter, we won't
89 * overwrite the bounds.
90 */
91 (void) sprintf(lp->line, "%s%s", patPrefix, cur);
92 }
93 }
94 }
95 } /* RemoteGlobCollapse */
96
97
98
99
100 #if 0
101 /* May need this later. */
102 static void
103 CheckForLS_d(FTPCIPtr cip)
104 {
105 LineList lines;
106 char *cp;
107
108 if (cip->hasNLST_d == kCommandAvailabilityUnknown) {
109 if (FTPListToMemory2(cip, ".", &lines, "-d ", 0, (int *) 0) == kNoErr) {
110 if ((lines.first != NULL) && (lines.first == lines.last)) {
111 /* If we have only one item in the list, see if it really was
112 * an error message we would recognize.
113 */
114 cp = strchr(lines.first->line, ':');
115 if ((cp != NULL) && STREQ(cp, ": No such file or directory")) {
116 cip->hasNLST_d = kCommandNotAvailable;
117 } else {
118 cip->hasNLST_d = kCommandAvailable;
119 }
120 } else {
121 cip->hasNLST_d = kCommandNotAvailable;
122 }
123 } else {
124 cip->hasNLST_d = kCommandNotAvailable;
125 }
126 DisposeLineListContents(&lines);
127 }
128 } /* CheckForLS_d */
129 #endif
130
131
132
133
134 static int
135 LsMonthNameToNum(char *cp)
136 {
137 int mon; /* 0..11 */
138
139 switch (*cp++) {
140 case 'A':
141 mon = (*cp == 'u') ? 7 : 3;
142 break;
143 case 'D':
144 mon = 11;
145 break;
146 case 'F':
147 mon = 1;
148 break;
149 default:
150 case 'J':
151 if (*cp++ == 'u')
152 mon = (*cp == 'l') ? 6 : 5;
153 else
154 mon = 0;
155 break;
156 case 'M':
157 mon = (*++cp == 'r') ? 2 : 4;
158 break;
159 case 'N':
160 mon = 10;
161 break;
162 case 'O':
163 mon = 9;
164 break;
165 case 'S':
166 mon = 8;
167 }
168 return (mon);
169 } /* LsMonthNameToNum */
170
171
172
173
174 static int
175 UnDosLine( char *const line,
176 const char *const curdir,
177 size_t curdirlen,
178 char *fname,
179 size_t fnamesize,
180 int *ftype,
181 longest_int *fsize,
182 time_t *ftime)
183 {
184 char *cp;
185 int hour, year;
186 char *filestart;
187 char *sizestart;
188 struct tm ftm;
189
190 /*
191 *
192 0123456789012345678901234567890123456789012345678901234567890123456789
193 04-27-99 10:32PM 270158 Game booklet.pdf
194 03-11-99 10:03PM <DIR> Get A3d Banner
195
196 We also try to parse the format from CMD.EXE, which is similar:
197
198 03/22/2001 06:23p 62,325 cls.pdf
199
200 *
201 */
202 cp = line;
203 if (
204 isdigit((int) cp[0])
205 && isdigit((int) cp[1])
206 && ispunct((int) cp[2])
207 && isdigit((int) cp[3])
208 && isdigit((int) cp[4])
209 && ispunct((int) cp[5])
210 && isdigit((int) cp[6])
211 && isdigit((int) cp[7])
212 ) {
213 (void) memset(&ftm, 0, sizeof(struct tm));
214 ftm.tm_isdst = -1;
215 cp[2] = '\0';
216 ftm.tm_mon = atoi(cp + 0);
217 if (ftm.tm_mon > 0)
218 ftm.tm_mon -= 1;
219 cp[5] = '\0';
220 ftm.tm_mday = atoi(cp + 3);
221 if ((isdigit((int) cp[8])) && (isdigit((int) cp[9]))) {
222 /* Four-digit year */
223 cp[10] = '\0';
224 year = atoi(cp + 6);
225 if (year > 1900)
226 year -= 1900;
227 ftm.tm_year = year; /* years since 1900 */
228 cp += 11;
229 } else {
230 /* Two-digit year */
231 cp[8] = '\0';
232 year = atoi(cp + 6);
233 if (year < 98)
234 year += 100;
235 ftm.tm_year = year; /* years since 1900 */
236 cp += 9;
237 }
238
239 for (;;) {
240 if (*cp == '\0')
241 return (-1);
242 if (isdigit(*cp))
243 break;
244 cp++;
245 }
246
247 cp[2] = '\0';
248 hour = atoi(cp);
249 if (((cp[5] == 'P') || (cp[5] == 'p')) && (hour < 12))
250 hour += 12;
251 else if (((cp[5] == 'A') || (cp[5] == 'a')) && (hour == 12))
252 hour -= 12;
253 ftm.tm_hour = hour;
254 cp[5] = '\0';
255 ftm.tm_min = atoi(cp + 3);
256 *ftime = mktime(&ftm);
257 if (*ftype == (time_t) -1)
258 return (-1);
259
260 cp += 6;
261 *ftype = '-';
262 for (;;) {
263 if (*cp == '\0')
264 return (-1);
265 if ((*cp == '<') && (cp[1] == 'D')) {
266 /* found <DIR> */
267 *ftype = 'd';
268 cp += 5;
269 break; /* size field will end up being empty string */
270 } else if ((*cp == '<') && (cp[1] == 'J')) {
271 /* found <JUNCTION>
272 *
273 * Will we ever really see this?
274 * IIS from Win2000sp1 sends <DIR>
275 * for FTP, but CMD.EXE prints
276 * <JUNCTION>.
277 */
278 *ftype = 'd';
279 cp += 10;
280 break;
281 } else if (isdigit(*cp)) {
282 break;
283 } else {
284 cp++;
285 }
286 }
287
288 sizestart = cp;
289 for (;;) {
290 if (*cp == '\0')
291 return (-1);
292 #ifdef HAVE_MEMMOVE
293 if (*cp == ',') {
294 /* Yuck -- US Locale dependency */
295 memmove(cp, cp + 1, strlen(cp + 1) + 1);
296 }
297 #endif
298 if (!isdigit(*cp)) {
299 *cp++ = '\0';
300 break;
301 }
302 cp++;
303 }
304
305 if (fsize != NULL) {
306 #if defined(HAVE_LONG_LONG) && defined(SCANF_LONG_LONG)
307 if (*ftype == 'd')
308 *fsize = 0;
309 else
310 (void) sscanf(sizestart, SCANF_LONG_LONG, fsize);
311 #elif defined(HAVE_LONG_LONG) && defined(HAVE_STRTOQ)
312 if (*ftype == 'd')
313 *fsize = 0;
314 else
315 *fsize = (longest_int) strtoq(sizestart, NULL, 0);
316 #else
317 *fsize = (longest_int) 0;
318 if (*ftype != 'd') {
319 long fsize2 = 0L;
320
321 (void) sscanf(sizestart, "%ld", &fsize2);
322 *fsize = (longest_int) fsize2;
323 }
324 #endif
325 }
326
327 for (;;) {
328 if (*cp == '\0')
329 return (-1);
330 if (!isspace(*cp)) {
331 break;
332 }
333 cp++;
334 }
335
336 filestart = cp;
337 if (curdirlen == 0) {
338 (void) Strncpy(fname, filestart, fnamesize);
339 } else {
340 (void) Strncpy(fname, curdir, fnamesize);
341 (void) Strncat(fname, filestart, fnamesize);
342 }
343
344 return (0);
345 }
346 return (-1);
347 } /* UnDosLine */
348
349
350
351
352 static int
353 UnLslRLine( char *const line,
354 const char *const curdir,
355 size_t curdirlen,
356 char *fname,
357 size_t fnamesize,
358 char *linkto,
359 size_t linktosize,
360 int *ftype,
361 longest_int *fsize,
362 time_t *ftime,
363 time_t now,
364 int thisyear,
365 int *plugend)
366 {
367 char *cp;
368 int mon = 0, dd = 0, hr = 0, min = 0, year = 0;
369 char *monstart, *ddstart, *hrstart, *minstart, *yearstart;
370 char *linktostart, *filestart = NULL;
371 char *sizestart;
372 char *pe;
373 struct tm ftm;
374
375 /*
376 * Look for the digit just before the space
377 * before the month name.
378 *
379 -rw-rw---- 1 gleason sysdev 33404 Mar 24 01:29 RCmd.o
380 -rw-rw-r-- 1 gleason sysdevzz 1829 Jul 7 1996 README
381 -rw-rw-r-- 1 gleason sysdevzz 1829 Jul 7 1996 README
382 -rw-rw-r-- 1 gleason sysdevzz 1829 Jul 7 1996 README
383 -rw-rw-r-- 1 gleason sysdevzz 1829 Jul 7 1996 README
384 *
385 *------------------------------^
386 * 0123456789012345
387 *------plugend--------^
388 * 9876543210
389 *
390 */
391 for (cp = line; *cp != '\0'; cp++) {
392 if ( (isdigit((int) *cp))
393 && (isspace((int) cp[1]))
394 && (isupper((int) cp[2]))
395 && (islower((int) cp[3]))
396 /* && (islower((int) cp[4])) */
397 && (isspace((int) cp[5]))
398 && (
399 ((isdigit((int) cp[6])) && (isdigit((int) cp[7])))
400 || ((isdigit((int) cp[6])) && (isspace((int) cp[7])))
401 || ((isspace((int) cp[6])) && (isdigit((int) cp[7])))
402 )
403 && (isspace((int) cp[8]))
404 ) {
405 monstart = cp + 2;
406 ddstart = cp + 6;
407 if ( ((isspace((int) cp[9])) || (isdigit((int) cp[9])))
408 && (isdigit((int) cp[10]))
409 && (isdigit((int) cp[11]))
410 && (isdigit((int) cp[12]))
411 && ((isdigit((int) cp[13])) || (isspace((int) cp[13])))
412 ) {
413 /* "Mon DD YYYY" form */
414 yearstart = cp + 9;
415 if (isspace((int) *yearstart))
416 yearstart++;
417 hrstart = NULL;
418 minstart = NULL;
419 filestart = cp + 15;
420 cp[1] = '\0'; /* end size */
421 cp[5] = '\0'; /* end mon */
422 cp[8] = '\0'; /* end dd */
423 cp[14] = '\0'; /* end year */
424 mon = LsMonthNameToNum(monstart);
425 dd = atoi(ddstart);
426 hr = 23;
427 min = 59;
428 year = atoi(yearstart);
429
430 pe = cp;
431 while (isdigit((int) *pe))
432 pe--;
433 while (isspace((int) *pe))
434 pe--;
435 *plugend = (int) (pe - line) + 1;
436 break;
437 } else if ( /*
438 * Windows NT does not 0 pad.
439 (isdigit((int) cp[9])) &&
440 */
441 (isdigit((int) cp[10]))
442 && (cp[11] == ':')
443 && (isdigit((int) cp[12]))
444 && (isdigit((int) cp[13]))
445 ) {
446 /* "Mon DD HH:MM" form */
447 yearstart = NULL;
448 hrstart = cp + 9;
449 minstart = cp + 12;
450 filestart = cp + 15;
451 cp[1] = '\0'; /* end size */
452 cp[5] = '\0'; /* end mon */
453 cp[8] = '\0'; /* end dd */
454 cp[11] = '\0'; /* end hr */
455 cp[14] = '\0'; /* end min */
456 mon = LsMonthNameToNum(monstart);
457 dd = atoi(ddstart);
458 hr = atoi(hrstart);
459 min = atoi(minstart);
460 year = 0;
461
462 pe = cp;
463 while (isdigit((int) *pe))
464 pe--;
465 while (isspace((int) *pe))
466 pe--;
467 *plugend = (int) (pe - line) + 1;
468 break;
469 }
470 }
471 }
472
473 if (*cp == '\0')
474 return (-1);
475
476 linktostart = strstr(filestart, " -> ");
477 if (linktostart != NULL) {
478 *linktostart = '\0';
479 linktostart += 4;
480 (void) Strncpy(linkto, linktostart, linktosize);
481 } else {
482 *linkto = '\0';
483 }
484
485 if (curdirlen == 0) {
486 (void) Strncpy(fname, filestart, fnamesize);
487 } else {
488 (void) Strncpy(fname, curdir, fnamesize);
489 (void) Strncat(fname, filestart, fnamesize);
490 }
491
492 if (ftime != NULL) {
493 (void) memset(&ftm, 0, sizeof(struct tm));
494 ftm.tm_mon = mon;
495 ftm.tm_mday = dd;
496 ftm.tm_hour = hr;
497 ftm.tm_min = min;
498 ftm.tm_isdst = -1;
499 if (year == 0) {
500 /* We guess the year, based on what the
501 * current year is. We know the file
502 * on the remote server is either less
503 * than six months old or less than
504 * one hour into the future.
505 */
506 ftm.tm_year = thisyear - 1900;
507 *ftime = mktime(&ftm);
508 if (*ftime == (time_t) -1) {
509 /* panic */
510 } else if (*ftime > (now + (15552000L + 86400L))) {
511 --ftm.tm_year;
512 *ftime = mktime(&ftm);
513 } else if (*ftime < (now - (15552000L + 86400L))) {
514 ++ftm.tm_year;
515 *ftime = mktime(&ftm);
516 }
517 } else {
518 ftm.tm_year = year - 1900;
519 *ftime = mktime(&ftm);
520 }
521 }
522
523 if (fsize != NULL) {
524 while ((cp > line) && (isdigit((int) *cp)))
525 --cp;
526 sizestart = cp + 1;
527 #if defined(HAVE_LONG_LONG) && defined(SCANF_LONG_LONG)
528 (void) sscanf(sizestart, SCANF_LONG_LONG, fsize);
529 #elif defined(HAVE_LONG_LONG) && defined(HAVE_STRTOQ)
530 *fsize = (longest_int) strtoq(sizestart, NULL, 0);
531 #else
532 {
533 long fsize2 = 0L;
534
535 (void) sscanf(sizestart, "%ld", &fsize2);
536 *fsize = (longest_int) fsize2;
537 }
538 #endif
539 }
540
541 switch (line[0]) {
542 case 'd':
543 case 'l':
544 *ftype = (int) line[0];
545 break;
546 case 'b':
547 case 'c':
548 case 's':
549 *ftype = (int) line[0];
550 return (-1);
551 default:
552 *ftype = '-';
553 }
554
555 return (0);
556 } /* UnLslRLine */
557
558
559
560 int
561 UnLslR(FileInfoListPtr filp, LineListPtr llp, int serverType)
562 {
563 char curdir[256];
564 char line[256];
565 int hadblankline = 0;
566 int len;
567 size_t curdirlen = 0;
568 char fname[256];
569 char linkto[256];
570 char *cp;
571 longest_int fsize;
572 int ftype;
573 time_t ftime, now;
574 int thisyear;
575 struct tm *nowtm;
576 int rc;
577 LinePtr lp;
578 FileInfo fi;
579 int linesread = 0;
580 int linesconverted = 0;
581 size_t maxFileLen = 0;
582 size_t maxPlugLen = 0;
583 size_t fileLen;
584 int plugend;
585
586 (void) time(&now);
587 nowtm = localtime(&now);
588 if (nowtm == NULL)
589 thisyear = 1970; /* should never happen */
590 else
591 thisyear = nowtm->tm_year + 1900;
592
593 curdir[0] = '\0';
594
595 InitFileInfoList(filp);
596 for (lp = llp->first; lp != NULL; lp = lp->next) {
597 len = (int) strlen(STRNCPY(line, lp->line));
598 if ((line[0] == 't') && (strncmp(line, "total", 5) == 0)) {
599 /* total XX line? */
600 if (line[len - 1] != ':') {
601 hadblankline = 0;
602 continue;
603 }
604 /* else it was a subdir named total */
605 } else {
606 for (cp = line; ; cp++) {
607 if ((*cp == '\0') || (!isspace((int) *cp)))
608 break;
609 }
610 if (*cp == '\0') {
611 /* Entire line was blank. */
612 /* separator line between dirs */
613 hadblankline = 1;
614 continue;
615 }
616 }
617
618 if ((hadblankline != 0) && (line[len - 1] == ':')) {
619 /* newdir */
620 hadblankline = 0;
621 if ((line[0] == '.') && (line[1] == '/')) {
622 line[len - 1] = '/';
623 (void) memcpy(curdir, line + 2, (size_t) len + 1 - 2);
624 curdirlen = (size_t) (len - 2);
625 } else if ((line[0] == '.') && (line[1] == '\\')) {
626 line[len - 1] = '\\';
627 (void) memcpy(curdir, line + 2, (size_t) len + 1 - 2);
628 curdirlen = (size_t) (len - 2);
629 } else {
630 line[len - 1] = '/';
631 (void) memcpy(curdir, line, (size_t) len + 1);
632 curdirlen = (size_t) len;
633 }
634 continue;
635 }
636
637 linesread++;
638 rc = UnLslRLine(line, curdir, curdirlen, fname, sizeof(fname), linkto, sizeof(linkto), &ftype, &fsize, &ftime, now, thisyear, &plugend);
639 if ((rc < 0) && (serverType == kServerTypeMicrosoftFTP)) {
640 rc = UnDosLine(line, curdir, curdirlen, fname, sizeof(fname), &ftype, &fsize, &ftime);
641 if (rc == 0) {
642 *linkto = '\0';
643 plugend = 0;
644 }
645 }
646 if (rc == 0) {
647 linesconverted++;
648 fileLen = strlen(fname);
649 if (fileLen > maxFileLen)
650 maxFileLen = fileLen;
651 fi.relnameLen = fileLen;
652 fi.relname = StrDup(fname);
653 fi.rname = NULL;
654 fi.lname = NULL;
655 fi.rlinkto = (linkto[0] == '\0') ? NULL : StrDup(linkto);
656 fi.mdtm = ftime;
657 fi.size = (longest_int) fsize;
658 fi.type = ftype;
659 if (plugend > 0) {
660 fi.plug = (char *) malloc((size_t) plugend + 1);
661 if (fi.plug != NULL) {
662 (void) memcpy(fi.plug, line, (size_t) plugend);
663 fi.plug[plugend] = '\0';
664 if ((size_t) plugend > maxPlugLen)
665 maxPlugLen = (size_t) plugend;
666 }
667 } else {
668 fi.plug = (char *) malloc(32);
669 if (fi.plug != NULL) {
670 strcpy(fi.plug, "---------- 1 ftpuser ftpusers");
671 fi.plug[0] = (char) ftype;
672 if (30 > maxPlugLen)
673 maxPlugLen = (size_t) 30;
674 }
675 }
676 (void) AddFileInfo(filp, &fi);
677 }
678
679 hadblankline = 0;
680 }
681
682 filp->maxFileLen = maxFileLen;
683 filp->maxPlugLen = maxPlugLen;
684 if (linesread == 0)
685 return (0);
686 return ((linesconverted > 0) ? linesconverted : (-1));
687 } /* UnLslR */
688
689
690
691
692 int
693 UnMlsT(const char *const line0, const MLstItemPtr mlip)
694 {
695 char *cp, *val, *fact;
696 int ec;
697 size_t len;
698 char line[1024];
699
700 memset(mlip, 0, sizeof(MLstItem));
701 mlip->mode = -1;
702 mlip->fsize = kSizeUnknown;
703 mlip->ftype = '-';
704 mlip->ftime = kModTimeUnknown;
705
706 len = strlen(line0);
707 if (len > (sizeof(line) - 1))
708 return (-1); /* Line too long, sorry. */
709 /* This should be re-coded so does not need to make a
710 * copy of the buffer; it could be done in place.
711 */
712 memcpy(line, line0, len + 1);
713
714 /* Skip leading whitespace. */
715 for (cp = line; *cp != '\0'; cp++) {
716 if (! isspace(*cp))
717 break;
718 }
719
720 while (*cp != '\0') {
721 for (fact = cp; ; cp++) {
722 if ((*cp == '\0') || (*cp == ' ')) {
723 /* protocol violation */
724 return (-1);
725 }
726 if (*cp == '=') {
727 /* End of fact name. */
728 *cp++ = '\0';
729 break;
730 }
731 }
732 for (val = cp; ; cp++) {
733 if (*cp == '\0') {
734 /* protocol violation */
735 return (-1);
736 }
737 if (*cp == ' ') {
738 ec = ' ';
739 *cp++ = '\0';
740 break;
741 } else if (*cp == ';') {
742 if (cp[1] == ' ') {
743 ec = ' ';
744 *cp++ = '\0';
745 *cp++ = '\0';
746 } else {
747 ec = ';';
748 *cp++ = '\0';
749 }
750 break;
751 }
752 }
753 if (ISTRNEQ(fact, "OS.", 3))
754 fact += 3;
755 if (ISTREQ(fact, "type")) {
756 if (ISTREQ(val, "file")) {
757 mlip->ftype = '-';
758 } else if (ISTREQ(val, "dir")) {
759 mlip->ftype = 'd';
760 } else if (ISTREQ(val, "cdir")) {
761 /* not supported: current directory */
762 return (-2);
763 } else if (ISTREQ(val, "pdir")) {
764 /* not supported: parent directory */
765 return (-2);
766 } else {
767 /* ? */
768 return (-1);
769 }
770 } else if (ISTREQ(fact, "UNIX.mode")) {
771 if (val[0] == '0')
772 sscanf(val, "%o", &mlip->mode);
773 else
774 sscanf(val, "%i", &mlip->mode);
775 if (mlip->mode != (-1))
776 mlip->mode &= 00777;
777 } else if (ISTREQ(fact, "perm")) {
778 STRNCPY(mlip->perm, val);
779 } else if (ISTREQ(fact, "size")) {
780 #if defined(HAVE_LONG_LONG) && defined(SCANF_LONG_LONG)
781 (void) sscanf(val, SCANF_LONG_LONG, &mlip->fsize);
782 #elif defined(HAVE_LONG_LONG) && defined(HAVE_STRTOQ)
783 mlip->fsize = (longest_int) strtoq(val, NULL, 0);
784 #else
785 {
786 long fsize2 = 0L;
787
788 (void) sscanf(val, "%ld", &fsize2);
789 mlip->fsize = (longest_int) fsize2;
790 }
791 #endif
792 } else if (ISTREQ(fact, "modify")) {
793 mlip->ftime = UnMDTMDate(val);
794 } else if (ISTREQ(fact, "UNIX.owner")) {
795 STRNCPY(mlip->owner, val);
796 } else if (ISTREQ(fact, "UNIX.group")) {
797 STRNCPY(mlip->group, val);
798 } else if (ISTREQ(fact, "UNIX.uid")) {
799 mlip->uid = atoi(val);
800 } else if (ISTREQ(fact, "UNIX.gid")) {
801 mlip->gid = atoi(val);
802 } else if (ISTREQ(fact, "perm")) {
803 STRNCPY(mlip->perm, val);
804 }
805
806 /* End of facts? */
807 if (ec == ' ')
808 break;
809 }
810
811 len = strlen(cp);
812 if (len > (sizeof(mlip->fname) - 1)) {
813 /* Filename too long */
814 return (-1);
815 }
816 memcpy(mlip->fname, cp, len);
817
818 /* also set linkto here if used */
819
820 return (0);
821 } /* UnMlsT */
822
823
824
825
826 int
827 UnMlsD(FileInfoListPtr filp, LineListPtr llp)
828 {
829 MLstItem mli;
830 char plug[64];
831 char og[32];
832 int rc;
833 LinePtr lp;
834 FileInfo fi;
835 int linesread = 0;
836 int linesconverted = 0;
837 int linesignored = 0;
838 size_t maxFileLen = 0;
839 size_t maxPlugLen = 0;
840 size_t fileLen, plugLen;
841 int m1, m2, m3;
842 const char *cm1, *cm2, *cm3;
843
844 InitFileInfoList(filp);
845 for (lp = llp->first; lp != NULL; lp = lp->next) {
846 linesread++;
847 rc = UnMlsT(lp->line, &mli);
848 if (rc == 0) {
849 linesconverted++;
850 fileLen = strlen(mli.fname);
851 if (fileLen > maxFileLen)
852 maxFileLen = fileLen;
853 fi.relnameLen = fileLen;
854 fi.relname = StrDup(mli.fname);
855 fi.rname = NULL;
856 fi.lname = NULL;
857 fi.rlinkto = (mli.linkto[0] == '\0') ? NULL : StrDup(mli.linkto);
858 fi.mdtm = mli.ftime;
859 fi.size = (longest_int) mli.fsize;
860 fi.type = mli.ftype;
861 plug[0] = (char) mli.ftype;
862 plug[1] = '\0';
863 m1 = 0;
864 m2 = 0;
865 m3 = -1;
866 if (mli.mode != (-1)) {
867 m1 = (mli.mode & 00700) >> 6;
868 m2 = (mli.mode & 00070) >> 3;
869 m3 = (mli.mode & 00007);
870 }
871 if (mli.perm[0] != '\0') {
872 m3 = 0;
873 if (fi.type == 'd') {
874 if (strchr(mli.perm, 'e') != NULL) {
875 /* execute -> execute */
876 m3 |= 00001;
877 }
878 if (strchr(mli.perm, 'c') != NULL) {
879 /* create -> write */
880 m3 |= 00002;
881 }
882 if (strchr(mli.perm, 'l') != NULL) {
883 /* list -> read */
884 m3 |= 00004;
885 }
886 } else {
887 if (strchr(mli.perm, 'w') != NULL) {
888 /* write -> write */
889 m3 |= 00002;
890 }
891 if (strchr(mli.perm, 'r') != NULL) {
892 /* read -> read */
893 m3 |= 00004;
894 }
895 }
896 }
897 if (m3 != (-1)) {
898 cm1 = rwx[m1];
899 cm2 = rwx[m2];
900 cm3 = rwx[m3];
901 sprintf(plug + 1, "%s%s%s", cm1, cm2, cm3);
902 }
903 if (mli.owner[0] != '\0') {
904 if (mli.group[0] != '\0') {
905 #ifdef HAVE_SNPRINTF
906 snprintf(og, sizeof(og) - 1,
907 #else
908 sprintf(og,
909 #endif /* HAVE_SNPRINTF */
910 " %-8.8s %s",
911 mli.owner, mli.group
912 );
913 STRNCAT(plug, og);
914 } else {
915 STRNCAT(plug, " ");
916 STRNCAT(plug, mli.owner);
917 }
918 }
919 fi.plug = StrDup(plug);
920 if (fi.plug != NULL) {
921 plugLen = strlen(plug);
922 if (plugLen > maxPlugLen)
923 maxPlugLen = plugLen;
924 }
925 (void) AddFileInfo(filp, &fi);
926 } else if (rc == (-2)) {
927 linesignored++;
928 }
929 }
930
931 filp->maxFileLen = maxFileLen;
932 filp->maxPlugLen = maxPlugLen;
933 if (linesread == 0)
934 return (0);
935 linesconverted += linesignored;
936 return ((linesconverted > 0) ? linesconverted : (-1));
937 } /* UnMlsD */
938
939
940
941 #if 0
942 static void
943 print1(FileInfoListPtr list)
944 {
945 FileInfoPtr fip;
946 int i;
947
948 for (i = 1, fip = list->first; fip != NULL; fip = fip->next, i++)
949 printf("%d: %s\n", i, fip->relname == NULL ? "NULL" : fip->relname);
950 }
951
952
953
954 static void
955 print2(FileInfoListPtr list)
956 {
957 FileInfoPtr fip;
958 int i, n;
959
960 n = list->nFileInfos;
961 for (i=0; i<n; i++) {
962 fip = list->vec[i];
963 printf("%d: %s\n", i + 1, fip->relname == NULL ? "NULL" : fip->relname);
964 }
965 }
966
967
968
969
970 static void
971 SortRecursiveFileList(FileInfoListPtr files)
972 {
973 VectorizeFileInfoList(files);
974 SortFileInfoList(files, 'b', '?');
975 UnvectorizeFileInfoList(files);
976 } /* SortRecursiveFileList */
977 #endif
978
979
980
981
982 int
983 FTPRemoteRecursiveFileList1(FTPCIPtr cip, char *const rdir, FileInfoListPtr files)
984 {
985 LineList dirContents;
986 FileInfoList fil;
987 char cwd[512];
988 int result;
989
990 if ((result = FTPGetCWD(cip, cwd, sizeof(cwd))) < 0)
991 return (result);
992
993 InitFileInfoList(files);
994
995 if (rdir == NULL)
996 return (-1);
997
998 if (FTPChdir(cip, rdir) < 0) {
999 /* Probably not a directory.
1000 * Just add it as a plain file
1001 * to the list.
1002 */
1003 (void) ConcatFileToFileInfoList(files, rdir);
1004 return (kNoErr);
1005 }
1006
1007 /* Paths collected must be relative. */
1008 if ((result = FTPListToMemory2(cip, "", &dirContents, "-lRa", 1, (int *) 0)) < 0) {
1009 if ((result = FTPChdir(cip, cwd)) < 0) {
1010 return (result);
1011 }
1012 }
1013
1014 (void) UnLslR(&fil, &dirContents, cip->serverType);
1015 DisposeLineListContents(&dirContents);
1016 /* Could sort it to breadth-first here. */
1017 /* (void) SortRecursiveFileList(&fil); */
1018 (void) ComputeRNames(&fil, rdir, 1, 1);
1019 (void) ConcatFileInfoList(files, &fil);
1020 DisposeFileInfoListContents(&fil);
1021
1022 if ((result = FTPChdir(cip, cwd)) < 0) {
1023 return (result);
1024 }
1025 return (kNoErr);
1026 } /* FTPRemoteRecursiveFileList1 */
1027
1028
1029
1030
1031 int
1032 FTPRemoteRecursiveFileList(FTPCIPtr cip, LineListPtr fileList, FileInfoListPtr files)
1033 {
1034 LinePtr filePtr, nextFilePtr;
1035 LineList dirContents;
1036 FileInfoList fil;
1037 char cwd[512];
1038 int result;
1039 char *rdir;
1040
1041 if ((result = FTPGetCWD(cip, cwd, sizeof(cwd))) < 0)
1042 return (result);
1043
1044 InitFileInfoList(files);
1045
1046 for (filePtr = fileList->first;
1047 filePtr != NULL;
1048 filePtr = nextFilePtr)
1049 {
1050 nextFilePtr = filePtr->next;
1051
1052 rdir = filePtr->line;
1053 if (rdir == NULL)
1054 continue;
1055
1056 if (FTPChdir(cip, rdir) < 0) {
1057 /* Probably not a directory.
1058 * Just add it as a plain file
1059 * to the list.
1060 */
1061 (void) ConcatFileToFileInfoList(files, rdir);
1062 continue;
1063 }
1064
1065 /* Paths collected must be relative. */
1066 if ((result = FTPListToMemory2(cip, "", &dirContents, "-lRa", 1, (int *) 0)) < 0) {
1067 goto goback;
1068 }
1069
1070 (void) UnLslR(&fil, &dirContents, cip->serverType);
1071 DisposeLineListContents(&dirContents);
1072 (void) ComputeRNames(&fil, rdir, 1, 1);
1073 (void) ConcatFileInfoList(files, &fil);
1074 DisposeFileInfoListContents(&fil);
1075
1076 goback:
1077 if ((result = FTPChdir(cip, cwd)) < 0) {
1078 return (result);
1079 }
1080 }
1081 return (kNoErr);
1082 } /* FTPRemoteRecursiveFileList */
1083
1084
1085
1086 #if defined(WIN32) || defined(_WINDOWS)
1087
1088 static void
1089 Traverse(FTPCIPtr cip, char *fullpath, struct Stat *st, char *relpath, FileInfoListPtr filp)
1090 {
1091 WIN32_FIND_DATA ffd;
1092 HANDLE searchHandle;
1093 DWORD dwErr;
1094 char *cp, *c2;
1095 const char *file;
1096 FileInfo fi;
1097
1098 /* Handle directory entry first. */
1099 if (relpath[0] != '\0') {
1100 fi.relname = StrDup(relpath);
1101 fi.rname = NULL;
1102 fi.lname = StrDup(fullpath);
1103 fi.rlinkto = NULL;
1104 fi.plug = NULL;
1105 fi.mdtm = st->st_mtime;
1106 fi.size = (longest_int) st->st_size;
1107 fi.type = 'd';
1108 (void) AddFileInfo(filp, &fi);
1109 }
1110
1111 cp = fullpath + strlen(fullpath);
1112 *cp++ = LOCAL_PATH_DELIM;
1113 strcpy(cp, "*.*");
1114
1115 c2 = relpath + strlen(relpath);
1116 *c2++ = LOCAL_PATH_DELIM;
1117 *c2 = '\0';
1118
1119 memset(&ffd, 0, sizeof(ffd));
1120
1121 /* "Open" the directory. */
1122 searchHandle = FindFirstFile(fullpath, &ffd);
1123 if (searchHandle == INVALID_HANDLE_VALUE) {
1124 return;
1125 }
1126
1127 for (;;) {
1128
1129 file = ffd.cFileName;
1130 if ((*file == '.') && ((file[1] == '\0') || ((file[1] == '.') && (file[2] == '\0')))) {
1131 /* It was "." or "..", so skip it. */
1132 goto next;
1133 }
1134
1135 (void) strcpy(cp, file); /* append name after slash */
1136 (void) strcpy(c2, file);
1137
1138 if (Lstat(fullpath, st) < 0) {
1139 Error(cip, kDoPerror, "could not stat %s.\n", fullpath);
1140 goto next;
1141 }
1142
1143 fi.relname = StrDup(relpath + (((relpath[0] == '/') || (relpath[0] == '\\')) ? 1 : 0));
1144 fi.rname = NULL;
1145 fi.lname = StrDup(fullpath);
1146 fi.mdtm = st->st_mtime;
1147 fi.size = (longest_int) st->st_size;
1148 fi.rlinkto = NULL;
1149 fi.plug = NULL;
1150
1151 if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
1152 Traverse(cip, fullpath, st, relpath, filp);
1153 } else {
1154 /* file */
1155 fi.type = '-';
1156 (void) AddFileInfo(filp, &fi);
1157 }
1158
1159 next:
1160 #if _DEBUG
1161 memset(&ffd, 0, sizeof(ffd));
1162 #endif
1163 if (!FindNextFile(searchHandle, &ffd)) {
1164 dwErr = GetLastError();
1165 if (dwErr != ERROR_NO_MORE_FILES) {
1166 FindClose(searchHandle);
1167 return;
1168 }
1169 break;
1170 }
1171 }
1172 FindClose(searchHandle);
1173 } // Traverse
1174
1175 #else
1176
1177 static void
1178 Traverse(FTPCIPtr cip, char *fullpath, struct Stat *st, char *relpath, FileInfoListPtr filp)
1179 {
1180 char *dname;
1181 struct dirent *dirp;
1182 mode_t m;
1183 DIR *dp;
1184 char *cp;
1185 char *c2;
1186 FileInfo fi;
1187
1188 if (relpath[0] != '\0') {
1189 fi.relname = StrDup(relpath);
1190 fi.rname = NULL;
1191 fi.lname = StrDup(fullpath);
1192 fi.rlinkto = NULL;
1193 fi.plug = NULL;
1194 fi.mdtm = st->st_mtime;
1195 fi.size = (longest_int) st->st_size;
1196 fi.type = 'd';
1197 (void) AddFileInfo(filp, &fi);
1198 }
1199
1200 /* Handle directory entry first. */
1201 cp = fullpath + strlen(fullpath);
1202 *cp++ = '/';
1203 *cp = '\0';
1204
1205 c2 = relpath + strlen(relpath);
1206 *c2++ = '/';
1207 *c2 = '\0';
1208
1209 if ((dp = opendir(fullpath)) == NULL) {
1210 cp[-1] = '\0';
1211 c2[-1] = '\0';
1212 Error(cip, kDoPerror, "could not opendir %s.\n", fullpath);
1213 return;
1214 }
1215
1216 while ((dirp = readdir(dp)) != NULL) {
1217 dname = dirp->d_name;
1218 if ((dname[0] == '.') && ((dname[1] == '\0') || ((dname[1] == '.') && (dname[2] == '\0'))))
1219 continue; /* skip "." and ".." directories. */
1220
1221 (void) strcpy(cp, dirp->d_name); /* append name after slash */
1222 (void) strcpy(c2, dirp->d_name);
1223 if (Lstat(fullpath, st) < 0) {
1224 Error(cip, kDoPerror, "could not stat %s.\n", fullpath);
1225 continue;
1226 }
1227
1228 fi.relname = StrDup(relpath + (((relpath[0] == '/') || (relpath[0] == '\\')) ? 1 : 0));
1229 fi.rname = NULL;
1230 fi.lname = StrDup(fullpath);
1231 fi.mdtm = st->st_mtime;
1232 fi.size = (longest_int) st->st_size;
1233 fi.rlinkto = NULL;
1234 fi.plug = NULL;
1235
1236 m = st->st_mode;
1237 if (S_ISREG(m) != 0) {
1238 /* file */
1239 fi.type = '-';
1240 (void) AddFileInfo(filp, &fi);
1241 } else if (S_ISDIR(m)) {
1242 Traverse(cip, fullpath, st, relpath, filp);
1243 #ifdef S_ISLNK
1244 } else if (S_ISLNK(m)) {
1245 fi.type = 'l';
1246 fi.rlinkto = calloc(128, 1);
1247 if (fi.rlinkto != NULL) {
1248 if (readlink(fullpath, fi.rlinkto, 127) < 0) {
1249 free(fi.rlinkto);
1250 } else {
1251 (void) AddFileInfo(filp, &fi);
1252 }
1253 }
1254 #endif /* S_ISLNK */
1255 }
1256 }
1257 cp[-1] = '\0';
1258 c2[-1] = '\0';
1259
1260 (void) closedir(dp);
1261 } /* Traverse */
1262
1263 #endif
1264
1265
1266
1267
1268
1269 int
1270 FTPLocalRecursiveFileList2(FTPCIPtr cip, LineListPtr fileList, FileInfoListPtr files, int erelative)
1271 {
1272 LinePtr filePtr, nextFilePtr;
1273 #if defined(WIN32) || defined(_WINDOWS)
1274 char fullpath[_MAX_PATH + 1];
1275 char relpath[_MAX_PATH + 1];
1276 #else
1277 char fullpath[512];
1278 char relpath[512];
1279 #endif
1280 struct Stat st;
1281 FileInfo fi;
1282 char *cp;
1283
1284 InitFileInfoList(files);
1285
1286 for (filePtr = fileList->first;
1287 filePtr != NULL;
1288 filePtr = nextFilePtr)
1289 {
1290 nextFilePtr = filePtr->next;
1291
1292 (void) STRNCPY(fullpath, filePtr->line); /* initialize fullpath */
1293 if ((erelative != 0) || (strcmp(filePtr->line, ".") == 0) || (filePtr->line[0] == '\0'))
1294 (void) STRNCPY(relpath, "");
1295 else if ((cp = StrRFindLocalPathDelim(filePtr->line)) == NULL)
1296 (void) STRNCPY(relpath, filePtr->line);
1297 else
1298 (void) STRNCPY(relpath, cp + 1);
1299 if (Lstat(fullpath, &st) < 0) {
1300 Error(cip, kDoPerror, "could not stat %s.\n", fullpath);
1301 continue;
1302 }
1303
1304 if (S_ISDIR(st.st_mode) == 0) {
1305 fi.relname = StrDup(relpath);
1306 fi.rname = NULL;
1307 fi.lname = StrDup(fullpath);
1308 fi.mdtm = st.st_mtime;
1309 fi.size = (longest_int) st.st_size;
1310 fi.rlinkto = NULL;
1311 fi.plug = NULL;
1312 fi.type = '-';
1313 (void) AddFileInfo(files, &fi);
1314 continue; /* wasn't a directory */
1315 }
1316
1317 /* Paths collected must be relative. */
1318 Traverse(cip, fullpath, &st, relpath, files);
1319 }
1320 return (kNoErr);
1321 } /* FTPLocalRecursiveFileList */
1322
1323
1324
1325
1326 int
1327 FTPLocalRecursiveFileList(FTPCIPtr cip, LineListPtr fileList, FileInfoListPtr files)
1328 {
1329 return (FTPLocalRecursiveFileList2(cip, fileList, files, 0));
1330 } /* FTPLocalRecursiveFileList */
1331
1332
1333
1334 int
1335 FTPRemoteGlob(FTPCIPtr cip, LineListPtr fileList, const char *pattern, int doGlob)
1336 {
1337 char *cp;
1338 const char *lsflags;
1339 LinePtr lp;
1340 int result;
1341
1342 if (cip == NULL)
1343 return (kErrBadParameter);
1344 if (strcmp(cip->magic, kLibraryMagic))
1345 return (kErrBadMagic);
1346
1347 if (fileList == NULL)
1348 return (kErrBadParameter);
1349 InitLineList(fileList);
1350
1351 if ((pattern == NULL) || (pattern[0] == '\0'))
1352 return (kErrBadParameter);
1353
1354 /* Note that we do attempt to use glob characters even if the remote
1355 * host isn't UNIX. Most non-UNIX remote FTP servers look for UNIX
1356 * style wildcards.
1357 */
1358 if ((doGlob == 1) && (GLOBCHARSINSTR(pattern))) {
1359 /* Use NLST, which lists files one per line. */
1360 lsflags = "";
1361
1362 /* Optimize for "NLST *" case which is same as "NLST". */
1363 if (strcmp(pattern, "*") == 0) {
1364 pattern = "";
1365 } else if (strcmp(pattern, "**") == 0) {
1366 /* Hack; Lets you try "NLST -a" if you're daring. */
1367 pattern = "";
1368 lsflags = "-a";
1369 }
1370
1371 if ((result = FTPListToMemory2(cip, pattern, fileList, lsflags, 0, (int *) 0)) < 0) {
1372 if (*lsflags == '\0')
1373 return (result);
1374 /* Try again, without "-a" */
1375 lsflags = "";
1376 if ((result = FTPListToMemory2(cip, pattern, fileList, lsflags, 0, (int *) 0)) < 0) {
1377 return (result);
1378 }
1379 }
1380 if (fileList->first == NULL) {
1381 cip->errNo = kErrGlobNoMatch;
1382 return (kErrGlobNoMatch);
1383 }
1384 if (fileList->first == fileList->last) {
1385 #define glberr(a) (ISTRNEQ(cp, a, strlen(a)))
1386 /* If we have only one item in the list, see if it really was
1387 * an error message we would recognize.
1388 */
1389 cp = strchr(fileList->first->line, ':');
1390 if (cp != NULL) {
1391 if (glberr(": No such file or directory")) {
1392 (void) RemoveLine(fileList, fileList->first);
1393 cip->errNo = kErrGlobFailed;
1394 return (kErrGlobFailed);
1395 } else if (glberr(": No match")) {
1396 cip->errNo = kErrGlobNoMatch;
1397 return (kErrGlobNoMatch);
1398 }
1399 }
1400 }
1401 RemoteGlobCollapse(pattern, fileList);
1402 for (lp=fileList->first; lp != NULL; lp = lp->next)
1403 PrintF(cip, " Rglob [%s]\n", lp->line);
1404 } else {
1405 /* Or, if there were no globbing characters in 'pattern', then the
1406 * pattern is really just a filename. So for this case the
1407 * file list is really just a single file.
1408 */
1409 fileList->first = fileList->last = NULL;
1410 (void) AddLine(fileList, pattern);
1411 }
1412 return (kNoErr);
1413 } /* FTPRemoteGlob */
1414
1415
1416
1417
1418 /* This does "tilde-expansion." Examples:
1419 * ~/pub --> /usr/gleason/pub
1420 * ~pdietz/junk --> /usr/pdietz/junk
1421 */
1422 static void
1423 ExpandTilde(char *pattern, size_t siz)
1424 {
1425 string pat;
1426 char *cp, *rest, *firstent;
1427 #if defined(WIN32) || defined(_WINDOWS)
1428 #else
1429 struct passwd *pw;
1430 #endif
1431 string hdir;
1432
1433 if ((pattern[0] == '~') &&
1434 (isalnum((int) pattern[1]) || IsLocalPathDelim(pattern[1]) || (pattern[1] == '\0'))) {
1435 (void) STRNCPY(pat, pattern);
1436 if ((cp = StrFindLocalPathDelim(pat)) != NULL) {
1437 *cp = 0;
1438 rest = cp + 1; /* Remember stuff after the ~/ part. */
1439 } else {
1440 rest = NULL; /* Was just a ~ or ~username. */
1441 }
1442 if (pat[1] == '\0') {
1443 /* Was just a ~ or ~/rest type. */
1444 GetHomeDir(hdir, sizeof(hdir));
1445 firstent = hdir;
1446 } else {
1447 #if defined(WIN32) || defined(_WINDOWS)
1448 return;
1449 #else
1450 /* Was just a ~username or ~username/rest type. */
1451 pw = getpwnam(pat + 1);
1452 if (pw != NULL)
1453 firstent = pw->pw_dir;
1454 else
1455 return; /* Bad user -- leave it alone. */
1456 #endif
1457 }
1458
1459 (void) Strncpy(pattern, firstent, siz);
1460 if (rest != NULL) {
1461 (void) Strncat(pattern, LOCAL_PATH_DELIM_STR, siz);
1462 (void) Strncat(pattern, rest, siz);
1463 }
1464 }
1465 } /* ExpandTilde */
1466
1467
1468
1469
1470
1471 #if defined(WIN32) || defined(_WINDOWS)
1472
1473 static int
1474 WinLocalGlob(FTPCIPtr cip, LineListPtr fileList, const char *const srcpat)
1475 {
1476 char pattern[_MAX_PATH];
1477 WIN32_FIND_DATA ffd;
1478 HANDLE searchHandle;
1479 DWORD dwErr;
1480 char *cp;
1481 const char *file;
1482 int result;
1483
1484 STRNCPY(pattern, srcpat);
1485
1486 /* Get rid of trailing slashes. */
1487 cp = pattern + strlen(pattern) - 1;
1488 while ((cp >= pattern) && IsLocalPathDelim(*cp))
1489 *cp-- = '\0';
1490
1491 memset(&ffd, 0, sizeof(ffd));
1492
1493 /* "Open" the directory. */
1494 searchHandle = FindFirstFile(pattern, &ffd);
1495 if (searchHandle == INVALID_HANDLE_VALUE) {
1496 dwErr = GetLastError();
1497 return ((dwErr == 0) ? 0 : -1);
1498 }
1499
1500 /* Get rid of basename. */
1501 cp = StrRFindLocalPathDelim(pattern);
1502 if (cp == NULL)
1503 cp = pattern;
1504 else
1505 cp++;
1506 *cp = '\0';
1507
1508 for (result = 0;;) {
1509 file = ffd.cFileName;
1510 if ((file[0] == '.') && ((file[1] == '\0') || ((file[1] == '.') && (file[2] == '\0')))) {
1511 /* skip */
1512 } else {
1513 Strncpy(cp, ffd.cFileName, sizeof(pattern) - (cp - pattern));
1514 PrintF(cip, " Lglob [%s]\n", pattern);
1515 (void) AddLine(fileList, pattern);
1516 }
1517
1518 if (!FindNextFile(searchHandle, &ffd)) {
1519 dwErr = GetLastError();
1520 if (dwErr != ERROR_NO_MORE_FILES) {
1521 result = ((dwErr == 0) ? 0 : -1);
1522 }
1523 break;
1524 }
1525 }
1526
1527 return (result);
1528 } // WinLocalGlob
1529
1530 #else
1531
1532 static int
1533 LazyUnixLocalGlob(FTPCIPtr cip, LineListPtr fileList, const char *const pattern)
1534 {
1535 string cmd;
1536 longstring gfile;
1537 FILE *fp;
1538 FTPSigProc sp;
1539
1540 /* Do it the easy way and have the shell do the dirty
1541 * work for us.
1542 */
1543 #ifdef HAVE_SNPRINTF
1544 (void) snprintf(cmd, sizeof(cmd) - 1, "%s -c \"%s %s %s\"", "/bin/sh", "/bin/ls",
1545 "-d", pattern);
1546 cmd[sizeof(cmd) - 1] = '\0';
1547 #else
1548 (void) sprintf(cmd, "%s -c \"%s %s %s\"", "/bin/sh", "/bin/ls",
1549 "-d", pattern);
1550 #endif
1551
1552 fp = (FILE *) popen(cmd, "r");
1553 if (fp == NULL) {
1554 Error(cip, kDoPerror, "Could not Lglob: [%s]\n", cmd);
1555 cip->errNo = kErrGlobFailed;
1556 return (kErrGlobFailed);
1557 }
1558 sp = NcSignal(SIGPIPE, (FTPSigProc) SIG_IGN);
1559 while (FGets(gfile, sizeof(gfile), (FILE *) fp) != NULL) {
1560 PrintF(cip, " Lglob [%s]\n", gfile);
1561 (void) AddLine(fileList, gfile);
1562 }
1563 (void) pclose(fp);
1564 (void) NcSignal(SIGPIPE, sp);
1565 return (kNoErr);
1566 } /* LazyUnixLocalGlob */
1567
1568 #endif
1569
1570
1571
1572
1573 int
1574 FTPLocalGlob(FTPCIPtr cip, LineListPtr fileList, const char *pattern, int doGlob)
1575 {
1576 string pattern2;
1577 int result;
1578
1579 if (cip == NULL)
1580 return (kErrBadParameter);
1581 if (strcmp(cip->magic, kLibraryMagic))
1582 return (kErrBadMagic);
1583
1584 if (fileList == NULL)
1585 return (kErrBadParameter);
1586 InitLineList(fileList);
1587
1588 if ((pattern == NULL) || (pattern[0] == '\0'))
1589 return (kErrBadParameter);
1590
1591 (void) STRNCPY(pattern2, pattern); /* Don't nuke the original. */
1592
1593 /* Pre-process for ~'s. */
1594 ExpandTilde(pattern2, sizeof(pattern2));
1595 InitLineList(fileList);
1596 result = kNoErr;
1597
1598 if ((doGlob == 1) && (GLOBCHARSINSTR(pattern2))) {
1599 #if defined(WIN32) || defined(_WINDOWS)
1600 result = WinLocalGlob(cip, fileList, pattern2);
1601 #else
1602 result = LazyUnixLocalGlob(cip, fileList, pattern2);
1603 #endif
1604 } else {
1605 /* Or, if there were no globbing characters in 'pattern', then
1606 * the pattern is really just a single pathname.
1607 */
1608 (void) AddLine(fileList, pattern2);
1609 }
1610
1611 return (result);
1612 } /* FTPLocalGlob */
1613
1614
1615
1616
1617 static int
1618 FTPFtwL2(const FTPCIPtr cip, char *dir, char *end, size_t dirsize, FTPFtwProc proc, int maxdepth)
1619 {
1620 LineList fileList;
1621 LinePtr filePtr;
1622 char *file, *cp;
1623 int result;
1624
1625 if (maxdepth <= 0) {
1626 result = cip->errNo = kErrRecursionLimitReached;
1627 return (result);
1628 }
1629
1630 result = FTPRemoteGlob(cip, &fileList, "**", kGlobYes);
1631 if (result != kNoErr) {
1632 if (result == kErrGlobNoMatch)
1633 result = kNoErr; /* empty directory is okay. */
1634 return (result);
1635 }
1636
1637 for (filePtr = fileList.first;
1638 filePtr != NULL;
1639 filePtr = filePtr->next)
1640 {
1641 file = filePtr->line;
1642 if (file == NULL) {
1643 cip->errNo = kErrBadLineList;
1644 break;
1645 }
1646
1647 if ((file[0] == '.') && ((file[1] == '\0') || ((file[1] == '.') && (file[2] == '\0'))))
1648 continue; /* Skip . and .. */
1649
1650 result = FTPIsDir(cip, file);
1651 if (result < 0) {
1652 /* error */
1653 /* could be just a stat error, so continue */
1654 continue;
1655 } else if (result == 1) {
1656 /* directory */
1657 cp = Strnpcat(dir, file, dirsize);
1658 result = (*proc)(cip, dir, kFtwDir);
1659 if (result != kNoErr)
1660 break;
1661
1662 if ((strchr(dir, '/') == NULL) && (strrchr(dir, '\\') != NULL))
1663 *cp++ = '\\';
1664 else
1665 *cp++ = '/';
1666 *cp = '\0';
1667
1668 result = FTPChdir(cip, file);
1669 if (result == kNoErr) {
1670 result = FTPFtwL2(cip, dir, cp, dirsize, proc, maxdepth - 1);
1671 if (result != kNoErr)
1672 break;
1673 if (FTPChdir(cip, "..") < 0) {
1674 result = kErrCannotGoToPrevDir;
1675 cip->errNo = kErrCannotGoToPrevDir;
1676 break;
1677 }
1678 }
1679
1680 *end = '\0';
1681 if (result != 0)
1682 break;
1683 } else {
1684 /* file */
1685 cp = Strnpcat(dir, file, dirsize);
1686 result = (*proc)(cip, dir, kFtwFile);
1687 *end = '\0';
1688 if (result != 0)
1689 break;
1690 }
1691 }
1692 DisposeLineListContents(&fileList);
1693
1694 return (result);
1695 } /* FTPFtwL2 */
1696
1697
1698
1699 int
1700 FTPFtw(const FTPCIPtr cip, const char *const dir, FTPFtwProc proc, int maxdepth)
1701 {
1702 int result, result2;
1703 char *cp;
1704 char savedcwd[1024];
1705 char curcwd[2048];
1706
1707 result = FTPIsDir(cip, dir);
1708 if (result < 0) {
1709 /* error */
1710 return result;
1711 } else if (result == 0) {
1712 result = cip->errNo = kErrNotADirectory;
1713 return (result);
1714 }
1715
1716 /* Preserve old working directory. */
1717 (void) FTPGetCWD(cip, savedcwd, sizeof(savedcwd));
1718
1719 result = FTPChdir(cip, dir);
1720 if (result != kNoErr) {
1721 return (result);
1722 }
1723
1724 /* Get full working directory we just changed to. */
1725 result = FTPGetCWD(cip, curcwd, sizeof(curcwd) - 3);
1726 if (result != kNoErr) {
1727 if (FTPChdir(cip, savedcwd) != kNoErr) {
1728 result = kErrCannotGoToPrevDir;
1729 cip->errNo = kErrCannotGoToPrevDir;
1730 }
1731 return (result);
1732 }
1733
1734 result2 = (*proc)(cip, curcwd, kFtwDir);
1735 if (result2 == kNoErr) {
1736 cp = curcwd + strlen(curcwd);
1737
1738 if ((strchr(curcwd, '/') == NULL) && (strrchr(curcwd, '\\') != NULL))
1739 *cp++ = '\\';
1740 else
1741 *cp++ = '/';
1742 *cp = '\0';
1743 result = FTPFtwL2(cip, curcwd, cp, sizeof(curcwd), proc, maxdepth - 1);
1744 }
1745
1746
1747 if (FTPChdir(cip, savedcwd) != kNoErr) {
1748 /* Could not cd back to the original user directory -- bad. */
1749 result = kErrCannotGoToPrevDir;
1750 cip->errNo = kErrCannotGoToPrevDir;
1751 return (result);
1752 }
1753
1754 if ((result2 != kNoErr) && (result == kNoErr))
1755 result = result2;
1756
1757 return (result);
1758 } /* FTPFtw */
1759
1760 /* eof */