1 /* mingw32.c: bits and pieces for mingw32
2
3 Copyright 2009-2013 Taco Hoekwater <taco@luatex.org>.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /* Original sources lifted from the distribution of XEmacs for Windows NT,
20 Copyright 1994-1996 Free Software Foundation, later adapted to
21 fpTeX 0.4 (2000) by Fabrice Popineau <Fabrice.Popineau@supelec.fr>,
22 then simplified and re-adapted to TeXLive (2009) by Taco Hoekwater
23 <taco@luatex.org>.
24 */
25
26 #ifdef __MINGW32__
27
28 #include <kpathsea/config.h>
29 #include <kpathsea/c-pathch.h>
30 #include <kpathsea/c-proto.h>
31 #include <kpathsea/mingw32.h>
32 #include <kpathsea/lib.h>
33 #include <kpathsea/concatn.h>
34 #include <kpathsea/variable.h>
35 #include <kpathsea/c-stat.h>
36 #include <shlobj.h>
37 #include <errno.h>
38
39 /* Emulate getpwuid, getpwnam and others. */
40
41 typedef HWND (WINAPI *pGetDesktopWindow)(void);
42
43 typedef HRESULT (WINAPI * pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL);
44
45 extern int __cdecl _set_osfhnd (int fd, long h);
46 extern int __cdecl _free_osfhnd (int fd);
47
48 static char *get_home_directory (void);
49 static int _parse_root (char * name, char ** pPath);
50
51 void
init_user_info(void)52 init_user_info (void)
53 {
54 /* Ensure HOME and SHELL are defined. */
55 char *home = get_home_directory();
56 if (home) {
57 putenv(concat("HOME=", home));
58 }
59 else {
60 putenv ("HOME=c:/");
61 }
62 if (getenv ("SHELL") == NULL)
63 putenv ((GetVersion () & 0x80000000) ? "SHELL=command" : "SHELL=cmd");
64
65 {
66 /* Win2K problem : we need a specific TEMP directory with
67 full access rights so that any user building a format file
68 or a font file will build it with full access rights. The installer
69 takes care of defining TEXMFTEMP=$SELFAUTOPARENT/tmp in the environment.
70 If it is defined, then use it as the TEMP and TMP variables.
71 */
72 char *p;
73 if ((p = getenv("TEXMFTEMP")) != NULL) {
74 putenv(concat("TEMP=", p));
75 putenv(concat("TMP=", p));
76 }
77 }
78 }
79
80 /* Returns the home directory, in external format */
81 static char *
get_home_directory(void)82 get_home_directory (void)
83 {
84 char *found_home_directory = NULL;
85
86 if ((found_home_directory = getenv("HOME")) != NULL) {
87 char q[MAXPATHLEN];
88 /* In case it is %HOMEDRIVE%%HOMEPATH% */
89 if (ExpandEnvironmentStrings(found_home_directory, q, sizeof(q)) == 0) {
90 /* Error */
91 found_home_directory = NULL;
92 }
93 else {
94 found_home_directory = xstrdup(q);
95 goto done;
96 }
97 }
98
99 {
100 char *homedrive, *homepath;
101 if ((homedrive = getenv("HOMEDRIVE")) != NULL &&
102 (homepath = getenv("HOMEPATH")) != NULL) {
103 found_home_directory = concat(homedrive, homepath);
104 goto done;
105 }
106 }
107
108 /* This method is the prefered one because even if it requires a more recent shell32.dll,
109 it does not need to call SHMalloc()->Free() */
110 {
111 /* This will probably give the wrong value */
112 char q [MAXPATHLEN];
113 HINSTANCE h;
114 pSHGetSpecialFolderPathA p1;
115 pGetDesktopWindow p2;
116 HWND hwnd = NULL;
117
118 if ((h = LoadLibrary("user32.dll"))) {
119 if ((p2 = (pGetDesktopWindow)GetProcAddress(h, "GetDesktopWindow")))
120 hwnd = (*p2)();
121 FreeLibrary(h);
122 }
123
124 if (hwnd && (h = LoadLibrary("shell32.dll"))) {
125 if ((p1 = (pSHGetSpecialFolderPathA)GetProcAddress(h, "SHGetSpecialFolderPathA")))
126 if ((*p1)(hwnd, q, CSIDL_PERSONAL, TRUE)) {
127 found_home_directory = xstrdup(q);
128 }
129 FreeLibrary(h);
130 }
131 if (found_home_directory) goto done;
132 }
133
134 if (1) {
135 fprintf(stderr, "kpathsea has been unable to determine a good value for the user's $HOME\n"
136 " directory, and will be using the value:\n"
137 " %s\n"
138 " This is probably incorrect.\n",
139 found_home_directory
140 );
141 }
142 done:
143 return found_home_directory;
144 }
145
146
147 /* Consider cached volume information to be stale if older than 10s,
148 at least for non-local drives. Info for fixed drives is never stale. */
149 #define DRIVE_INDEX( c ) ( (c) <= 'Z' ? (c) - 'A' : (c) - 'a' )
150 #define VOLINFO_STILL_VALID( root_dir, info ) \
151 ( ( isalpha (root_dir[0]) ) \
152 || GetTickCount () - info->timestamp < 10000 )
153
154
155 /* Normalize filename by converting all path separators to
156 the specified separator. Also conditionally convert upper
157 case path name components to lower case.
158 Returns the index of the first meaningful char in the path
159 past any drive specifier of unc name specifier.
160 Remove any multiple path separators after a leading
161 drive specifier or double path separator.
162 */
163
164 static int
normalize_filename(char * fp,char path_sep)165 normalize_filename (char *fp, char path_sep)
166 {
167 char *p;
168 int ret, i;
169
170 /* Always lower-case drive letters a-z, even if the filesystem
171 preserves case in filenames.
172 This is so filenames can be compared by string comparison
173 functions that are case-sensitive. Even case-preserving filesystems
174 do not distinguish case in drive letters. */
175 if (fp[1] == ':' && *fp >= 'A' && *fp <= 'Z') {
176 *fp += 'a' - 'A';
177 }
178
179 /* Remove unneeded double slashes */
180 ret = (IS_UNC_NAME(fp) ? 2 :
181 NAME_BEGINS_WITH_DEVICE(fp) ?
182 (IS_DIR_SEP(*(fp+2)) ? 3 : 2) : IS_DIR_SEP(*fp) ? 1 : 0);
183 for (i = ret, p = fp+i;
184 IS_DIR_SEP(*p);
185 i++, p++);
186 if (i > ret) {
187 int len = strlen(fp+i);
188 /* remove unneeded slashes, for the sake of win95 */
189 #if 0
190 fprintf(stderr, "moving %s to %s\n", fp+ret, fp+i);
191 #endif
192 memmove (fp+ret, fp+i, len+1);
193 }
194
195 /* conditionnally rewrite to same path_sep, slash preferably */
196 if (path_sep) {
197 for (p = fp; *p; p++)
198 if (IS_DIR_SEP(*p))
199 *p = path_sep;
200 }
201
202 #if 0
203 fprintf(stderr, "normalize_filename returned (%d) %s\n", ret, fp);
204 #endif
205
206 return ret;
207 }
208
209
210 /* Destructively turn backslashes into slashes. */
211 #if 0 /* unused */
212 static void
213 dostounix_filename (char *p)
214 {
215 normalize_filename (p, '/');
216 }
217 #endif
218
219 /* Destructively turn slashes into backslashes. */
220 static void
unixtodos_filename(char * p)221 unixtodos_filename (char *p)
222 {
223 normalize_filename (p, '\\');
224 }
225
226 /* Remove all CR's that are followed by a LF.
227 (From msdos.c...probably should figure out a way to share it,
228 although this code isn't going to ever change.) */
229 #if 0 /* unused */
230 static int
231 crlf_to_lf (int n, unsigned char *buf, unsigned *lf_count)
232 {
233 unsigned char *np = buf;
234 unsigned char *startp = buf;
235 unsigned char *endp = buf + n;
236
237 if (n == 0)
238 return n;
239 while (buf < endp - 1)
240 {
241 if (*buf == 0x0a)
242 (*lf_count)++;
243 if (*buf == 0x0d)
244 {
245 if (*(++buf) != 0x0a)
246 *np++ = 0x0d;
247 }
248 else
249 *np++ = *buf++;
250 }
251 if (buf < endp)
252 {
253 if (*buf == 0x0a)
254 (*lf_count)++;
255 *np++ = *buf++;
256 }
257 return np - startp;
258 }
259 #endif
260
261 /* Parse the root part of file name, if present. Return length and
262 optionally store pointer to char after root. */
263 static int
_parse_root(char * name,char ** pPath)264 _parse_root (char * name, char ** pPath)
265 {
266 char * start = name;
267
268 if (name == NULL)
269 return 0;
270
271 /* find the root name of the volume if given */
272 if (isalpha (name[0]) && name[1] == ':')
273 {
274 /* skip past drive specifier */
275 name += 2;
276 if (IS_DIR_SEP (name[0]))
277 name++;
278 }
279 else if (IS_DIR_SEP (name[0]) && IS_DIR_SEP (name[1]))
280 {
281 int slashes = 2;
282 name += 2;
283 do
284 {
285 if (IS_DIR_SEP (*name) && --slashes == 0)
286 break;
287 name++;
288 }
289 while ( *name );
290 if (IS_DIR_SEP (name[0]))
291 name++;
292 }
293
294 if (pPath)
295 *pPath = name;
296
297 return name - start;
298 }
299
300 /* Get long base name for name; name is assumed to be absolute. */
301 static int
get_long_basename(char * name,char * buf,int size)302 get_long_basename (char * name, char * buf, int size)
303 {
304 WIN32_FIND_DATA find_data;
305 HANDLE dir_handle;
306 int len = 0;
307 #ifdef PIGSFLY
308 char *p;
309
310 /* If the last component of NAME has a wildcard character,
311 return it as the basename. */
312 p = name + strlen (name);
313 while (*p != '\\' && *p != ':' && p > name) p--;
314 if (p > name) p++;
315 if (strchr (p, '*') || strchr (p, '?'))
316 {
317 if ((len = strlen (p)) < size)
318 memcpy (buf, p, len + 1);
319 else
320 len = 0;
321 return len;
322 }
323 #endif
324
325 dir_handle = FindFirstFile (name, &find_data);
326 if (dir_handle != INVALID_HANDLE_VALUE)
327 {
328 if ((len = strlen (find_data.cFileName)) < size)
329 memcpy (buf, find_data.cFileName, len + 1);
330 else
331 len = 0;
332 FindClose (dir_handle);
333 }
334 return len;
335 }
336
337 /* Get long name for file, if possible (assumed to be absolute). */
338 BOOL
win32_get_long_filename(char * name,char * buf,int size)339 win32_get_long_filename (char * name, char * buf, int size)
340 {
341 char * o = buf;
342 char * p;
343 char * q;
344 char full[ MAX_PATH ];
345 int len;
346
347 len = strlen (name);
348 if (len >= MAX_PATH)
349 return FALSE;
350
351 /* Use local copy for destructive modification. */
352 memcpy (full, name, len+1);
353 unixtodos_filename (full);
354
355 /* Copy root part verbatim. */
356 len = _parse_root (full, &p);
357 memcpy (o, full, len);
358 o += len;
359 size -= len;
360
361 do
362 {
363 q = p;
364 p = strchr (q, '\\');
365 if (p) *p = '\0';
366 len = get_long_basename (full, o, size);
367 if (len > 0)
368 {
369 o += len;
370 size -= len;
371 if (p != NULL)
372 {
373 *p++ = '\\';
374 if (size < 2)
375 return FALSE;
376 *o++ = '\\';
377 size--;
378 *o = '\0';
379 }
380 }
381 else
382 return FALSE;
383 }
384 while (p != NULL && *p);
385
386 return TRUE;
387 }
388
389 /* special TeXLive Ghostscript */
390
is_dir(char * buff)391 static int is_dir (char *buff)
392 {
393 struct stat stats;
394
395 return stat (buff, &stats) == 0 && S_ISDIR (stats.st_mode);
396 }
397
398 /*
399 TeXlive uses its own gs in
400 $SELFAUTOPARENT/tlpkg/tlgs
401 */
texlive_gs_init(void)402 void texlive_gs_init(void)
403 {
404 char *nptr, *path;
405 char tlgsbindir[512];
406 char tlgslibdir[512];
407 nptr = kpse_var_value("TEXLIVE_WINDOWS_EXTERNAL_GS");
408 if (nptr == NULL || !strcmp(nptr, "0") || !strcmp(nptr, "n") || !strcmp(nptr, "f")) {
409 if (nptr)
410 free (nptr);
411 nptr = kpse_var_value("SELFAUTOPARENT");
412 if (nptr) {
413 strcpy(tlgsbindir, nptr);
414 strcat(tlgsbindir,"/tlpkg/tlgs");
415 if(is_dir(tlgsbindir)) {
416 strcpy(tlgslibdir, tlgsbindir);
417 strcat(tlgslibdir, "/lib;");
418 strcat(tlgslibdir, tlgsbindir);
419 strcat(tlgslibdir, "/fonts");
420 strcat(tlgsbindir, "/bin;");
421 free(nptr);
422 for(nptr = tlgsbindir; *nptr; nptr++) {
423 if(*nptr == '/') *nptr = '\\';
424 }
425 nptr = getenv("PATH");
426 path = (char *)malloc(strlen(nptr) + strlen(tlgsbindir) + 6);
427 strcpy(path, tlgsbindir);
428 strcat(path, nptr);
429 xputenv("PATH", path);
430 xputenv("GS_LIB", tlgslibdir);
431 }
432 }
433 } else {
434 free (nptr);
435 }
436 }
437
438 #endif /* __MINGW32__ */
439