home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff236.lzh / DiskHandler / dos1_2.c < prev    next >
C/C++ Source or Header  |  1989-08-09  |  45KB  |  1,344 lines

  1. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  2. /* |_o_o|\\ Copyright (c) 1987 The Software Distillery.  All Rights Reserved */
  3. /* |. o.| || This program may not be distributed without the permission of   */
  4. /* | .  | || the authors:                                          BBS:      */
  5. /* | o  | ||   John Toebes     Dave Baker    John Mainwaring                 */
  6. /* |  . |//                                                                  */
  7. /* ======                                                                    */
  8. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  9. /* Change History                                                            */
  10. /*   10-JUN-88 JAT - Corrected code which handles file extension blocks      */
  11. /*                   the AmigaDos technical reference manual is incorrect in */
  12. /*                   its description of the HighSeq and DataBlockCount fields*/
  13. /*                                                                           */
  14. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  15. /* AmigaDos 1.2 support routines */
  16.  
  17. #include "handler.h"
  18.  
  19. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  20. /* Routine: IsSameName                                                     */
  21. /* Purpose: Compare a BSTR to a string/length for a directory match        */
  22. /*          NOTE: Case is insensitive                                      */
  23. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  24. int IsSameName(bstr, name, len)
  25. register char * bstr;
  26. register char * name;
  27. register int len;
  28. {
  29.    register char c1, c2;
  30.  
  31.    /* Make sure they are the same length */
  32.    if (len != *bstr++)
  33.       {
  34.       BUG(("Length mismatch B=%ld n=%ld\n", *--bstr, len));
  35.       return(0);
  36.       }
  37.  
  38.    /* Compare all the characters (ignoring case) */
  39.    while(len--)
  40.       {
  41.       /* Get the next characters to compare */
  42.       c1 = *name++; c2 = *bstr++;
  43.       /* Convert to lower case */
  44.       if ((c1 >= 'A') && (c1 <= 'Z')) c1 += ('a'-'A');
  45.       if ((c2 >= 'A') && (c2 <= 'Z')) c2 += ('a'-'A');
  46.       /* and make sure they match */
  47.       if (c1 != c2) return(0);
  48.       }
  49.  
  50.    /* Seems to work so let it pass... */
  51.    return(1);
  52. }
  53.  
  54. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  55. /* Routine: NextKey                                                        */
  56. /* Purpose: Find next key in directory rootkey starting at position key.   */
  57. /* Note:    Key == 0 searches for first key.  Traverses all hash chains.   */
  58. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  59. KEY NextKey(global, key, info, rootkey)
  60. GLOBAL global;
  61. struct FileInfoBlock *info;
  62. KEY key, rootkey;
  63. {
  64.    KEY limit;
  65.    int hashval;
  66.    struct DirBlock *block;
  67.  
  68.    limit = 0;
  69.  
  70.    if (key == 0)
  71.       hashval = 0;
  72.    else
  73.       {
  74.       /* Note that we at least can count on the directory entries being in  */
  75.       /* ascending order.  This has two effects.  First it means that we are*/
  76.       /* not running wild with back and forth seeks.  Secondly it means that*/
  77.       /* if something is deleted, we can figure out what entries we already */
  78.       /* gave them by just a simple key comparison                          */
  79.  
  80.       /* Already doing a directory, might as well continue on */
  81.       /* first locate the last block they looked at           */
  82.       if ((block = (struct DirBlock *)GetBlock(global, key)) == NULL)
  83.          return(0);
  84.  
  85.       hashval = hash(info->fib_FileName+1, info->fib_FileName[0]);
  86.  
  87.       /* Now make sure it is the same block that we examined last time  */
  88.       /* Make sure it is a directory entry belonging to the same parent */
  89.       /* it isn't safe to compare the name as case might have changed   */
  90.       /* although we could compare the hash for the name.               */
  91.       if ((block->db_Type != T_SHORT)   ||
  92.           (block->db_Parent != rootkey) ||
  93.           (hash(block->db_Name+1, block->db_Name[0]) != hashval))
  94.          {
  95.          /* Well, something is certainly wrong here.  We have to go back */
  96.          /* and start at the beginning of the current hash chain         */
  97.          limit = key;
  98.          key = 0;
  99.          }
  100.       else
  101.          {
  102.          /* If there is nothing on the chain then tell them to look at the */
  103.          /* next hash value chain                                          */
  104.          key = block->db_HashChain;
  105.          hashval++;
  106.          }
  107.       }
  108.  
  109.    /* did we find something or will we have to look again */
  110.    if (key == 0)
  111.       {
  112.       /* OK, now we need to go to the root and search the hash chains */
  113.       if ((block = (struct DirBlock *)GetBlock(global,rootkey)) == NULL)
  114.          /* Funny, couldn't find the parent directory so just fail the   */
  115.          /* search for the next key                                      */
  116.          return(0);
  117.  
  118.       while(hashval < HASHTABLESIZE)
  119.          {
  120.          if (key = block->db_HashTable[hashval++])
  121.             {
  122.             if (key > limit)
  123.                /* Had a hit on the key so return it.                     */
  124.                break;
  125.  
  126.             /* Walk down the list until we hit an entry greater than the */
  127.             /* limit or the end of the list                              */
  128.             while((key != 0) && (key < limit))
  129.                {
  130.                if ((block = (struct DirBlock *)GetBlock(global, key)) == NULL)
  131.                   return(0);
  132.                key = block->db_HashChain;
  133.                }
  134.  
  135.             /* Relocate the parent block */
  136.             limit = 0;
  137.             if ((block = (struct DirBlock *)GetBlock(global,rootkey)) == NULL)
  138.                /* Funny, couldn't find the parent directory so just fail the */
  139.                /* search for the next key                                    */
  140.                return(0);
  141.             }
  142.          }
  143.       }
  144.    /* if we didn't get anything useful, key is 0 */
  145.    return (key);
  146. }
  147.  
  148. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  149. /* Routine: ParentKey                                                      */
  150. /* Purpose: Find key of parent directory of input key                      */
  151. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  152. KEY ParentKey(global, key)
  153. GLOBAL global;
  154. KEY key;
  155. {
  156.    struct DirBlock *block;
  157.  
  158.    if (key == 0 ||
  159.       (block = (struct DirBlock *)GetBlock(global,key)) == NULL)
  160.       return(0);
  161.  
  162.    return(block->db_Parent);
  163. }
  164.  
  165. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  166. /* Routine: hash                                                           */
  167. /* Purpose: Compute the hash value of a name                               */
  168. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  169. int hash(name, len)
  170. unsigned char *name;   /* pointer to str to hash on */
  171. int len;
  172. {
  173. int c;
  174. int hashval;
  175.  
  176.    /* Now compute a hashvalue for the result */
  177.    hashval = len;
  178.    while(len--)
  179.       {
  180.       c = *name++;
  181.       if (c >= 'a' && c <= 'z')
  182.          c = c - 'a' + 'A';
  183.         hashval = ((hashval * 13 + c ) & 0x7ff);
  184.       }
  185.  
  186.    return(hashval % HASHTABLESIZE);
  187. }
  188.  
  189. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  190. /* Routine: Parse                                                          */
  191. /* Purpose: Separate out the next node in a file name                      */
  192. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  193. int parse(name, len, buf)
  194. char **name;
  195. int *len;
  196. register char *buf;
  197. {
  198. register int left;
  199. register int c;
  200. register int curlen;
  201.  
  202.    curlen = 0;
  203.    left = *len;
  204.  
  205.    memset(buf, 0, 32);
  206.  
  207.    while((--left >= 0) && ((c = *(*name)++) != '/') )
  208.       {
  209.       if (c == ':')
  210.          {
  211.          /* got a volume name terminator.  We need to throw everything away */
  212.          curlen = 0;
  213.          buf[++curlen] = c;
  214.          break;
  215.          }
  216.       else
  217.          buf[++curlen] = c;
  218.       }
  219.  
  220.    /* update our parse length */
  221.    if (left < 0)
  222.       *len = 0;
  223.    else
  224.       *len = left;
  225.  
  226.    buf[0] = curlen;
  227.  
  228.    BUGLSTR("Parsed Name:", buf+1, curlen);
  229.  
  230.    /* Now compute a hashvalue for the result */
  231.    return(hash(buf+1, curlen));
  232. }
  233.  
  234. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  235. /* Routine: GetProtect                                                     */
  236. /* Purpose: Return the protection bits associated with a file entry        */
  237. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  238. int GetProtect(global,key)
  239. GLOBAL global;
  240. KEY key;
  241. {
  242. struct DirBlock *block;
  243.  
  244.    block = (struct DirBlock *)GetBlock(global,key);
  245.    if (block == NULL)
  246.       return(FIBF_WRITE|FIBF_READ|FIBF_DELETE|FIBF_EXECUTE);
  247.  
  248.    return(block->db_Protect);
  249. }
  250.  
  251. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  252. /* Routine: SetProtect                                                     */
  253. /* Purpose: Set the protection bits for a file                             */
  254. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  255. int SetProtect(global, key, prot)
  256. GLOBAL global;
  257. KEY key;
  258. long prot;
  259. {
  260. struct DirBlock *block;
  261.  
  262.    if (key == 0 ||
  263.        (block = (struct DirBlock *)ModBlock(global,key)) == NULL)
  264.       return(DOS_FALSE);
  265.  
  266.    block->db_Protect = prot;
  267.    return(DOS_TRUE);
  268. }
  269.  
  270. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  271. /* Routine: GetType                                                        */
  272. /* Purpose: Determine the type of a file entry                             */
  273. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  274. int GetType(global, key)
  275. GLOBAL global;
  276. KEY key;
  277. {
  278. struct DirBlock *block;
  279.  
  280.    block = (struct DirBlock *)GetBlock(global,key);
  281.    if (block == NULL)
  282.       return(0);  /* A truely invalid type */
  283.  
  284.    BUG(("GetType: addr %08lx for key %ld\n", block, key));
  285.    return(block->db_SecondaryType);
  286. }
  287.  
  288. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  289. /* Routine: SetCommentStr                                                  */
  290. /* Purpose: Set the comment string for a file                              */
  291. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  292. int SetCommentStr(global, key, comment)
  293. GLOBAL global;
  294. KEY key;
  295. char *comment;
  296. {
  297.    struct DirBlock *block;
  298.  
  299.    if (key == 0 ||
  300.        (block = (struct DirBlock *)ModBlock(global,key)) == NULL)
  301.       return(DOS_FALSE);
  302.  
  303.    memcpy((char *)block->db_Comment, comment, (*comment)+1 );
  304.    return(DOS_TRUE);
  305. }
  306.  
  307. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  308. /* Routine: GetInfo                                                        */
  309. /* Purpose: Copy the file information for a dos info block                 */
  310. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  311. int GetInfo(global, key, info)
  312. GLOBAL global;
  313. KEY key;
  314. register struct FileInfoBlock *info;
  315. {
  316.    register struct DirBlock *block;
  317.    char *p;
  318.  
  319.    /* get the block associated with the key */
  320.    if ((key == 0) ||
  321.        ((block = (struct DirBlock *)GetBlock(global,key)) == NULL))
  322.       return(DOS_FALSE);
  323.  
  324.    /* It would be prudent here to check the type of block and see that it */
  325.    /* Is a true file entry block                                          */
  326.  
  327.    /* Now copy over the information they want */
  328.    info->fib_DiskKey        = key;
  329.    info->fib_EntryType      =
  330.    info->fib_DirEntryType   = block->db_SecondaryType;
  331.  
  332.    /* Set the entry type to 2 to make dir happy */
  333.    if (info->fib_EntryType == 1)
  334.       info->fib_EntryType      =
  335.       info->fib_DirEntryType   = 2;
  336.  
  337.    p = (char *)block->db_Name;
  338.  
  339.    memcpy(info->fib_FileName, p, (*p)+1);
  340.    info->fib_Protection     = block->db_Protect;
  341.    info->fib_Size           = block->db_Size;
  342.    info->fib_NumBlocks      = (block->db_Size + (BLOCKSIZE-1))/BLOCKSIZE;
  343.    info->fib_Date.ds_Days   = block->db_Days;
  344.    info->fib_Date.ds_Minute = block->db_Minutes;
  345.    info->fib_Date.ds_Tick   = block->db_Ticks;
  346.  
  347.    p = (char *)block->db_Comment;
  348.    memcpy(info->fib_Comment,  p, (*p)+1 );
  349.  
  350. #if 0
  351.    BUG(("Done setting up info for key %ld\n",info->fib_DiskKey));
  352.    BUG(("Entry=%ld Protect=%ld Size=%ld Blocks=%ld\n", info->fib_DirEntryType, info->fib_Protection, info->fib_Size, info->fib_NumBlocks));
  353.    BUG(("Day=%ld Min=%ld Tick=%ld\n", info->fib_Date.ds_Days, info->fib_Date.ds_Minute, info->fib_Date.ds_Tick));
  354.    BUGBSTR("Name :", info->fib_FileName);
  355.    BUGBSTR("Comment :", info->fib_Comment);
  356. #endif
  357.  
  358.    return(DOS_TRUE);
  359. }
  360.  
  361. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  362. /* Routine: FindDir                                                        */
  363. /* Purpose: Find a Directory associated with a file and parse out the name */
  364. /*          of the file from the file string                               */
  365. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  366. KEY FindDir(global, key, namep, lenp)
  367. GLOBAL global;
  368. KEY key;
  369. char **namep;
  370. int *lenp;
  371. {
  372. int len;
  373. char *p, *t;
  374. register struct DirBlock *block;
  375. char namebuf[32];
  376. int hashval;
  377.  
  378.    len = *lenp;
  379.    p = *namep + len - 1;
  380.  
  381.    /* Skip to the last colon or / in the name */
  382.    while ((*p != ':') && (*p != '/') && len) { p--; len--; }
  383.    *lenp -= len;
  384.  
  385.    t = p;
  386.    if (len) t++;
  387.    /* Are they attempting to access '.' or '..' ? */
  388.    if (*t == '.' &&
  389.        ((*lenp == 1) ||
  390.         ((*lenp == 2) && (*(t+1) == '.'))))
  391.       {
  392.       /* Yes, so treat it as part of the directory */
  393.       len += *lenp;
  394.       *lenp = 0;
  395.       }
  396.  
  397.    BUGLSTR("Locate: Path is :", *namep, len);
  398.  
  399.    /* Now run through the string searching the directory */
  400.    while(len && key)
  401.       {
  402.       /* first go to the root of the directory they asked for */
  403.       block = (struct DirBlock *)GetBlock(global,key);
  404.  
  405.       if (block == NULL)
  406.          {
  407.          BUG(("FindDir: Error accessing block %ld\n", key));
  408.          return(0);
  409.          }
  410.  
  411.       if ((block->db_SecondaryType != ST_ROOT) &&
  412.           (block->db_SecondaryType != ST_USERDIR))
  413.          {
  414.          /* they want to traverse a file as a directory - give them hell */
  415.          global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
  416.          return(0);
  417.          }
  418.  
  419.       /* break down the next subcomponent of the name */
  420.       hashval = parse(namep, &len, namebuf);
  421.  
  422.       BUGBSTR("Parsed Name :", namebuf);
  423.       BUG(("Hashval = %ld\n", hashval));
  424.  
  425.       /* Did they specify the root directory ? */
  426.       if ((namebuf[0] == 1) && (namebuf[1] == ':'))
  427.          {
  428.          key = global->Root;
  429.          BUG(("Moving to root: %ld\n", key));
  430.          }
  431.  
  432.       else if ((namebuf[0] == 0) ||
  433.                ((namebuf[0] == 2) && (namebuf[1]=='.') && (namebuf[2] == '.')))
  434.          {
  435.          key = block->db_Parent;
  436.          BUG(("Moving to paremt: %ld\n", key));
  437.          }
  438.  
  439.       else if ((namebuf[0] != 1) || (namebuf[1] != '.'))
  440.          {
  441.          /* Search the current block for the given name */
  442.          for( key = block->db_HashTable[hashval]; key != 0;
  443.               key = block->db_HashChain)
  444.             {
  445.             block = (struct DirBlock *)GetBlock(global,key);
  446.             if (block == NULL)
  447.                {
  448.                BUG(("FindDir: Can't get block %ld\n", key));
  449.                return(0);
  450.                }
  451.  
  452.             BUGBSTR("Block name:", block->db_Name);
  453.             if (IsSameName(block->db_Name, (char *)namebuf+1, namebuf[0]))
  454.                break;
  455.             }
  456.          }
  457.       }
  458.  
  459.    if (key == 0)
  460.       global->pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
  461.  
  462.    BUG(("locate: key is %ld\n", key));
  463.    return(key);
  464. }
  465.  
  466. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  467. /* Routine: FindEntry                                                      */
  468. /* Purpose: Locate an entry in a directory                                 */
  469. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  470. KEY FindEntry(global, key, name, len)
  471. GLOBAL global;
  472. KEY key;
  473. char *name;
  474. int len;
  475. {
  476. int hashval;
  477. struct DirBlock *block;
  478.  
  479.    BUGLSTR("FindEntry: Name is ", name, len);
  480.  
  481.    if (len == 0)
  482.       {
  483.       BUG(("Length is zero, returning key=%ld\n", key));
  484.       return(key);
  485.       }
  486.  
  487.    hashval = hash(name, len);
  488.    /* first go to the root of the directory they asked for */
  489.    if ((block = (struct DirBlock *)GetBlock(global,key)) == NULL)
  490.       return(0);
  491.  
  492.    BUG(("Starting search for hash=%ld from key=%ld\n", hashval, key));
  493.  
  494.    if ((block->db_SecondaryType != ST_ROOT) &&
  495.        (block->db_SecondaryType != ST_USERDIR))
  496.       {
  497.       BUG(("Secondary type %ld bad for key %ld\n", block->db_SecondaryType, key));
  498.       /* they want to traverse a file as a directory - give them hell */
  499.       global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
  500.       return(0);
  501.       }
  502.  
  503.    global->prevkey = 0;
  504.  
  505.    /* Search the current block for the given name */
  506.    for( key = block->db_HashTable[hashval]; key != 0;
  507.         key = block->db_HashChain)
  508.       {
  509.       if ((block = (struct DirBlock *)GetBlock(global,key)) == NULL)
  510.          return(0);
  511.  
  512.       BUGBSTR("Block name:", block->db_Name);
  513.       if (IsSameName(block->db_Name, name, len))
  514.          break;
  515.  
  516.       global->prevkey = key;
  517.       }
  518.  
  519.    if (!key)
  520.       global->pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
  521.  
  522.    return(key);
  523. }
  524.  
  525. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  526. /* Routine: MakeEntry                                                      */
  527. /* Purpose: Create a new entry for a file                                  */
  528. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  529. KEY MakeEntry(global, key, name, len, type)
  530. GLOBAL global;
  531. KEY key;
  532. char *name;
  533. int len;
  534. int type;
  535. {
  536. int hashval;
  537. KEY newkey, nextkey;
  538. struct DirBlock *block;
  539. LONG *zeroptr;
  540. zeroptr = 0;
  541.  
  542.    BUGLSTR("MakeEntry: name ", name, len);
  543.  
  544.    hashval = hash(name, len);
  545.  
  546.    /* first go to the root of the directory they asked for */
  547.    block = (struct DirBlock *)GetBlock(global,key);
  548.  
  549.    /* When all else fails pop up to let it be handled */
  550.    if (block == NULL) return(0);
  551.  
  552.    if ((block->db_SecondaryType != ST_ROOT) &&
  553.        (block->db_SecondaryType != ST_USERDIR))
  554.       {
  555.       /* they want to traverse a file as a directory - give them hell */
  556.       global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
  557.       return(0);
  558.       }
  559.  
  560.    /* Get us a block to put the newly created item */
  561.    if (!(newkey = AllocateBlock(global, key, type)))
  562.       {
  563.       BUG(("MakeEntry:Unable to allocate a block %ld\n", newkey));
  564.       global->pkt->dp_Res2 = ERROR_DISK_FULL;
  565.       return(0);
  566.       }
  567.  
  568.    /* try to put block into right place in parent directory */
  569.    if ((nextkey = LinkDir(global, key, newkey, hashval)) == -1)
  570.       {
  571.       FreeBlock(global, newkey);
  572.       return(0); /* invalid key - admission of failure */
  573.       }
  574.    BUG(("MakeEntry: new entry %d points to successor %d\n", newkey, nextkey)); 
  575.  
  576.    /* Find the newly created block and fill it in */
  577.    block = (struct DirBlock *)ModBlock(global, newkey);
  578.    if (block == NULL)
  579.       {
  580.       /* Actually getting an error here is quite fatal because we already   */
  581.       /* modified the directory entry.  We need to figure out a way to back */
  582.       /* out of the creation.  The only situation that should cause this is */
  583.       /* if the allocated block gets flushed out to disk and we are unable  */
  584.       /* to read it in.  In this case it is quite likely that the media is  */
  585.       /* not usable.  However we eventually may have to handle this case    */
  586.       FreeBlock(global, newkey);
  587.       return(0);
  588.       }
  589.    memset((char *)block, 0, sizeof(struct DirBlock));
  590.  
  591.    BUG(("MakeEntry: New entry %ld at %08lx\n", newkey, block));
  592.    block->db_HashChain = nextkey;
  593.  
  594.    /* Fill in the remainder of the information */
  595.    block->db_Type              = T_SHORT;
  596.    block->db_OwnKey            = newkey;
  597.    block->db_SecondaryType     = type;
  598.    memcpy(((char *)block->db_Name)+1, name, len);
  599.    *(char *)&block->db_Name[0] = len;
  600.    block->db_Parent            = key;
  601.    SetDateInfo(global, block);
  602.    return(newkey);
  603. }
  604.  
  605. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  606. /* Routine: LocateEntry                                                    */
  607. /* Purpose: Find an file entry                                             */
  608. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  609. KEY LocateEntry(global, key, name)
  610. GLOBAL global;
  611. KEY key;
  612. char *name;
  613. {
  614. int len;
  615.  
  616. len = *name++;
  617.  
  618. BUGLSTR("LocateEntry: Full Name is:", name, len);
  619. if (!(key = FindDir(global, key, &name, &len)))
  620.    return(0);
  621.  
  622. BUGLSTR("LocateEntry: name ", name, len);
  623.  
  624. return(FindEntry(global, key, name, len));
  625. }
  626.  
  627. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  628. /* Routine: CreateEntry                                                    */
  629. /* Purpose: Create a new file entry                                        */
  630. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  631. KEY CreateEntry(global, key, name, mode, type)
  632. GLOBAL global;
  633. KEY key;
  634. char *name;
  635. int mode;
  636. int type;
  637. {
  638. int len;
  639. KEY newkey;
  640. struct DirBlock *block;
  641.  
  642. len = *name++;
  643. BUGLSTR("CreateEntry: Name ", name, len);
  644.  
  645. if (!(key = FindDir(global, key, &name, &len)))
  646.    return(0);
  647.  
  648. BUGLSTR("CreateEntry: Parsed Name ", name, len);
  649. if (newkey = FindEntry(global, key, name, len))
  650.    {
  651.    /* Entry already exists, may need to blow it away */
  652.    if (mode == NEWFILE)
  653.       {
  654.       global->pkt->dp_Res2 = ERROR_OBJECT_EXISTS;
  655.       return(0);
  656.       }
  657. #if 0
  658.    /* Although logical course of action, the current filing system seems */
  659.    /* to not exactly work this way.                                      */
  660.    if (GetProtect(global, newkey) & FIBF_DELETE)
  661.       {
  662.       global->pkt->dp_Res2 = ERROR_DELETE_PROTECTED;
  663.       return(0);
  664.       }
  665. #endif
  666.    if (GetProtect(global, newkey) & FIBF_WRITE)
  667.       {
  668.       global->pkt->dp_Res2 = ERROR_WRITE_PROTECTED;
  669.       return(0);
  670.       }
  671.    if ((GetType(global, newkey) == ST_USERDIR) || (type == ST_USERDIR))
  672.       {
  673.       global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
  674.       return(0);
  675.       }
  676.    FreeDataChain(global, newkey);
  677.  
  678.    /* Also we need to update the name information in the file            */
  679.    /* This allows them to change the case of the name.  Note that by     */
  680.    /* doing this in place we preserve all the comment information and    */
  681.    /* attributes of the old entry.  Also since the entry doesn't move on */
  682.    /* disk we can keep it in the same plae on the hash chain.            */
  683.    block = (struct DirBlock *)ModBlock(global, newkey);
  684.    if (block != NULL)
  685.       {
  686.       memcpy(((char *)block->db_Name)+1, name, len);
  687.       *(char *)&block->db_Name[0] = len;
  688.  
  689.       /* Clear the archive bit so backup programs know it has been modified */
  690.       block->db_Protect &= ~FIBF_ARCHIVE;
  691.       }
  692.    }
  693. else
  694.    {
  695.    /* We have a brand new file here, so make us an entry for it */
  696.    newkey = MakeEntry(global, key, name, len, type);
  697.    BUG(("CreateEntry: newkey is %ld\n", newkey));
  698.    }
  699. return(newkey);
  700. }
  701.  
  702. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  703. /* Routine: FreeDataChain                                                  */
  704. /* Purpose: Truncate the data chain for a file                             */
  705. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  706. int FreeDataChain(global, basekey)
  707. GLOBAL global;
  708. KEY basekey;
  709. {
  710.    KEY key, savekey;
  711.    long i;
  712.    struct FileBlock *block;
  713.  
  714.    key = basekey;
  715.    while(key)
  716.       {
  717.       if ((block = (struct FileBlock *)GetBlock(global, key)) == NULL)
  718.          return(0);
  719.  
  720.       for (i = 0; i < block->fb_HighSeq; i++)
  721.           FreeBlock(global, block->fb_DataBlock[(BLOCKSPERENTRY-1)-i]);
  722.       savekey = block->fb_Extension;
  723.       BUG(("FreeDataChain: Base:%ld Key:%ld next:%ld\n", basekey, key, savekey));
  724.       if (key != basekey)
  725.          FreeBlock(global, key);
  726.       key = savekey;
  727.       }
  728.  
  729.    if ((block = (struct FileBlock *)ModBlock(global, basekey)) == NULL)
  730.       {
  731.       return(0);
  732.       }
  733.  
  734.    block->fb_FirstData = block->fb_DataBlockCount = block->fb_HighSeq = 0;
  735.    block->fb_Extension = 0;
  736.    block->fb_Size = 0;
  737.    return(1);
  738. }
  739.  
  740. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  741. /* Routine: SeekDataChain                                                  */
  742. /* Purpose: Locate a block in a file data chain                            */
  743. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  744. KEY SeekDataChain(global, efh, num)
  745. GLOBAL global;
  746. struct EFileHandle *efh;
  747. long num;
  748. {
  749.    struct FileBlock *block;
  750.    KEY key;
  751.    long off;
  752.    long entryblock;
  753.  
  754.    if (num-- <= 0)
  755.       {
  756.       BUG(("Attempt to seek to %ld on %08lx\n", num+1, efh));
  757.       global->pkt->dp_Res2 = ERROR_SEEK_ERROR;
  758.       return(0);
  759.       }
  760.  
  761.    entryblock = num/BLOCKSPERENTRY;
  762.    BUG(("SeekDataChain: num=%ld entry=%ld cur=%ld\n",num,entryblock,efh->efh_CurExtBlock));
  763.  
  764.    if (entryblock != efh->efh_CurExtBlock)
  765.       {
  766.       /* Are we seeking forward? */
  767.       BUG(("Forward seek\n"));
  768.       if (entryblock > efh->efh_CurExtBlock)
  769.          {
  770.          entryblock -= efh->efh_CurExtBlock;
  771.          key = efh->efh_ExtBlockKey;
  772.          efh->efh_CurExtBlock += entryblock;
  773.          }
  774.       else
  775.          {
  776.          BUG(("Must seek from beginning\n"));
  777.          /* Need to seek from beginning to target */
  778.          efh->efh_CurExtBlock = entryblock;
  779.          key = efh->efh_Lock->fl_Key;
  780.          }
  781.       while(key && entryblock--)
  782.          {
  783.          if ((block = (struct FileBlock *)GetBlock(global, key)) == NULL)
  784.             return(0);
  785.          key = block->fb_Extension;
  786.          }
  787.       efh->efh_ExtBlockKey = key;
  788.       }
  789.    else
  790.       key = efh->efh_ExtBlockKey;
  791.  
  792.    if (key)
  793.       {
  794.       off = num - (efh->efh_CurExtBlock * BLOCKSPERENTRY);
  795.  
  796.       if ((block = (struct FileBlock *)GetBlock(global, key)) == NULL)
  797.          return(0);
  798.  
  799.       if (off >= block->fb_HighSeq)
  800.          {
  801.          BUG(("SeekDataChain: %ld Out of range %ld for %ld\n", off, block->fb_HighSeq, off));
  802.          key = 0;
  803.          }
  804.       else
  805.          key = block->fb_DataBlock[(BLOCKSPERENTRY-1) - off];
  806.       }
  807.  
  808.    BUG(("SeekDataChain: Returning block %ld\n", key));
  809.    return(key);
  810. }
  811.  
  812. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  813. /* Routine: AppendDataChain                                                */
  814. /* Purpose: Allocate a new block to extend a file data chain               */
  815. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  816. KEY AppendDataChain(global, efh, highseq)
  817. GLOBAL global;
  818. struct EFileHandle *efh;
  819.    long highseq;
  820. {
  821.    KEY key, blockkey, newkey;
  822.    struct FileBlock *block;
  823.    struct DataBlock *data;
  824.    KEY firstdata, filekey;
  825.  
  826.    filekey = efh->efh_Lock->fl_Key;
  827.    if ((block = (struct FileBlock *)GetBlock(global, filekey)) == NULL)
  828.       {
  829.       BUG(("Couldn't locate the base node for key %ld\n", filekey))
  830.       return(0);
  831.       }
  832.  
  833.    firstdata = block->fb_FirstData;
  834.    if (highseq)
  835.       {
  836.       key = SeekDataChain(global, efh, highseq);
  837.       if (key == 0)
  838.          {
  839.          /* Too far past end of file, issue an error */
  840.          BUG(("Append Error: efh:%08lx seq=%ld\n", efh, highseq));
  841.          global->pkt->dp_Res1 = DOS_FALSE;
  842.          global->pkt->dp_Res2 = ERROR_SEEK_ERROR;
  843.          return(0);
  844.          }
  845.       }
  846.    else
  847.       key = filekey;
  848.  
  849.    /* Get the block to put the data into */
  850.    if ((newkey = AllocateBlock(global, key, T_DATA)) == 0)
  851.       return(0);
  852.  
  853.    BUG(("Append: New data block #%ld at %ld Ext = %ld\n", highseq, newkey, efh->efh_ExtBlockKey));
  854.    if ((block = (struct FileBlock *)
  855.                 ModBlock(global, efh->efh_ExtBlockKey)) == NULL)
  856.       goto appenderr;
  857.  
  858.    if (block->fb_HighSeq != BLOCKSPERENTRY)
  859.       {
  860.       /* Will fit in the current file chain so put it there and go on */
  861.       block->fb_DataBlock[(BLOCKSPERENTRY-1)-block->fb_HighSeq] = newkey;
  862.       block->fb_HighSeq++;
  863.       }
  864.    else
  865.       {
  866.       /* need to allocate another extension chain */
  867.       BUG(("Need to allocate extension\n"));
  868.       if ((blockkey =
  869.            AllocateBlock(global, efh->efh_ExtBlockKey, T_SHORT)) == 0)
  870.          goto appenderr;
  871.  
  872.       /* Succeeded so initialize the new extension chain */
  873.       if ((block = (struct FileBlock *)
  874.                    ModBlock(global, efh->efh_ExtBlockKey)) == NULL)
  875.          goto initerr;
  876.  
  877.       block->fb_Extension = blockkey;
  878.  
  879.       if ((block = (struct FileBlock *)ModBlock(global, blockkey)) == NULL)
  880.          {
  881.          /* odds of this situation are extremely low since allocateblock */
  882.          /* was supposed to set up the sitution, but let's be safe       */
  883. initerr:
  884.          FreeBlock(global, blockkey);
  885. appenderr:
  886.          FreeBlock(global, newkey);
  887.          return(0);
  888.          }
  889.  
  890.       memset((char *)block, 0, sizeof(struct FileBlock));
  891.       block->fb_Type           = T_LIST;
  892.       block->fb_OwnKey         = blockkey;
  893.       block->fb_DataBlockCount = 0;
  894.       block->fb_HighSeq        = 1;
  895.       block->fb_FirstData      = firstdata;
  896.       block->fb_Parent         = filekey;
  897.       block->fb_Extension      = 0;
  898.       block->fb_DataBlock[BLOCKSPERENTRY-1] = newkey;
  899.       block->fb_SecondaryType  = ST_FILE;
  900.  
  901.       efh->efh_CurExtBlock++;
  902.       efh->efh_ExtBlockKey = blockkey;
  903.       }
  904.  
  905.    /* Go back and put the forward chain links in */
  906.    if ((block = (struct FileBlock *)ModBlock(global, key)) == NULL)
  907.       {
  908.       BUG(("Unable to mod previous block %ld\n", key));
  909.       return(0);
  910.       }
  911.  
  912.    block->fb_FirstData = newkey;
  913.  
  914.    /* Now lastly we need to set up the new data block                 */
  915.    if ((data = (struct DataBlock *)ModBlock(global, newkey)) == NULL)
  916.       {
  917.       BUG(("Unable to locate the new data block\n"));
  918.       return(0);
  919.       }
  920.  
  921.    memset((char *)data, 0, sizeof(struct DataBlock));
  922.    data->db_Type = T_DATA;
  923.    data->db_Header = filekey;
  924.    data->db_SeqNum = highseq+1;
  925.    data->db_DataSize = 0;  /* Since there is nothing in it yet */
  926.    data->db_NextData = 0;  /* This is the last block in the file */
  927.    return(newkey);
  928. }
  929.  
  930. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  931. /* Routine: PUTDATA                                                        */
  932. /* Purpose: Stuff file data into a data block                              */
  933. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  934. void PUTDATA(block, pos, data, len)
  935. char *block;
  936. long pos;
  937. char *data;
  938. long len;
  939. {
  940. struct DataBlock *db = (struct DataBlock *)block;
  941. memcpy(((char *)db->db_Data) + pos, data, len);
  942. if ((len+pos) > db->db_DataSize)
  943.    db->db_DataSize = len+pos;
  944. }
  945.  
  946. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  947. /* Routine: GETDATA                                                        */
  948. /* Purpose: Extract file data from a data block                            */
  949. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  950. void GETDATA(data, len, block, pos)
  951. char *data;
  952. long len;
  953. char *block;
  954. long pos;
  955. {
  956. struct DataBlock *db = (struct DataBlock *)block;
  957. memcpy(data, ((char *)db->db_Data) + pos, len);
  958. }
  959.  
  960. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  961. /* Routine: GetFileSize                                                    */
  962. /* Purpose: Determine the current length of a file                         */
  963. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  964. long GetFileSize(global, efh)
  965. GLOBAL global;
  966. EFH efh;
  967. {
  968. struct DirBlock *db;
  969.  
  970. if ((db = (struct DirBlock *)GetBlock(global, efh->efh_Lock->fl_Key)) == NULL)
  971.    /* Couldn't read the directory entry so let them know */
  972.    return(-1);
  973.  
  974. return(db->db_Size);
  975. }
  976.  
  977. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  978. /* Routine: UpdateFile                                                     */
  979. /* Purpose: Update the directory entry to indicate the current file length */
  980. /*          The date time stamp is also updated                            */
  981. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  982. int UpdateFile(global, efh, blknum, blkpos)
  983. GLOBAL global;
  984. EFH efh;
  985. long blknum;
  986. long blkpos;
  987. {
  988. struct DirBlock *db;
  989.  
  990. BUG(("UpdateFile: %ld to %ld/%ld\n", efh->efh_Lock->fl_Key, blknum, blkpos));
  991. if ((db = (struct DirBlock *)ModBlock(global, efh->efh_Lock->fl_Key)) == NULL)
  992.    /* Couldn't read the directory entry so let them know */
  993.    return(0);
  994.  
  995. db->db_Size     = ((blknum-1)*BLOCKSIZE) + blkpos;
  996.  
  997. /* Clear the archive bit so the backup programs know it has been modified */
  998. db->db_Protect &= ~FIBF_ARCHIVE;
  999.  
  1000. SetDateInfo(global, db);
  1001.  
  1002. return(1);
  1003. }
  1004.  
  1005. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1006. /* Routine: BlockKey                                                       */
  1007. /* Purpose: Return the key associated with a specific block in a file.     */
  1008. /*          If the block is one past the end of the file it will extend    */
  1009. /*          the file by one block                                          */
  1010. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1011. KEY BlockKey(global, efh, seq, writemode)
  1012. GLOBAL global;
  1013. EFH efh;
  1014. long seq;
  1015. int writemode;
  1016. {
  1017. KEY key;
  1018.  
  1019. key = SeekDataChain(global, efh, seq);
  1020.  
  1021. /* If we didn't find an already existing block when we are attempting to   */
  1022. /* write to the block we want to append to the end of the file.  Note that */
  1023. /* the test of seq is just an additional paranoid protection               */
  1024. if (writemode && (key == 0) && seq)
  1025.    {
  1026.    BUG(("BlockKey: Extending file to %ld\n", seq));
  1027.    key = AppendDataChain(global, efh, seq-1);
  1028.    }
  1029.  
  1030. BUG(("BlockKey: Returning %ld\n", key));
  1031. return(key);
  1032. }
  1033.  
  1034. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1035. /* Routine: SetDateInfo                                                    */
  1036. /* Purpose: Set the modified date/time stamp on the current entry          */
  1037. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1038. void SetDateInfo(global, db)
  1039. GLOBAL global;
  1040. struct DirBlock *db;
  1041. {
  1042. /* Note that this presumes DOS is around to do the data stamping */
  1043. #if 0
  1044. DateStamp((long *)&db->db_Days);
  1045. #else
  1046. GetDateStamp(global, (struct DateStamp *)&db->db_Days);  /* Just to experiment */
  1047. #endif
  1048. }
  1049.  
  1050. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1051. /* Routine: RenameDisk                                                     */
  1052. /* Purpose: Change the current disk label                                  */
  1053. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1054. int RenameDisk(global, name)
  1055. GLOBAL global;
  1056. char *name;
  1057. {
  1058. struct RootBlock *root;
  1059. int len;
  1060.  
  1061.    if ((global->volume == NULL) ||
  1062.        ((root = (struct RootBlock *)global->Root) == NULL))
  1063.       return(DOS_FALSE);
  1064.  
  1065.    /* Make sure we don't get a name to big */
  1066.    len = *name++;
  1067.    if (len >= NAMESIZE)
  1068.       len = NAMESIZE - 1;
  1069.  
  1070.    root->rb_Name[0] = len;
  1071.    memcpy(root->rb_Name+1, name, len);
  1072.    
  1073.    return(DOS_TRUE);
  1074. }
  1075.  
  1076. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1077. /* Routine: DeleteEntry                                                    */
  1078. /* Purpose: Remove an directory entry                                      */
  1079. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1080. int DeleteEntry(global, key, name)
  1081. GLOBAL global;
  1082. KEY key;
  1083. char *name;
  1084. {
  1085. int len;
  1086. int i;
  1087. KEY parent, nextkey;
  1088. struct DirBlock *block;
  1089.  
  1090. len = *name++;
  1091.  
  1092. /* First see if the entry really exists */
  1093. if (!(parent = FindDir(global, key, &name, &len)))
  1094.    return(DOS_FALSE);
  1095.  
  1096. /* Ok, we got the directory, is the file or entry there ? */
  1097. if (!(key = FindEntry(global, parent, name, len)))
  1098.    return(DOS_FALSE);
  1099.  
  1100. /* Make sure they haven't protected it against deletion */
  1101. if (GetProtect(global, key) & FIBF_DELETE)
  1102.    {
  1103.    global->pkt->dp_Res2 = ERROR_DELETE_PROTECTED;
  1104.    return(DOS_FALSE);
  1105.    }
  1106.  
  1107. /* Gee, it is there, now we need to go through and delete it */
  1108. /* First we get rid of the data chain for the file */
  1109. switch(GetType(global, key)) {
  1110.    case ST_USERDIR:
  1111.       if ((block = (struct DirBlock *)GetBlock(global, key)) == NULL)
  1112.          return(DOS_FALSE);
  1113.  
  1114.       /* Make sure the directory is empty first */
  1115.       for (i= 0; i < HASHTABLESIZE; i++)
  1116.          if (block->db_HashTable[i] != 0)
  1117.             {
  1118.             global->pkt->dp_Res2 = ERROR_DIRECTORY_NOT_EMPTY;
  1119.             return(DOS_FALSE);
  1120.             }
  1121.       break;
  1122.  
  1123.    case ST_FILE:
  1124.       if (!FreeDataChain(global, key))
  1125.          {
  1126.          BUG(("Unable to release the data chain for the file %ld\n", key));
  1127.          return(DOS_FALSE);
  1128.          }
  1129.       break;
  1130.  
  1131.    default:
  1132.       BUG(("What type of a thing is this they are deleting\n"));
  1133.       global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
  1134.       return(DOS_FALSE);
  1135. /*    break;   compiler doesn't really like this */
  1136.    }
  1137.  
  1138. /* Now we want to remove the entry from the directory */
  1139. /* to do this we want to find either the parent or the previous entry */
  1140. /* When we called FindEntry, it filled in global->prevkey with the    */
  1141. /* previous hash chain entry if we were in the list OR it is 0 which  */
  1142. /* means that we need to unlink it from our parent                    */
  1143. /* First we need to get the next entry in the hash chain */
  1144. if ((block = (struct DirBlock *)ModBlock(global, key)) == NULL)
  1145.    return(DOS_FALSE);
  1146.  
  1147. /* Just to be nice, mark it as deleted */
  1148. block->db_Type |= T_DELETED;
  1149. nextkey = block->db_HashChain;
  1150.  
  1151. if (global->prevkey)
  1152.    {
  1153.    if ((block = (struct DirBlock *)ModBlock(global, global->prevkey)) == NULL)
  1154.       return(DOS_FALSE);
  1155.    block->db_HashChain = nextkey;
  1156.    if ((block = (struct DirBlock *)ModBlock(global, parent)) == NULL)
  1157.       return(DOS_FALSE);
  1158.    }
  1159. else
  1160.    {
  1161.    if ((block = (struct DirBlock *)ModBlock(global, parent)) == NULL)
  1162.       return(DOS_FALSE);
  1163.    block->db_HashTable[hash(name, len)] = nextkey;
  1164.    }
  1165.  
  1166. /* We are now at the parent block, so update the date time information on it */
  1167. SetDateInfo(global, block);
  1168.  
  1169. /* Also, mark the directory block for the file as empty */
  1170. FreeBlock(global, key);
  1171.  
  1172. return(DOS_TRUE);
  1173. }
  1174.  
  1175. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1176. /* Routine: RenameEntry                                                    */
  1177. /* Purpose: Implement most of ActRenameEntry.  Check for valid parameters  */
  1178. /*          then relink entry block to new directory location.             */
  1179. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1180. int RenameEntry(global, fromkey, tokey, fromname, toname)
  1181. GLOBAL global;
  1182. KEY fromkey, tokey;
  1183. char *fromname, *toname;
  1184. {
  1185. struct DirBlock *block;
  1186. KEY parentkey, prevkey, nextkey, dirkey, ancestor;
  1187. int hashval, len;
  1188.  
  1189.    if (!(fromkey = LocateEntry(global, fromkey, fromname)))
  1190.       return(DOS_FALSE);
  1191.    /* get some useful info while it's available, we're sure to need it     */
  1192.    prevkey = global->prevkey;
  1193.    if ((block = (struct DirBlock *)GetBlock(global, fromkey)) == NULL)
  1194.       return(DOS_FALSE);
  1195.    parentkey = block->db_Parent;
  1196.    hashval = hash(&block->db_Name[1], block->db_Name[0]);
  1197.    nextkey = block->db_HashChain;
  1198.  
  1199.  
  1200.    /* Now see if we can find the 'to' file.  It shouldn't exist, except    */
  1201.    /* if we are only using rename to change the case of the name.    */
  1202.    len = *toname++;
  1203.    if (!(dirkey = FindDir(global, tokey, &toname, &len)))
  1204.       {
  1205.       global->pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;  /* or something..    */
  1206.       return(DOS_FALSE);
  1207.       }
  1208.    if(tokey = FindEntry(global, dirkey, toname, len))
  1209.       /* check this is not same file name with just case changed    */
  1210.       if (tokey != fromkey)
  1211.      {
  1212.      global->pkt->dp_Res2 = ERROR_OBJECT_EXISTS;
  1213.         return(DOS_FALSE);
  1214.      }
  1215.  
  1216.    /* Check to see if we're trying to rename an ancestor to a        */
  1217.    /* descendant, which has the unfortunate effect of orphaning it.    */
  1218.    /* Can save some thrashing if source & dest are in same directory    */
  1219.    if (dirkey != parentkey)
  1220.       {
  1221.       ancestor = dirkey;
  1222.       BUG(("RenameEntry: Ancestor = %d\n", ancestor));
  1223.       while (ancestor)
  1224.          {
  1225.          if (ancestor == fromkey)
  1226.             {
  1227.         global->pkt->dp_Res2 = ERROR_OBJECT_IN_USE;  /* Better ideas?  */
  1228.         return(DOS_FALSE);
  1229.         }
  1230.          if ((block = (struct DirBlock *)GetBlock(global, ancestor)) == NULL)
  1231.             /* haven't located anything oedipal, nothing can be wrong, can */
  1232.         /* be wrong, can be wrong...                   */
  1233.             break;
  1234.          ancestor = block->db_Parent;
  1235.          }
  1236.       BUG(("RenameEntry: Root = %d\n", ancestor));
  1237.       }
  1238.  
  1239.    /* Now ready to unlink the 'from' entry from its directory.           */
  1240.  
  1241.    BUG(("RenameEntry: parentkey=%d, prevkey=%d, nextkey=%d\n",
  1242.            parentkey, prevkey, nextkey));
  1243.  
  1244.    if (prevkey)
  1245.       {
  1246.       if ((block = (struct DirBlock *)ModBlock(global, prevkey))
  1247.                  == NULL)
  1248.          return(DOS_FALSE);
  1249.       block->db_HashChain = nextkey;
  1250.       }
  1251.    else
  1252.       {
  1253.       if ((block = (struct DirBlock *)ModBlock(global, parentkey)) == NULL)
  1254.          return(DOS_FALSE);
  1255.       BUG(("  hashval = %d\n", hashval));
  1256.       block->db_HashTable[hashval] = nextkey;
  1257.       }
  1258.  
  1259.    /* The disk seems to be writable - let's hope it stays that way    */
  1260.  
  1261.    /* Now link into the new directory under the new name        */
  1262.    nextkey = LinkDir(global, dirkey, fromkey, hash(toname, len));
  1263.    if (nextkey == -1)
  1264.       {
  1265.       /* Relink from file back into from directory and return.        */
  1266.       if ((nextkey = LinkDir(global, parentkey, fromkey, hashval)) == -1)
  1267.       /* How embarrasing!  Still, with the checks we did earlier, this    */
  1268.       /* is definitely not likely to happen (I think...)        */
  1269.          return(DOS_FALSE);
  1270.       if ((block = (struct DirBlock *)ModBlock(global, fromkey)) == NULL)
  1271.          return(DOS_FALSE);
  1272.       block->db_HashChain = nextkey;
  1273.          return(DOS_FALSE);  /* but we maintain our dignity        */
  1274.       }
  1275.  
  1276.    /* if this next bit fails, we've screwed up the new directory...    */
  1277.    if ((block = (struct DirBlock *)ModBlock(global, fromkey)) == NULL)
  1278.       return(DOS_FALSE);  /* hope ModBlock set a good error in Res2     */
  1279.    block->db_HashChain = nextkey;  /* complete fwd link in directory    */
  1280.    block->db_Parent = dirkey;
  1281.    memcpy(&block->db_Name[1], toname, len);
  1282.    block->db_Name[0] = len;
  1283.    BUG(("RenameEntry: Entry %d linked to directory %d\n", fromkey, dirkey));
  1284.    return(DOS_TRUE);
  1285. }
  1286.  
  1287. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1288. /* Routine: LinkDir                                                        */
  1289. /* Purpose: link a block into a directory (almost - for logistic reasons,  */
  1290. /*          caller must fill return value into his own block->db_HashChain)*/
  1291. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  1292. KEY LinkDir(global, parentkey, filekey, hashval)
  1293. GLOBAL global;
  1294. KEY parentkey, filekey;
  1295. int hashval;
  1296. {
  1297. struct DirBlock *block;
  1298. KEY nextkey;
  1299.  
  1300.    block = (struct DirBlock *)ModBlock(global, parentkey);
  1301.    if (block == NULL)
  1302.       return(-1);  /* not a nice key */
  1303.  
  1304.    SetDateInfo(global, block);    /* timestamp the change in the parent dir */
  1305.    BUG(("LinkDir: Mod block %ld at %08lx newkey = %ld\n",
  1306.            parentkey, block, filekey));
  1307.  
  1308.    /* Enter the key into the list in a sorted fashion.  The lowest key must */
  1309.    /* appear first.                                                         */
  1310.    nextkey = block->db_HashTable[hashval];
  1311.    if (nextkey == 0 || filekey < nextkey)
  1312.       {
  1313.       /* it goes at the head of the chain, so we can simply put it there    */
  1314.       block->db_HashTable[hashval] = filekey;
  1315.       }
  1316.    else
  1317.       {
  1318.       while(1)
  1319.         {
  1320.         /* Not at the head of the chain.  We need to find where it goes on */
  1321.         /* the chain.  First we get the next block in the chain and see    */
  1322.         /* what it links to.                                               */
  1323.         if ((block = (struct DirBlock *)GetBlock(global, nextkey)) == NULL)
  1324.            return(-1);  /* the bad news key */
  1325.         /* Should we go after this block ?                                 */
  1326.         if (block->db_HashChain == 0 ||
  1327.             block->db_HashChain > filekey)
  1328.            {
  1329.            /* Yes, make sure we can modify this block                      */
  1330.            if ((block = (struct DirBlock *)ModBlock(global, nextkey)) == NULL)
  1331.               return(-1);  /* this'll slay them */
  1332.            /* Insert us in the chain and stop the search                   */
  1333.            nextkey = block->db_HashChain;
  1334.            block->db_HashChain = filekey;
  1335.            break;
  1336.            }
  1337.         /* advance search                            */
  1338.         nextkey = block->db_HashChain;
  1339.         }
  1340.       }
  1341.    /* if we got here we done good */
  1342.    return(nextkey);  /* note 0 is a healthy value here: end of the line */
  1343. }
  1344.