|
25.3.2.2 Separators and Drive Letters
As discussed earlier (see section 15.3.5.6 Windows Separators and Drive Letters), the
Windows file systems use different delimiters for separating directories
and path elements than their Unix cousins. There are three places
where this has an effect:
- the shell command line
- Up until Cygwin b20.1, it was possible to refer to drive letter prefixed
paths from the shell using the `//c/path/to/file' syntax to refer
to the directory root at `C:\path\to\file'. Unfortunately, the
Windows kernel confused this with the its own network share notation,
causing the shell to pause for a short while to look for a machine named
`c' in its network neighbourhood. Since release 1.0 of Cygwin, the
`//c/path/to/file' notation now really does refer to a machine
named `c' from Cygwin as well as from Windows. To refer to drive
letter rooted paths on the local machine from Cygwin there is a new
hybrid `c:/path/to/file' notation. This notation also works in
Cygwin b20, and is probably the system you should use.
On the other hand, using the new hybrid notation in shell scripts means
that they won't run on old Cygwin releases. Shell code embedded In
`configure.in' scripts, should test whether the hybrid notation
works, and use an alternate macro to translate hybrid notation to the
old style if necessary.
I must confess that from the command line I now use the longer
`/cygdrive/c/path/to/file' notation, since TAB completion
doesn't yet work for the newer hybrid notation. It is important to use
the new notation in shell scripts however, or they will fail on the latest
releases of Cygwin.
- shell scripts
- For a shell script to work correctly on non-Cygwin development
environments, it needs to be aware of and handle Windows path and
directory separator and drive letters. The Libtool scripts use the
following idiom:
| case "$path" in
# Accept absolute paths.
[\\/]* | [A-Za-\]:[\\/]*)
# take care of absolute paths
insert some code here
;;
*)
# what is left must be a relative path
insert some code here
;;
esac
|
- source code
-
When porting Unix software to Cygwin, this is much less of an issue
because these differences are hidden beneath the emulation layer, and by
the
mount command respectively; although I have found that
GCC, for example, returns a mixed mode `/' and `\' delimitted
include path which upsets Automake's dependency tracking on occasion.
Cygwin provides convenience functions to convert back and forth between
the different notations, which we call POSIX paths or path lists,
and WIN32 paths or path lists:
- Function: int posix_path_list_p (const char *path)
- Return `0', unless path is a `/' and `:' separated
path list. The determination is rather simplistic, in that a string
which contains a `;' or begins with a single letter followed by a
`:' causes the `0' return.
- Function: void cygwin_win32_to_posix_path_list (const char *win32, char *posix)
- Converts the `\' and `;' delimiters in win32, into the
equivalent `/' and `:' delimiters while copying into the
buffer at address posix. This buffer must be preallocated before
calling the function.
- Function: void cygwin_conv_to_posix_path (const char *path, char *posix_path)
- If path is a `\' delimitted path, the equivalent, `/'
delimitted path is written to the buffer at address posix_path.
This buffer must be preallocated before calling the function.
- Function: void cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
- If path is a, possibly relative, `\' delimitted path, the
equivalent, absolute, `/' delimitted path is written to the buffer
at address posix_path. This buffer must be preallocated before
calling the function.
- Function: void cygwin_posix_to_win32_path_list (const char *posix, char *win32)
- Converts the `/' and `:' delimiters in posix, into the
equivalent `\' and `;' delimiters while copying into the
buffer at address win32. This buffer must be preallocated before
calling the function.
- Function: void cygwin_conv_to_win32_path (const char *path, char *win32_path)
- If path is a `/' delimitted path, the equivalent, `\'
delimitted path is written to the buffer at address win32_path.
This buffer must be preallocated before calling the function.
- Function: void cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
- If path is a, possibly relative, `/' delimitted path, the
equivalent, absolute, `\' delimitted path is written to the buffer
at address win32_path. This buffer must be preallocated before
calling the function.
You can use these functions something like this:
|
void
display_canonical_path(const char *maybe_relative_or_win32)
{
char buffer[MAX_PATH];
cygwin_conv_to_full_posix_path(maybe_relative_or_win32,
buffer);
printf("canonical path for %s: %s\n",
maybe_relative_or_win32, buffer);
}
|
For your code to be fully portable however, you cannot rely on these Cygwin
functions as they are not implemented on Unix, or even mingw or
DJGPP. Instead you should add the following to a shared header, and be
careful to use it when processing and building paths and path lists:
|
#if defined __CYGWIN32__ && !defined __CYGWIN__
/* For backwards compatibility with Cygwin b19 and
earlier, we define __CYGWIN__ here, so that
we can rely on checking just for that macro. */
# define __CYGWIN__ __CYGWIN32__
#endif
#if defined _WIN32 && !defined __CYGWIN__
/* Use Windows separators on all _WIN32 defining
environments, except Cygwin. */
# define DIR_SEPARATOR_CHAR '\\'
# define DIR_SEPARATOR_STR "\\"
# define PATH_SEPARATOR_CHAR ';'
# define PATH_SEPARATOR_STR ";"
#endif
#ifndef DIR_SEPARATOR_CHAR
/* Assume that not having this is an indicator that all
are missing. */
# define DIR_SEPARATOR_CHAR '/'
# define DIR_SEPARATOR_STR "/"
# define PATH_SEPARATOR_CHAR ':'
# define PATH_SEPARATOR_STR ":"
#endif /* !DIR_SEPARATOR_CHAR */
|
With this in place we can use the macros defined above to write code
which will compile and work just about anywhere:
| char path[MAXBUFLEN];
snprintf(path, MAXBUFLEN, "%ctmp%c%s\n",
DIR_SEPARATOR_CHAR, DIR_SEPARATOR_CHAR, foo);
file = fopen(path, "tw+");
|
|