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