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 #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 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 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 1333 FTPLocalRecursiveFileList(FTPCIPtr cip, LineListPtr fileList, FileInfoListPtr files) 1334 { 1335 return (FTPLocalRecursiveFileList2(cip, fileList, files, 0)); 1336 } /* FTPLocalRecursiveFileList */ 1337 1338 1339 1340 int 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 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 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 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 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 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 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