source: trunk/minix/commands/i386/mtools-3.9.7/mk_direntry.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 14.2 KB
RevLine 
[9]1/*
2 * mk_direntry.c
3 * Make new directory entries, and handles name clashes
4 *
5 */
6
7/*
8 * This file is used by those commands that need to create new directory entries
9 */
10
11#include "sysincludes.h"
12#include "msdos.h"
13#include "mtools.h"
14#include "vfat.h"
15#include "nameclash.h"
16#include "fs.h"
17#include "stream.h"
18#include "mainloop.h"
19
20static inline int ask_rename(ClashHandling_t *ch,
21 char *longname, int isprimary, char *argname)
22{
23 char shortname[13];
24 int mangled;
25
26 /* TODO: Would be nice to suggest "autorenamed" version of name, press
27 * <Return> to get it.
28 */
29#if 0
30 fprintf(stderr,"Entering ask_rename, isprimary=%d.\n", isprimary);
31#endif
32
33 if(!opentty(0))
34 return 0;
35
36#define maxsize (isprimary ? MAX_VNAMELEN+1 : 11+1)
37#define name (isprimary ? argname : shortname)
38
39 mangled = 0;
40 do {
41 fprintf(stderr, "New %s name for \"%s\": ",
42 isprimary ? "primary" : "secondary", longname);
43 fflush(stderr);
44 if (! fgets(name, maxsize, opentty(0)))
45 return 0;
46
47 /* Eliminate newline(s) in the file name */
48 name[strlen(name)-1]='\0';
49 if (!isprimary)
50 ch->name_converter(shortname,0, &mangled, argname);
51 } while (mangled & 1);
52 return 1;
53#undef maxsize
54#undef name
55}
56
57static inline clash_action ask_namematch(char *name, int isprimary,
58 ClashHandling_t *ch, int no_overwrite,
59 int reason)
60{
61 char ans[10];
62 clash_action a;
63 int perm;
64 char unix_shortname[13];
65
66
67#define EXISTS 0
68#define RESERVED 1
69#define ILLEGALS 2
70
71 static const char *reasons[]= {
72 "already exists",
73 "is reserved",
74 "contains illegal character(s)"};
75
76
77 if (!isprimary)
78 name = unix_normalize(unix_shortname, name, name+8);
79
80 a = ch->action[isprimary];
81
82 if(a == NAMEMATCH_NONE && !opentty(1)) {
83 /* no default, and no tty either . Skip the troublesome file */
84 return NAMEMATCH_SKIP;
85 }
86
87 perm = 0;
88 while (a == NAMEMATCH_NONE) {
89 fprintf(stderr, "%s file name \"%s\" %s.\n",
90 isprimary ? "Long" : "Short", name, reasons[reason]);
91 fprintf(stderr,
92 "a)utorename A)utorename-all r)ename R)ename-all ");
93 if(!no_overwrite)
94 fprintf(stderr,"o)verwrite O)verwrite-all");
95 fprintf(stderr,
96 "\ns)kip S)kip-all q)uit (aArR");
97 if(!no_overwrite)
98 fprintf(stderr,"oO");
99 fprintf(stderr,"sSq): ");
100 fflush(stderr);
101 fflush(opentty(1));
102 if (mtools_raw_tty) {
103 int rep;
104 rep = fgetc(opentty(1));
105 fputs("\n", stderr);
106 if(rep == EOF)
107 ans[0] = 'q';
108 else
109 ans[0] = rep;
110 } else {
111 fgets(ans, 9, opentty(0));
112 }
113 perm = isupper((unsigned char)ans[0]);
114 switch(tolower((unsigned char)ans[0])) {
115 case 'a':
116 a = NAMEMATCH_AUTORENAME;
117 break;
118 case 'r':
119 if(isprimary)
120 a = NAMEMATCH_PRENAME;
121 else
122 a = NAMEMATCH_RENAME;
123 break;
124 case 'o':
125 if(no_overwrite)
126 continue;
127 a = NAMEMATCH_OVERWRITE;
128 break;
129 case 's':
130 a = NAMEMATCH_SKIP;
131 break;
132 case 'q':
133 perm = 0;
134 a = NAMEMATCH_QUIT;
135 break;
136 default:
137 perm = 0;
138 }
139 }
140
141 /* Keep track of this action in case this file collides again */
142 ch->action[isprimary] = a;
143 if (perm)
144 ch->namematch_default[isprimary] = a;
145
146 /* if we were asked to overwrite be careful. We can't set the action
147 * to overwrite, else we get won't get a chance to specify another
148 * action, should overwrite fail. Indeed, we'll be caught in an
149 * infinite loop because overwrite will fail the same way for the
150 * second time */
151 if(a == NAMEMATCH_OVERWRITE)
152 ch->action[isprimary] = NAMEMATCH_NONE;
153 return a;
154}
155
156/* Returns:
157 * 2 if file is to be overwritten
158 * 1 if file was renamed
159 * 0 if it was skipped
160 *
161 * If a short name is involved, handle conversion between the 11-character
162 * fixed-length record DOS name and a literal null-terminated name (e.g.
163 * "COMMAND COM" (no null) <-> "COMMAND.COM" (null terminated)).
164 *
165 * Also, immediately copy the original name so that messages can use it.
166 */
167static inline clash_action process_namematch(char *name,
168 char *longname,
169 int isprimary,
170 ClashHandling_t *ch,
171 int no_overwrite,
172 int reason)
173{
174 clash_action action;
175
176#if 0
177 fprintf(stderr,
178 "process_namematch: name=%s, default_action=%d, ask=%d.\n",
179 name, default_action, ch->ask);
180#endif
181
182 action = ask_namematch(name, isprimary, ch, no_overwrite, reason);
183
184 switch(action){
185 case NAMEMATCH_QUIT:
186 got_signal = 1;
187 return NAMEMATCH_SKIP;
188 case NAMEMATCH_SKIP:
189 return NAMEMATCH_SKIP;
190 case NAMEMATCH_RENAME:
191 case NAMEMATCH_PRENAME:
192 /* We need to rename the file now. This means we must pass
193 * back through the loop, a) ensuring there isn't a potential
194 * new name collision, and b) finding a big enough VSE.
195 * Change the name, so that it won't collide again.
196 */
197 ask_rename(ch, longname, isprimary, name);
198 return action;
199 case NAMEMATCH_AUTORENAME:
200 /* Very similar to NAMEMATCH_RENAME, except that we need to
201 * first generate the name.
202 * TODO: Remember previous name so we don't
203 * keep trying the same one.
204 */
205 if (isprimary) {
206 autorename_long(name, 1);
207 return NAMEMATCH_PRENAME;
208 } else {
209 autorename_short(name, 1);
210 return NAMEMATCH_RENAME;
211 }
212 case NAMEMATCH_OVERWRITE:
213 if(no_overwrite)
214 return NAMEMATCH_SKIP;
215 else
216 return NAMEMATCH_OVERWRITE;
217 default:
218 return NAMEMATCH_NONE;
219 }
220}
221
222
223static void clear_scan(char *longname, int use_longname, struct scan_state *s)
224{
225 s->shortmatch = s->longmatch = s->slot = -1;
226 s->free_end = s->got_slots = s->free_start = 0;
227
228 if (use_longname & 1)
229 s->size_needed = 2 + (strlen(longname)/VSE_NAMELEN);
230 else
231 s->size_needed = 1;
232}
233
234
235static int contains_illegals(const char *string, const char *illegals)
236{
237 for(; *string ; string++)
238 if((*string < ' ' && *string != '\005' && !(*string & 0x80)) ||
239 strchr(illegals, *string))
240 return 1;
241 return 0;
242}
243
244static int is_reserved(char *ans, int islong)
245{
246 int i;
247 static const char *dev3[] = {"CON", "AUX", "PRN", "NUL", " "};
248 static const char *dev4[] = {"COM", "LPT" };
249
250 for (i = 0; i < sizeof(dev3)/sizeof(*dev3); i++)
251 if (!strncasecmp(ans, dev3[i], 3) &&
252 ((islong && !ans[3]) ||
253 (!islong && !strncmp(ans+3," ",5))))
254 return 1;
255
256 for (i = 0; i < sizeof(dev4)/sizeof(*dev4); i++)
257 if (!strncasecmp(ans, dev4[i], 3) &&
258 (ans[3] >= '1' && ans[3] <= '4') &&
259 ((islong && !ans[4]) ||
260 (!islong && !strncmp(ans+4," ",4))))
261 return 1;
262
263 return 0;
264}
265
266static inline clash_action get_slots(Stream_t *Dir,
267 char *dosname, char *longname,
268 struct scan_state *ssp,
269 ClashHandling_t *ch)
270{
271 int error;
272 clash_action ret;
273 int match=0;
274 direntry_t entry;
275 int isprimary;
276 int no_overwrite;
277 int reason;
278 int pessimisticShortRename;
279
280 pessimisticShortRename = (ch->action[0] == NAMEMATCH_AUTORENAME);
281
282 entry.Dir = Dir;
283 no_overwrite = 1;
284 if((is_reserved(longname,1)) ||
285 longname[strspn(longname,". ")] == '\0'){
286 reason = RESERVED;
287 isprimary = 1;
288 } else if(contains_illegals(longname,long_illegals)) {
289 reason = ILLEGALS;
290 isprimary = 1;
291 } else if(is_reserved(dosname,0)) {
292 reason = RESERVED;
293 ch->use_longname = 1;
294 isprimary = 0;
295 } else if(contains_illegals(dosname,short_illegals)) {
296 reason = ILLEGALS;
297 ch->use_longname = 1;
298 isprimary = 0;
299 } else {
300 reason = EXISTS;
301 clear_scan(longname, ch->use_longname, ssp);
302 switch (lookupForInsert(Dir, dosname, longname, ssp,
303 ch->ignore_entry,
304 ch->source_entry,
305 pessimisticShortRename &&
306 ch->use_longname)) {
307 case -1:
308 return NAMEMATCH_ERROR;
309
310 case 0:
311 return NAMEMATCH_SKIP;
312 /* Single-file error error or skip request */
313
314 case 5:
315 return NAMEMATCH_GREW;
316 /* Grew directory, try again */
317
318 case 6:
319 return NAMEMATCH_SUCCESS; /* Success */
320 }
321 match = -2;
322 if (ssp->longmatch > -1) {
323 /* Primary Long Name Match */
324#ifdef debug
325 fprintf(stderr,
326 "Got longmatch=%d for name %s.\n",
327 longmatch, longname);
328#endif
329 match = ssp->longmatch;
330 isprimary = 1;
331 } else if ((ch->use_longname & 1) && (ssp->shortmatch != -1)) {
332 /* Secondary Short Name Match */
333#ifdef debug
334 fprintf(stderr,
335 "Got secondary short name match for name %s.\n",
336 longname);
337#endif
338
339 match = ssp->shortmatch;
340 isprimary = 0;
341 } else if (ssp->shortmatch >= 0) {
342 /* Primary Short Name Match */
343#ifdef debug
344 fprintf(stderr,
345 "Got primary short name match for name %s.\n",
346 longname);
347#endif
348 match = ssp->shortmatch;
349 isprimary = 1;
350 } else
351 return NAMEMATCH_RENAME;
352
353 if(match > -1) {
354 entry.entry = match;
355 dir_read(&entry, &error);
356 if (error)
357 return NAMEMATCH_ERROR;
358 /* if we can't overwrite, don't propose it */
359 no_overwrite = (match == ch->source || IS_DIR(&entry));
360 }
361 }
362 ret = process_namematch(isprimary ? longname : dosname, longname,
363 isprimary, ch, no_overwrite, reason);
364
365 if (ret == NAMEMATCH_OVERWRITE && match > -1){
366 if((entry.dir.attr & 0x5) &&
367 (ask_confirmation("file is read only, overwrite anyway (y/n) ? ",0,0)))
368 return NAMEMATCH_RENAME;
369
370 /* Free up the file to be overwritten */
371 if(fatFreeWithDirentry(&entry))
372 return NAMEMATCH_ERROR;
373
374#if 0
375 if(isprimary &&
376 match - ssp->match_free + 1 >= ssp->size_needed){
377 /* reuse old entry and old short name for overwrite */
378 ssp->free_start = match - ssp->size_needed + 1;
379 ssp->free_size = ssp->size_needed;
380 ssp->slot = match;
381 ssp->got_slots = 1;
382 strncpy(dosname, dir.name, 3);
383 strncpy(dosname + 8, dir.ext, 3);
384 return ret;
385 } else
386#endif
387 {
388 entry.dir.name[0] = DELMARK;
389 dir_write(&entry);
390 return NAMEMATCH_RENAME;
391 }
392 }
393
394 return ret;
395}
396
397
398static inline int write_slots(Stream_t *Dir,
399 char *dosname,
400 char *longname,
401 struct scan_state *ssp,
402 write_data_callback *cb,
403 void *arg,
404 int Case)
405{
406 direntry_t entry;
407
408 /* write the file */
409 if (fat_error(Dir))
410 return 0;
411
412 entry.Dir = Dir;
413 entry.entry = ssp->slot;
414 strncpy(entry.name, longname, sizeof(entry.name)-1);
415 entry.name[sizeof(entry.name)-1]='\0';
416 entry.dir.Case = Case & (EXTCASE | BASECASE);
417 if (cb(dosname, longname, arg, &entry) >= 0) {
418 if ((ssp->size_needed > 1) &&
419 (ssp->free_end - ssp->free_start >= ssp->size_needed)) {
420 ssp->slot = write_vfat(Dir, dosname, longname,
421 ssp->free_start, &entry);
422 } else {
423 ssp->size_needed = 1;
424 write_vfat(Dir, dosname, 0,
425 ssp->free_start, &entry);
426 }
427 /* clear_vses(Dir, ssp->free_start + ssp->size_needed,
428 ssp->free_end); */
429 } else
430 return 0;
431
432 return 1; /* Successfully wrote the file */
433}
434
435static void stripspaces(char *name)
436{
437 char *p,*non_space;
438
439 non_space = name;
440 for(p=name; *p; p++)
441 if (*p != ' ')
442 non_space = p;
443 if(name[0])
444 non_space[1] = '\0';
445}
446
447
448int _mwrite_one(Stream_t *Dir,
449 char *argname,
450 char *shortname,
451 write_data_callback *cb,
452 void *arg,
453 ClashHandling_t *ch)
454{
455 char longname[VBUFSIZE];
456 const char *dstname;
457 char dosname[13];
458 int expanded;
459 struct scan_state scan;
460 clash_action ret;
461
462 expanded = 0;
463
464 if(isSpecial(argname)) {
465 fprintf(stderr, "Cannot create entry named . or ..\n");
466 return -1;
467 }
468
469 if(ch->name_converter == dos_name) {
470 if(shortname)
471 stripspaces(shortname);
472 if(argname)
473 stripspaces(argname);
474 }
475
476 if(shortname){
477 ch->name_converter(shortname,0, &ch->use_longname, dosname);
478 if(ch->use_longname & 1){
479 /* short name mangled, treat it as a long name */
480 argname = shortname;
481 shortname = 0;
482 }
483 }
484
485 /* Skip drive letter */
486 dstname = skip_drive(argname);
487
488 /* Copy original argument dstname to working value longname */
489 strncpy(longname, dstname, VBUFSIZE-1);
490
491 if(shortname) {
492 ch->name_converter(shortname,0, &ch->use_longname, dosname);
493 if(strcmp(shortname, longname))
494 ch->use_longname |= 1;
495 } else
496 ch->name_converter(longname,0, &ch->use_longname, dosname);
497
498 ch->action[0] = ch->namematch_default[0];
499 ch->action[1] = ch->namematch_default[1];
500
501 while (1) {
502 switch((ret=get_slots(Dir, dosname, longname,
503 &scan, ch))){
504 case NAMEMATCH_ERROR:
505 return -1; /* Non-file-specific error,
506 * quit */
507
508 case NAMEMATCH_SKIP:
509 return -1; /* Skip file (user request or
510 * error) */
511
512 case NAMEMATCH_PRENAME:
513 ch->name_converter(longname,0,
514 &ch->use_longname, dosname);
515 continue;
516 case NAMEMATCH_RENAME:
517 continue; /* Renamed file, loop again */
518
519 case NAMEMATCH_GREW:
520 /* No collision, and not enough slots.
521 * Try to grow the directory
522 */
523 if (expanded) { /* Already tried this
524 * once, no good */
525 fprintf(stderr,
526 "%s: No directory slots\n",
527 progname);
528 return -1;
529 }
530 expanded = 1;
531
532 if (dir_grow(Dir, scan.max_entry))
533 return -1;
534 continue;
535 case NAMEMATCH_OVERWRITE:
536 case NAMEMATCH_SUCCESS:
537 return write_slots(Dir, dosname, longname,
538 &scan, cb, arg,
539 ch->use_longname);
540 default:
541 fprintf(stderr,
542 "Internal error: clash_action=%d\n",
543 ret);
544 return -1;
545 }
546
547 }
548}
549
550int mwrite_one(Stream_t *Dir,
551 const char *_argname,
552 const char *_shortname,
553 write_data_callback *cb,
554 void *arg,
555 ClashHandling_t *ch)
556{
557 char *argname;
558 char *shortname;
559 int ret;
560
561 if(_argname)
562 argname = strdup(_argname);
563 else
564 argname = 0;
565 if(_shortname)
566 shortname = strdup(_shortname);
567 else
568 shortname = 0;
569 ret = _mwrite_one(Dir, argname, shortname, cb, arg, ch);
570 if(argname)
571 free(argname);
572 if(shortname)
573 free(shortname);
574 return ret;
575}
576
577void init_clash_handling(ClashHandling_t *ch)
578{
579 ch->ignore_entry = -1;
580 ch->source_entry = -2;
581 ch->nowarn = 0; /*Don't ask, just do default action if name collision */
582 ch->namematch_default[0] = NAMEMATCH_AUTORENAME;
583 ch->namematch_default[1] = NAMEMATCH_NONE;
584 ch->name_converter = dos_name; /* changed by mlabel */
585 ch->source = -2;
586}
587
588int handle_clash_options(ClashHandling_t *ch, char c)
589{
590 int isprimary;
591 if(isupper(c))
592 isprimary = 0;
593 else
594 isprimary = 1;
595 c = tolower(c);
596 switch(c) {
597 case 'o':
598 /* Overwrite if primary name matches */
599 ch->namematch_default[isprimary] = NAMEMATCH_OVERWRITE;
600 return 0;
601 case 'r':
602 /* Rename primary name interactively */
603 ch->namematch_default[isprimary] = NAMEMATCH_RENAME;
604 return 0;
605 case 's':
606 /* Skip file if primary name collides */
607 ch->namematch_default[isprimary] = NAMEMATCH_SKIP;
608 return 0;
609 case 'm':
610 ch->namematch_default[isprimary] = NAMEMATCH_NONE;
611 return 0;
612 case 'a':
613 ch->namematch_default[isprimary] = NAMEMATCH_AUTORENAME;
614 return 0;
615 default:
616 return -1;
617 }
618}
Note: See TracBrowser for help on using the repository browser.