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