xref: /openbsd/gnu/usr.sbin/mkhybrid/src/name.c (revision 30de0159)
1 /*
2  * File name.c - map full Unix file names to unique 8.3 names that
3  * would be valid on DOS.
4  *
5 
6    Written by Eric Youngdale (1993).
7 
8    Copyright 1993 Yggdrasil Computing, Incorporated
9 
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2, or (at your option)
13    any later version.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
23 
24 #include "config.h"
25 #include "mkisofs.h"
26 
27 #include <ctype.h>
28 
29 extern int allow_leading_dots;
30 
31 /*
32  * Function:	iso9660_file_length
33  *
34  * Purpose:	Map file name to 8.3 format, return length
35  *		of result.
36  *
37  * Arguments:	name	file name we need to map.
38  *		sresult	directory entry structure to contain mapped name.
39  *		dirflag	flag indicating whether this is a directory or not.
40  *
41  * Notes:	This procedure probably needs to be rationalized somehow.
42  *		New options to affect the behavior of this function
43  *		would also be nice to have.
44  */
FDECL3(iso9660_file_length,const char *,name,struct directory_entry *,sresult,int,dirflag)45 int FDECL3(iso9660_file_length,
46 	   const char*, name,
47 	   struct directory_entry *, sresult,
48 	   int, dirflag)
49 {
50   char		* c;
51   int		  chars_after_dot  = 0;
52   int		  chars_before_dot = 0;
53   int		  current_length   = 0;
54   int		  extra		   = 0;
55   int		  ignore	   = 0;
56   char		* last_dot;
57   const char	* pnt;
58   int		  priority	   = 32767;
59   char		* result;
60   int		  seen_dot	   = 0;
61   int		  seen_semic	   = 0;
62   int		  tildes	   = 0;
63 
64   result = sresult->isorec.name;
65 
66   /*
67    * For the '.' entry, generate the correct record, and return
68    * 1 for the length.
69    */
70   if(strcmp(name,".") == 0)
71     {
72       if(result)
73 	{
74 	  *result = 0;
75 	}
76       return 1;
77     }
78 
79   /*
80    * For the '..' entry, generate the correct record, and return
81    * 1 for the length.
82    */
83   if(strcmp(name,"..") == 0)
84     {
85       if(result)
86 	{
87 	  *result++ = 1;
88 	  *result++ = 0;
89 	}
90       return 1;
91     }
92 
93   /*
94    * Now scan the directory one character at a time, and figure out
95    * what to do.
96    */
97   pnt = name;
98 
99   /*
100    * Find the '.' that we intend to use for the extension.  Usually this
101    * is the last dot, but if we have . followed by nothing or a ~, we
102    * would consider this to be unsatisfactory, and we keep searching.
103    */
104   last_dot = strrchr (pnt,'.');
105   if(    (last_dot != NULL)
106       && (    (last_dot[1] == '~')
107  	   || (last_dot[1] == '\0')) )
108     {
109       c = last_dot;
110       *c = '\0';
111       last_dot = strrchr (pnt,'.');
112       *c = '.';
113     }
114 
115   while(*pnt)
116     {
117 #ifdef VMS
118       if( strcmp(pnt,".DIR;1") == 0 )
119 	{
120 	  break;
121 	}
122 #endif
123 
124       /*
125        * This character indicates a Unix style of backup file
126        * generated by some editors.  Lower the priority of
127        * the file.
128        */
129       if(*pnt == '#')
130 	{
131 	  priority = 1;
132 	  pnt++;
133 	  continue;
134 	}
135 
136       /*
137        * This character indicates a Unix style of backup file
138        * generated by some editors.  Lower the priority of
139        * the file.
140        */
141       if(*pnt == '~')
142 	{
143 	  priority = 1;
144 	  tildes++;
145 	  pnt++;
146 	  continue;
147 	}
148 
149       /*
150        * This might come up if we had some joker already try and put
151        * iso9660 version numbers into the file names.  This would be
152        * a silly thing to do on a Unix box, but we check for it
153        * anyways.  If we see this, then we don't have to add our
154        * own version number at the end.
155        * UNLESS the ';' is part of the filename and no version
156        * number is following. [VK]
157        */
158        if(*pnt == ';')
159 	 {
160 	   /* [VK] */
161 	   if (pnt[1] != '\0' && (pnt[1] < '0' || pnt[1] > '9'))
162 	     {
163 	       pnt++;
164 	       ignore++;
165 	       continue;
166 	     }
167 	 }
168 
169       /*
170        * If we have a name with multiple '.' characters, we ignore everything
171        * after we have gotten the extension.
172        */
173       if(ignore)
174 	{
175 	  pnt++;
176 	  continue;
177 	}
178 
179       /*
180        * Spin past any iso9660 version number we might have.
181        */
182       if(seen_semic)
183 	{
184 	  if(*pnt >= '0' && *pnt <= '9')
185 	    {
186 	      *result++ = *pnt;
187 	    }
188 	  extra++;
189 	  pnt++;
190 	  continue;
191 	}
192 
193 	/*
194 	 * If we have full names, the names we generate will not
195 	 * work on a DOS machine, since they are not guaranteed
196 	 * to be 8.3.  Nonetheless, in many cases this is a useful
197 	 * option.  We still only allow one '.' character in the
198 	 * name, however.
199 	 */
200       if(full_iso9660_filenames)
201 	{
202 	  /* Here we allow a more relaxed syntax. */
203 	  if(*pnt == '.')
204 	    {
205 	      if (seen_dot)
206 		{
207 		  ignore++;
208 		  continue;
209 		}
210 	      seen_dot++;
211 	    }
212 	  if(current_length < 30)
213 	    {
214 	      if(!isascii((unsigned char)*pnt))
215 		{
216 		  *result++ = '_';
217 		}
218 	      else
219 		{
220 		  *result++ = (islower((unsigned char)*pnt) ? toupper((unsigned char)*pnt) : *pnt);
221 		}
222 	    }
223 	}
224       else
225 	{
226 	  /*
227 	   * Dos style filenames.  We really restrict the
228 	   * names here.
229 	   */
230 	  /* It would be nice to have .tar.gz transform to .tgz,
231 	   * .ps.gz to .psz, ...
232 	   */
233 	  if(*pnt == '.')
234 	    {
235 	      if (!chars_before_dot && !allow_leading_dots)
236 		{
237 		  /* DOS can't read files with dot first */
238 		  chars_before_dot++;
239 		  if (result)
240 		    {
241 		      *result++ = '_'; /* Substitute underscore */
242 		    }
243 		}
244 	      else if( pnt != last_dot )
245 		{
246 		  /*
247 		   * If this isn't the dot that we use for the extension,
248 		   * then change the character into a '_' instead.
249 		   */
250 		  if(chars_before_dot < 8)
251 		    {
252 		      chars_before_dot++;
253 		      if(result)
254 			{
255 			  *result++ = '_';
256 			}
257 		    }
258 		}
259 	      else
260 		{
261 		  if (seen_dot)
262 		    {
263 		      ignore++; continue;
264 		    }
265 		  if(result)
266 		    {
267 		      *result++ = '.';
268 		    }
269 		  seen_dot++;
270 		}
271 	    }
272 	  else
273 	    {
274 	      if(    (seen_dot && (chars_after_dot < 3) && ++chars_after_dot)
275 		     || (!seen_dot && (chars_before_dot < 8) && ++chars_before_dot) )
276 		{
277 		  if(result)
278 		    {
279 		      switch (*pnt)
280 			{
281 			default:
282 			  if(!isascii((unsigned char)*pnt))
283 			    {
284 			      *result++ = '_';
285 			    }
286 			  else
287 			    {
288 			      *result++ = islower((unsigned char)*pnt) ? toupper((unsigned char)*pnt) : *pnt;
289 			    }
290 			  break;
291 
292 			/*
293 			 * Descriptions of DOS's 'Parse Filename'
294 			 * (function 29H) describes V1 and V2.0+
295 			 * separator and terminator characters.
296 			 * These characters in a DOS name make
297 			 * the file visible but un-manipulable
298 			 * (all useful operations error off.
299 			 */
300 			/* separators */
301 			case '+':
302 			case '=':
303 			case '%': /* not legal DOS filename */
304 			case ':':
305 			case ';': /* already handled */
306 			case '.': /* already handled */
307 			case ',': /* already handled */
308 			case '\t':
309 			case ' ':
310 			  /* V1 only separators */
311 			case '/':
312 			case '"':
313 			case '[':
314 			case ']':
315 			  /* terminators */
316 			case '>':
317 			case '<':
318 			case '|':
319 			  /* Hmm - what to do here?  Skip?
320 			   * Win95 looks like it substitutes '_'
321 			   */
322 			  *result++ = '_';
323 			  break;
324 			} /* switch (*pnt) */
325 		    } /* if (result) */
326 		} /* if (chars_{after,before}_dot) ... */
327 	    } /* else *pnt == '.' */
328 	} /* else DOS file names */
329       current_length++;
330       pnt++;
331     } /* while (*pnt) */
332 
333   /*
334    * OK, that wraps up the scan of the name.  Now tidy up a few other
335    * things.
336    */
337 
338   /*
339    * Look for emacs style of numbered backups, like foo.c.~3~.  If
340    * we see this, convert the version number into the priority
341    * number.  In case of name conflicts, this is what would end
342    * up being used as the 'extension'.
343    */
344   if(tildes == 2)
345     {
346       int prio1 = 0;
347       pnt = name;
348       while (*pnt && *pnt != '~')
349 	{
350 	  pnt++;
351 	}
352       if (*pnt)
353 	{
354 	  pnt++;
355 	}
356       while(*pnt && *pnt != '~')
357 	{
358 	  prio1 = 10*prio1 + *pnt - '0';
359 	  pnt++;
360 	}
361       priority = prio1;
362     }
363 
364   /*
365    * If this is not a directory, force a '.' in case we haven't
366    * seen one, and add a version number if we haven't seen one
367    * of those either.
368    */
369   if (!dirflag)
370     {
371       if (!seen_dot && !omit_period)
372 	{
373 	  if (result) *result++ = '.';
374 	  extra++;
375 	}
376       if(!omit_version_number && !seen_semic)
377 	{
378 	  if(result)
379 	    {
380 	      *result++ = ';';
381 	      *result++ = '1';
382 	    };
383 	  extra += 2;
384 	}
385     }
386 
387   if(result)
388     {
389       *result++ = 0;
390     }
391   sresult->priority = priority;
392 
393   return (chars_before_dot + chars_after_dot + seen_dot + extra);
394 }
395