/*** *fullpath.c - * * Copyright (c) 1987-1992, Microsoft Corporation. All rights reserved. * *Purpose: contains the function _fullpath which makes an absolute path out * of a relative path. i.e. ..\pop\..\main.c => c:\src\main.c if the * current directory is c:\src\src * *Revision History: * * 12-21-87 WAJ Initial version * 01-08-88 WAJ now treats / as an \ * 06-22-88 WAJ now handles network paths ie \\sl\users * 01-31-89 SKS/JCR Renamed _canonic to _fullpath * 04-03-89 WAJ Now returns "d:\dir" for "." * 05-09-89 SKS Do not change the case of arguments * 11-30-89 JCR Preserve errno setting from _getdcwd() call on errors * 03-07-90 GJF Replaced _LOAD_DS with _CALLTYPE1, added #include * , removed #include and fixed * the copyright. * 04-25-90 JCR Fixed an incorrect errno setting * 06-14-90 SBM Fixed bugs in which case of user provided drive letter * was not always preserved, and c:\foo\\bar did not * generate an error * 08-10-90 SBM Compiles cleanly with -W3 * 08-28-90 SBM Fixed bug in which UNC names were being rejected * 09-27-90 GJF New-style function declarator. * 01-18-91 GJF ANSI naming. * 11-30-92 KRS Ported _MBCS code from 16-bit tree. * 08-03-93 KRS Change to use _ismbstrail instead of isdbcscode. * 09-27-93 CFW Avoid cast bug. * *******************************************************************************/ #include #include #include #include #include #include #include #include #ifdef _MBCS #include #include #endif #define ISSLASH(a) ((a) == '\\' || (a) == '/') /*** *char *_fullpath( char *buf, const char *path, maxlen ); * *Purpose: * * _fullpath - combines the current directory with path to form * an absolute path. i.e. _fullpath takes care of .\ and ..\ * in the path. * * The result is placed in buf. If the length of the result * is greater than maxlen NULL is returned, otherwise * the address of buf is returned. * * If buf is NULL then a buffer is malloc'ed and maxlen is * ignored. If there are no errors then the address of this * buffer is returned. * * If path specifies a drive, the curent directory of this * drive is combined with path. If the drive is not valid * and _fullpath needs the current directory of this drive * then NULL is returned. If the current directory of this * non existant drive is not needed then a proper value is * returned. * For example: path = "z:\\pop" does not need z:'s current * directory but path = "z:pop" does. * * * *Entry: * char *buf - pointer to a buffer maintained by the user; * char *path - path to "add" to the current directory * int maxlen - length of the buffer pointed to by buf * *Exit: * Returns pointer to the buffer containing the absolute path * (same as buf if non-NULL; otherwise, malloc is * used to allocate a buffer) * *Exceptions: * *******************************************************************************/ char * _CALLTYPE1 _fullpath ( char *UserBuf, const char *path, size_t maxlen ) { char *RetValue; char *buf; char *StartBuf; char *EndBuf; char *SaveBuf; char c; int count; unsigned drive; #ifdef _MBCS const char *StartPath = path; #endif if( !path || !*path ) /* no work to do */ return( _getcwd( UserBuf, maxlen ) ); /* allocate buffer if necessary */ if( !UserBuf ) if( !(buf = malloc(_MAX_PATH)) ){ errno = ENOMEM; return( NULL ); } else maxlen = _MAX_PATH; else { if( maxlen < _MAX_DRIVE+1 ){ /* we need at least 4 chars for "A:\" */ errno = ERANGE; return( NULL ); } buf = UserBuf; } RetValue = buf; EndBuf = buf + maxlen - 1; if( ISSLASH(*(path+1)) && ISSLASH(*path) ){ /* check for network drive */ count = 0; /* count the '\\'s */ while( (c = *path) ){ *buf = c; path++; if( buf >= EndBuf ){ ReturnError1: errno = ERANGE; ReturnError2: if( UserBuf == NULL ) free( RetValue ); return( NULL ); } #ifdef _MBCS if ( !_ISLEADBYTE(*(buf-2))) #endif if( ISSLASH( c ) ){ /* ensure that path contains a '\\', not a '/' */ *buf = '\\'; /* ensure that '\\\\' is followed by something */ if( ++count == 2 ){ if( !*path ){ errno = EINVAL; goto ReturnError2; } } /* check that third and fourth '\\'are preceded by non-'\\' */ if( count >= 3 ){ if( *(buf-1) == '\\' ){ errno = EINVAL; goto ReturnError2; } } /* stop on fourth '\\', \\foo\bar\ <-- namely this one */ if( count == 4 ) break; } buf++; } *buf = '\\'; StartBuf = buf; } else { /* not a UNC path */ if( path[1] == ':' && isalpha((unsigned char)path[0]) ) { /* get drive information */ *buf = *path; /* ** drive must be in the range 1-26 ** "*buf" may be upper or lower case */ drive = (*buf++ - 'A' + 1) & 0x1F; *buf++ = ':'; path += 2; } else drive = 0; if( ISSLASH( *path ) ){ if( drive == 0 ){ *buf++ = (char)(_getdrive() + 'A' - 1); *buf++ = ':'; } path++; } else { /* preserve the driveletter in original case from path, since _getdcwd will always return it in upper case */ if (drive) c = *(path-2); if( !_getdcwd( drive, RetValue, maxlen ) ) goto ReturnError2; /* error: preserve errno value */ buf = RetValue + strlen( RetValue ); /* restore drive letter in original case */ if (drive) *RetValue = c; #ifdef _MBCS if (!_ISLEADBYTE(*(buf-2))) #endif if( ISSLASH( *(buf-1) ) ) /* buf must point to last slash */ buf--; /* if this is a root directory */ } *buf = '\\'; StartBuf = RetValue + 2; } while( *path ){ if( *path == '.' && *(path+1) == '.' && ( ISSLASH(*(path+2)) || !*(path+2) ) ){ /* handle .. */ do{ buf--; #ifdef _MBCS if (ISSLASH(*buf)) if (_ismbstrail(StartBuf,buf)) buf--; #endif } while( !ISSLASH( *buf ) && buf > StartBuf ); if( buf < StartBuf ) { /* unable to do cd .. */ errno = EACCES; goto ReturnError2; } path += 2; if( *path ) /* if it was "..\" */ path++; } else if( *path == '.' && ( ISSLASH(*(path+1)) || !*(path+1) )){ path++; /* handle . */ if( *path ) /* if it was ".\" */ path++; } else { SaveBuf = buf; /* save for DBCS-safe test for null path */ while((*path) && (!ISSLASH(*path)) && (buf < EndBuf)) { *++buf = *path++; #ifdef _MBCS if (_ISLEADBYTE(*(path-1))) { if ((*path) && (buf < EndBuf)) *++buf = *path++; else { /* ill-formed DBCS character */ errno = EINVAL; goto ReturnError2; } } #endif } if( buf >= EndBuf ) /* if buf == EndBuf then */ goto ReturnError1; /* no room for '\0' */ /* DBCS-safe test for non-empty path component */ if ((buf==SaveBuf) /* && (*buf=='\\') */ ) /* 2nd test redundent */ { errno = EINVAL; goto ReturnError2; } *++buf = '\\'; if( ISSLASH( *path ) ) #ifdef _MBCS if (!_ismbstrail(StartPath,path)) #endif path++; } } /* buf points to last character in the string which is an '\\' */ if( *(buf-1) == ':' ) /* keep trailing '\' if */ #ifdef _MBCS if (!_ismbstrail(StartBuf,buf-1)) #endif buf++; /* this is a root directory */ *buf = '\0'; return( RetValue ); }