home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / utility / disk / undelete / undel.c < prev    next >
C/C++ Source or Header  |  1993-02-14  |  27KB  |  893 lines

  1. /**********************************************************************
  2. *
  3. * undel    -- a file undelete utility
  4. *
  5. * Written in GNU c by Trevor Blight, Jan 93
  6. *
  7. * Portions of this program are based (loosely) on the program RESCUE2,
  8. * published in "The MS-DOS Developer's Guide", 2nd ed., by Howard W. Sams & Co.
  9. *
  10. * This software may be used freely for any purpose whatsoever.
  11. * Please enhance and return it to the public domain.
  12. *
  13. ***********************************************************************/
  14.  
  15. #include <osbind.h>
  16. #include <aesbind.h>
  17. #include <vdibind.h>
  18. #include <gemfast.h>
  19. #include <string.h>
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include <errno.h>
  24. #include <assert.h>
  25. #include "undel.h"
  26.  
  27.  
  28. #define  MAX_CLUSTERS   4078        /* max nr clusters for 12 bit FAT */
  29. #define  CHAIN_END      1           /* used by get_cluster() to indicate eof */
  30.  
  31. typedef unsigned char   BYTE;
  32. typedef unsigned short  WORD;
  33. typedef unsigned long   LONG;
  34.  
  35.  
  36. typedef struct {
  37.    char  name[8];                   /* name */
  38.    char  ext[3];                    /* extension */
  39.    BYTE  attrib;                    /* attribute */
  40.    BYTE  reserved[10];
  41.    WORD  time;                      /* time: hhhhh mmm - mmm sssss */
  42.    WORD  date;                      /* date: yyyyyyy m - mmm ddddd */
  43.    WORD  cluster;                   /* starting cluster (Intel format) */
  44.    LONG  fsize;                     /* total size in bytes (Intel format) */
  45. } DENTRY;
  46.  
  47.  
  48. BYTE    *FatBuffer;                 /* FAT table buffer address */
  49. DENTRY  *dirBuffer;                 /* directory buffer address */
  50. DENTRY  *dirend;
  51. int     wi_handle;                 /* window handle */
  52.  
  53. /*** forward declarations ***/
  54.  
  55. PRIVATE void win_tidy( void );
  56. PRIVATE BOOL fill_dta( WORD srch_att );
  57. PRIVATE void undelete_file( const char *pathp );
  58. PRIVATE WORD next_cluster( const WORD clust );
  59. PRIVATE BOOL match( const char *sname, const char *fname );
  60. PRIVATE WORD swap_word( const WORD word );
  61. PRIVATE LONG swap_long( const LONG l );
  62. PRIVATE void fnmcpy( char* d, const char *s );
  63. PRIVATE int  find_subdir( WORD dnum, const char *pathp, WORD *snump );
  64.  
  65.  
  66. /*** sector <--> cluster conversion macros ***/
  67. #define  sector_of(cl)      ((cl - 2)*bpbp->clsiz + bpbp->datrec)
  68. #define  cluster_of(s)      (2 + (s - bpbp->datrec)/bpbp->clsiz)
  69.  
  70.  
  71. /********************************************************
  72. *
  73. * call this function at end of program to tidy up AES windows
  74. *
  75. *******************************************************/
  76.  
  77. PRIVATE void win_tidy( void )
  78. {
  79.    wind_close(wi_handle);
  80.    wind_delete(wi_handle);
  81.    appl_exit();
  82. } /* win_tidy() */
  83.  
  84.  
  85. /*********************************************************
  86. *
  87. *  main entry point
  88. *
  89. *********************************************************/
  90.  
  91. int main(void)                  /* ignore params */
  92. {
  93.  
  94. #define WI_KIND        0
  95.  
  96. char      dname[80];     /* directory name buffer */
  97. int       dnum;                  /* drive nr, A: = 0, etc */
  98. int       r;                     /* fsel return value */
  99. int       sextn;
  100. char      fname[64];
  101. int      xdesk,ydesk,hdesk,wdesk;
  102. char      extn3str[4];
  103. char      extn4str[4];
  104.  
  105.    appl_init();
  106.    wind_get(0, WF_WORKXYWH, &xdesk, &ydesk, &wdesk, &hdesk);
  107.    wi_handle = wind_create(WI_KIND, xdesk, ydesk, wdesk, hdesk);
  108.    if( wi_handle < 0 ) {
  109.       form_alert(1, "[1][too many windows open][QUIT]" );
  110.       appl_exit();
  111.       exit(1);            /* quit  */
  112.    } /* if */
  113.    wind_open(wi_handle,xdesk,ydesk,wdesk,hdesk);
  114.    if (atexit(win_tidy) != 0 ) {
  115.       form_alert(1, "[1][can't arrange tidy up at exit][QUIT]" );
  116.       win_tidy();
  117.       exit(1);
  118.    } /* if */
  119.  
  120.    graf_mouse(ARROW, 0x0L);
  121.    strcpy (dname, "\\");
  122.    dnum = -1;
  123.    sextn = 0x0001; /* select extension 1 */
  124.    strcpy( extn3str, "c  " );
  125.    strcpy( extn4str, "h  " );
  126.    r = fsel("Select a file to undelete ...",       /* I  display title for box */
  127.         dname,       /* IO initial path spec \ ... \ no drive; returns selected path */
  128.         &dnum,       /* IO selects drive -1=default 0=A: .. 5=F: */
  129.         &sextn,      /* IO bitmap selected extn boxes :
  130.                       *    0x0001 = extn0    0x0002 = extn1    0x0004 = extn2
  131.                       *    0x0008 = extn3    0x0010 = extn4
  132.                       */
  133.         "*  ",       /* I  extension text not including "." for each of 1st 3 extn boxes  */
  134.         "PRG",       /*    NOTE for all 5 boxes this should be 3 chars long, even if spaces   */
  135.         "DOC",
  136.         extn3str,
  137.         extn4str,    /* IO extension text for last 2 extn boxes (editable) */
  138.         fname);      /* O  returns complete path and file */
  139.  
  140.  
  141.    if (r != 0) {
  142.       undelete_file( fname );
  143.    } /* if */
  144.  
  145.    return 0; /* successful completion */
  146.  
  147. } /* main */
  148.  
  149.  
  150. /********************************************************
  151. *
  152. *  replacement for Fsfirst() & Fsnext() to recognise deleted files
  153. *
  154. ********************************************************/
  155.  
  156. struct BPB {
  157.    short recsiz;        /* physical sector size in bytes */
  158.    short clsiz;         /* cluster size in sectors */
  159.    short clsizb;        /* cluster size in bytes */
  160.    short rdlen;         /* root directory length in sectors */
  161.    short fsiz;          /* FAT size in sectors */
  162.    short fatrec;        /* second FAT starts at this sector */
  163.    short datrec;        /* data sectors start here */
  164.    short numcl;         /* nr of data clusters on disk */
  165.    short bflags;        /* flags */
  166. };
  167.  
  168. /*** custom dta buffer for d_First() & d_Next() ***/
  169. struct DTA {
  170.     WORD            dta_sector;
  171.         DENTRY         *dta_dirBuffer;
  172.         DENTRY         *dta_entryp;
  173.         DENTRY         *dta_dirend;
  174.         unsigned short  dta_srchattr;
  175.         char        dta_buf[4];
  176.     char        dta_drv;
  177.     char        dta_attribute;
  178.     unsigned short    dta_time;
  179.     unsigned short    dta_date;
  180.     long        dta_size;
  181.     char        dta_name[14];
  182. };
  183.  
  184. PRIVATE struct BPB  *bpbp;
  185. PRIVATE struct DTA  *dtap;
  186.  
  187. short d_First(char *drvp, char *pathp, short attr)
  188. {
  189. WORD      dnum;                  /* drive nr, A: = 0, etc */
  190. int       error;
  191. WORD      snum;                  /* sector number */
  192.  
  193.    dnum = toupper(*drvp) - 'A';
  194.    if( (Drvmap() & (1 << dnum)) == 0 ) {
  195.       return ENXIO;
  196.    } /* if */
  197.  
  198.    if( *pathp == '\\' ) {
  199.       pathp++;
  200.    } /* if */
  201.  
  202.    /*** get FAT size, root directory sector, etc from Bios Parameter Block...  ***/
  203.    if( (bpbp = (struct BPB *)Getbpb(dnum)) == 0 ) {
  204.       char  emsg[80];
  205.       sprintf( emsg, "[1][can't get parameters for drive %c:][QUIT]", 'A'+dnum);
  206.       form_alert(1, emsg );
  207.       exit(1);            /* quit  */
  208.    } /* if */
  209.  
  210.    /*** allocate memory for directory buffers & FAT tables ***/
  211.  
  212.    if( ((dirBuffer = (DENTRY *)malloc(bpbp->clsizb)) == NULL
  213.       OR (FatBuffer = (BYTE *)malloc(bpbp->fsiz * bpbp->recsiz)) == NULL) )
  214.    {
  215.       form_alert(1, "[1][can't allocate enough memory][QUIT]" );
  216.       exit(1);            /* quit  */
  217.    } /* if */
  218.  
  219.    dirend = &dirBuffer[bpbp->clsizb/sizeof(*dirBuffer)];
  220.  
  221.    /*** read in FAT ***/
  222.  
  223.    error = Rwabs( 0 /* read mode */, FatBuffer, bpbp->fsiz,
  224.                   bpbp->fatrec - bpbp->fsiz, /* first FAT starts here */
  225.                   dnum );
  226.    if (error != 0) {
  227.       char  emsg[80];
  228.       sprintf( emsg, "[1][%s|while reading FAT on drive %c][QUIT]",
  229.                                      strerror(-error), dnum + 'A' );
  230.       form_alert(1, emsg);
  231.       exit(1);              /* quit */
  232.    } /* if */
  233.  
  234.    if( (error = find_subdir( dnum, pathp, &snum )) != 0 )
  235.    {
  236.       return error;
  237.    } /* if */
  238.  
  239.    if( (attr & FA_DIR) == 0 ) {
  240.       char  emsg[80];
  241.       sprintf( emsg, "[1][path is not a directory:|%s][QUIT]", pathp );
  242.       form_alert(1, emsg);
  243.       exit(1);            /* quit */
  244.    } /* if */
  245.  
  246.    /*******************************************************
  247.    *
  248.    * snum is the sector which has the directory entries
  249.    *
  250.    *******************************************************/
  251.  
  252.    error = Rwabs( 0 /* read mode */, dirBuffer, bpbp->clsiz, snum, dnum );
  253.    if (error != 0) {
  254.       char  emsg[80];
  255.       sprintf( emsg, "[1][%s|while reading|directory entries on drive %c][QUIT]",
  256.                      strerror(-error), dnum + 'A' );
  257.       form_alert(1, emsg);
  258.       exit(1);            /* quit */
  259.    } /* if */
  260.  
  261.    /*** fill the dta buffer with the first directory entry in dirBuffer ***/
  262.  
  263.    dtap = (struct DTA *)Fgetdta();
  264.  
  265.    dtap->dta_sector = snum;
  266.    dtap->dta_dirBuffer = dirBuffer;
  267.    dtap->dta_entryp = dirBuffer;
  268.    dtap->dta_dirend = dirend;
  269.    dtap->dta_drv = dnum;
  270.  
  271.    return fill_dta(0) ? 0 : -ENOENT;
  272.  
  273. } /* d_First() */
  274.  
  275.  
  276. short d_Next()
  277. {
  278.    return fill_dta(0) ? 0 : -ENMFILES;   /* OK ? */
  279. } /* d_Next() */
  280.  
  281.  
  282. /*************************************************************
  283. *
  284. * copy next subdirectory entry into dta
  285. *
  286. *************************************************************/
  287.  
  288. PRIVATE BOOL fill_dta( WORD srch_att )
  289. {
  290. DENTRY     *dptr;                  /* current directory entry */
  291. WORD        cluster;
  292. int         error;
  293. struct DTA *dtap;
  294.  
  295.    dtap = (struct DTA *)Fgetdta();
  296.    dptr = dtap->dta_entryp++;
  297.  
  298.    if( dptr >= dtap->dta_dirend ) {
  299.  
  300.       /*** all entries in this cluster exhausted, get next cluster ***/
  301.  
  302.       if( dtap->dta_sector >= bpbp->datrec ) {
  303.      /*** this is a subdirectory ***/
  304.      cluster = cluster_of( dtap->dta_sector );
  305.      if( (cluster = next_cluster( cluster )) <= CHAIN_END ) {
  306.         return( FALSE ); /* no more files */
  307.      }
  308.      else {
  309.         dtap->dta_sector = sector_of( cluster );
  310.      } /* if */
  311.       }
  312.       else if( dtap->dta_sector >= bpbp->fatrec + bpbp->fsiz ) {
  313.      /*** this is the root directory ***/
  314.      dtap->dta_sector += bpbp->clsiz;    /* next cluster in root dir */
  315.      if( dtap->dta_sector >= bpbp->datrec ) {
  316.         return( FALSE ); /* no more files */
  317.      } /* if */
  318.       }
  319.       else {
  320.      /*** this shouldn't happen ***/
  321.      char  emsg[80];
  322.      sprintf( emsg, "[1][error while reading FAT on drive %c][QUIT]",
  323.                      dtap->dta_drv + 'A' );
  324.      form_alert(1, emsg);
  325.      exit(1);           /* quit */
  326.       } /* if */
  327.  
  328.       error = Rwabs( 0 /* read mode */, dtap->dta_dirBuffer,
  329.              bpbp->clsiz, dtap->dta_sector, dtap->dta_drv );
  330.       if (error != 0) {
  331.      char  emsg[80];
  332.      sprintf( emsg, "[1][%s|while reading|directory entries on drive %c][QUIT]",
  333.                 strerror(-error), dtap->dta_drv + 'A' );
  334.      form_alert(1, emsg);
  335.      exit(1);           /* quit */
  336.       } /* if */
  337.       dtap->dta_entryp = dtap->dta_dirBuffer;
  338.       dptr = dtap->dta_entryp++;
  339.    } /* if */
  340.  
  341.    if( dptr->name[0] == '\0' ) {
  342.       return( FALSE );         /* no more files */
  343.    } /* if */
  344.  
  345.    dtap->dta_attribute = dptr->attrib;
  346.    dtap->dta_time = dptr->time;
  347.    dtap->dta_date = dptr->date;
  348.    dtap->dta_size = dptr->fsize;
  349.    fnmcpy( dtap->dta_name, dptr->name );
  350.    assert( strlen(dtap->dta_name) < sizeof(dtap->dta_name) ); /* in case overflow */
  351.  
  352.    return TRUE;
  353.  
  354. } /* fill_dta() */
  355.  
  356.  
  357. /*************************************************************
  358. *
  359. * find the first sector for a pathname
  360. * (the sector contains the subdirectory list)
  361. *
  362. **************************************************************/
  363.  
  364. PRIVATE int find_subdir( WORD dnum, const char *pathp, WORD *snump )
  365. {
  366. WORD      snum;
  367. WORD      attr;
  368. WORD      cluster;
  369. int       error;
  370. DENTRY   *dptr;                  /* current directory entry */
  371.  
  372.    /*** follow the chain of directory entries to match path name ***/
  373.  
  374.    snum = bpbp->fatrec + bpbp->fsiz;        /* root directory starts here */
  375.  
  376.    if( *pathp == '\\' ) {
  377.          pathp++;
  378.    } /* if */
  379.    while( *pathp != '\0' ) {   /* loop thru all subdirectories in the path */
  380.  
  381.       error = Rwabs( 0 /* read mode */, dirBuffer, bpbp->clsiz, snum, dnum );
  382.       if (error != 0) {
  383.      char  emsg[80];
  384.      sprintf( emsg, "[1][%s|while reading|directory entries on drive %c][QUIT]",
  385.                     strerror(-error), dnum + 'A' );
  386.      form_alert(1, emsg);
  387.      exit(1);           /* quit */
  388.       } /* if */
  389.       dptr = dirBuffer;
  390.  
  391.       /*** loop thru all directory entries until the name is found ***/
  392.  
  393.       while( ((dptr->attrib & FA_DIR) == 0)         /* not a directory ? */
  394.             OR NOT match( pathp, dptr->name ) ) {   /* names don't match ? */
  395.  
  396.      if( ++dptr >= dirend ) {
  397.  
  398.         /*** name not found in this cluster, get next cluster ***/
  399.    
  400.         if( snum >= bpbp->datrec ) {
  401.            /*** this is a subdirectory ***/
  402.            cluster = cluster_of( snum );
  403.            if( (cluster = next_cluster( cluster )) <= CHAIN_END ) {
  404.           return( -EPATH );    /* path not found */
  405.            }
  406.            else {
  407.           snum = sector_of( cluster );
  408.            } /* if */
  409.         }
  410.         else if( snum >= bpbp->fatrec + bpbp->fsiz ) {
  411.            /*** this is the root directory ***/
  412.            snum += bpbp->clsiz;    /* next cluster in root dir */
  413.            if( snum >= bpbp->datrec ) {
  414.           return( -EPATH );    /* path not found */
  415.            } /* if */
  416.         }
  417.         else {
  418.            /*** this shouldn't happen ***/
  419.            char  emsg[80];
  420.            sprintf( emsg, "[1][error while reading|directory on drive %c][QUIT]",
  421.                            dnum + 'A' );
  422.            form_alert(1, emsg);
  423.            exit(1);          /* quit */
  424.         } /* if */
  425.    
  426.         error = Rwabs( 0 /* read mode */, dirBuffer, bpbp->clsiz, snum, dnum );
  427.         if (error != 0) {
  428.            char  emsg[80];
  429.            sprintf( emsg, "[1][%s|while reading|directory entries on drive %c][QUIT]",
  430.                           strerror(-error), dnum + 'A' );
  431.            form_alert(1, emsg);
  432.            exit(1);          /* quit */
  433.         } /* if */
  434.       
  435.         dptr = dirBuffer;
  436.      } /* if */
  437.      
  438.       } /* while */
  439.  
  440.       /*** the next subdirectory name has been found ***/
  441.  
  442.       snum = sector_of( swap_word(dptr->cluster) );
  443.       attr = dptr->attrib;
  444.       while( (*pathp != '\0') AND (*pathp != '\\') ) {
  445.          pathp++;
  446.       } /* while */
  447.       if( *pathp == '\\' ) {
  448.          pathp++;
  449.       } /* if */
  450.    } /* while */
  451.  
  452.    *snump = snum;
  453.    return 0;
  454.  
  455. } /* find_subdir() */
  456.  
  457.  
  458. /**************************************************************
  459. *
  460. * undelete_file
  461. *  pathp      -- points to full pathname of file
  462. *                eg A:\..dir_path..\file_name
  463. *
  464. * - find the directory entry of the file
  465. * - assume the file contents are in the empty clusters following the starting
  466. *   cluster of the file.
  467. * - build an image of the file in data_buffer
  468. * - write out data_buffer to a new file
  469. *
  470. ***************************************************************/
  471.  
  472. PRIVATE void undelete_file( const char *pathp )
  473. {
  474. WORD     snum;             /* sector number of directory entry */
  475. WORD     dnum;             /* disk drive number */
  476. WORD     cluster;
  477. WORD     current;          /* current cluster in chain of deleted file */
  478. WORD     previous;         /* unused ??? */
  479. unsigned nrcl;             /* number of clusters in deleted file */
  480. void    *data_buffer;      /* put copy of restored file here */
  481. void    *datap;            /* points into data_buffer */
  482. int      i;
  483. int      error;            /* error return code for various functions */
  484. char     fname[13];        /* name of file to be undeleted */
  485. char     dir_path[80];     /* directory path of file to be undeleted */
  486. long     file_size;        /* size of the file */
  487. DENTRY  *dptr;             /* points to a copy of the directory entry of file */
  488. int      fh;               /* file handle for restored copy of file */
  489. char     restored_fn[80];  /* name of restored file */
  490. char     Path[80];         /* used for fsel_input() */
  491. int      ExitButton;       /* used for fsel_input() */
  492. long     bytes_written;    /* returned by Fwrite() */
  493.  
  494.    assert( pathp[1] == ':' );
  495.    dnum = toupper(*pathp) - 'A';
  496.    if( (Drvmap() & (1 << dnum)) == 0 ) {
  497.       char  emsg[80];
  498.       sprintf( emsg, "[1][drive %c: doesn't exist!][QUIT]", *pathp );
  499.       form_alert(1, emsg );
  500.       exit(1);            /* quit  */
  501.    } /* if */
  502.  
  503.    /*** split pathp into directory path & filename path ***/
  504.    strcpy (dir_path, pathp+2);
  505.    i = strlen( dir_path );
  506.    while( i >= 0 ) {
  507.       if( dir_path[i] == '\\' ) {
  508.          strcpy(fname, dir_path+i+1);
  509.          dir_path[i+1] = '\0';
  510.          break;
  511.       } /* if */
  512.       i--;
  513.    } /* while */
  514.  
  515.    if( find_subdir( dnum, dir_path, &snum ) != 0 ) {
  516.       char  emsg[80];
  517.       sprintf( emsg, "[1][can't find file|%s][QUIT]", fname);
  518.       form_alert(1, emsg );
  519.       exit(1);            /* quit  */
  520.    } /* if */
  521.  
  522.  
  523.    /*** look for file name in directory sector ***/
  524.  
  525.    if (Rwabs( 0 /* read mode */, dirBuffer, bpbp->clsiz, snum, dnum ) != 0) {
  526.       char  emsg[80];
  527.       sprintf( emsg, "[1][%s|while reading|directory entries on drive %c][QUIT]",
  528.                 strerror(-error), dtap->dta_drv + 'A' );
  529.       form_alert(1, emsg);
  530.       exit(1);           /* quit */
  531.    } /* if */
  532.    dptr = dirBuffer;
  533.  
  534.    while( NOT match( fname, dptr->name ) ) {
  535.  
  536.       dptr++;         /* point to next name */
  537.       if( dptr >= dirend ) {
  538.  
  539.      /*** all entries in this cluster exhausted, get next cluster ***/
  540.  
  541.      if( snum >= bpbp->datrec ) {
  542.         /*** this is a subdirectory ***/
  543.         cluster = cluster_of( snum );
  544.         if( (cluster = next_cluster( cluster )) <= CHAIN_END ) {
  545.            char  emsg[80];
  546.            sprintf( emsg, "[1][can't find file|\"%s\"][QUIT]", fname);
  547.            form_alert(1, emsg );
  548.            exit(1);          /* quit  */
  549.         }
  550.         else {
  551.            snum = sector_of( cluster );
  552.         } /* if */
  553.      }
  554.      else if( snum >= bpbp->fatrec + bpbp->fsiz ) {
  555.         /*** this is the root directory ***/
  556.         snum += bpbp->clsiz;    /* next cluster in root dir */
  557.         if( snum >= bpbp->datrec ) {
  558.            char  emsg[80];
  559.            sprintf( emsg, "[1][can't find file|\"%s\"][QUIT]", fname);
  560.            form_alert(1, emsg );
  561.            exit(1);          /* quit  */
  562.         } /* if */
  563.      }
  564.      else {
  565.         /*** this shouldn't happen ***/
  566.         char  emsg[80];
  567.         sprintf( emsg, "[1][error while reading|root directory on drive %c][QUIT]",
  568.                         dnum + 'A' );
  569.         form_alert(1, emsg);
  570.         exit(1);          /* quit */
  571.      } /* if */
  572.  
  573.      if (Rwabs(0 /* read mode */, dirBuffer, bpbp->clsiz, snum, dnum) != 0) {
  574.         char  emsg[80];
  575.         sprintf( emsg, "[1][%s|while reading|directory entries on drive %c][QUIT]",
  576.                    strerror(-error), dtap->dta_drv + 'A' );
  577.         form_alert(1, emsg);
  578.         exit(1);          /* quit */
  579.      } /* if */
  580.      dptr = dirBuffer;
  581.       } /* if */
  582.  
  583.       if( dptr->name[0] == '\0' ) {
  584.      char  emsg[80];
  585.          sprintf( emsg, "[1][can't find file|\"%s\"][QUIT]", fname);
  586.      form_alert(1, emsg );
  587.      exit(1);           /* quit    */
  588.       } /* if */
  589.    } /* while */
  590.  
  591.  
  592.    /********************************************************
  593.    *
  594.    * found the file to be undeleted:
  595.    * - dptr points to a copy of the directory entry
  596.    *
  597.    ********************************************************/
  598.  
  599.    if( (char)dptr->name[0] != (char)0xe5) {
  600.       char  emsg[80];
  601.       sprintf( emsg, "[1][file is not deleted|\"%s\"][QUIT]", pathp);
  602.       form_alert(1, emsg );
  603.       exit(1);            /* quit  */
  604.    } /* if */
  605.  
  606.    current = previous = swap_word(dptr->cluster);
  607.    if( next_cluster(current) != 0 ) {
  608.       char  emsg[80];
  609.       sprintf( emsg, "[1][\"%s\"|has been overwritten][QUIT]", pathp);
  610.       form_alert(1, emsg );
  611.       exit(1);            /* quit  */
  612.    } /* if */
  613.  
  614.    if( dptr->attrib == FA_DIR ) {
  615.       nrcl = 0;                  /* directories have file size = 0 */
  616.    }
  617.    else {
  618.       file_size = swap_long(dptr->fsize);
  619.       nrcl = (file_size + bpbp->clsizb - 1)/bpbp->clsizb;
  620.    } /* if */
  621.  
  622.    if( (datap = data_buffer = malloc( nrcl*bpbp->clsizb )) == NULL ) {
  623.       form_alert(1, "[1][can't allocate enough memory][QUIT]" );
  624.       exit(1);            /* quit  */
  625.    } /* if */
  626.  
  627.    while( nrcl > 0 ) {
  628.       if( current > bpbp->numcl ) {
  629.          char  emsg[80];
  630.          sprintf( emsg, "[1][can't recover all of|\"%s\"][Carry On][QUIT]", pathp );
  631.          if( form_alert(1, emsg ) == 2 ) {
  632.             exit(1);            /* quit  */
  633.          } /* if */
  634.          file_size = (datap-data_buffer)*sizeof(*datap);
  635.       } /* if */
  636.       if( next_cluster( current ) == 0 ) {
  637.      error = Rwabs( 0 /* read mode */, datap, bpbp->clsiz, sector_of(current), dnum );
  638.      if (error != 0) {
  639.         char  emsg[80];
  640.         sprintf( emsg, "[1][%s|while reading|file sectors on drive %c][QUIT]",
  641.                    strerror(-error), dtap->dta_drv + 'A' );
  642.         form_alert(1, emsg);
  643.         exit(1);          /* quit */
  644.      } /* if */
  645.      datap += bpbp->clsizb;
  646.          previous = current;
  647.          nrcl--;
  648.       } /* if */
  649.       current++;
  650.    } /* while */
  651.  
  652.    /*** now write the file out ***/
  653.  
  654.    strcpy(Path, "A:\\*.*");
  655.    Path[0] += Dgetdrv();
  656.    if( fsel_input(Path, restored_fn, &ExitButton) == 0 )
  657.    {
  658.       form_alert(1, "[1]can't select a name|for restored file[oh dear]");
  659.    } /* if */
  660.    if( ExitButton == 0 )
  661.    {
  662.       /* user has cancelled !!! */
  663.       exit(1);
  664.    } /* if */
  665.  
  666.    /*** merge Path & restored file name ***/
  667.    i = strlen( Path );
  668.    while( i >= 0 ) {
  669.       if( Path[i] == '\\' ) {
  670.          strcpy(Path+i+1, restored_fn);
  671.          strcpy(restored_fn, Path);
  672.          break;
  673.       } /* if */
  674.       i--;
  675.    } /* while */
  676.  
  677.    
  678.    if( (fh = Fopen( restored_fn, 1)) < 0 ) {
  679.       if( fh != -ENOENT ) {
  680.      char  emsg[80];
  681.      sprintf( emsg, "[1][%s|while opening %s][QUIT]",
  682.                 strerror(-fh), restored_fn );
  683.      form_alert(1, emsg);
  684.      exit(1);           /* quit */
  685.       } /* if */
  686.       if( (fh = Fcreate( restored_fn, 0)) < 0 ) {
  687.      char  emsg[80];
  688.      sprintf( emsg, "[1][%s|while creating %s][QUIT]",
  689.                 strerror(-fh), restored_fn );
  690.      form_alert(1, emsg);
  691.      exit(1);           /* quit */
  692.       } /* if */
  693.    } /* if */
  694.    if( (bytes_written = Fwrite(fh, file_size, data_buffer)) < 0 ) {
  695.       char  emsg[80];
  696.       error = (int)bytes_written;
  697.       sprintf( emsg, "[1][%s|while writing %s][QUIT]",
  698.                  strerror(-error), restored_fn );
  699.       form_alert(1, emsg);
  700.       exit(1);            /* quit */
  701.    } /* if */
  702.  
  703.    if( (error = Fclose(fh)) != 0 ) {
  704.       char  emsg[80];
  705.       sprintf( emsg, "[1][%s|while trying to close %s][QUIT]",
  706.                  strerror(-error), restored_fn );
  707.       form_alert(1, emsg);
  708.       exit(1);            /* quit */
  709.    } /* if */
  710.  
  711. } /* undelete_file() */
  712.  
  713.  
  714. /**************************************************************
  715. *
  716. *  find next cluster in a chain of FAT entries ...
  717. *
  718. *  FAT entries:   12-bit      16-bit         meaning
  719. *                 ------      ------         -------
  720. *                 000         0000           free cluster
  721. *                 001         0001           shouldn't happen
  722. *                 002 - FEF   0002 - 7FFF    next cluster nr
  723. *                             8000 - FFEF    shouldn't happen
  724. *                 FF0 - FF7   FFF0 - FFF7    bad cluster
  725. *                 FF8 - FFF   FFF8 - FFFF    end of chain
  726. *
  727. *  max nr clusters for 12 bit FAT = 4078
  728. *
  729. **************************************************************/
  730.  
  731. PRIVATE WORD next_cluster( const WORD clust )
  732. {
  733. int   index;      /* index into FAT */
  734. WORD  newclust;   /* next cluster */
  735.  
  736.    /*** if nr clusters on disk > MAX_CLUSTERS use 16-bit FATs ***/
  737.  
  738.    if( bpbp->numcl > MAX_CLUSTERS ) {
  739.       /*** 16-bit FATs ***/
  740.       index = clust;
  741.       newclust = FatBuffer[index] + (FatBuffer[index+1] << 8); /* byte swap */
  742.       if( newclust >= 0xfff8 ) {
  743.          return CHAIN_END;
  744.       }
  745.       else if( newclust >= 0x8000 )
  746.       {
  747.          /*** there is a file system problem if this happens ***/
  748.      char  emsg[128];
  749.          sprintf(emsg, "[1][file system problem|bad cluster chain|"
  750.              "at cluster %1d, value is %1d][QUIT]", (int)clust, (int)newclust );
  751.      form_alert(1, emsg );
  752.          exit(1);            /* quit  */
  753.       } /* if */
  754.    }
  755.    else {
  756.       /*** 12-bit FATs ***/
  757.       index = clust + (clust >> 1);
  758.       newclust = FatBuffer[index] + (FatBuffer[index+1] << 8); /* byte swap */
  759.       if( clust & 1) {        /* is clust odd ?? */
  760.          newclust >>= 4;
  761.       } /* if */
  762.       newclust &= 0x0fff;
  763.       if( newclust >= 0xff8 ) {
  764.          return CHAIN_END;
  765.       }
  766.       else if( newclust >= 0x0ff0 )
  767.       {
  768.          /*** there is a file system problem if this happens ***/
  769.      char  emsg[128];
  770.          sprintf(emsg, "[1][file system problem|bad cluster chain|"
  771.              "at cluster %1d, value is %1d][QUIT]", (int)clust, (int)newclust );
  772.      form_alert(1, emsg );
  773.          exit(1);            /* quit  */
  774.       } /* if */
  775.    } /* if */
  776.  
  777.    return newclust;
  778.  
  779. } /* next_cluster() */
  780.  
  781.  
  782. /********************************************************
  783. *
  784. *  check names for a match.
  785. *  fname in format 'nnnnnnnneee', ie 8 chars name + 3 chars extension
  786. *  sname is a null terminated string.
  787. *  match up to first directory separator (ie '\') in sname
  788. *  return TRUE if match found, FALSE otherwise
  789. *
  790. ********************************************************/
  791.  
  792. PRIVATE BOOL match( const char *sname,    /* search match name */
  793.                     const char *fname     /* file or directory name */
  794.                   )
  795. {
  796. const char  *fext;    /* file or dir extension */
  797.  
  798.    fext = fname + 8;
  799.    while( fname < fext+3 ) {
  800.       if( *fname == *sname ) {
  801.          fname++;
  802.          sname++;
  803.       }
  804.       else {
  805.          /*** check why names differ ***/
  806.          switch( *sname++ ) {
  807.          case '.':
  808.             if( (*fname == ' ') OR (fname == fext) ) {
  809.                fname = fext;
  810.             }
  811.             else {
  812.                return FALSE;
  813.             } /* if */
  814.             break;
  815.          case '\\':
  816.          case '\0':
  817.             if( *fname == ' ' ) {
  818.                return TRUE;
  819.             } /* if */
  820.             break;
  821.          default:
  822.             return FALSE;
  823.          } /* switch */
  824.       } /* if */
  825.    } /* while */
  826.    return TRUE;
  827. } /* match() */
  828.  
  829.  
  830. /*******************************
  831. *
  832. *  swap bytes the bytes in a word argument
  833. *
  834. *******************************/
  835.  
  836. PRIVATE WORD swap_word( const WORD word )
  837. {
  838. WORD  r;
  839.  
  840.    ((char *)&r)[0] = ((char *)&word)[1];
  841.    ((char *)&r)[1] = ((char *)&word)[0];
  842.    return r;
  843.  
  844. } /* swap_word() */
  845.  
  846.  
  847. PRIVATE LONG swap_long( const LONG l )
  848. {
  849. LONG  r;
  850.  
  851.    ((char *)&r)[0] = ((char *)&l)[3];
  852.    ((char *)&r)[1] = ((char *)&l)[2];
  853.    ((char *)&r)[2] = ((char *)&l)[1];
  854.    ((char *)&r)[3] = ((char *)&l)[0];
  855.    return r;
  856.  
  857. } /* swap_word() */
  858.  
  859.  
  860. /********************************************
  861. *
  862. * copy filename from directory entry to dta
  863. *
  864. ********************************************/
  865.  
  866. PRIVATE void fnmcpy(char* d, const char *s)
  867. {
  868. int         i;     /* character counter */
  869. const char *extp;  /* pointer to extension */
  870.  
  871.    extp = s+8;
  872.  
  873.    i = 8;   /* copy up to 8 chars for name */
  874.    while( (i > 0) AND ((unsigned)*s > ' ') ) {     /* stop at '\0' or ' ' */
  875.       *d++ = *s++;
  876.       i--;
  877.    } /* while */
  878.  
  879.    if( (unsigned)*extp > ' ' ) {
  880.       *d++ = '.';
  881.       i = 3;   /* copy up to 3 chars for extension */
  882.       while( (i > 0) AND ((unsigned)*extp > ' ') ) {
  883.      *d++ = *extp++;
  884.      i--;
  885.       } /* while */
  886.    } /* if */
  887.    *d = '\0';
  888. } /* fnmcpy() */
  889.  
  890.  
  891.  
  892. /************************* end of undel.c *************************/
  893.