1 /* @(#)name.c	1.38 10/12/19 joerg */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)name.c	1.38 10/12/19 joerg";
6 
7 #endif
8 /*
9  * File name.c - map full Unix file names to unique 8.3 names that
10  * would be valid on DOS.
11  *
12  *
13  * Written by Eric Youngdale (1993).
14  * Almost totally rewritten by J. Schilling (2000).
15  *
16  * Copyright 1993 Yggdrasil Computing, Incorporated
17  * Copyright (c) 1999,2000-2010 J. Schilling
18  *
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 2, or (at your option)
22  * any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program; if not, write to the Free Software
31  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32  */
33 
34 #include "mkisofs.h"
35 #include <schily/standard.h>
36 #include <schily/schily.h>
37 #include <schily/ctype.h>
38 
39 void	iso9660_check		__PR((struct iso_directory_record *idr,	struct directory_entry *ndr));
40 int	iso9660_file_length	__PR((const char *name,
41 					struct directory_entry *sresult,
42 					int dirflag));
43 
44 void
iso9660_check(idr,ndr)45 iso9660_check(idr, ndr)
46 	struct iso_directory_record *idr;
47 	struct directory_entry *ndr;
48 {
49 	int	nlen;
50 	char	schar;
51 	char	*p;
52 	char	*np;
53 
54 	nlen = idr->name_len[0];
55 	schar = idr->name[nlen];
56 
57 	if (nlen == 1 && (idr->name[0] == '\0' || idr->name[0] == '\001'))
58 		return;
59 
60 	idr->name[nlen] = '\0';		/* Make it null terminated */
61 	if ((p = strrchr(idr->name, ';')) != NULL) {
62 		*p = '\0';		/* Strip off old version # */
63 	}
64 	iso9660_file_length(idr->name, ndr,
65 				(idr->flags[0] & ISO_DIRECTORY) != 0);
66 
67 	if ((np = strrchr(ndr->isorec.name, ';')) != NULL) {
68 		*np = '\0';		/* Strip off new version # */
69 	}
70 	if (strcmp(idr->name, ndr->isorec.name) != 0) {
71 		if (p)
72 			*p = ';';	/* Restore old version # */
73 		if (np)
74 			*np = ';';	/* Restore new version # */
75 		errmsgno(EX_BAD,
76 			_("Old session has illegal name '%.*s' length %d\n"),
77 			idr->name_len[0],
78 			idr->name,
79 			idr->name_len[0]);
80 		errmsgno(EX_BAD,
81 			_("New session will use    name '%s'\n"),
82 			ndr->isorec.name);
83 	}
84 	if (p)
85 		*p = ';';		/* Restore old version # */
86 	if (np)
87 		*np = ';';		/* Restore new version # */
88 	idr->name[nlen] = schar;	/* Restore old iso record*/
89 }
90 
91 /*
92  * Function:	iso9660_file_length
93  *
94  * Purpose:	Map file name to 8.3 format, return length
95  *		of result.
96  *
97  * Arguments:	name	file name we need to map.
98  *		sresult	directory entry structure to contain mapped name.
99  *		dirflag	flag indicating whether this is a directory or not.
100  *
101  * Note:	name being const * is a bug introduced by Eric but hard to
102  *		fix without going through the whole source.
103  */
104 int
iso9660_file_length(name,sresult,dirflag)105 iso9660_file_length(name, sresult, dirflag)
106 	const char	*name;			/* Not really const !!! */
107 	struct directory_entry *sresult;
108 	int		dirflag;
109 {
110 	char		c;
111 	char		*cp;
112 	int		before_dot = 8;
113 	int		after_dot = 3;
114 	int		chars_after_dot = 0;
115 	int		chars_before_dot = 0;
116 	int		current_length = 0;
117 	int		extra = 0;
118 	int		ignore = 0;
119 	char		*last_dot;
120 	const char	*pnt;
121 	int		priority = 32767;
122 	char		*result;
123 	int		ochars_after_dot;
124 	int		ochars_before_dot;
125 	int		seen_dot = 0;
126 	int		seen_semic = 0;
127 #ifdef		Eric_code_does_not_work
128 	int		tildes = 0;
129 #endif
130 
131 	result = sresult->isorec.name;
132 
133 	if (sresult->priority)
134 		priority = sresult->priority;
135 
136 	/*
137 	 * For the '.' entry, generate the correct record, and return 1 for
138 	 * the length.
139 	 */
140 	if (strcmp(name, ".") == 0) {
141 		*result = 0;
142 		return (1);
143 	}
144 	/*
145 	 * For the '..' entry, generate the correct record, and return 1
146 	 * for the length.
147 	 */
148 	if (strcmp(name, "..") == 0) {
149 		*result++ = 1;
150 		*result++ = 0;
151 		return (1);
152 	}
153 	/*
154 	 * Now scan the directory one character at a time, and figure out
155 	 * what to do.
156 	 */
157 	pnt = name;
158 
159 	/*
160 	 * Find the '.' that we intend to use for the extension.
161 	 * Usually this is the last dot, but if we have . followed by nothing
162 	 * or a ~, we would consider this to be unsatisfactory, and we keep
163 	 * searching.
164 	 */
165 	last_dot = strrchr(pnt, '.');
166 	if ((last_dot != NULL) &&
167 	    ((last_dot[1] == '~') || (last_dot[1] == '\0'))) {
168 		cp = last_dot;
169 		*cp = '\0';
170 		last_dot = strrchr(pnt, '.');
171 		*cp = '.';
172 		/*
173 		 * If we found no better '.' back up to the last match.
174 		 */
175 		if (last_dot == NULL)
176 			last_dot = cp;
177 	}
178 
179 	if (last_dot != NULL) {
180 		ochars_after_dot = strlen(last_dot);	/* dot counts */
181 		ochars_before_dot = last_dot - pnt;
182 	} else {
183 		ochars_before_dot = 128;
184 		ochars_after_dot = 0;
185 	}
186 	/*
187 	 * If we have full names, the names we generate will not work
188 	 * on a DOS machine, since they are not guaranteed to be 8.3.
189 	 * Nonetheless, in many cases this is a useful option.  We
190 	 * still only allow one '.' character in the name, however.
191 	 */
192 	if (full_iso9660_filenames || iso9660_level > 1) {
193 		before_dot = iso9660_namelen;
194 		after_dot = before_dot - 1;
195 
196 		if (!dirflag) {
197 			if (ochars_after_dot > ((iso9660_namelen/2)+1)) {
198 				/*
199 				 * The minimum number of characters before
200 				 * the dot is 3 to allow renaming.
201 				 * Let us allow to have 15 characters after
202 				 * dot to give more rational filenames.
203 				 */
204 				before_dot = iso9660_namelen/2;
205 				after_dot = ochars_after_dot;
206 			} else {
207 				before_dot -= ochars_after_dot; /* dot counts */
208 				after_dot = ochars_after_dot;
209 			}
210 		}
211 	}
212 
213 	while (*pnt) {
214 #ifdef VMS
215 		if (strcmp(pnt, ".DIR;1") == 0) {
216 			break;
217 		}
218 #endif
219 
220 #ifdef		Eric_code_does_not_work
221 		/*
222 		 * XXX If we make this code active we get corrupted direcrory
223 		 * XXX trees with infinite loops.
224 		 */
225 		/*
226 		 * This character indicates a Unix style of backup file
227 		 * generated by some editors.  Lower the priority of the file.
228 		 */
229 		if (iso_translate && *pnt == '#') {
230 			priority = 1;
231 			pnt++;
232 			continue;
233 		}
234 		/*
235 		 * This character indicates a Unix style of backup file
236 		 * generated by some editors.  Lower the priority of the file.
237 		 */
238 		if (iso_translate && *pnt == '~') {
239 			priority = 1;
240 			tildes++;
241 			pnt++;
242 			continue;
243 		}
244 #endif
245 		/*
246 		 * This might come up if we had some joker already try and put
247 		 * iso9660 version numbers into the file names.  This would be
248 		 * a silly thing to do on a Unix box, but we check for it
249 		 * anyways.  If we see this, then we don't have to add our own
250 		 * version number at the end. UNLESS the ';' is part of the
251 		 * filename and no valid version number is following.
252 		 */
253 		if (use_fileversion && *pnt == ';' && seen_dot) {
254 			/*
255 			 * Check if a valid version number follows.
256 			 * The maximum valid version number is 32767.
257 			 */
258 			for (c = 1, cp = (char *)&pnt[1]; c < 6 && *cp; c++, cp++) {
259 				if (*cp < '0' || *cp > '9')
260 					break;
261 			}
262 			if (c <= 6 && *cp == '\0' && atoi(&pnt[1]) <= 32767)
263 				seen_semic++;
264 		}
265 		/*
266 		 * If we have a name with multiple '.' characters, we ignore
267 		 * everything after we have gotten the extension.
268 		 */
269 		if (ignore) {
270 			pnt++;
271 			continue;
272 		}
273 		if (current_length >= iso9660_namelen) {
274 #ifdef	nono
275 			/*
276 			 * Does not work as we may truncate before the dot.
277 			 */
278 			error(_("Truncating '%s' to '%.*s'.\n"),
279 				name,
280 				current_length, sresult->isorec.name);
281 			ignore++;
282 #endif
283 			pnt++;
284 			continue;
285 		}
286 		/* Spin past any iso9660 version number we might have. */
287 		if (seen_semic) {
288 			if (seen_semic == 1) {
289 				seen_semic++;
290 				*result++ = ';';
291 			}
292 			if (*pnt >= '0' && *pnt <= '9') {
293 				*result++ = *pnt;
294 			}
295 			extra++;
296 			pnt++;
297 			continue;
298 		}
299 
300 		if (*pnt == '.') {
301 			if (!allow_multidot) {
302 				if (strcmp(pnt, ".tar.gz") == 0)
303 					pnt = last_dot = ".tgz";
304 				if (strcmp(pnt, ".ps.gz") == 0)
305 					pnt = last_dot = ".psz";
306 			}
307 
308 			if (!chars_before_dot && !allow_leading_dots) {
309 				/*
310 				 * DOS can't read files with dot first
311 				 */
312 				chars_before_dot++;
313 				*result++ = '_'; /* Substitute underscore */
314 
315 			} else if (pnt == last_dot) {
316 				if (seen_dot) {
317 					ignore++;
318 					continue;
319 				}
320 				*result++ = '.';
321 				seen_dot++;
322 			} else if (allow_multidot) {
323 				if (chars_before_dot < before_dot) {
324 					chars_before_dot++;
325 					*result++ = '.';
326 				}
327 			} else {
328 				/*
329 				 * If this isn't the dot that we use
330 				 * for the extension, then change the
331 				 * character into a '_' instead.
332 				 */
333 				if (chars_before_dot < before_dot) {
334 					chars_before_dot++;
335 					*result++ = '_';
336 				}
337 			}
338 		} else {
339 			if ((seen_dot && (chars_after_dot < after_dot) &&
340 						++chars_after_dot) ||
341 			    (!seen_dot && (chars_before_dot < before_dot) &&
342 			    ++chars_before_dot)) {
343 
344 				c = *pnt;
345 				if (c & 0x80) {
346 					/*
347 					 * We allow 8 bit chars if -iso-level
348 					 * is at least 4
349 					 *
350 					 * XXX We should check if the output
351 					 * XXX character set is a 7 Bit ASCI
352 					 * extension.
353 					 */
354 					if (iso9660_level >= 4) {
355 						size_t	flen = 1;
356 						size_t	tlen = 1;
357 
358 						/*
359 						 * XXX This currently only works for
360 						 * XXX non iconv() based locales.
361 						 */
362 						if (in_nls->sic_cd2uni == NULL) {
363 							conv_charset(
364 							    (Uchar *)&c, &tlen,
365 							    (Uchar *)&c, &flen,
366 							    in_nls, out_nls);
367 						}
368 					} else {
369 						c = '_';
370 					}
371 				} else if (!allow_lowercase) {
372 					c = islower((unsigned char)c) ?
373 						toupper((unsigned char)c) : c;
374 				}
375 				if (relaxed_filenames) {
376 					/*
377 					 * Here we allow a more relaxed syntax.
378 					 */
379 					if (c == '/')
380 						c = '_';
381 					*result++ = c;
382 				} else switch (c) {
383 					/*
384 					 * Dos style filenames.
385 					 * We really restrict the names here.
386 					 */
387 
388 				default:
389 					*result++ = c;
390 					break;
391 
392 				/*
393 				 * Descriptions of DOS's 'Parse Filename'
394 				 * (function 29H) describes V1 and V2.0+
395 				 * separator and terminator characters. These
396 				 * characters in a DOS name make the file
397 				 * visible but un-manipulable (all useful
398 				 * operations error off.
399 				 */
400 				/* separators */
401 				case '+':
402 				case '=':
403 				case '%': /* not legal DOS */
404 						/* filename */
405 				case ':':
406 				case ';': /* already handled */
407 				case '.': /* already handled */
408 				case ',': /* already handled */
409 				case '\t':
410 				case ' ':
411 				/* V1 only separators */
412 				case '/':
413 				case '"':
414 				case '[':
415 				case ']':
416 				/* terminators */
417 				case '>':
418 				case '<':
419 				case '|':
420 				/*
421 				 * Other characters that are not valid ISO-9660
422 				 * characters.
423 				 */
424 				case '!':
425 /*				case '#':*/
426 				case '$':
427 				case '&':
428 				case '\'':
429 				case '(':
430 				case ')':
431 				case '*':
432 /*				case '-':*/
433 				case '?':
434 				case '@':
435 				case '\\':
436 				case '^':
437 				case '`':
438 				case '{':
439 				case '}':
440 /*				case '~':*/
441 				/*
442 				 * All characters below 32 (space) are not
443 				 * allowed too.
444 				 */
445 				case 1: case 2: case 3: case 4:
446 				case 5: case 6: case 7: case 8:
447 				/* case 9: */
448 				case 10: case 11: case 12:
449 				case 13: case 14: case 15:
450 				case 16: case 17: case 18:
451 				case 19: case 20: case 21:
452 				case 22: case 23: case 24:
453 				case 25: case 26: case 27:
454 				case 28: case 29: case 30:
455 				case 31:
456 
457 					/*
458 					 * Hmm - what to do here? Skip? Win95
459 					 * looks like it substitutes '_'
460 					 */
461 					*result++ = '_';
462 					break;
463 
464 				case '#':
465 				case '-':
466 				case '~':
467 					/*
468 					 * Check if we should allow these
469 					 * illegal characters used by
470 					 * Microsoft.
471 					 */
472 					if (iso_translate)
473 						*result++ = '_';
474 					else
475 						*result++ = c;
476 					break;
477 				}	/* switch (*pnt) */
478 			} else {	/* if (chars_{after,before}_dot) ... */
479 				pnt++;
480 				continue;
481 			}
482 		}	/* else *pnt == '.' */
483 		current_length++;
484 		pnt++;
485 	}	/* while (*pnt) */
486 
487 	/*
488 	 * OK, that wraps up the scan of the name.  Now tidy up a few other
489 	 * things.
490 	 * Look for emacs style of numbered backups, like foo.c.~3~.  If we
491 	 * see this, convert the version number into the priority number.
492 	 * In case of name conflicts, this is what would end up being used as
493 	 * the 'extension'.
494 	 */
495 #ifdef		Eric_code_does_not_work
496 	if (tildes == 2) {
497 		int	prio1 = 0;
498 
499 		pnt = name;
500 		while (*pnt && *pnt != '~') {
501 			pnt++;
502 		}
503 		if (*pnt) {
504 			pnt++;
505 		}
506 		while (*pnt && *pnt != '~') {
507 			prio1 = 10 * prio1 + *pnt - '0';
508 			pnt++;
509 		}
510 		priority = prio1;
511 	}
512 #endif
513 	/*
514 	 * If this is not a directory, force a '.' in case we haven't seen one,
515 	 * and add a version number if we haven't seen one of those either.
516 	 */
517 	if (!dirflag) {
518 		if (!seen_dot && !omit_period) {
519 			if (chars_before_dot >= (iso9660_namelen-1)) {
520 				chars_before_dot--;
521 				result--;
522 			}
523 			*result++ = '.';
524 			extra++;
525 		}
526 		if (!omit_version_number && !seen_semic) {
527 			*result++ = ';';
528 			*result++ = '1';
529 			extra += 2;
530 		}
531 	}
532 	*result++ = 0;
533 	sresult->priority = priority;
534 
535 /*#define	DEBBUG*/
536 #ifdef	DEBBUG
537 	error("NAME: '%s'\n", sresult->isorec.name);
538 	error("chars_before_dot %d chars_after_dot %d seen_dot %d extra %d\n",
539 		chars_before_dot, chars_after_dot, seen_dot, extra);
540 #endif
541 	return (chars_before_dot + chars_after_dot + seen_dot + extra);
542 }
543