[MKISOFS]
[reactos.git] / reactos / sdk / tools / mkisofs / schilytools / mkisofs / name.c
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
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
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 }