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
RemoteGlobCollapse(const char * pattern,LineListPtr fileList)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
LsMonthNameToNum(char * cp)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
UnDosLine(char * const line,const char * const curdir,size_t curdirlen,char * fname,size_t fnamesize,int * ftype,longest_int * fsize,time_t * ftime)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
UnLslRLine(char * const line,const char * const curdir,size_t curdirlen,char * fname,size_t fnamesize,char * linkto,size_t linktosize,int * ftype,longest_int * fsize,time_t * ftime,time_t now,int thisyear,int * plugend)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
UnLslR(FileInfoListPtr filp,LineListPtr llp,int serverType)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
UnMlsT(const char * const line0,const MLstItemPtr mlip)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
UnMlsD(FileInfoListPtr filp,LineListPtr llp)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
FTPRemoteRecursiveFileList1(FTPCIPtr cip,char * const rdir,FileInfoListPtr files)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
FTPRemoteRecursiveFileList(FTPCIPtr cip,LineListPtr fileList,FileInfoListPtr files)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
Traverse(FTPCIPtr cip,char * fullpath,struct Stat * st,char * relpath,FileInfoListPtr filp)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 #ifndef __REACTOS__
1161 #if _DEBUG
1162 memset(&ffd, 0, sizeof(ffd));
1163 #endif
1164 #else // __REACTOS__
1165 #ifdef _DEBUG
1166 memset(&ffd, 0, sizeof(ffd));
1167 #endif
1168 #endif // __REACTOS__
1169 if (!FindNextFile(searchHandle, &ffd)) {
1170 dwErr = GetLastError();
1171 if (dwErr != ERROR_NO_MORE_FILES) {
1172 FindClose(searchHandle);
1173 return;
1174 }
1175 break;
1176 }
1177 }
1178 FindClose(searchHandle);
1179 } // Traverse
1180
1181 #else
1182
1183 static void
Traverse(FTPCIPtr cip,char * fullpath,struct Stat * st,char * relpath,FileInfoListPtr filp)1184 Traverse(FTPCIPtr cip, char *fullpath, struct Stat *st, char *relpath, FileInfoListPtr filp)
1185 {
1186 char *dname;
1187 struct dirent *dirp;
1188 mode_t m;
1189 DIR *dp;
1190 char *cp;
1191 char *c2;
1192 FileInfo fi;
1193
1194 if (relpath[0] != '\0') {
1195 fi.relname = StrDup(relpath);
1196 fi.rname = NULL;
1197 fi.lname = StrDup(fullpath);
1198 fi.rlinkto = NULL;
1199 fi.plug = NULL;
1200 fi.mdtm = st->st_mtime;
1201 fi.size = (longest_int) st->st_size;
1202 fi.type = 'd';
1203 (void) AddFileInfo(filp, &fi);
1204 }
1205
1206 /* Handle directory entry first. */
1207 cp = fullpath + strlen(fullpath);
1208 *cp++ = '/';
1209 *cp = '\0';
1210
1211 c2 = relpath + strlen(relpath);
1212 *c2++ = '/';
1213 *c2 = '\0';
1214
1215 if ((dp = opendir(fullpath)) == NULL) {
1216 cp[-1] = '\0';
1217 c2[-1] = '\0';
1218 Error(cip, kDoPerror, "could not opendir %s.\n", fullpath);
1219 return;
1220 }
1221
1222 while ((dirp = readdir(dp)) != NULL) {
1223 dname = dirp->d_name;
1224 if ((dname[0] == '.') && ((dname[1] == '\0') || ((dname[1] == '.') && (dname[2] == '\0'))))
1225 continue; /* skip "." and ".." directories. */
1226
1227 (void) strcpy(cp, dirp->d_name); /* append name after slash */
1228 (void) strcpy(c2, dirp->d_name);
1229 if (Lstat(fullpath, st) < 0) {
1230 Error(cip, kDoPerror, "could not stat %s.\n", fullpath);
1231 continue;
1232 }
1233
1234 fi.relname = StrDup(relpath + (((relpath[0] == '/') || (relpath[0] == '\\')) ? 1 : 0));
1235 fi.rname = NULL;
1236 fi.lname = StrDup(fullpath);
1237 fi.mdtm = st->st_mtime;
1238 fi.size = (longest_int) st->st_size;
1239 fi.rlinkto = NULL;
1240 fi.plug = NULL;
1241
1242 m = st->st_mode;
1243 if (S_ISREG(m) != 0) {
1244 /* file */
1245 fi.type = '-';
1246 (void) AddFileInfo(filp, &fi);
1247 } else if (S_ISDIR(m)) {
1248 Traverse(cip, fullpath, st, relpath, filp);
1249 #ifdef S_ISLNK
1250 } else if (S_ISLNK(m)) {
1251 fi.type = 'l';
1252 fi.rlinkto = calloc(128, 1);
1253 if (fi.rlinkto != NULL) {
1254 if (readlink(fullpath, fi.rlinkto, 127) < 0) {
1255 free(fi.rlinkto);
1256 } else {
1257 (void) AddFileInfo(filp, &fi);
1258 }
1259 }
1260 #endif /* S_ISLNK */
1261 }
1262 }
1263 cp[-1] = '\0';
1264 c2[-1] = '\0';
1265
1266 (void) closedir(dp);
1267 } /* Traverse */
1268
1269 #endif
1270
1271
1272
1273
1274
1275 int
FTPLocalRecursiveFileList2(FTPCIPtr cip,LineListPtr fileList,FileInfoListPtr files,int erelative)1276 FTPLocalRecursiveFileList2(FTPCIPtr cip, LineListPtr fileList, FileInfoListPtr files, int erelative)
1277 {
1278 LinePtr filePtr, nextFilePtr;
1279 #if defined(WIN32) || defined(_WINDOWS)
1280 char fullpath[_MAX_PATH + 1];
1281 char relpath[_MAX_PATH + 1];
1282 #else
1283 char fullpath[512];
1284 char relpath[512];
1285 #endif
1286 struct Stat st;
1287 FileInfo fi;
1288 char *cp;
1289
1290 InitFileInfoList(files);
1291
1292 for (filePtr = fileList->first;
1293 filePtr != NULL;
1294 filePtr = nextFilePtr)
1295 {
1296 nextFilePtr = filePtr->next;
1297
1298 (void) STRNCPY(fullpath, filePtr->line); /* initialize fullpath */
1299 if ((erelative != 0) || (strcmp(filePtr->line, ".") == 0) || (filePtr->line[0] == '\0'))
1300 (void) STRNCPY(relpath, "");
1301 else if ((cp = StrRFindLocalPathDelim(filePtr->line)) == NULL)
1302 (void) STRNCPY(relpath, filePtr->line);
1303 else
1304 (void) STRNCPY(relpath, cp + 1);
1305 if (Lstat(fullpath, &st) < 0) {
1306 Error(cip, kDoPerror, "could not stat %s.\n", fullpath);
1307 continue;
1308 }
1309
1310 if (S_ISDIR(st.st_mode) == 0) {
1311 fi.relname = StrDup(relpath);
1312 fi.rname = NULL;
1313 fi.lname = StrDup(fullpath);
1314 fi.mdtm = st.st_mtime;
1315 fi.size = (longest_int) st.st_size;
1316 fi.rlinkto = NULL;
1317 fi.plug = NULL;
1318 fi.type = '-';
1319 (void) AddFileInfo(files, &fi);
1320 continue; /* wasn't a directory */
1321 }
1322
1323 /* Paths collected must be relative. */
1324 Traverse(cip, fullpath, &st, relpath, files);
1325 }
1326 return (kNoErr);
1327 } /* FTPLocalRecursiveFileList */
1328
1329
1330
1331
1332 int
FTPLocalRecursiveFileList(FTPCIPtr cip,LineListPtr fileList,FileInfoListPtr files)1333 FTPLocalRecursiveFileList(FTPCIPtr cip, LineListPtr fileList, FileInfoListPtr files)
1334 {
1335 return (FTPLocalRecursiveFileList2(cip, fileList, files, 0));
1336 } /* FTPLocalRecursiveFileList */
1337
1338
1339
1340 int
FTPRemoteGlob(FTPCIPtr cip,LineListPtr fileList,const char * pattern,int doGlob)1341 FTPRemoteGlob(FTPCIPtr cip, LineListPtr fileList, const char *pattern, int doGlob)
1342 {
1343 char *cp;
1344 const char *lsflags;
1345 LinePtr lp;
1346 int result;
1347
1348 if (cip == NULL)
1349 return (kErrBadParameter);
1350 if (strcmp(cip->magic, kLibraryMagic))
1351 return (kErrBadMagic);
1352
1353 if (fileList == NULL)
1354 return (kErrBadParameter);
1355 InitLineList(fileList);
1356
1357 if ((pattern == NULL) || (pattern[0] == '\0'))
1358 return (kErrBadParameter);
1359
1360 /* Note that we do attempt to use glob characters even if the remote
1361 * host isn't UNIX. Most non-UNIX remote FTP servers look for UNIX
1362 * style wildcards.
1363 */
1364 if ((doGlob == 1) && (GLOBCHARSINSTR(pattern))) {
1365 /* Use NLST, which lists files one per line. */
1366 lsflags = "";
1367
1368 /* Optimize for "NLST *" case which is same as "NLST". */
1369 if (strcmp(pattern, "*") == 0) {
1370 pattern = "";
1371 } else if (strcmp(pattern, "**") == 0) {
1372 /* Hack; Lets you try "NLST -a" if you're daring. */
1373 pattern = "";
1374 lsflags = "-a";
1375 }
1376
1377 if ((result = FTPListToMemory2(cip, pattern, fileList, lsflags, 0, (int *) 0)) < 0) {
1378 if (*lsflags == '\0')
1379 return (result);
1380 /* Try again, without "-a" */
1381 lsflags = "";
1382 if ((result = FTPListToMemory2(cip, pattern, fileList, lsflags, 0, (int *) 0)) < 0) {
1383 return (result);
1384 }
1385 }
1386 if (fileList->first == NULL) {
1387 cip->errNo = kErrGlobNoMatch;
1388 return (kErrGlobNoMatch);
1389 }
1390 if (fileList->first == fileList->last) {
1391 #define glberr(a) (ISTRNEQ(cp, a, strlen(a)))
1392 /* If we have only one item in the list, see if it really was
1393 * an error message we would recognize.
1394 */
1395 cp = strchr(fileList->first->line, ':');
1396 if (cp != NULL) {
1397 if (glberr(": No such file or directory")) {
1398 (void) RemoveLine(fileList, fileList->first);
1399 cip->errNo = kErrGlobFailed;
1400 return (kErrGlobFailed);
1401 } else if (glberr(": No match")) {
1402 cip->errNo = kErrGlobNoMatch;
1403 return (kErrGlobNoMatch);
1404 }
1405 }
1406 }
1407 RemoteGlobCollapse(pattern, fileList);
1408 for (lp=fileList->first; lp != NULL; lp = lp->next)
1409 PrintF(cip, " Rglob [%s]\n", lp->line);
1410 } else {
1411 /* Or, if there were no globbing characters in 'pattern', then the
1412 * pattern is really just a filename. So for this case the
1413 * file list is really just a single file.
1414 */
1415 fileList->first = fileList->last = NULL;
1416 (void) AddLine(fileList, pattern);
1417 }
1418 return (kNoErr);
1419 } /* FTPRemoteGlob */
1420
1421
1422
1423
1424 /* This does "tilde-expansion." Examples:
1425 * ~/pub --> /usr/gleason/pub
1426 * ~pdietz/junk --> /usr/pdietz/junk
1427 */
1428 static void
ExpandTilde(char * pattern,size_t siz)1429 ExpandTilde(char *pattern, size_t siz)
1430 {
1431 string pat;
1432 char *cp, *rest, *firstent;
1433 #if defined(WIN32) || defined(_WINDOWS)
1434 #else
1435 struct passwd *pw;
1436 #endif
1437 string hdir;
1438
1439 if ((pattern[0] == '~') &&
1440 (isalnum((int) pattern[1]) || IsLocalPathDelim(pattern[1]) || (pattern[1] == '\0'))) {
1441 (void) STRNCPY(pat, pattern);
1442 if ((cp = StrFindLocalPathDelim(pat)) != NULL) {
1443 *cp = 0;
1444 rest = cp + 1; /* Remember stuff after the ~/ part. */
1445 } else {
1446 rest = NULL; /* Was just a ~ or ~username. */
1447 }
1448 if (pat[1] == '\0') {
1449 /* Was just a ~ or ~/rest type. */
1450 GetHomeDir(hdir, sizeof(hdir));
1451 firstent = hdir;
1452 } else {
1453 #if defined(WIN32) || defined(_WINDOWS)
1454 return;
1455 #else
1456 /* Was just a ~username or ~username/rest type. */
1457 pw = getpwnam(pat + 1);
1458 if (pw != NULL)
1459 firstent = pw->pw_dir;
1460 else
1461 return; /* Bad user -- leave it alone. */
1462 #endif
1463 }
1464
1465 (void) Strncpy(pattern, firstent, siz);
1466 if (rest != NULL) {
1467 (void) Strncat(pattern, LOCAL_PATH_DELIM_STR, siz);
1468 (void) Strncat(pattern, rest, siz);
1469 }
1470 }
1471 } /* ExpandTilde */
1472
1473
1474
1475
1476
1477 #if defined(WIN32) || defined(_WINDOWS)
1478
1479 static int
WinLocalGlob(FTPCIPtr cip,LineListPtr fileList,const char * const srcpat)1480 WinLocalGlob(FTPCIPtr cip, LineListPtr fileList, const char *const srcpat)
1481 {
1482 char pattern[_MAX_PATH];
1483 WIN32_FIND_DATA ffd;
1484 HANDLE searchHandle;
1485 DWORD dwErr;
1486 char *cp;
1487 const char *file;
1488 int result;
1489
1490 STRNCPY(pattern, srcpat);
1491
1492 /* Get rid of trailing slashes. */
1493 cp = pattern + strlen(pattern) - 1;
1494 while ((cp >= pattern) && IsLocalPathDelim(*cp))
1495 *cp-- = '\0';
1496
1497 memset(&ffd, 0, sizeof(ffd));
1498
1499 /* "Open" the directory. */
1500 searchHandle = FindFirstFile(pattern, &ffd);
1501 if (searchHandle == INVALID_HANDLE_VALUE) {
1502 dwErr = GetLastError();
1503 return ((dwErr == 0) ? 0 : -1);
1504 }
1505
1506 /* Get rid of basename. */
1507 cp = StrRFindLocalPathDelim(pattern);
1508 if (cp == NULL)
1509 cp = pattern;
1510 else
1511 cp++;
1512 *cp = '\0';
1513
1514 for (result = 0;;) {
1515 file = ffd.cFileName;
1516 if ((file[0] == '.') && ((file[1] == '\0') || ((file[1] == '.') && (file[2] == '\0')))) {
1517 /* skip */
1518 } else {
1519 Strncpy(cp, ffd.cFileName, sizeof(pattern) - (cp - pattern));
1520 PrintF(cip, " Lglob [%s]\n", pattern);
1521 (void) AddLine(fileList, pattern);
1522 }
1523
1524 if (!FindNextFile(searchHandle, &ffd)) {
1525 dwErr = GetLastError();
1526 if (dwErr != ERROR_NO_MORE_FILES) {
1527 result = ((dwErr == 0) ? 0 : -1);
1528 }
1529 break;
1530 }
1531 }
1532
1533 return (result);
1534 } // WinLocalGlob
1535
1536 #else
1537
1538 static int
LazyUnixLocalGlob(FTPCIPtr cip,LineListPtr fileList,const char * const pattern)1539 LazyUnixLocalGlob(FTPCIPtr cip, LineListPtr fileList, const char *const pattern)
1540 {
1541 string cmd;
1542 longstring gfile;
1543 FILE *fp;
1544 FTPSigProc sp;
1545
1546 /* Do it the easy way and have the shell do the dirty
1547 * work for us.
1548 */
1549 #ifdef HAVE_SNPRINTF
1550 (void) snprintf(cmd, sizeof(cmd) - 1, "%s -c \"%s %s %s\"", "/bin/sh", "/bin/ls",
1551 "-d", pattern);
1552 cmd[sizeof(cmd) - 1] = '\0';
1553 #else
1554 (void) sprintf(cmd, "%s -c \"%s %s %s\"", "/bin/sh", "/bin/ls",
1555 "-d", pattern);
1556 #endif
1557
1558 fp = (FILE *) popen(cmd, "r");
1559 if (fp == NULL) {
1560 Error(cip, kDoPerror, "Could not Lglob: [%s]\n", cmd);
1561 cip->errNo = kErrGlobFailed;
1562 return (kErrGlobFailed);
1563 }
1564 sp = NcSignal(SIGPIPE, (FTPSigProc) SIG_IGN);
1565 while (FGets(gfile, sizeof(gfile), (FILE *) fp) != NULL) {
1566 PrintF(cip, " Lglob [%s]\n", gfile);
1567 (void) AddLine(fileList, gfile);
1568 }
1569 (void) pclose(fp);
1570 (void) NcSignal(SIGPIPE, sp);
1571 return (kNoErr);
1572 } /* LazyUnixLocalGlob */
1573
1574 #endif
1575
1576
1577
1578
1579 int
FTPLocalGlob(FTPCIPtr cip,LineListPtr fileList,const char * pattern,int doGlob)1580 FTPLocalGlob(FTPCIPtr cip, LineListPtr fileList, const char *pattern, int doGlob)
1581 {
1582 string pattern2;
1583 int result;
1584
1585 if (cip == NULL)
1586 return (kErrBadParameter);
1587 if (strcmp(cip->magic, kLibraryMagic))
1588 return (kErrBadMagic);
1589
1590 if (fileList == NULL)
1591 return (kErrBadParameter);
1592 InitLineList(fileList);
1593
1594 if ((pattern == NULL) || (pattern[0] == '\0'))
1595 return (kErrBadParameter);
1596
1597 (void) STRNCPY(pattern2, pattern); /* Don't nuke the original. */
1598
1599 /* Pre-process for ~'s. */
1600 ExpandTilde(pattern2, sizeof(pattern2));
1601 InitLineList(fileList);
1602 result = kNoErr;
1603
1604 if ((doGlob == 1) && (GLOBCHARSINSTR(pattern2))) {
1605 #if defined(WIN32) || defined(_WINDOWS)
1606 result = WinLocalGlob(cip, fileList, pattern2);
1607 #else
1608 result = LazyUnixLocalGlob(cip, fileList, pattern2);
1609 #endif
1610 } else {
1611 /* Or, if there were no globbing characters in 'pattern', then
1612 * the pattern is really just a single pathname.
1613 */
1614 (void) AddLine(fileList, pattern2);
1615 }
1616
1617 return (result);
1618 } /* FTPLocalGlob */
1619
1620
1621
1622
1623 static int
FTPFtwL2(const FTPCIPtr cip,char * dir,char * end,size_t dirsize,FTPFtwProc proc,int maxdepth)1624 FTPFtwL2(const FTPCIPtr cip, char *dir, char *end, size_t dirsize, FTPFtwProc proc, int maxdepth)
1625 {
1626 LineList fileList;
1627 LinePtr filePtr;
1628 char *file, *cp;
1629 int result;
1630
1631 if (maxdepth <= 0) {
1632 result = cip->errNo = kErrRecursionLimitReached;
1633 return (result);
1634 }
1635
1636 result = FTPRemoteGlob(cip, &fileList, "**", kGlobYes);
1637 if (result != kNoErr) {
1638 if (result == kErrGlobNoMatch)
1639 result = kNoErr; /* empty directory is okay. */
1640 return (result);
1641 }
1642
1643 for (filePtr = fileList.first;
1644 filePtr != NULL;
1645 filePtr = filePtr->next)
1646 {
1647 file = filePtr->line;
1648 if (file == NULL) {
1649 cip->errNo = kErrBadLineList;
1650 break;
1651 }
1652
1653 if ((file[0] == '.') && ((file[1] == '\0') || ((file[1] == '.') && (file[2] == '\0'))))
1654 continue; /* Skip . and .. */
1655
1656 result = FTPIsDir(cip, file);
1657 if (result < 0) {
1658 /* error */
1659 /* could be just a stat error, so continue */
1660 continue;
1661 } else if (result == 1) {
1662 /* directory */
1663 cp = Strnpcat(dir, file, dirsize);
1664 result = (*proc)(cip, dir, kFtwDir);
1665 if (result != kNoErr)
1666 break;
1667
1668 if ((strchr(dir, '/') == NULL) && (strrchr(dir, '\\') != NULL))
1669 *cp++ = '\\';
1670 else
1671 *cp++ = '/';
1672 *cp = '\0';
1673
1674 result = FTPChdir(cip, file);
1675 if (result == kNoErr) {
1676 result = FTPFtwL2(cip, dir, cp, dirsize, proc, maxdepth - 1);
1677 if (result != kNoErr)
1678 break;
1679 if (FTPChdir(cip, "..") < 0) {
1680 result = kErrCannotGoToPrevDir;
1681 cip->errNo = kErrCannotGoToPrevDir;
1682 break;
1683 }
1684 }
1685
1686 *end = '\0';
1687 if (result != 0)
1688 break;
1689 } else {
1690 /* file */
1691 cp = Strnpcat(dir, file, dirsize);
1692 result = (*proc)(cip, dir, kFtwFile);
1693 *end = '\0';
1694 if (result != 0)
1695 break;
1696 }
1697 }
1698 DisposeLineListContents(&fileList);
1699
1700 return (result);
1701 } /* FTPFtwL2 */
1702
1703
1704
1705 int
FTPFtw(const FTPCIPtr cip,const char * const dir,FTPFtwProc proc,int maxdepth)1706 FTPFtw(const FTPCIPtr cip, const char *const dir, FTPFtwProc proc, int maxdepth)
1707 {
1708 int result, result2;
1709 char *cp;
1710 char savedcwd[1024];
1711 char curcwd[2048];
1712
1713 result = FTPIsDir(cip, dir);
1714 if (result < 0) {
1715 /* error */
1716 return result;
1717 } else if (result == 0) {
1718 result = cip->errNo = kErrNotADirectory;
1719 return (result);
1720 }
1721
1722 /* Preserve old working directory. */
1723 (void) FTPGetCWD(cip, savedcwd, sizeof(savedcwd));
1724
1725 result = FTPChdir(cip, dir);
1726 if (result != kNoErr) {
1727 return (result);
1728 }
1729
1730 /* Get full working directory we just changed to. */
1731 result = FTPGetCWD(cip, curcwd, sizeof(curcwd) - 3);
1732 if (result != kNoErr) {
1733 if (FTPChdir(cip, savedcwd) != kNoErr) {
1734 result = kErrCannotGoToPrevDir;
1735 cip->errNo = kErrCannotGoToPrevDir;
1736 }
1737 return (result);
1738 }
1739
1740 result2 = (*proc)(cip, curcwd, kFtwDir);
1741 if (result2 == kNoErr) {
1742 cp = curcwd + strlen(curcwd);
1743
1744 if ((strchr(curcwd, '/') == NULL) && (strrchr(curcwd, '\\') != NULL))
1745 *cp++ = '\\';
1746 else
1747 *cp++ = '/';
1748 *cp = '\0';
1749 result = FTPFtwL2(cip, curcwd, cp, sizeof(curcwd), proc, maxdepth - 1);
1750 }
1751
1752
1753 if (FTPChdir(cip, savedcwd) != kNoErr) {
1754 /* Could not cd back to the original user directory -- bad. */
1755 result = kErrCannotGoToPrevDir;
1756 cip->errNo = kErrCannotGoToPrevDir;
1757 return (result);
1758 }
1759
1760 if ((result2 != kNoErr) && (result == kNoErr))
1761 result = result2;
1762
1763 return (result);
1764 } /* FTPFtw */
1765
1766 /* eof */
1767