/* util.c * * Copyright (c) 1992-2006 by Mike Gleason. * All rights reserved. * */ #include "syshdrs.h" #ifdef PRAGMA_HDRSTOP # pragma hdrstop #endif #include "shell.h" #include "trace.h" #include "util.h" uid_t gUid; char gUser[32]; char gHome[256]; char gShell[256]; char gOurDirectoryPath[260]; char gOurInstallationPath[260]; int gNcFTP_Uses_Me_To_Quiet_Variable_Unused_Warnings = 0; #ifdef ncftp static int gResolveSig; #endif #if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__) #elif defined(HAVE_SIGSETJMP) sigjmp_buf gGetHostByNameJmp; #else /* HAVE_SIGSETJMP */ jmp_buf gGetHostByNameJmp; #endif /* HAVE_SIGSETJMP */ #ifndef HAVE_MEMMOVE void *memmove(void *dst0, void *src0, size_t length); #endif static const unsigned char B64EncodeTable[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; static const unsigned char B64DecodeTable[256] = { '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 000-007 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 010-017 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 020-027 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 030-037 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 040-047 */ '\177', '\177', '\177', '\76', '\177', '\177', '\177', '\77', /* 050-057 */ '\64', '\65', '\66', '\67', '\70', '\71', '\72', '\73', /* 060-067 */ '\74', '\75', '\177', '\177', '\177', '\100', '\177', '\177', /* 070-077 */ '\177', '\0', '\1', '\2', '\3', '\4', '\5', '\6', /* 100-107 */ '\7', '\10', '\11', '\12', '\13', '\14', '\15', '\16', /* 110-117 */ '\17', '\20', '\21', '\22', '\23', '\24', '\25', '\26', /* 120-127 */ '\27', '\30', '\31', '\177', '\177', '\177', '\177', '\177', /* 130-137 */ '\177', '\32', '\33', '\34', '\35', '\36', '\37', '\40', /* 140-147 */ '\41', '\42', '\43', '\44', '\45', '\46', '\47', '\50', /* 150-157 */ '\51', '\52', '\53', '\54', '\55', '\56', '\57', '\60', /* 160-167 */ '\61', '\62', '\63', '\177', '\177', '\177', '\177', '\177', /* 170-177 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 200-207 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 210-217 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 220-227 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 230-237 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 240-247 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 250-257 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 260-267 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 270-277 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 300-307 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 310-317 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 320-327 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 330-337 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 340-347 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 350-357 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 360-367 */ '\177', '\177', '\177', '\177', '\177', '\177', '\177', '\177', /* 370-377 */ }; void ToBase64(void *dst0, const void *src0, size_t n, int terminate) { unsigned char *dst; const unsigned char *src, *srclim; unsigned int c0, c1, c2; unsigned int ch; src = src0; srclim = src + n; dst = dst0; while (src < srclim) { c0 = *src++; if (src < srclim) { c1 = *src++; } else { c1 = 0; } if (src < srclim) { c2 = *src++; } else { c2 = 0; } ch = c0 >> 2; dst[0] = B64EncodeTable[ch & 077]; ch = ((c0 << 4) & 060) | ((c1 >> 4) & 017); dst[1] = B64EncodeTable[ch & 077]; ch = ((c1 << 2) & 074) | ((c2 >> 6) & 03); dst[2] = B64EncodeTable[ch & 077]; ch = (c2 & 077); dst[3] = B64EncodeTable[ch & 077]; dst += 4; } if (terminate != 0) *dst = '\0'; } /* ToBase64 */ void FromBase64(void *dst0, const void *src0, size_t n, int terminate) { unsigned char *dst; const unsigned char *src, *srclim; unsigned int c0, c1, c2, c3; unsigned int ch; src = src0; srclim = src + n; dst = dst0; while (src < srclim) { c0 = *src++; if (src < srclim) { c1 = *src++; } else { c1 = 0; } if (src < srclim) { c2 = *src++; } else { c2 = 0; } if (src < srclim) { c3 = *src++; } else { c3 = 0; } ch = (((unsigned int) B64DecodeTable[c0]) << 2) | (((unsigned int) B64DecodeTable[c1]) >> 4); dst[0] = (unsigned char) ch; ch = (((unsigned int) B64DecodeTable[c1]) << 4) | (((unsigned int) B64DecodeTable[c2]) >> 2); dst[1] = (unsigned char) ch; ch = (((unsigned int) B64DecodeTable[c2]) << 6) | (((unsigned int) B64DecodeTable[c3])); dst[2] = (unsigned char) ch; dst += 3; } if (terminate != 0) *dst = '\0'; } /* FromBase64 */ /* This should only be called if the program wouldn't function * usefully without the memory requested. */ void OutOfMemory(void) { (void) fprintf(stderr, "Out of memory!\n"); exit(1); } /* OutOfMemory */ void MyInetAddr(char *dst, size_t siz, void *src, int i) { struct in_addr *ia; (void) Strncpy(dst, "???", siz); if (src != NULL) { ia = ((struct in_addr **) src)[i]; InetNtoA(dst, ia, siz); } } /* MyInetAddr */ char * FileToURL(char *url, size_t urlsize, const char *const fn, const char *const rcwd, const char *const startdir, const char *const user, const char *const pass, const char *const hname, const unsigned int port) { size_t ulen, dsize; char *dst, pbuf[32]; int isUser; /* //:@:/ */ /* Note that if an absolute path is given, * you need to escape the first entry, i.e. /pub -> %2Fpub */ (void) Strncpy(url, "ftp://", urlsize); isUser = 0; if ((user != NULL) && (user[0] != '\0') && (strcmp(user, "anonymous") != 0) && (strcmp(user, "ftp") != 0)) { isUser = 1; (void) Strncat(url, user, urlsize); if ((pass != NULL) && (pass[0] != '\0')) { (void) Strncat(url, ":", urlsize); (void) Strncat(url, "PASSWORD", urlsize); } (void) Strncat(url, "@", urlsize); } (void) Strncat(url, hname, urlsize); if ((port != 21) && (port != 0)) { (void) sprintf(pbuf, ":%u", (unsigned int) port); (void) Strncat(url, pbuf, urlsize); } ulen = strlen(url); dst = url + ulen; dsize = urlsize - ulen; PathCat(dst, dsize, rcwd, fn, 0); if ((startdir != NULL) && (startdir[0] != '\0') && (startdir[1] /* i.e. not "/" */ != '\0')) { if (strncmp(dst, startdir, strlen(startdir)) == 0) { /* Form relative URL. */ memmove(dst, dst + strlen(startdir), strlen(dst) - strlen(startdir) + 1); } else if (isUser != 0) { /* Absolute URL, but different from start dir. * Make sure to use %2f as first slash so that * the translation uses "/pub" instead of "pub" * since the / characters are just delimiters. */ dst[dsize - 1] = '\0'; dst[dsize - 2] = '\0'; dst[dsize - 3] = '\0'; dst[dsize - 4] = '\0'; memmove(dst + 4, dst + 1, strlen(dst + 1)); dst[0] = '/'; dst[1] = '%'; dst[2] = '2'; dst[3] = 'F'; } } return (url); } /* FileToURL */ /* This will abbreviate a string so that it fits into max characters. * It will use ellipses as appropriate. Make sure the string has * at least max + 1 characters allocated for it. */ void AbbrevStr(char *dst, const char *src, size_t max, int mode) { int len; len = (int) strlen(src); if (len > (int) max) { if (mode == 0) { /* ...Put ellipses at left */ (void) strcpy(dst, "..."); (void) Strncat(dst, src + len - (int) max + 3, max + 1); } else { /* Put ellipses at right... */ (void) Strncpy(dst, src, max + 1); (void) strcpy(dst + max - 3, "..."); } } else { (void) Strncpy(dst, src, max + 1); } } /* AbbrevStr */ char * Path(char *const dst, const size_t siz, const char *const parent, const char *const fname) { (void) Strncpy(dst, parent, siz); (void) Strncat(dst, LOCAL_PATH_DELIM_STR, siz); return (Strncat(dst, fname, siz)); } /* Path */ char * OurDirectoryPath(char *const dst, const size_t siz, const char *const fname) { return (Path(dst, siz, gOurDirectoryPath, fname)); } /* OurDirectoryPath */ char * OurInstallationPath(char *const dst, const size_t siz, const char *const fname) { return (Path(dst, siz, gOurInstallationPath, fname)); } /* OurInstallationPath */ /* Create, if necessary, a directory in the user's home directory to * put our incredibly important stuff in. */ void InitOurDirectory(void) { #if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__) DWORD dwType, dwSize; HKEY hkey; char *cp; int rc; ZeroMemory(gOurDirectoryPath, (DWORD) sizeof(gOurDirectoryPath)); ZeroMemory(gOurInstallationPath, (DWORD) sizeof(gOurInstallationPath)); if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\ncftp.exe", (DWORD) 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) { dwSize = (DWORD) (sizeof(gOurInstallationPath) - 1); dwType = 0; if (RegQueryValueEx( hkey, NULL, (DWORD *) 0, &dwType, (LPBYTE) gOurInstallationPath, &dwSize) == ERROR_SUCCESS) { // This gave us the path to ncftp.exe; // But we use a subdirectory in that directory. // cp = StrRFindLocalPathDelim(gOurInstallationPath); if (cp == NULL) ZeroMemory(gOurInstallationPath, (DWORD) sizeof(gOurInstallationPath)); else ZeroMemory(cp, (DWORD) (cp - gOurInstallationPath)); } RegCloseKey(hkey); } if (gOurInstallationPath[0] == '\0') { if (GetModuleFileName(NULL, gOurInstallationPath, (DWORD) sizeof(gOurInstallationPath) - 1) <= 0) { ZeroMemory(gOurInstallationPath, (DWORD) sizeof(gOurInstallationPath)); } else { // This gave us the path to the current .exe; // But we use a subdirectory in that directory. // cp = StrRFindLocalPathDelim(gOurInstallationPath); if (cp == NULL) ZeroMemory(gOurInstallationPath, (DWORD) sizeof(gOurInstallationPath)); else ZeroMemory(cp, (DWORD) (cp - gOurInstallationPath)); } } if (gOurInstallationPath[0] != '\0') { if ((cp = getenv("NCFTPDIR")) != NULL) { if (*cp == '"') cp++; (void) STRNCPY(gOurDirectoryPath, cp); cp = strrchr(gOurDirectoryPath, '"'); if ((cp != NULL) && (cp[1] == '\0')) *cp = '\0'; } else if ((cp = getenv("USERPROFILE")) != NULL) { if (*cp == '"') cp++; (void) STRNCPY(gOurDirectoryPath, cp); cp = strrchr(gOurDirectoryPath, '"'); if ((cp != NULL) && (cp[1] == '\0')) *cp = '\0'; (void) STRNCAT(gOurDirectoryPath, "\\"); (void) STRNCAT(gOurDirectoryPath, kOurDirectoryName); } else { STRNCPY(gOurDirectoryPath, gOurInstallationPath); if (gUser[0] == '\0') { STRNCAT(gOurDirectoryPath, "\\Users\\default"); } else { STRNCAT(gOurDirectoryPath, "\\Users\\"); STRNCAT(gOurDirectoryPath, gUser); } } rc = MkDirs(gOurDirectoryPath, 00755); } #else struct stat st; char *cp; #ifdef BINDIR (void) STRNCPY(gOurInstallationPath, BINDIR); #else memset(gOurInstallationPath, 0, sizeof(gOurInstallationPath)); #endif cp = getenv("NCFTPDIR"); if (cp != NULL) { (void) STRNCPY(gOurDirectoryPath, cp); } else if (STREQ(gHome, "/")) { /* Don't create it if you're root and your home directory * is the root directory. * * If you are root and you want to store your ncftp * config files, move your home directory somewhere else, * such as /root or /home/root. */ gOurDirectoryPath[0] = '\0'; return; } else { (void) Path(gOurDirectoryPath, sizeof(gOurDirectoryPath), gHome, kOurDirectoryName ); } if (stat(gOurDirectoryPath, &st) < 0) { if (mkdir(gOurDirectoryPath, 00755) < 0) { gOurDirectoryPath[0] = '\0'; } } #endif } /* InitOurDirectory */ void InitUserInfo(void) { #if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__) DWORD nSize; char *cp; memset(gUser, 0, sizeof(gUser)); nSize = sizeof(gUser) - 1; if (! GetUserName(gUser, &nSize)) STRNCPY(gUser, "default"); memset(gHome, 0, sizeof(gHome)); (void) GetTempPath((DWORD) sizeof(gHome) - 1, gHome); cp = strrchr(gHome, '\\'); if ((cp != NULL) && (cp[1] == '\0')) *cp = '\0'; memset(gShell, 0, sizeof(gShell)); #else struct passwd *pwptr; char *envp; gUid = geteuid(); pwptr = getpwuid(gUid); if (pwptr == NULL) { envp = getenv("LOGNAME"); if (envp == NULL) { (void) fprintf(stderr, "Who are you?\n"); (void) fprintf(stderr, "You have a user id number of %d, but no username associated with it.\n", (int) gUid); (void) STRNCPY(gUser, "unknown"); } else { (void) STRNCPY(gUser, envp); } envp = getenv("HOME"); if (envp == NULL) (void) STRNCPY(gHome, "/"); else (void) STRNCPY(gHome, envp); envp = getenv("SHELL"); if (envp == NULL) (void) STRNCPY(gShell, "/bin/sh"); else (void) STRNCPY(gShell, envp); } else { /* Copy home directory. */ (void) STRNCPY(gHome, pwptr->pw_dir); /* Copy user name. */ (void) STRNCPY(gUser, pwptr->pw_name); /* Copy shell. */ (void) STRNCPY(gShell, pwptr->pw_shell); } #endif InitOurDirectory(); } /* InitUserInfo */ char * TolowerStr(char *dst) { if (dst == NULL) return NULL; while (*dst != '\0') { if (isupper((int) *dst)) *dst = (char) tolower(*dst); dst++; } return (dst); } /* TolowerStr */ int MayUseFirewall(const char *const hn0, int firewallType, const char *const firewallExceptionList) { #ifdef HAVE_STRSTR char buf[256]; char *tok; char *parse; #endif /* HAVE_STRSTR */ char hn[80]; STRNCPY(hn, hn0); TolowerStr(hn); if (firewallType == kFirewallNotInUse) return (0); if (firewallExceptionList[0] == '\0') { if (strchr(hn, '.') == NULL) { /* Unqualified host name, * assume it is in local domain. */ return (0); } else { return (1); } } if (strchr(hn, '.') == NULL) { /* Unqualified host name, * assume it is in local domain. * * If "localdomain" is in the exception list, * do not use the firewall for this host. */ (void) STRNCPY(buf, firewallExceptionList); for (parse = buf; (tok = strtok(parse, ", \n\t\r")) != NULL; parse = NULL) { if (ISTREQ(tok, "localdomain")) return (0); } /* fall through */ } #ifdef HAVE_STRSTR (void) STRNCPY(buf, firewallExceptionList); TolowerStr(buf); for (parse = buf; (tok = strtok(parse, ", \n\t\r")) != NULL; parse = NULL) { /* See if host or domain was from exclusion list * matches the host to open. */ if (strstr(hn, tok) != NULL) return (0); } #endif /* HAVE_STRSTR */ return (1); } /* MayUseFirewall */ int StrToBool(const char *const s) { int c; int result; c = *s; if (isupper(c)) c = tolower(c); result = 0; switch (c) { case 'f': /* false */ case 'n': /* no */ break; case 'o': /* test for "off" and "on" */ c = (int) s[1]; if (isupper(c)) c = tolower(c); if (c == 'f') break; result = 1; break; case 't': /* true */ case 'y': /* yes */ result = 1; break; default: /* 1, 0, -1, other number? */ if (atoi(s) != 0) result = 1; } return result; } /* StrToBool */ void AbsoluteToRelative(char *const dst, const size_t dsize, const char *const dir, const char *const root, const size_t rootlen) { *dst = '\0'; if (strcmp(dir, root) != 0) { if (strcmp(root, "/") == 0) { (void) Strncpy(dst, dir + 1, dsize); } else if ((strncmp(root, dir, rootlen) == 0) && (dir[rootlen] == '/')) { (void) Strncpy(dst, dir + rootlen + 1, dsize); } else { /* Still absolute. */ (void) Strncpy(dst, dir, dsize); } } } /* AbsoluteToRelative */ #if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__) #else /* Some commands may want to jump back to the start too. */ static void CancelGetHostByName(int sigNum) { #ifdef ncftp gResolveSig = sigNum; #endif #ifdef HAVE_SIGSETJMP siglongjmp(gGetHostByNameJmp, (sigNum != 0) ? 1 : 0); #else /* HAVE_SIGSETJMP */ longjmp(gGetHostByNameJmp, (sigNum != 0) ? 1 : 0); #endif /* HAVE_SIGSETJMP */ } /* CancelGetHostByName */ #endif int MyGetHostByName(char *const volatile dst, size_t dsize, const char *const hn, int t) { struct addrinfo hints; struct addrinfo *res; int error; #if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__) memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(hn, NULL, &hints, &res); if (error == 0) { char buf[NI_MAXHOST]; getnameinfo(res->ai_addr, res->ai_addrlen, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST); (void) Strncpy(dst, buf, dsize); freeaddrinfo(res); return (0); } #else int sj; vsigproc_t osigpipe, osigint, osigalrm; #ifdef HAVE_SIGSETJMP osigpipe = osigint = osigalrm = (sigproc_t) 0; sj = sigsetjmp(gGetHostByNameJmp, 1); #else /* HAVE_SIGSETJMP */ osigpipe = osigint = osigalrm = (sigproc_t) 0; sj = setjmp(gGetHostByNameJmp); #endif /* HAVE_SIGSETJMP */ if (sj != 0) { /* Caught a signal. */ (void) alarm(0); (void) NcSignal(SIGPIPE, osigpipe); (void) NcSignal(SIGINT, osigint); (void) NcSignal(SIGALRM, osigalrm); #ifdef ncftp Trace(0, "Canceled GetHostByName because of signal %d.\n", gResolveSig); #endif } else { osigpipe = NcSignal(SIGPIPE, CancelGetHostByName); osigint = NcSignal(SIGINT, CancelGetHostByName); osigalrm = NcSignal(SIGALRM, CancelGetHostByName); if (t > 0) (void) alarm((unsigned int) t); memset(&hints, 0, sizeof(hints)); hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; error = getaddrinfo(hn, NULL, &hints, &res); if (t > 0) (void) alarm(0); (void) NcSignal(SIGPIPE, osigpipe); (void) NcSignal(SIGINT, osigint); (void) NcSignal(SIGALRM, osigalrm); if (error == 0) { char buf[NI_MAXHOST]; getnameinfo(res->ai_addr, res->ai_addrlen, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST); (void) Strncpy(dst, buf, dsize); freeaddrinfo(res); return (0); } } #endif /* !Windows */ *dst = '\0'; return (-1); } /* MyGetHostByName */ /* Converts a date string, like "19930602204445" into to a time_t. */ time_t UnDate(char *dstr) { #ifndef HAVE_MKTIME return ((time_t) -1); #else struct tm ut; time_t result = (time_t) -1; (void) Localtime(0, &ut); /* The time we get back from the server is (should be) in UTC. */ if (sscanf(dstr, "%04d%02d%02d%02d%02d%02d", &ut.tm_year, &ut.tm_mon, &ut.tm_mday, &ut.tm_hour, &ut.tm_min, &ut.tm_sec) == 6) { --ut.tm_mon; ut.tm_year -= 1900; result = mktime(&ut); } return result; #endif /* HAVE_MKTIME */ } /* UnDate */ #ifndef HAVE_MEMMOVE /* This code is derived from software contributed to Berkeley by * Chris Torek. */ /* * sizeof(word) MUST BE A POWER OF TWO * SO THAT wmask BELOW IS ALL ONES */ typedef int word; /* "word" used for optimal copy speed */ #define wsize sizeof(word) #define wmask (wsize - 1) /* * Copy a block of memory, handling overlap. * This is the routine that actually implements * (the portable versions of) bcopy, memcpy, and memmove. */ void * memmove(void *dst0, void *src0, size_t length) { register char *dst = (char *) dst0; register const char *src = (char *) src0; register size_t t; if (length == 0 || dst == src) /* nothing to do */ return dst; /* * Macros: loop-t-times; and loop-t-times, t>0 */ #define TLOOP(s) if (t) TLOOP1(s) #define TLOOP1(s) do { s; } while (--t) if ((unsigned long)dst < (unsigned long)src) { /* * Copy forward. */ t = (int)src; /* only need low bits */ if ((t | (int)dst) & wmask) { /* * Try to align operands. This cannot be done * unless the low bits match. */ if ((t ^ (int)dst) & wmask || length < wsize) t = length; else t = wsize - (t & wmask); length -= t; TLOOP1(*dst++ = *src++); } /* * Copy whole words, then mop up any trailing bytes. */ t = length / wsize; TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); t = length & wmask; TLOOP(*dst++ = *src++); } else { /* * Copy backwards. Otherwise essentially the same. * Alignment works as before, except that it takes * (t&wmask) bytes to align, not wsize-(t&wmask). */ src += length; dst += length; t = (int)src; if ((t | (int)dst) & wmask) { if ((t ^ (int)dst) & wmask || length <= wsize) t = length; else t &= wmask; length -= t; TLOOP1(*--dst = *--src); } t = length / wsize; TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); t = length & wmask; TLOOP(*--dst = *--src); } return(dst0); } /* MemMove */ #endif /* ! HAVE_MEMMOVE */ #if defined(HAVE_STRCOLL) && !defined(HAVE_STRNCOLL) int strncoll(const char *a, const char *b, size_t n) { int rc; if (n < 511) { char sa[512], sb[512]; memset(sa, 0, sizeof(sa)); memset(sb, 0, sizeof(sb)); (void) strncpy(sa, a, n); (void) strncpy(sb, b, n); rc = (strcoll(sa, sb)); } else { char *ma, *mb; n += 5; /* enough for a 32-bit zero char. */ ma = (char *) malloc(n); if (ma == NULL) return (0); /* panic */ mb = (char *) malloc(n); if (mb == NULL) { free(ma); return (0); /* panic */ } (void) strncpy(ma, a, n); (void) strncpy(mb, b, n); rc = (strcoll(ma, mb)); free(ma); free(mb); } return (rc); } /* strncoll */ #endif int DecodeDirectoryURL( const FTPCIPtr cip, /* area pointed to may be modified */ char *url, /* always modified */ FTPLineListPtr cdlist, /* always modified */ char *fn, /* always modified */ size_t fnsize ) { int rc; char urlstr2[256]; char *cp; /* Add a trailing slash, if needed, i.e., convert * "ftp://ftp.gnu.org/pub/gnu" to * "ftp://ftp.gnu.org/pub/gnu/" * * We also generalize and assume that if the user specified * something with a .extension that the user was intending * to specify a file instead of a directory. */ cp = strrchr(url, '/'); if ((cp != NULL) && (cp[1] != '\0') && (strchr(cp, '.') == NULL)) { (void) STRNCPY(urlstr2, url); (void) STRNCAT(urlstr2, "/"); url = urlstr2; } rc = FTPDecodeURL(cip, url, cdlist, fn, fnsize, NULL, NULL); return (rc); } /* DecodeDirectoryURL */ #if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__) void SysPerror(const char *const errMsg) { char reason[128]; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reason, (DWORD) sizeof(reason), NULL ); if (reason[strlen(reason) - 1] == '\n') reason[strlen(reason) - 1] = '\0'; (void) fprintf(stderr, "%s: %s\n", errMsg, reason); } /* SysPerror */ #endif