Git fork
at reftables-rust 202 lines 5.3 kB view raw
1/* 2 * Converts filenames from decomposed unicode into precomposed unicode. 3 * Used on MacOS X. 4 */ 5 6#define PRECOMPOSE_UNICODE_C 7#define USE_THE_REPOSITORY_VARIABLE 8 9#include "git-compat-util.h" 10#include "config.h" 11#include "environment.h" 12#include "gettext.h" 13#include "path.h" 14#include "strbuf.h" 15#include "utf8.h" 16#include "precompose_utf8.h" 17 18typedef char *iconv_ibp; 19static const char *repo_encoding = "UTF-8"; 20static const char *path_encoding = "UTF-8-MAC"; 21 22static size_t has_non_ascii(const char *s, size_t maxlen, size_t *strlen_c) 23{ 24 const uint8_t *ptr = (const uint8_t *)s; 25 size_t strlen_chars = 0; 26 size_t ret = 0; 27 28 if (!ptr || !*ptr) 29 return 0; 30 31 while (*ptr && maxlen) { 32 if (*ptr & 0x80) 33 ret++; 34 strlen_chars++; 35 ptr++; 36 maxlen--; 37 } 38 if (strlen_c) 39 *strlen_c = strlen_chars; 40 41 return ret; 42} 43 44 45void probe_utf8_pathname_composition(void) 46{ 47 struct strbuf path = STRBUF_INIT; 48 static const char *auml_nfc = "\xc3\xa4"; 49 static const char *auml_nfd = "\x61\xcc\x88"; 50 int output_fd; 51 if (precomposed_unicode != -1) 52 return; /* We found it defined in the global config, respect it */ 53 repo_git_path_replace(the_repository, &path, "%s", auml_nfc); 54 output_fd = open(path.buf, O_CREAT|O_EXCL|O_RDWR, 0600); 55 if (output_fd >= 0) { 56 close(output_fd); 57 repo_git_path_replace(the_repository, &path, "%s", auml_nfd); 58 precomposed_unicode = access(path.buf, R_OK) ? 0 : 1; 59 repo_config_set(the_repository, "core.precomposeunicode", 60 precomposed_unicode ? "true" : "false"); 61 repo_git_path_replace(the_repository, &path, "%s", auml_nfc); 62 if (unlink(path.buf)) 63 die_errno(_("failed to unlink '%s'"), path.buf); 64 } 65 strbuf_release(&path); 66} 67 68const char *precompose_string_if_needed(const char *in) 69{ 70 size_t inlen; 71 size_t outlen; 72 if (!in) 73 return NULL; 74 if (has_non_ascii(in, (size_t)-1, &inlen)) { 75 iconv_t ic_prec; 76 char *out; 77 if (precomposed_unicode < 0) 78 repo_config_get_bool(the_repository, "core.precomposeunicode", &precomposed_unicode); 79 if (precomposed_unicode != 1) 80 return in; 81 ic_prec = iconv_open(repo_encoding, path_encoding); 82 if (ic_prec == (iconv_t) -1) 83 return in; 84 85 out = reencode_string_iconv(in, inlen, ic_prec, 0, &outlen); 86 if (out) { 87 if (outlen == inlen && !memcmp(in, out, outlen)) 88 free(out); /* no need to return indentical */ 89 else 90 in = out; 91 } 92 iconv_close(ic_prec); 93 94 } 95 return in; 96} 97 98const char *precompose_argv_prefix(int argc, const char **argv, const char *prefix) 99{ 100 int i = 0; 101 102 while (i < argc) { 103 argv[i] = precompose_string_if_needed(argv[i]); 104 i++; 105 } 106 return precompose_string_if_needed(prefix); 107} 108 109 110PREC_DIR *precompose_utf8_opendir(const char *dirname) 111{ 112 PREC_DIR *prec_dir = xmalloc(sizeof(PREC_DIR)); 113 prec_dir->dirent_nfc = xmalloc(sizeof(dirent_prec_psx)); 114 prec_dir->dirent_nfc->max_name_len = sizeof(prec_dir->dirent_nfc->d_name); 115 116 prec_dir->dirp = opendir(dirname); 117 if (!prec_dir->dirp) { 118 free(prec_dir->dirent_nfc); 119 free(prec_dir); 120 return NULL; 121 } else { 122 int ret_errno = errno; 123 prec_dir->ic_precompose = iconv_open(repo_encoding, path_encoding); 124 /* if iconv_open() fails, die() in readdir() if needed */ 125 errno = ret_errno; 126 } 127 128 return prec_dir; 129} 130 131struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir) 132{ 133 struct dirent *res; 134 res = readdir(prec_dir->dirp); 135 if (res) { 136 size_t namelenz = strlen(res->d_name) + 1; /* \0 */ 137 size_t new_maxlen = namelenz; 138 139 int ret_errno = errno; 140 141 if (new_maxlen > prec_dir->dirent_nfc->max_name_len) { 142 size_t new_len = sizeof(dirent_prec_psx) + new_maxlen - 143 sizeof(prec_dir->dirent_nfc->d_name); 144 145 prec_dir->dirent_nfc = xrealloc(prec_dir->dirent_nfc, new_len); 146 prec_dir->dirent_nfc->max_name_len = new_maxlen; 147 } 148 149 prec_dir->dirent_nfc->d_ino = res->d_ino; 150 prec_dir->dirent_nfc->d_type = res->d_type; 151 152 if ((precomposed_unicode == 1) && has_non_ascii(res->d_name, (size_t)-1, NULL)) { 153 if (prec_dir->ic_precompose == (iconv_t)-1) { 154 die("iconv_open(%s,%s) failed, but needed:\n" 155 " precomposed unicode is not supported.\n" 156 " If you want to use decomposed unicode, run\n" 157 " \"git config core.precomposeunicode false\"\n", 158 repo_encoding, path_encoding); 159 } else { 160 iconv_ibp cp = (iconv_ibp)res->d_name; 161 size_t inleft = namelenz; 162 char *outpos = &prec_dir->dirent_nfc->d_name[0]; 163 size_t outsz = prec_dir->dirent_nfc->max_name_len; 164 errno = 0; 165 iconv(prec_dir->ic_precompose, &cp, &inleft, &outpos, &outsz); 166 if (errno || inleft) { 167 /* 168 * iconv() failed and errno could be E2BIG, EILSEQ, EINVAL, EBADF 169 * MacOS X avoids illegal byte sequences. 170 * If they occur on a mounted drive (e.g. NFS) it is not worth to 171 * die() for that, but rather let the user see the original name 172 */ 173 namelenz = 0; /* trigger strlcpy */ 174 } 175 } 176 } else 177 namelenz = 0; 178 179 if (!namelenz) 180 strlcpy(prec_dir->dirent_nfc->d_name, res->d_name, 181 prec_dir->dirent_nfc->max_name_len); 182 183 errno = ret_errno; 184 return prec_dir->dirent_nfc; 185 } 186 return NULL; 187} 188 189 190int precompose_utf8_closedir(PREC_DIR *prec_dir) 191{ 192 int ret_value; 193 int ret_errno; 194 ret_value = closedir(prec_dir->dirp); 195 ret_errno = errno; 196 if (prec_dir->ic_precompose != (iconv_t)-1) 197 iconv_close(prec_dir->ic_precompose); 198 free(prec_dir->dirent_nfc); 199 free(prec_dir); 200 errno = ret_errno; 201 return ret_value; 202}