Changeset 3925
- Timestamp:
- Oct 26, 2014, 2:06:31 AM (11 years ago)
- Location:
- trunk/libc
- Files:
-
- 1 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/libc/src/libc/misc/abspath.c
r3879 r3925 1 /* abspath.c (emx+gcc) -- Copyright (c) 1992-1998 by Eberhard Mattes */ 2 1 /* $Id$ */ 2 /** @file 3 * kLibC - Implementation of _abspath(). 4 * 5 * @copyright Copyright (C) 2014 knut st. osmundsen <bird-klibc-spam-xiv@anduin.net> 6 * @licenses MIT, BSD2, BSD3, BSD4, LGPLv2.1, LGPLv3, LGPLvFuture. 7 */ 8 9 10 /******************************************************************************* 11 * Header Files * 12 *******************************************************************************/ 3 13 #include "libc-alias.h" 4 14 #include <stdlib.h> 15 16 #include <assert.h> 17 #include <errno.h> 18 #include <stdbool.h> 5 19 #include <string.h> 6 20 #include <alloca.h> 7 #include < errno.h>21 #include <sys/cdefs.h> 8 22 #include <sys/param.h> 9 #include <emx/syscalls.h>10 23 #include <InnoTekLIBC/libc.h> 11 24 #include <InnoTekLIBC/locale.h> 12 25 #include <InnoTekLIBC/backend.h> 13 14 #define FALSE 0 15 #define TRUE 1 16 #define IS_PATH_DELIM(c) ((c)=='\\' || (c)=='/') 17 18 int _abspath (char *dst, const char *src, int size) 26 #ifdef __OS2__ 27 # include <InnoTekLIBC/pathrewrite.h> 28 extern int __libc_gcchUnixRoot; /* fs.c / b_fs.h */ 29 #endif 30 31 32 /** Macro that advance the a_pszSrc variable past all leading slashes. */ 33 #define _ABSPATH_SKIP_SLASHES(a_pszSrc) \ 34 do { \ 35 char ch = *(a_pszSrc); \ 36 while (__KLIBC_PATH_IS_SLASH(ch)) \ 37 ch = *++(a_pszSrc); \ 38 } while (0) 39 40 41 /** 42 * Sets errno to ERANGE and returns -1. 43 * 44 * Convenience for dropping three lines of code for each overflow check and 45 * return. 46 * 47 * @returns -1 48 */ 49 static int _abspath_overflow(void) 19 50 { 20 char drive, dir[MAXPATHLEN+1], src_drive, *s, *p; 21 int i, j, rel, server, trail, mbcl; 22 char chSlash = '/'; 23 24 s = alloca (strlen (src) + 1); 25 strcpy (s, src); 26 src_drive = _fngetdrive (s); 27 if (src_drive == 0) 28 drive = _getdrive (); 29 else 30 { 31 drive = src_drive; 32 s += 2; 33 } 34 dir[0] = 0; rel = FALSE; server = FALSE; trail = FALSE; 35 if (IS_PATH_DELIM (*s)) 36 { 37 ++s; 38 if (IS_PATH_DELIM (*s)) 39 { 40 ++s; server = TRUE; 41 } 42 } 43 else if (__libc_Back_fsDirCurrentGet (dir, sizeof(dir), src_drive, __LIBC_BACK_FSCWD_NO_DRIVE | __LIBC_BACK_FSCWD_NO_ROOT_SLASH) == 0) 44 _fnslashify (dir); 45 else 46 { 47 dir[0] = 0; 48 rel = TRUE; 49 } 50 51 while (*s != 0) 52 { 53 if (s[0] == '.' && (IS_PATH_DELIM (s[1]) || s[1] == 0)) 54 ++s; 55 else if (s[0] == '.' && s[1] == '.' && (IS_PATH_DELIM (s[2]) || 56 s[2] == 0)) 57 { 58 s += 2; 59 if (*dir == 0) 60 strcpy (dir, ".."); 61 else 62 { 63 p = _getname (dir); 64 if (p == dir) 65 p[0] = 0; 66 else 67 p[-1] = 0; 68 } 69 } 70 else 71 { 72 i = strlen (dir); 73 if (i < sizeof (dir) - 1) 74 dir[i++] = chSlash; 75 while (*s != 0) 76 { 77 if (CHK_MBCS_PREFIX (&__libc_GLocaleCtype, *s, mbcl) && s[1] != 0) 51 errno = ERANGE; 52 return -1; 53 } 54 55 56 /** 57 * Figures out the length of the directory component @a pszSrc points to. 58 * 59 * @returns Length in bytes. 60 * @param pszSrc Pointer to the path component which length we're 61 * interested in. This should not point to a slash but may 62 * point to the zero terminator character. 63 */ 64 static int _abspath_component_length(const char *pszSrc) 65 { 66 char const *psz = pszSrc; 67 char ch; 68 assert(!__KLIBC_PATH_IS_SLASH(*psz)); 69 70 if (__libc_GLocaleCtype.mbcs) 71 { 72 /* May contain multibyte chars. */ 73 int cchMultibyteCodepoint; 74 while ((ch = *psz) != '\0') 75 { 76 if ( CHK_MBCS_PREFIX(&__libc_GLocaleCtype, ch, cchMultibyteCodepoint) 77 && psz[1] != '\0') 78 psz += cchMultibyteCodepoint; 79 else if (__KLIBC_PATH_IS_SLASH(ch)) 80 break; 81 else 82 psz++; 83 } 84 } 85 else 86 { 87 /* All chars are single byte. */ 88 while ((ch = *psz) != '\0' && !__KLIBC_PATH_IS_SLASH(ch)) 89 psz++; 90 } 91 return (int)(psz - pszSrc); 92 } 93 94 95 /** 96 * Fixes the slashes of a current directory we got from the backend. 97 * 98 * @returns Length of the current returned directory. 99 * @param pszDst The destination buffer which slashes we should fix. 100 * @param chSlash The prefereed slash. 101 */ 102 static int _abspath_slashify(char *pszDst, char chSlash) 103 { 104 #ifdef __KLIBC_PATH_SLASH_ALT 105 char *psz = pszDst; 106 char ch; 107 if (__libc_GLocaleCtype.mbcs) 108 { 109 /* May contain multibyte chars. */ 110 int cchMultibyteCodepoint; 111 while ((ch = *psz) != '\0') 112 { 113 if ( CHK_MBCS_PREFIX(&__libc_GLocaleCtype, ch, cchMultibyteCodepoint) 114 && psz[1] != '\0') 115 psz += cchMultibyteCodepoint; 116 else if (__KLIBC_PATH_IS_SLASH(ch)) 117 *psz++ = chSlash; 118 else 119 psz++; 120 } 121 } 122 else 123 { 124 /* All chars are single byte. */ 125 while ((ch = *psz) != '\0') 126 { 127 if (__KLIBC_PATH_IS_SLASH(ch)) 128 *psz = chSlash; 129 psz++; 130 } 131 } 132 return (int)(psz - pszDst); 133 #else 134 return strlen(pszDst); 135 #endif 136 } 137 138 139 #ifdef __OS2__ 140 /** 141 * Checks if @a pszSrc points something that could be an OS/2 device name. 142 * 143 * @returns 1 if likely OS/2 device name, 0 if not. 144 * @param pszSrc Pointer to the char after "/DEV/". 145 */ 146 static int _abspath_is_likely_device_name(const char *pszSrc) 147 { 148 _ABSPATH_SKIP_SLASHES(pszSrc); 149 size_t cch = _abspath_component_length(pszSrc); 150 if (cch > 0 && cch <= 8 && !pszSrc[cch]) 151 { 152 if (cch == 1) 153 return pszSrc[0] != '.'; 154 if (cch == 2) 155 return pszSrc[0] != '.' || pszSrc[1] != '.'; 156 return 1; 157 } 158 return 0; 159 } 160 #endif 161 162 int _abspath(char *pszDst, const char *pszSrc, int cbDst) 163 { 164 /* 165 * Note! This code must mimick fsResolveUnix and fsResolveOS2 166 */ 167 #ifdef __KLIBC_PATH_SLASH_ALT 168 char const chSlash = !__libc_gfNoUnix ? __KLIBC_PATH_SLASH_ALT : __KLIBC_PATH_SLASH; 169 #else 170 char const chSlash = __KLIBC_PATH_SLASH; 171 #endif 172 #ifdef __KLIBC_PATH_HAVE_DRIVE_LETTERS 173 char chDrive; 174 #endif 175 176 /* 177 * Make a copy of the input if it overlaps the destination buffer 178 * OR if the 179 */ 180 size_t cchSrc = strlen(pszSrc); 181 if ( (uintptr_t)pszDst <= (uintptr_t)pszSrc + cchSrc 182 && (uintptr_t)pszDst + (uintptr_t)cbDst > (uintptr_t)pszSrc) 183 { 184 char *pszSrcCopy = (char *)alloca(cchSrc + 1); 185 memcpy(pszSrcCopy, pszSrc, cchSrc + 1); 186 pszSrc = pszSrcCopy; 187 } 188 189 /* 190 * Deal with the start of the path. This will skip 191 */ 192 int cch; 193 int offDst = 0; 194 #define _ABSPATH_ERANGE_RETURN_CHECK(a_cbNeeded) if (cbDst <= (a_cbNeeded)) return _abspath_overflow(); else do {} while (0) 195 int offDstRoot = 0; 196 /* Root slash? */ 197 if (__KLIBC_PATH_IS_SLASH(*pszSrc)) 198 { 199 #ifdef __KLIBC_PATH_HAVE_UNC 200 /* 201 * Check for and deal with UNC. 202 * Note! We do not allow '..' to advance higher than the share name, 203 * i.e. "//server/share/.." becomes "//server/share/". 204 */ 205 /** @todo Should "//server/share/.." become "//server/share" rather than "//server/share/" ?? */ 206 if ( __KLIBC_PATH_IS_SLASH(pszSrc[1]) 207 && !__KLIBC_PATH_IS_SLASH(pszSrc[2]) 208 && pszSrc[2]) 209 { 210 /* The server part. */ 211 cch = _abspath_component_length(&pszSrc[2]); 212 _ABSPATH_ERANGE_RETURN_CHECK(2 + cch); 213 pszDst[0] = chSlash; 214 pszDst[1] = chSlash; 215 memcpy(&pszDst[2], &pszSrc[2], cch); 216 offDst = 2 + cch; 217 pszSrc += 2 + cch; 218 219 /* The share/resource too, if present. */ 220 if (*pszSrc) 221 { 222 pszSrc++; 223 _ABSPATH_SKIP_SLASHES(pszSrc); 224 cch = _abspath_component_length(pszSrc); 225 _ABSPATH_ERANGE_RETURN_CHECK(offDst + 1 + cch); 226 pszDst[offDst++] = chSlash; 227 memcpy(&pszDst[offDst], pszSrc, cch); 228 offDst += cch; 229 pszSrc += cch; 230 if (*pszSrc) 78 231 { 79 if (i < sizeof (dir) - mbcl) 80 { 81 memcpy (dir + i, s, mbcl); 82 i += mbcl; 83 } 84 s += mbcl; 232 _ABSPATH_ERANGE_RETURN_CHECK(offDst + 1); 233 pszDst[offDst++] = chSlash; 234 pszSrc++; 235 _ABSPATH_SKIP_SLASHES(pszSrc); 85 236 } 86 else if (IS_PATH_DELIM (*s)) 237 } 238 offDstRoot = offDst; 239 } 240 else 241 #endif 242 { 243 /* Skip leading slashes before continuing. */ 244 pszSrc++; 245 _ABSPATH_SKIP_SLASHES(pszSrc); 246 247 if (0) 248 { /* nothing */ } 249 #ifdef __OS2__ 250 /* 251 * The \pipe\ path is special on OS/2, all named pipes are invisibly 252 * living under it. May include subdirectories. Unsure how '..' is 253 * handled, but assuming the caller wants to straighten those out. 254 */ 255 else if ( (pszSrc[0] == 'p' || pszSrc[0] == 'P') 256 && (pszSrc[1] == 'i' || pszSrc[1] == 'I') 257 && (pszSrc[2] == 'p' || pszSrc[2] == 'P') 258 && (pszSrc[3] == 'e' || pszSrc[3] == 'E') 259 && __KLIBC_PATH_IS_SLASH(pszSrc[4]) ) 260 { 261 _ABSPATH_ERANGE_RETURN_CHECK(6); 262 pszDst[0] = chSlash; 263 pszDst[1] = 'P'; 264 pszDst[2] = 'I'; 265 pszDst[3] = 'P'; 266 pszDst[4] = 'E'; 267 pszDst[5] = chSlash; 268 offDst = 6; 269 pszSrc += 5; 270 _ABSPATH_SKIP_SLASHES(pszSrc); 271 } 272 /* 273 * The \dev\ path is also special on OS/2, all devices are visibly 274 * (via stat but not readdir) living under it. Names are limited 275 * to 8 chars and there should not be any subdirs AFAIK. Since we 276 * may misidentify kNIX pseudo device path here, we do not 277 * uppercase the prefix like we did for the pipes. 278 */ 279 else if ( (pszSrc[0] == 'd' || pszSrc[0] == 'D') 280 && (pszSrc[1] == 'e' || pszSrc[1] == 'E') 281 && (pszSrc[2] == 'v' || pszSrc[2] == 'V') 282 && __KLIBC_PATH_IS_SLASH(pszSrc[3]) 283 && _abspath_is_likely_device_name(&pszSrc[4])) 284 { 285 _ABSPATH_ERANGE_RETURN_CHECK(5); 286 pszDst[0] = chSlash; 287 pszDst[1] = pszSrc[0]; 288 pszDst[2] = pszSrc[1]; 289 pszDst[3] = pszSrc[2]; 290 pszDst[4] = chSlash; 291 offDst = 5; 292 pszSrc += 4; 293 _ABSPATH_SKIP_SLASHES(pszSrc); 294 } 295 /* 296 * If the path rewriter triggers on the path, we will not prefix 297 * it with the current driver letter. 298 */ 299 else if (__libc_PathRewrite(pszSrc - 1, NULL, 0) > 0) 300 { 301 _ABSPATH_ERANGE_RETURN_CHECK(1); 302 pszDst[0] = chSlash; 303 offDst = 1; 304 } 305 #endif /* __OS2__ */ 306 #ifdef __KLIBC_PATH_HAVE_DRIVE_LETTERS 307 /* 308 * Add the current drive letter before the root slash, unless we've 309 * done chroot() or similar that means '/' refers to the unixroot 310 * instead of the root of the current drive. 311 */ 312 else if ( __libc_gcchUnixRoot == 0 313 && (chDrive = _getdrive()) > 0) /** @todo UNC CWD */ 314 { 315 _ABSPATH_ERANGE_RETURN_CHECK(3); 316 pszDst[0] = chDrive; 317 pszDst[1] = ':'; 318 pszDst[2] = chSlash; 319 offDst = 3; 320 } 321 #endif 322 /* 323 * Unix root slash. 324 */ 325 else 326 { 327 _ABSPATH_ERANGE_RETURN_CHECK(1); 328 pszDst[0] = chSlash; 329 offDst = 1; 330 } 331 offDstRoot = offDst; 332 } 333 } 334 #ifdef __KLIBC_PATH_HAVE_DRIVE_LETTERS 335 /* 336 * Drive letter? 337 */ 338 else if ( pszSrc[0] != '\0' 339 && pszSrc[1] == ':' 340 && (chDrive = _fngetdrive(pszSrc)) ) 341 { 342 if (__KLIBC_PATH_IS_SLASH(pszSrc[2])) 343 { 344 /* 345 * Absolute path, great :-). 346 */ 347 _ABSPATH_ERANGE_RETURN_CHECK(3); 348 pszDst[0] = chDrive; 349 pszDst[1] = ':'; 350 pszDst[2] = chSlash; 351 offDstRoot = offDst = 3; 352 pszSrc += 3; 353 _ABSPATH_SKIP_SLASHES(pszSrc); 354 } 355 else 356 { 357 /* 358 * Drive letter relative path. Try add the current directory for 359 * that drive. If we cannot get it (drive letter exists or 360 * something, return the relative path. 361 */ 362 pszSrc += 2; 363 int rc = __libc_Back_fsDirCurrentGet(pszDst, cbDst, chDrive, 0 /*fFlags*/); 364 if (rc == -ERANGE) 365 return _abspath_overflow(); 366 if (rc == 0) 367 { 368 offDst = _abspath_slashify(pszDst, chSlash); 369 if (*pszSrc && pszDst[offDst - 1] != chSlash) 370 { 371 _ABSPATH_ERANGE_RETURN_CHECK(offDst + 1); 372 pszDst[offDst++] = chSlash; 373 } 374 offDstRoot = 3; 375 } 376 else 377 { 378 pszDst[0] = chDrive; 379 pszDst[1] = ':'; 380 offDstRoot = offDst = 2; 381 } 382 } 383 } 384 #endif 385 /* 386 * The path is relative to the current directory, so try add it. 387 */ 388 else if (*pszSrc) 389 { 390 int rc = __libc_Back_fsDirCurrentGet(pszDst, cbDst, '\0' /* current drive */, 0 /*fFlags*/); 391 if (rc == -ERANGE) 392 return _abspath_overflow(); 393 if (rc == 0) 394 { 395 offDst = _abspath_slashify(pszDst, chSlash); 396 if (pszDst[offDst - 1] != chSlash) 397 { 398 _ABSPATH_ERANGE_RETURN_CHECK(offDst + 1); 399 pszDst[offDst++] = chSlash; 400 } 401 402 #ifdef __KLIBC_PATH_HAVE_DRIVE_LETTERS 403 if (_fngetdrive(pszDst)) 404 offDstRoot = __KLIBC_PATH_IS_SLASH(pszDst[2]) ? 3 : 2; 405 else 406 #endif 407 #ifdef __KLIBC_PATH_HAVE_UNC 408 if ( __KLIBC_PATH_IS_SLASH(pszDst[0]) 409 && __KLIBC_PATH_IS_SLASH(pszDst[1])) 410 { 411 offDstRoot = 2 + _abspath_component_length(&pszSrc[2]); 412 if (pszDst[offDstRoot]) 413 offDstRoot += 1 + _abspath_component_length(&pszSrc[offDstRoot]); 414 } 415 else 416 #endif 417 offDstRoot = 1; 418 } 419 else 420 offDstRoot = offDst = 0; 421 } 422 /* 423 * Input is empty. Return empty path and failure. 424 */ 425 else 426 { 427 if (cbDst) 428 *pszDst = '\0'; 429 errno = EINVAL; 430 return -1; 431 } 432 433 /* 434 * Straighten out '.', '..' and slashes. There shall be no slash at the 435 * head of the input string at this point, that way we can more easily 436 * handle the trailing directory slash correctly. It is also assumed that 437 * offDstRoot - 1 is a slash or similar, so we can back up all the way to 438 * offDstRoot without needing to thing about slashes. 439 */ 440 int offParentDir = -1; 441 for (;;) 442 { 443 assert(!__KLIBC_PATH_IS_SLASH(*pszSrc)); 444 cch = _abspath_component_length(pszSrc); 445 if (!cch) 446 break; 447 448 /* 449 * "." - No change. 450 */ 451 if (cch == 1 && pszSrc[0] == '.') 452 { 453 pszSrc++; 454 if (!*pszSrc) 455 { 456 if (offDst > offDstRoot) 457 offDst--; /* No trailing slash. */ 87 458 break; 88 else 459 } 460 pszSrc++; 461 } 462 /* 463 * ".." - Drop the last directory in the destination path (unless we're 464 * constructing a relative path because we couldn't for some reason or 465 * other resolve the current directory). 466 */ 467 else if (cch == 2 && pszSrc[0] == '.' && pszSrc[1] == '.' && offDstRoot != 0) 468 { 469 if (offDst > offDstRoot) 470 { 471 if (offParentDir > 0) 472 offDst = offParentDir; 473 else 89 474 { 90 if (i < sizeof (dir) - 1) 91 dir[i++] = *s; 92 ++s; 475 pszDst[offDst - 1] = '\0'; /* terminate and drop trailing slash. */ 476 offDst = (int)(_getname(&pszDst[offDstRoot]) - pszDst); 93 477 } 94 478 } 95 dir[i] = 0; 96 } 97 if (*s != 0) 98 { 99 ++s; 100 if (*s == 0) 101 trail = 1; 102 } 103 } 104 i = 0; 105 if (i+1 < size && (src_drive != 0 || !server)) 106 { 107 dst[i++] = drive; 108 dst[i++] = ':'; 109 } 110 if (i < size && server && src_drive == 0) 111 dst[i++] = chSlash; 112 if (i < size && !rel && dir[0] != chSlash) 113 dst[i++] = chSlash; 114 j = 0; 115 if (rel && dir[j] == chSlash) 116 ++j; 117 while (i < size && dir[j] != 0) 118 dst[i++] = dir[j++]; 119 if (trail && i < size && (i == 0 || dst[i-1] != chSlash)) 120 dst[i++] = chSlash; 121 if (i >= size) 122 { 123 dir[size-1] = 0; 124 errno = ERANGE; 125 return -1; 126 } 127 dst[i] = 0; 128 return 0; 479 /* else: Hit the root, it's ".." link doesn't go anywhere. So ignore it. */ 480 offParentDir = -1; 481 482 pszSrc += 2; 483 if (!*pszSrc) 484 { 485 if (offDst > offDstRoot) 486 offDst--; /* No trailing slash. */ 487 break; 488 } 489 pszSrc++; 490 } 491 /* 492 * Append the component and trailing slash, if any. 493 */ 494 else 495 { 496 offParentDir = offDst; /* Cache the last dir offset for efficient '..' handling. */ 497 if (!pszSrc[cch]) 498 { 499 _ABSPATH_ERANGE_RETURN_CHECK(offDst + cch); 500 memcpy(&pszDst[offDst], pszSrc, cch); 501 offDst += cch; 502 break; 503 } 504 505 _ABSPATH_ERANGE_RETURN_CHECK(offDst + cch + 1); 506 memcpy(&pszDst[offDst], pszSrc, cch); 507 offDst += cch; 508 pszDst[offDst++] = chSlash; 509 pszSrc += cch + 1; 510 } 511 512 /* Skip extra slashes before we go on to the next component or reach 513 the end of the string. */ 514 _ABSPATH_SKIP_SLASHES(pszSrc); 515 } 516 517 /* 518 * Add the terminator and return. 519 */ 520 if (offDst < cbDst) 521 { 522 pszDst[offDst] = '\0'; 523 return 0; 524 } 525 return _abspath_overflow(); 129 526 } 527 -
trunk/libc/tests/libc/Makefile
r3914 r3925 58 58 SMOKETESTS := \ 59 59 smoketests/64bitio-1.c \ 60 smoketests/abspath-1.c \ 60 61 smoketests/access-1.c \ 61 62 smoketests/alloca-1.c \
Note:
See TracChangeset
for help on using the changeset viewer.