home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / disk-man / mtools-3.000 / mtools-3 / mtools-3.0 / vfat.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-08  |  7.4 KB  |  339 lines

  1. /* vfat.c
  2.  *
  3.  * Miscellaneous VFAT-related functions
  4.  */
  5.  
  6. #include "sysincludes.h"
  7. #include "msdos.h"
  8. #include "mtools.h"
  9. #include "vfat.h"
  10. #include "file.h"
  11.  
  12. /* #define DEBUG */
  13.  
  14. char *short_illegals=";+=[]',\"*\\<>/?:|";
  15. char *long_illegals = "\"*\\<>/?:|\005";
  16.  
  17. /* Automatically derive a new name */
  18. static void autorename(char *name,
  19.                char tilda, char dot, char *illegals,
  20.                int limit, int bump)
  21. {
  22.     int tildapos, dotpos;
  23.     unsigned int seqnum=0, maxseq=0;
  24.     char tmp;
  25.     char *p;
  26.     
  27. #ifdef DEBUG
  28.     printf("In autorename for name=%s.\n", name);
  29. #endif
  30.     tildapos = -1;
  31.  
  32.     for(p=name; *p ; p++)
  33.         if((*p < ' ' && *p != '\005') || strchr(illegals, *p)) {
  34.             *p = '_';
  35.             bump = 0;
  36.         }
  37.  
  38.     for(dotpos=0;
  39.         name[dotpos] && dotpos < limit && name[dotpos] != dot ;
  40.         dotpos++) {
  41.         if(name[dotpos] == tilda) {
  42.             tildapos = dotpos;
  43.             seqnum = 0;
  44.             maxseq = 1;
  45.         } else if (name[dotpos] >= '0' && name[dotpos] <= '9') {
  46.             seqnum = seqnum * 10 + name[dotpos] - '0';
  47.             maxseq = maxseq * 10;
  48.         } else
  49.             tildapos = -1; /* sequence number interrupted */
  50.     }
  51.     if(tildapos == -1) {
  52.         /* no sequence number yet */
  53.         if(dotpos > limit - 2) {
  54.             tildapos = limit - 2;
  55.             dotpos = limit;
  56.         } else {
  57.             tildapos = dotpos;
  58.             dotpos += 2;
  59.         }
  60.         seqnum = 1;
  61.     } else {
  62.         if(bump)
  63.             seqnum++;
  64.         if(seqnum > 999999) {
  65.             seqnum = 1;
  66.             tildapos = dotpos - 2;
  67.             /* this matches Win95's behavior, and also guarantees
  68.              * us that the sequence numbers never get shorter */
  69.         }
  70.         if (seqnum == maxseq) {
  71.             if(dotpos >= limit)
  72.             tildapos--;
  73.             else
  74.             dotpos++;
  75.         }
  76.     }
  77.  
  78.     tmp = name[dotpos];
  79.     sprintf(name+tildapos,"%c%d",tilda, seqnum);
  80.     if(dot)
  81.         name[dotpos]=tmp;
  82.     /* replace the character if it wasn't a space */
  83. }
  84.  
  85.  
  86. void autorename_short(char *name, int bump)
  87. {
  88.     autorename(name, '~', ' ', short_illegals, 8, bump);
  89. }
  90.  
  91. void autorename_long(char *name, int bump)
  92. {
  93.     autorename(name, '-', '\0', long_illegals, 255, bump);
  94. }
  95.  
  96. void clear_vfat(struct vfat_state *v)
  97. {
  98.     v->subentries = 0;
  99.     v->status = 0;
  100. }
  101.  
  102.  
  103. /* sum_shortname
  104.  *
  105.  * Calculate the checksum that results from the short name in *dir.
  106.  *
  107.  * The sum is formed by circularly right-shifting the previous sum
  108.  * and adding in each character, from left to right, padding both
  109.  * the name and extension to maximum length with spaces and skipping
  110.  * the "." (hence always summing exactly 11 characters).
  111.  * 
  112.  * This exact algorithm is required in order to remain compatible
  113.  * with Microsoft Windows-95 and Microsoft Windows NT 3.5.
  114.  * Thanks to Jeffrey Richter of Microsoft Systems Journal for
  115.  * pointing me to the correct algorithm.
  116.  *
  117.  * David C. Niemi (niemidc@erols.com) 95.01.19
  118.  */
  119. unsigned char sum_shortname(name)
  120.     char *name;
  121. {
  122.     unsigned char sum;
  123.     int j;
  124.  
  125.     for (j=sum=0; j<11; ++j)
  126.         sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1)
  127.             + (name[j] ? name[j] : ' ');
  128.     return(sum);
  129. }
  130.  
  131. /* check_vfat
  132.  *
  133.  * Inspect a directory and any associated VSEs.
  134.  * Return 1 if the VSEs comprise a valid long file name,
  135.  * 0 if not.
  136.  */
  137. int check_vfat(struct vfat_state *v, struct directory *dir)
  138. {
  139.     char name[12];
  140.  
  141.     if (! v->subentries) {
  142. #ifdef DEBUG
  143.         fprintf(stderr, "check_vfat: no VSEs.\n");
  144. #endif
  145.         return 0;
  146.     }
  147.  
  148.     strncpy((char *)name, (char *)dir->name, 8);
  149.     strncpy((char *)name + 8, (char *)dir->ext, 3);
  150.     name[11] = '\0';
  151.  
  152.     if (v->sum != sum_shortname(name))
  153.         return 0;
  154.     
  155.     if( (v->status & ((1<<v->subentries) - 1)) != (1<<v->subentries) - 1)
  156.         return 0; /* missing entries */
  157.  
  158.     /* zero out byte following last entry, for good measure */
  159.     v->name[VSE_NAMELEN * v->subentries] = 0;
  160.  
  161.     return 1;
  162. }
  163.  
  164.  
  165. void clear_vses(Stream_t *Dir, int entry, int last)
  166. {
  167.     struct directory dir;
  168.  
  169.     maximize(&last, entry + MAX_VFAT_SUBENTRIES);
  170.     for (; entry <= last; ++entry) {
  171. #ifdef DEBUG
  172.         fprintf(stderr,"Clearing entry %d.\n", entry);
  173. #endif
  174.         dir_read(Dir, &dir, entry, NULL);
  175.         if(!dir.name[0] || dir.name[0] == DELMARK)
  176.             break;
  177.         dir.name[0] = DELMARK;
  178.         if (dir.attr == 0xf)
  179.             dir.attr = '\0';
  180.         dir_write(Dir, entry, &dir);
  181.     }
  182. }
  183.  
  184. int write_vfat(Stream_t *Dir, char *shortname, char *longname, int start)
  185. {
  186.     struct vfat_subentry vse;
  187.     int vse_id, num_vses;
  188.     char *c;
  189.  
  190. #ifdef DEBUG
  191. printf("Entering write_vfat with longname=\"%s\", start=%d.\n",longname,start);
  192. #endif
  193.     /* Fill in invariant part of vse */
  194.     vse.attribute = 0x0f;
  195.     vse.hash1 = vse.sector_l = vse.sector_u = 0;
  196.     vse.sum = sum_shortname(shortname);
  197. #ifdef DEBUG
  198.     printf("Wrote checksum=%d for shortname %s.\n", vse.sum,shortname);
  199. #endif
  200.  
  201.     num_vses = strlen(longname)/VSE_NAMELEN + 1;
  202.     for (vse_id = num_vses; vse_id; --vse_id) {
  203.         int end = 0;
  204.  
  205.         c = longname + (vse_id - 1) * VSE_NAMELEN;
  206.         
  207.  
  208.         c += unicode_write(c, vse.text1, VSE1SIZE, &end);
  209.         c += unicode_write(c, vse.text2, VSE2SIZE, &end);
  210.         c += unicode_write(c, vse.text3, VSE3SIZE, &end);
  211.  
  212.  
  213.         vse.id = (vse_id == num_vses) ? (vse_id | VSE_LAST) : vse_id;
  214. #ifdef DEBUG
  215. printf("Writing longname=(%s), VSE %d (%13s) at %d, end = %d.\n",
  216. longname, vse_id, longname + (vse_id-1) * VSE_NAMELEN,
  217. start + num_vses - vse_id, start + num_vses);
  218. #endif
  219.         dir_write(Dir, start + num_vses - vse_id,
  220.             (struct directory *)&vse);
  221.     }
  222.     return start + num_vses;
  223. }
  224.  
  225. /*
  226.  * vfat_lookup looks for filenames in directory dir.
  227.  * if a name if found, it is returned in outname
  228.  * if applicable, the file is opened and its stream is returned in File
  229.  */
  230.  
  231. int vfat_lookup(Stream_t *Dir, Stream_t *Fs, struct directory *dir,
  232.         int *entry, int *vfat_start,
  233.         char *filename, 
  234.         int flags, char *outname, char *shortname, char *longname,
  235.         Stream_t **File)
  236. {
  237.     int found;
  238.     struct vfat_state vfat;
  239.     char newfile[13];
  240.     int vfat_present = 0; /* zeroed by clear_vfat, but make GCC happy */
  241.  
  242.     if (*entry == -1)
  243.         return -1;
  244.  
  245.     found = 0;
  246.     clear_vfat(&vfat);
  247.     while(1){
  248.         if(!dir_read(Dir, dir, *entry, &vfat)){
  249.             if(vfat_start)
  250.                 *vfat_start = *entry;
  251.             break;
  252.         }
  253.         (*entry)++;
  254.  
  255.         /*---------------- Empty ---------------*/
  256.         if (dir->name[0] == '\0'){
  257.             if(!vfat_start)
  258.                 break;
  259.             continue;
  260.         }
  261.  
  262.         if (dir->attr == 0x0f) {
  263.             /* VSE, keep going */
  264.             continue;
  265.         }
  266.         
  267.         /* If we get here, it's a short name FAT entry, maybe erased.
  268.          * thus we should make sure that the vfat structure will be
  269.          * cleared before the next loop run */
  270.  
  271.         /* entry of non-requested type */
  272.         if ( (dir->name[0] == DELMARK) ||
  273.              ((dir->attr & 0x10) && !(flags & ACCEPT_DIR)) ||
  274.              ((dir->attr & 0x8) && !(flags & ACCEPT_LABEL)) ||
  275.              (( !(dir->attr & 0x18)) && !(flags & ACCEPT_PLAIN))){
  276.             clear_vfat(&vfat);
  277.             continue;
  278.         }
  279.  
  280.         vfat_present = check_vfat(&vfat, dir);
  281.         if (vfat_start) {
  282.             *vfat_start = *entry - 1;
  283.             if(vfat_present)
  284.                 *vfat_start -= vfat.subentries;
  285.         }
  286.  
  287.         if (dir->attr & 0x8){
  288.             strncpy(newfile, dir->name,8);
  289.             newfile[8]='\0';
  290.             strncat(newfile, dir->ext,3);
  291.             newfile[11]='\0';
  292.         } else
  293.             unix_name(dir->name, dir->ext, dir->Case, newfile);
  294.         
  295.  
  296.         if(flags & MATCH_ANY){
  297.             found = 1;
  298.             break;
  299.         }
  300.  
  301.  
  302.         /*---------- multiple files ----------*/
  303. #ifdef DEBUG
  304.         printf(
  305.     "!single, vfat_present=%d, vfat.name=%s, newfile=%s, filename=%s.\n",
  306.             vfat_present, vfat.name, newfile, filename);
  307. #endif                        
  308.         if ((vfat_present && match(vfat.name, filename, outname, 0)) ||
  309.             match(newfile, filename, outname, 1)) {
  310.             found = 1;
  311.             break;
  312.         }
  313.         clear_vfat(&vfat);
  314.     }
  315.  
  316.     if(found){
  317.         if((flags & DO_OPEN) && Fs && File){
  318.             *File = open_file(COPY(Fs), dir);
  319.             if (! *File)
  320.                 FREE( &Fs);
  321.         }
  322.         if(longname){
  323.             if(vfat_present)
  324.                 strcpy(longname, vfat.name);
  325.             else
  326.                 *longname ='\0';
  327.         }
  328.         if(shortname)
  329.             strcpy(shortname, newfile);
  330.             
  331.         return 0; /* file found */
  332.     } else {
  333.         *entry = -1;
  334.         return -1; /* no file found */
  335.     }
  336. }
  337.  
  338. /* End vfat.c */
  339.