dvitomp fix from Akira
[mplib] / src / texk / kpathsea / filename.c
1 /* Utility and Unix shadow routines for XEmacs on Windows NT.
2    Copyright (C) 1994, 1995 Free Software Foundation, Inc.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING.  If not, write to the Free
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20
21
22    Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */
23
24 /* Adapted for XEmacs by David Hobley <david@spook-le0.cia.com.au> */
25 /* Sync'ed with Emacs 19.34.6 by Marc Paquette <marcpa@cam.org> */
26 /* Adapted to fpTeX 0.4 by Fabrice Popineau <Fabrice.Popineau@supelec.fr> */
27
28 #ifdef __MINGW32__
29
30 #include <kpathsea/config.h>
31 #include <kpathsea/c-proto.h>
32 #include <kpathsea/win32lib.h>
33 #include <kpathsea/lib.h>
34
35 /* Normalize filename by converting all path separators to
36    the specified separator.  Also conditionally convert upper
37    case path name components to lower case.
38    Returns the index of the first meaningful char in the path
39    past any drive specifier of unc name specifier.
40    Remove any multiple path separators after a leading
41    drive specifier or double path separator.
42 */
43
44 int
45 normalize_filename (char *fp, char path_sep)
46 {
47   char *p;
48   int ret, i;
49
50   /* Always lower-case drive letters a-z, even if the filesystem
51      preserves case in filenames.
52      This is so filenames can be compared by string comparison
53      functions that are case-sensitive.  Even case-preserving filesystems
54      do not distinguish case in drive letters.  */
55   if (fp[1] == ':' && *fp >= 'A' && *fp <= 'Z') {
56     *fp += 'a' - 'A';
57   }
58
59   /* Remove unneeded double slashes */
60   ret = (IS_UNC_NAME(fp) ? 2 :
61          NAME_BEGINS_WITH_DEVICE(fp) ? 
62          (IS_DIR_SEP(*(fp+2)) ? 3 : 2) : IS_DIR_SEP(*fp) ? 1 : 0);
63   for (i = ret, p = fp+i; 
64        IS_DIR_SEP(*p); 
65        i++, p++);
66   if (i > ret) {
67     int len = strlen(fp+i);
68     /* remove unneeded slashes, for the sake of win95 */
69 #if 0
70     fprintf(stderr, "moving %s to %s\n", fp+ret, fp+i);
71 #endif
72     memcpy(fp+ret, fp+i, len);
73     *(char *)(fp+ret+len) = '\0';
74   }
75
76   /* conditionnally rewrite to same path_sep, slash preferably */
77   if (path_sep) {
78     for (p = fp; *p; p++)
79       if (IS_DIR_SEP(*p))
80         *p = path_sep;
81   }
82
83 #if 0
84     fprintf(stderr, "normalize_filename returned (%d) %s\n", ret, fp);
85 #endif  
86
87   return ret;
88 }
89
90
91 /* Destructively turn backslashes into slashes.  */
92 void
93 dostounix_filename (char *p)
94 {
95   normalize_filename (p, '/');
96 }
97
98 /* Destructively turn slashes into backslashes.  */
99 void
100 unixtodos_filename (char *p)
101 {
102   normalize_filename (p, '\\');
103 }
104
105 /* Remove all CR's that are followed by a LF.
106    (From msdos.c...probably should figure out a way to share it,
107    although this code isn't going to ever change.)  */
108 int
109 crlf_to_lf (int n, unsigned char *buf, unsigned *lf_count)
110 {
111   unsigned char *np = buf;
112   unsigned char *startp = buf;
113   unsigned char *endp = buf + n;
114
115   if (n == 0)
116     return n;
117   while (buf < endp - 1)
118     {
119       if (*buf == 0x0a)
120         (*lf_count)++;
121       if (*buf == 0x0d)
122         {
123           if (*(++buf) != 0x0a)
124             *np++ = 0x0d;
125         }
126       else
127         *np++ = *buf++;
128     }
129   if (buf < endp)
130     {
131       if (*buf == 0x0a)
132         (*lf_count)++;
133     *np++ = *buf++;
134     }
135   return np - startp;
136 }
137
138 /* Parse the root part of file name, if present.  Return length and
139     optionally store pointer to char after root.  */
140 int
141 _parse_root (char * name, char ** pPath)
142 {
143   char * start = name;
144
145   if (name == NULL)
146     return 0;
147
148   /* find the root name of the volume if given */
149   if (isalpha (name[0]) && name[1] == ':')
150     {
151       /* skip past drive specifier */
152       name += 2;
153       if (IS_DIR_SEP (name[0]))
154         name++;
155     }
156   else if (IS_DIR_SEP (name[0]) && IS_DIR_SEP (name[1]))
157     {
158       int slashes = 2;
159       name += 2;
160       do
161         {
162           if (IS_DIR_SEP (*name) && --slashes == 0)
163             break;
164           name++;
165         }
166       while ( *name );
167       if (IS_DIR_SEP (name[0]))
168         name++;
169     }
170
171   if (pPath)
172     *pPath = name;
173
174   return name - start;
175 }
176
177 /* Get long base name for name; name is assumed to be absolute.  */
178 static int
179 get_long_basename (char * name, char * buf, int size)
180 {
181   WIN32_FIND_DATA find_data;
182   HANDLE dir_handle;
183   int len = 0;
184 #ifdef PIGSFLY
185   char *p;
186
187   /* If the last component of NAME has a wildcard character, 
188      return it as the basename.  */
189   p = name + strlen (name);
190   while (*p != '\\' && *p != ':' && p > name) p--;
191   if (p > name) p++;
192   if (strchr (p, '*') || strchr (p, '?'))
193     {
194       if ((len = strlen (p)) < size)
195         memcpy (buf, p, len + 1);
196       else
197         len = 0;
198       return len;
199     }
200 #endif
201
202   dir_handle = FindFirstFile (name, &find_data);
203   if (dir_handle != INVALID_HANDLE_VALUE)
204     {
205       if ((len = strlen (find_data.cFileName)) < size)
206         memcpy (buf, find_data.cFileName, len + 1);
207       else
208         len = 0;
209       FindClose (dir_handle);
210     }
211   return len;
212 }
213
214 /* Get long name for file, if possible (assumed to be absolute).  */
215 BOOL
216 win32_get_long_filename (char * name, char * buf, int size)
217 {
218   char * o = buf;
219   char * p;
220   char * q;
221   char full[ MAX_PATH ];
222   int len;
223
224   len = strlen (name);
225   if (len >= MAX_PATH)
226     return FALSE;
227
228   /* Use local copy for destructive modification.  */
229   memcpy (full, name, len+1);
230   unixtodos_filename (full);
231
232   /* Copy root part verbatim.  */
233   len = _parse_root (full, &p);
234   memcpy (o, full, len);
235   o += len;
236   size -= len;
237
238   do
239     {
240       q = p;
241       p = strchr (q, '\\');
242       if (p) *p = '\0';
243       len = get_long_basename (full, o, size);
244       if (len > 0)
245         {
246           o += len;
247           size -= len;
248           if (p != NULL)
249             {
250               *p++ = '\\';
251               if (size < 2)
252                 return FALSE;
253               *o++ = '\\';
254               size--;
255               *o = '\0';
256             }
257         }
258       else
259         return FALSE;
260     }
261   while (p != NULL && *p);
262
263   return TRUE;
264 }
265
266 /* Map filename to a legal 8.3 name if necessary. */
267 const char *
268 map_win32_filename (const char * name, const char ** pPath)
269 {
270   static char shortname[MAX_PATH];
271   char * str = shortname;
272   char c;
273   const char * path;
274   const char * save_name = name;
275
276   if (is_fat_volume (name, &path)) /* truncate to 8.3 */
277     {
278       register int left = 8;    /* maximum number of chars in part */
279       register int extn = 0;    /* extension added? */
280       register int dots = 2;    /* maximum number of dots allowed */
281
282       while (name < path)
283         *str++ = *name++;       /* skip past UNC header */
284
285       while ((c = *name++))
286         {
287           switch ( c )
288             {
289             case '\\':
290             case '/':
291               *str++ = '\\';
292               extn = 0;         /* reset extension flags */
293               dots = 2;         /* max 2 dots */
294               left = 8;         /* max length 8 for main part */
295               break;
296             case ':':
297               *str++ = ':';
298               extn = 0;         /* reset extension flags */
299               dots = 2;         /* max 2 dots */
300               left = 8;         /* max length 8 for main part */
301               break;
302             case '.':
303               if ( dots )
304                 {
305                   /* Convert path components of the form .xxx to _xxx,
306                      but leave . and .. as they are.  This allows .emacs
307                      to be read as _emacs, for example.  */
308
309                   if (! *name ||
310                       *name == '.' ||
311                       IS_DIR_SEP (*name))
312                     {
313                       *str++ = '.';
314                       dots--;
315                     }
316                   else
317                     {
318                       *str++ = '_';
319                       left--;
320                       dots = 0;
321                     }
322                 }
323               else if ( !extn )
324                 {
325                   *str++ = '.';
326                   extn = 1;             /* we've got an extension */
327                   left = 3;             /* 3 chars in extension */
328                 }
329               else
330                 {
331                   /* any embedded dots after the first are converted to _ */
332                   *str++ = '_';
333                 }
334               break;
335             case '~':
336             case '#':                   /* don't lose these, they're important */
337               if ( ! left )
338                 str[-1] = c;            /* replace last character of part */
339               /* FALLTHRU */
340             default:
341               if ( left )
342                 {
343                   *str++ = tolower (c); /* map to lower case (looks nicer) */
344                   left--;
345                   dots = 0;             /* started a path component */
346                 }
347               break;
348             }
349         }
350       *str = '\0';
351     }
352   else
353     {
354       strcpy (shortname, name);
355       unixtodos_filename (shortname);
356     }
357
358   if (pPath)
359     *pPath = shortname + (path - save_name);
360
361   return shortname;
362 }
363
364 #endif