diff options
author | Jason Rumney <jasonr@gnu.org> | 2009-03-24 14:14:53 +0000 |
---|---|---|
committer | Jason Rumney <jasonr@gnu.org> | 2009-03-24 14:14:53 +0000 |
commit | 7664ac3d9afe9b30fc8886d41c72f73706f0294d (patch) | |
tree | 48c17f1361bb65c11ee7bbca1bf29eddd71b6921 /src/fileio.c | |
parent | e6f7b1e8af1e7040d26769cc407891daaf9d2fb4 (diff) | |
download | emacs-7664ac3d9afe9b30fc8886d41c72f73706f0294d.tar.gz |
(Fsubstitute_in_file_name): Always work on a copy.
Calculate total size precisely. Decode environment variables
before substituting. (Bug#38)
Diffstat (limited to 'src/fileio.c')
-rw-r--r-- | src/fileio.c | 53 |
1 files changed, 33 insertions, 20 deletions
diff --git a/src/fileio.c b/src/fileio.c index ae64be4ba03..4f3573b02d7 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -1629,11 +1629,14 @@ those `/' is discarded. */) unsigned char *target = NULL; int total = 0; int substituted = 0; + int multibyte; unsigned char *xnm; Lisp_Object handler; CHECK_STRING (filename); + multibyte = STRING_MULTIBYTE (filename); + /* If the file name has special constructs in it, call the corresponding file handler. */ handler = Ffind_file_name_handler (filename, Qsubstitute_in_file_name); @@ -1641,8 +1644,11 @@ those `/' is discarded. */) return call2 (handler, Qsubstitute_in_file_name, filename); nm = SDATA (filename); -#ifdef DOS_NT + /* Always work on a copy of the string, in case GC happens during + decode of environment variables, causing the original Lisp_String + data to be relocated. */ nm = strcpy (alloca (strlen (nm) + 1), nm); +#ifdef DOS_NT CORRECT_DIR_SEPS (nm); substituted = (strcmp (nm, SDATA (filename)) != 0); #endif @@ -1655,9 +1661,7 @@ those `/' is discarded. */) again. Important with filenames like "/home/foo//:/hello///there" which whould substitute to "/:/hello///there" rather than "/there". */ return Fsubstitute_in_file_name - (make_specified_string (p, -1, endp - p, - STRING_MULTIBYTE (filename))); - + (make_specified_string (p, -1, endp - p, multibyte)); /* See if any variables are substituted into the string and find the total length of their values in `total' */ @@ -1703,8 +1707,16 @@ those `/' is discarded. */) /* Get variable value */ o = (unsigned char *) egetenv (target); if (o) - { /* Eight-bit chars occupy upto 2 bytes in multibyte. */ - total += strlen (o) * (STRING_MULTIBYTE (filename) ? 2 : 1); + { + /* Don't try to guess a maximum length - UTF8 can use up to + four bytes per character. This code is unlikely to run + in a situation that requires performance, so decoding the + env variables twice should be acceptable. Note that + decoding may cause a garbage collect. */ + Lisp_Object orig, decoded; + orig = make_unibyte_string (o, strlen (o)); + decoded = DECODE_FILE (orig); + total += SBYTES (decoded); substituted = 1; } else if (*p == '}') @@ -1762,21 +1774,22 @@ those `/' is discarded. */) *x++ = '$'; strcpy (x, target); x+= strlen (target); } - else if (STRING_MULTIBYTE (filename)) - { - /* If the original string is multibyte, - convert what we substitute into multibyte. */ - while (*o) - { - int c = *o++; - c = unibyte_char_to_multibyte (c); - x += CHAR_STRING (c, x); - } - } else { - strcpy (x, o); - x += strlen (o); + Lisp_Object orig, decoded; + int orig_length, decoded_length; + orig_length = strlen (o); + orig = make_unibyte_string (o, orig_length); + decoded = DECODE_FILE (orig); + decoded_length = SBYTES (decoded); + strncpy (x, SDATA (decoded), decoded_length); + x += decoded_length; + + /* If environment variable needed decoding, return value + needs to be multibyte. */ + if (decoded_length != orig_length + || strncmp (SDATA (decoded), o, orig_length)) + multibyte = 1; } } @@ -1789,7 +1802,7 @@ those `/' is discarded. */) need to quote some $ to $$ first. */ xnm = p; - return make_specified_string (xnm, -1, x - xnm, STRING_MULTIBYTE (filename)); + return make_specified_string (xnm, -1, x - xnm, multibyte); badsubst: error ("Bad format environment-variable substitution"); |