home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / mtools_3.6.src.lzh / MTOOLS_3.6 / mk_direntry.c < prev    next >
Text File  |  1997-11-12  |  16KB  |  655 lines

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