home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / fst03f.zip / fst.c < prev    next >
C/C++ Source or Header  |  1996-08-13  |  41KB  |  1,560 lines

  1. /* fst.c -- Main module of fst
  2.    Copyright (c) 1995-1996 by Eberhard Mattes
  3.  
  4. This file is part of fst.
  5.  
  6. fst is free software; you can redistribute it and/or modify it
  7. under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. fst is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with fst; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 59 Temple Place - Suite 330,
  19. Boston, MA 02111-1307, USA.  */
  20.  
  21.  
  22. #define INCL_DOSDEVIOCTL
  23. #define INCL_DOSNLS
  24. #include <os2.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <stdarg.h>
  28. #include <string.h>
  29. #include <ctype.h>
  30. #include <errno.h>
  31. #include "fst.h"
  32. #include "crc.h"
  33. #include "diskio.h"
  34. #include "fat.h"
  35. #include "do_hpfs.h"
  36. #include "do_fat.h"
  37.  
  38. static char banner[] =
  39. "fst 0.3f -- Copyright (c) 1995-1996 by Eberhard Mattes\n";
  40.  
  41. /* Increase the heap for -Zsys to 64MB. */
  42. unsigned _sys_heap_size = 0x4000000;
  43.  
  44. char verbose;                   /* Non-zero for `check -v' and `save -v' */
  45. char sector_number_format;      /* 'x' for `-x', 0 otherwise */
  46. char a_info;                    /* Non-zero for `info' action */
  47. char a_save;                    /* Non-zero for `save' action */
  48. char a_check;                   /* Non-zero for `check' action */
  49. char a_what;                    /* Non-zero for `info <number>' action */
  50. char a_where;                   /* Non-zero for `info <path>' action */
  51. char a_copy;                    /* Non-zero for `copy' action */
  52. char a_dir;                     /* Non-zero for `dir' action */
  53. char a_find;                    /* Non-zero for finding a file */
  54. char plenty_memory;             /* Non-zero for `check -m' */
  55. char check_unused;              /* Non-zero for `check -u' */
  56. char check_pedantic;            /* Non-zero for `check -p' */
  57. char show_unused;               /* Non-zero for `info -u' */
  58. char show_free_frag;            /* Non-zero for `info -f' */
  59. char show_frag;                 /* Non-zero for `check -f' */
  60. char show_eas;                  /* Non-zero for `info -e <path>' */
  61. char show_summary;              /* Non-zero for `check -s' */
  62. char force_fs;                  /* Non-zero to force to a specific fs */
  63. ULONG what_sector;              /* Sector number for `info <number>' action */
  64. char what_cluster_flag;         /* `what_sector' is a cluster number */
  65. const char *find_path;          /* (Remainder of) pathname for a_find */
  66.  
  67. /* This table maps lower-case letters to upper case, using the current
  68.    code page. */
  69. BYTE cur_case_map[256];
  70.  
  71. FILE *diag_file;                /* Stream for diagnostics */
  72. FILE *prog_file;                /* Stream for progress report */
  73. static FILE *info_file;         /* Stream for information */
  74.  
  75. static int warning_count[2];    /* Index 0: warnings, index 1: errors */
  76.  
  77. static char list_going;         /* Non-zero if list started */
  78. static int list_x;              /* Column */
  79. static char list_msg[80];       /* Message */
  80.  
  81. /* path_chain_new() tries to avoid malloc()'s memory overhead by
  82.    allocating reasonably big buffers out of which the strings and
  83.    path_chains will be allotted.  Fortunately, we never free those
  84.    structures. */
  85.  
  86. struct str_buf
  87. {
  88.   char *buf;
  89.   size_t size;
  90.   size_t used;
  91.   struct str_buf *next;
  92. };
  93.  
  94. struct str_buf *str_buf_head;
  95.  
  96. static path_chain *pc_buf;
  97. static size_t pc_count;
  98. static size_t pc_used;
  99.  
  100. /* Clean up and terminate the process.  RC is the return code passed
  101.    to the parent process.  If SHOW is non-zero, show the number of
  102.    warnings and errors even if both numbers are zero. */
  103.  
  104. void quit (int rc, int show)
  105. {
  106.   if (save_file != NULL)
  107.     {
  108.       fclose (save_file);
  109.       save_file = NULL;
  110.       remove (save_fname);
  111.     }
  112.   if (warning_count[0] != 0 || warning_count[1] != 0 || show)
  113.     fprintf (stdout, "Total warnings: %d, total errors: %d\n",
  114.              warning_count[0], warning_count[1]);
  115.   if (rc == 0 && warning_count[1] != 0)
  116.     rc = 1;
  117.   exit (rc);
  118. }
  119.  
  120.  
  121. /* Treat `#%lu' in FMT as format specifiers for sector numbers.  We
  122.    cannot introduce our own format specifier (such as `%N') because
  123.    that would cause loads of GCC warnings or we would loose checking
  124.    of format strings. */
  125.  
  126. static void adjust_format_string (char *dst, const char *src)
  127. {
  128.   while (*src != 0)
  129.     if (src[0] != '#' || src[1] != '%' || src[2] != 'l' || src[3] != 'u')
  130.       *dst++ = *src++;
  131.     else
  132.       {
  133.         src += 4;
  134.         switch (sector_number_format)
  135.           {
  136.           case 'x':
  137.             *dst++ = '0'; *dst++ = 'x'; *dst++ = '%';
  138.             *dst++ = '.'; *dst++ = '8'; *dst++ = 'l'; *dst++ = 'x';
  139.             break;
  140.           default:
  141.             *dst++ = '%'; *dst++ = 'l'; *dst++ = 'u';
  142.             break;
  143.           }
  144.       }
  145.   *dst = 0;
  146. }
  147.  
  148.  
  149. /* Like vfprintf(), but treat #%lu specially (sector number).  Return
  150.    the number of characters printed. */
  151.  
  152. int my_vfprintf (FILE *f, const char *fmt, va_list arg_ptr)
  153. {
  154.   char new_fmt[512];
  155.  
  156.   adjust_format_string (new_fmt, fmt);
  157.   return vfprintf (f, new_fmt, arg_ptr);
  158. }
  159.  
  160.  
  161. /* Like fprintf(), but treat #%lu specially (sector number).  Return
  162.    the number of characters printed. */
  163.  
  164. int my_fprintf (FILE *f, const char *fmt, ...)
  165. {
  166.   va_list arg_ptr;
  167.   char new_fmt[512];
  168.   int r;
  169.  
  170.   adjust_format_string (new_fmt, fmt);
  171.   va_start (arg_ptr, fmt);
  172.   r = my_vfprintf (f, new_fmt, arg_ptr);
  173.   va_end (arg_ptr);
  174.   return r;
  175. }
  176.  
  177.  
  178. /* Like vsprintf(), but treat #%lu specially (sector number).  Return
  179.    the number of characters printed. */
  180.  
  181. int my_vsprintf (char *buf, const char *fmt, va_list arg_ptr)
  182. {
  183.   char new_fmt[512];
  184.  
  185.   adjust_format_string (new_fmt, fmt);
  186.   return vsprintf (buf, new_fmt, arg_ptr);
  187. }
  188.  
  189.  
  190. /* Like sprintf(), but treat #%lu specially (sector number).  Return
  191.    the number of characters printed. */
  192.  
  193. int my_sprintf (char *buf, const char *fmt, ...)
  194. {
  195.   va_list arg_ptr;
  196.   char new_fmt[512];
  197.   int r;
  198.  
  199.   adjust_format_string (new_fmt, fmt);
  200.   va_start (arg_ptr, fmt);
  201.   r = my_vsprintf (buf, new_fmt, arg_ptr);
  202.   va_end (arg_ptr);
  203.   return r;
  204. }
  205.  
  206.  
  207. /* Display a fatal error message and terminate the process. */
  208.  
  209. void error (const char *fmt, ...)
  210. {
  211.   va_list arg_ptr;
  212.  
  213.   list_end ();
  214.   fflush (info_file);
  215.   fprintf (stderr, "ERROR: ");
  216.   va_start (arg_ptr, fmt);
  217.   my_vfprintf (stderr, fmt, arg_ptr);
  218.   va_end (arg_ptr);
  219.   fputc ('\n', stderr);
  220.   warning_count[1] += 1;
  221.   quit (2, TRUE);
  222. }
  223.  
  224.  
  225. /* Common code for all functions which display warnings.  Show the
  226.    severity (LEVEL) of the message and increment the appropriate
  227.    counter. */
  228.  
  229. void warning_prolog (int level)
  230. {
  231.   list_end ();
  232.   fflush (info_file);
  233.   switch (level)
  234.     {
  235.     case 0:
  236.       fprintf (diag_file, "WARNING: ");
  237.       break;
  238.     case 1:
  239.       fprintf (diag_file, "ERROR: ");
  240.       break;
  241.     default:
  242.       abort ();
  243.     }
  244.   warning_count[level] += 1;
  245. }
  246.  
  247.  
  248. /* Common code for all functions which display warnings.  This is
  249.    called after printing a message. */
  250.  
  251. void warning_epilog (void)
  252. {
  253.   fflush (diag_file);
  254. }
  255.  
  256.  
  257. /* Display a warning of severity LEVEL (0=warning, 1=error). */
  258.  
  259. void warning (int level, const char *fmt, ...)
  260. {
  261.   va_list arg_ptr;
  262.  
  263.   warning_prolog (level);
  264.   va_start (arg_ptr, fmt);
  265.   my_vfprintf (diag_file, fmt, arg_ptr);
  266.   va_end (arg_ptr);
  267.   fputc ('\n', diag_file);
  268.   warning_epilog ();
  269. }
  270.  
  271.  
  272. /* Continue a warning. */
  273.  
  274. void warning_cont (const char *fmt, ...)
  275. {
  276.   va_list arg_ptr;
  277.  
  278.   va_start (arg_ptr, fmt);
  279.   fputs ("  ", diag_file);
  280.   my_vfprintf (diag_file, fmt, arg_ptr);
  281.   va_end (arg_ptr);
  282.   fputc ('\n', diag_file);
  283.   warning_epilog ();
  284. }
  285.  
  286.  
  287. /* Display information.  No linefeed is appended!  Return the number
  288.    of characters printed. */
  289.  
  290. int info (const char *fmt, ...)
  291. {
  292.   va_list arg_ptr;
  293.   int r;
  294.  
  295.   va_start (arg_ptr, fmt);
  296.   r = my_vfprintf (info_file, fmt, arg_ptr);
  297.   va_end (arg_ptr);
  298.   return r;
  299. }
  300.  
  301.  
  302. /* Display information, indented with INDENT spaces (up to 8).  No
  303.    linefeed is appended! */
  304.  
  305. void infoi (const char *fmt, int indent, ...)
  306. {
  307.   va_list arg_ptr;
  308.  
  309.   if (indent > 0)
  310.     fprintf (info_file, "%.*s", indent, "        ");
  311.   va_start (arg_ptr, indent);
  312.   my_vfprintf (info_file, fmt, arg_ptr);
  313.   va_end (arg_ptr);
  314. }
  315.  
  316.  
  317. /* Allocate N bytes of memory.  Terminate the process on error. */
  318.  
  319. void *xmalloc (size_t n)
  320. {
  321.   void *p;
  322.  
  323.   p = malloc (n);
  324.   if (p == NULL)
  325.     error ("Out of memory");
  326.   return p;
  327. }
  328.  
  329.  
  330. /* Return a pointer to a string containing a formatted range of sector
  331.    numbers.  Note that the pointer points to static memory; do not use
  332.    format_sector_range() more than once in one expression! */
  333.  
  334. const char *format_sector_range (ULONG start, ULONG count)
  335. {
  336.   static char buf[60];
  337.   static char fmt[40];
  338.  
  339.   if (count == 1)
  340.     {
  341.       adjust_format_string (fmt, "sector #%lu");
  342.       sprintf (buf, fmt, start);
  343.     }
  344.   else
  345.     {
  346.       adjust_format_string (fmt, "%lu sectors #%lu-#%lu");
  347.       sprintf (buf, fmt, count, start, start + count - 1);
  348.     }
  349.   return buf;
  350. }
  351.  
  352.  
  353. /* Return a pointer to a string containing a printable representation
  354.    of the string of N characters pointed to by S.  If ZERO_TERM is
  355.    true, the string pointed to by S can be zero terminated. */
  356.  
  357. const char *format_string (const unsigned char *s, size_t n, int zero_term)
  358. {
  359.   size_t i;
  360.   char *p;
  361.   static char buf[800];
  362.   static const char hex_digits[] = "0123456789abcdef";
  363.  
  364.   for (i = 0; i < n; ++i)
  365.     if (s[i] < 0x20 || s[i] == 0xff)
  366.       break;
  367.   if (i >= n || (zero_term && s[i] == 0))
  368.     {
  369.       buf[0] = '"';
  370.       memcpy (buf + 1, s, i);
  371.       buf[i+1] = '"';
  372.       buf[i+2] = 0;
  373.     }
  374.   else
  375.     {
  376.       memcpy (buf, "0x", 2);
  377.       p = buf + 2;
  378.       for (i = 0; i < n; ++i)
  379.         {
  380.           *p++ = hex_digits[(s[i] >> 4) & 0x0f];
  381.           *p++ = hex_digits[s[i] & 0x0f];
  382.         }
  383.       *p = 0;
  384.     }
  385.   return buf;
  386. }
  387.  
  388.  
  389. /* Format the name of the extended attribute pointed to by PFEA. */
  390.  
  391. const char *format_ea_name (const FEA *pfea)
  392. {
  393.   return format_string ((const unsigned char *)pfea + sizeof (FEA),
  394.                         pfea->cbName, FALSE);
  395. }
  396.  
  397.  
  398. /* Allocate a new path chain link.  PARENT points to the parent link,
  399.    NAME is the name to add (at the bottom of the link). */
  400.  
  401. path_chain *path_chain_new (const path_chain *parent, const char *name)
  402. {
  403.   char *n;
  404.   size_t size;
  405.   struct str_buf *sb;
  406.  
  407.   size = strlen (name) + 1;
  408.   for (sb = str_buf_head; sb != NULL; sb = sb->next)
  409.     if (sb->used + size <= sb->size)
  410.       break;
  411.   if (sb == NULL)
  412.     {
  413.       /* Unlink string buffers which are too full to be useful.  This
  414.          decreases search time in the loop above. */
  415.  
  416.       while (str_buf_head != NULL
  417.              && str_buf_head->used + 1 >= str_buf_head->size)
  418.         {
  419.           sb = str_buf_head->next;
  420.           free (str_buf_head);
  421.           str_buf_head = sb;
  422.         }
  423.       sb = xmalloc (sizeof (struct str_buf));
  424.       sb->used = 0;
  425.       sb->size = 65520;
  426.       sb->buf = xmalloc (sb->size);
  427.       sb->next = str_buf_head;
  428.       str_buf_head = sb;
  429.     }
  430.   n = sb->buf + sb->used;
  431.   memcpy (n, name, size);
  432.   sb->used += size;
  433.  
  434.   if (pc_used >= pc_count)
  435.     {
  436.       pc_used = 0;
  437.       pc_count = 65520 / sizeof (*pc_buf);
  438.       pc_buf = xmalloc (pc_count * sizeof (*pc_buf));
  439.     }
  440.  
  441.   pc_buf[pc_used].name = n;
  442.   pc_buf[pc_used].parent = parent;
  443.   return &pc_buf[pc_used++];
  444. }
  445.  
  446.  
  447. /* Return the length of a path name chain, in characters. */
  448.  
  449. int path_chain_len (const path_chain *p)
  450. {
  451.   if (p == NULL)
  452.     return -1;
  453.   else
  454.     return strlen (p->name) + 1 + path_chain_len (p->parent);
  455. }
  456.  
  457.  
  458. /* Recursive helper function for format_path_chain(). */
  459.  
  460. static int fpc_recurse (char *dst, size_t dst_size, const path_chain *p)
  461. {
  462.   int start;
  463.   size_t len;
  464.  
  465.   if (p->parent == NULL)
  466.     start = 0;
  467.   else
  468.     start = fpc_recurse (dst, dst_size, p->parent);
  469.   if (start < 0)
  470.     return start;
  471.  
  472.   len = strlen (p->name);
  473.   if (start + len + 2 > dst_size)
  474.     return -1;
  475.   if (start == 0 || dst[start-1] != '\\')
  476.     dst[start++] = '\\';
  477.   memcpy (dst + start, p->name, len);
  478.   return start + len;
  479. }
  480.  
  481.  
  482. /* Format the path name chain, optionally appending the file name
  483.    LAST.  Return a pointer to a statically allocated string. */
  484.  
  485. const char *format_path_chain (const path_chain *bottom, const char *last)
  486. {
  487.   static char buf[260];
  488.   int len;
  489.   path_chain link;
  490.  
  491.   if (last == NULL)
  492.     len = fpc_recurse (buf, sizeof (buf), bottom);
  493.   else
  494.     {
  495.       link.parent = bottom;
  496.       link.name = last;
  497.       len = fpc_recurse (buf, sizeof (buf), &link);
  498.     }
  499.  
  500.   if (len >= 0)
  501.     buf[len] = 0;
  502.   else
  503.     {
  504.       if (strlen (bottom->name) + 5 < sizeof (buf))
  505.         sprintf (buf, "...\\%s", bottom->name);
  506.       else if (strlen (bottom->name) < sizeof (buf))
  507.         strcpy (buf, bottom->name);
  508.       else
  509.         strcpy (buf, "...");
  510.     }
  511.   return buf;
  512. }
  513.  
  514.  
  515. /* Initialize cur_case_map. */
  516.  
  517. static void init_cur_case_map (void)
  518. {
  519.   COUNTRYCODE cc;
  520.   int i;
  521.  
  522.   for (i = 0; i < 256; ++i)
  523.     cur_case_map[i] = (BYTE)i;
  524.   for (i = 'a'; i <= 'z'; ++i)
  525.     cur_case_map[i] = (BYTE)toupper (i);
  526.   cc.country = 0;
  527.   cc.codepage = 0;
  528.   DosMapCase (128, &cc, (PCHAR)cur_case_map + 128);
  529. }
  530.  
  531.  
  532. /* Apply the selected action to an entire disk. */
  533.  
  534. static void do_disk (DISKIO *d)
  535. {
  536.   FAT_SECTOR boot;
  537.  
  538.   read_sec (d, &boot, 0, 1, TRUE);
  539.   if (a_info)
  540.     {
  541.       info ("Boot sector:\n");
  542.       info ("  OEM:                      %s\n",
  543.             format_string (boot.boot.oem, 8, FALSE));
  544.       info ("  Bytes per sector:         %u\n",
  545.             USHORT_FROM_FS (boot.boot.bytes_per_sector));
  546.       info ("  Sectors per cluster:      %u\n",
  547.             boot.boot.sectors_per_cluster);
  548.       info ("  Reserved sectors:         %u\n",
  549.             USHORT_FROM_FS (boot.boot.reserved_sectors));
  550.       info ("  FATs:                     %u\n",
  551.             boot.boot.fats);
  552.       info ("  Root directory entries:   %u\n",
  553.             USHORT_FROM_FS (boot.boot.root_entries));
  554.       if (USHORT_FROM_FS (boot.boot.sectors) != 0)
  555.         info ("  Sectors:                  %u\n",
  556.               USHORT_FROM_FS (boot.boot.sectors));
  557.       else
  558.         info ("  Sectors:                  %lu\n",
  559.               ULONG_FROM_FS (boot.boot.large_sectors));
  560.       info ("  Media descriptor:         0x%x\n",
  561.             boot.boot.media);
  562.       info ("  Sectors per FAT:          %u\n",
  563.             USHORT_FROM_FS (boot.boot.sectors_per_fat));
  564.       info ("  Sectors per track:        %u\n",
  565.             USHORT_FROM_FS (boot.boot.sectors_per_track));
  566.       info ("  Heads:                    %u\n",
  567.             USHORT_FROM_FS (boot.boot.heads));
  568.       info ("  Hidden sectors:           %u\n",
  569.             USHORT_FROM_FS (boot.boot.hidden_sectors_lo));
  570.       info ("  Drive number:             %u\n",
  571.             boot.boot.drive_no);
  572.       info ("  Extended signature:       0x%x\n",
  573.             boot.boot.extended_sig);
  574.       if (boot.boot.extended_sig == 40 || boot.boot.extended_sig == 41)
  575.         {
  576.           info ("  Volume ID:                0x%.8lx\n",
  577.                 ULONG_FROM_FS (boot.boot.vol_id));
  578.           info ("  Volume label:             %s\n",
  579.                 format_string (boot.boot.vol_label, 11, TRUE));
  580.           info ("  Volume type:              %s\n",
  581.                 format_string (boot.boot.vol_type, 8, FALSE));
  582.         }
  583.     }
  584.  
  585.   if (force_fs == 'h')
  586.     do_hpfs (d);
  587.   else if (force_fs == 'f')
  588.     do_fat (d, &boot);
  589.   else if (boot.boot.extended_sig == 40
  590.            && memcmp (boot.boot.vol_type, "HPFS", 4) == 0)
  591.     do_hpfs (d);
  592.   else if (boot.boot.extended_sig == 41
  593.            && memcmp (boot.boot.vol_type, "HPOFS", 5) == 0)
  594.     error ("HPOFS not supported");
  595.   else
  596.     do_fat (d, &boot);
  597. }
  598.  
  599.  
  600. /* Start a list.  Print FMT before the first entry of the list. */
  601.  
  602. void list_start (const char *fmt, ...)
  603. {
  604.   va_list arg_ptr;
  605.  
  606.   list_going = FALSE;
  607.   va_start (arg_ptr, fmt);
  608.   my_vsprintf (list_msg, fmt, arg_ptr);
  609.   va_end (arg_ptr);
  610. }
  611.  
  612.  
  613. /* List one number (NUMBER) using format FMT. */
  614.  
  615. void list (const char *fmt, ...)
  616. {
  617.   int len;
  618.   va_list arg_ptr;
  619.   char temp[256];
  620.  
  621.   if (!list_going)
  622.     {
  623.       list_going = TRUE;
  624.       list_x = info ("%s", list_msg);
  625.     }
  626.   va_start (arg_ptr, fmt);
  627.   len = my_vsprintf (temp, fmt, arg_ptr);
  628.   va_end (arg_ptr);
  629.   if (list_x + len + 1 >= 80 - 1)
  630.     {
  631.       info ("\n ");
  632.       list_x = 1;
  633.     }
  634.   list_x += info (" %s", temp);
  635. }
  636.  
  637.  
  638. /* End of a list of sector numbers.  (The list can be continued after
  639.    calling this function, see warning()!) */
  640.  
  641. void list_end (void)
  642. {
  643.   if (list_going)
  644.     {
  645.       info ("\n");
  646.       list_going = FALSE;
  647.     }
  648.   list_x = 0;
  649. }
  650.  
  651.  
  652. /* Tell them how to call this program. */
  653.  
  654. static void usage (void)
  655. {
  656.   char buf[10];
  657.  
  658.   puts (banner);
  659.  
  660.   puts ("fst comes with ABSOLUTELY NO WARRANTY. For details see file\n"
  661.         "`COPYING' that should have come with this program.\n"
  662.         "fst is free software, and you are welcome to redistribute it\n"
  663.         "under certain conditions. See the file `COPYING' for details.\n");
  664.   fputs ("Type RETURN to contine: ", stdout); fflush (stdout);
  665.   fgets (buf, sizeof (buf), stdin);
  666.  
  667.   puts ("\nUsage:\n"
  668.         "  fst [<fst_options>] <action> [<action_options>] <arguments>\n"
  669.         "\n<fst_options>:\n"
  670.         "  -h        Show help about <action>\n"
  671.         "  -d        Use DosRead/DosWrite (default: logical disk track I/O)\n"
  672.         "  -n        Continue if disk cannot be locked\n"
  673.         "  -w        Enable writing to disk\n"
  674.         "  -x        Show sector numbers in hexadecimal\n"
  675.         "\n<action>:\n"
  676.         "  info      Show information about the file system, a sector, or a path name\n"
  677.         "  check     Check the file system\n"
  678.         "  save      Take a snapshot of the file system\n"
  679.         "  diff      Compare snapshot files, CRC files, and disks\n"
  680.         "  restore   Copy sectors from snapshot file to disk\n"
  681.         "  dir       List a directory\n"
  682.         "  copy      Copy a file from the disk\n"
  683.         "  read      Copy a sector to a file\n"
  684.         "  write     Write a sector from a file to disk\n"
  685.         "  crc       Save CRCs for all sectors of a disk");
  686.   quit (1, FALSE);
  687. }
  688.  
  689.  
  690. static void usage_info (void)
  691. {
  692.   puts (banner);
  693.   puts ("Usage:\n"
  694.         " fst [<fst_options>] info [-f] [-u] <source>\n"
  695.         " fst [<fst_options>] info [-e]      <source> <path>\n"
  696.         " fst [<fst_options>] info [-c]      <source> <number>\n"
  697.         "Options:\n"
  698.         "  -c        <number> is a cluster number instead of a sector number\n"
  699.         "  -e        Show names of extended attributes\n"
  700.         "  -f        Show fragmentation of free space\n"
  701.         "  -u        Show unallocated sectors\n"
  702.         "Arguments:\n"
  703.         "  <source>  A drive name (eg, \"C:\") or snapshot file\n"
  704.         "  <path>    Full path name of a file or directory (without drive name)\n"
  705.         "  <number>  A sector number (without -c) or a cluster number (-c)");
  706.   quit (1, FALSE);
  707. }
  708.  
  709.  
  710. static void usage_check (void)
  711. {
  712.   puts (banner);
  713.   puts ("Usage:\n"
  714.         "  fst [<fst_options>] check [-f] [-m] [-p] [-s] [-u] [-v] <source>\n"
  715.         "Options:\n"
  716.         "  -f        Show fragmentation\n"
  717.         "  -m        Use more memory\n"
  718.         "  -p        Pedantic checks\n"
  719.         "  -s        Show summary\n"
  720.         "  -u        List sectors which are allocated but not used\n"
  721.         "  -v        Verbose -- show path names\n"
  722.         "Arguments:\n"
  723.         "  <source>  A drive name (eg, \"C:\") or a snapshot file");
  724.   quit (1, FALSE);
  725. }
  726.  
  727.  
  728. static void usage_save (void)
  729. {
  730.   puts (banner);
  731.   puts ("Usage:\n"
  732.         "  fst [<fst_options>] save [-v] <source> <target>\n"
  733.         "Options:\n"
  734.         "  -v        Verbose -- show path names\n"
  735.         "Arguments:\n"
  736.         "  <source>  A drive name (eg, \"C:\") or a snapshot file\n"
  737.         "  <target>  Name of target file");
  738.   quit (1, FALSE);
  739. }
  740.  
  741.  
  742. static void usage_restore (void)
  743. {
  744.   puts (banner);
  745.   puts ("Usage:\n"
  746.         "  fst [<fst_options>] restore [-s=<backup>] <target> <source> [<sector>]\n"
  747.         "Options:\n"
  748.         "  -s        Save old sectors into snapshot file <backup>\n"
  749.         "Arguments:\n"
  750.         "  <target>  A drive name (eg, \"C:\") or a snapshot file\n"
  751.         "  <source>  Name of the snapshot file to be copied to disk\n"
  752.         "  <sector>  A sector number (optional)");
  753.   quit (1, FALSE);
  754. }
  755.  
  756.  
  757. static void usage_copy (void)
  758. {
  759.   puts (banner);
  760.   puts ("Usage:\n"
  761.         "  fst [<fst_options>] copy <source> <path> <target>\n"
  762.         "Arguments:\n"
  763.         "  <source>  A drive name (eg, \"C:\")\n"
  764.         "  <path>    Full path name of the source file (without drive name)\n"
  765.         "  <target>  Name of target file");
  766.   quit (1, FALSE);
  767. }
  768.  
  769.  
  770. static void usage_dir (void)
  771. {
  772.   puts (banner);
  773.   puts ("Usage:\n"
  774.         "  fst [<fst_options>] dir <source> <path>\n"
  775.         "Arguments:\n"
  776.         "  <source>  A drive name (eg, \"C:\") or a snapshot file\n"
  777.         "  <path>    Full path name of directory or file (without drive name)");
  778.   quit (1, FALSE);
  779. }
  780.  
  781.  
  782. static void usage_read (void)
  783. {
  784.   puts (banner);
  785.   puts ("Usage:\n"
  786.         "  fst [<fst_options>] read <source> <target> <sector>\n"
  787.         "Arguments:\n"
  788.         "  <source>  A drive name (eg, \"C:\") or a snapshot file\n"
  789.         "  <target>  Name of target file\n"
  790.         "  <sector>  A sector number");
  791.   quit (1, FALSE);
  792. }
  793.  
  794.  
  795. static void usage_write (void)
  796. {
  797.   puts (banner);
  798.   puts ("Usage:\n"
  799.         "  fst [<fst_options>] write <target> <source> <sector>\n"
  800.         "Arguments:\n"
  801.         "  <target>  A drive name (eg, \"C:\") or a snapshot file\n"
  802.         "  <source>  Name of source file\n"
  803.         "  <sector>  A sector number");
  804.   quit (1, FALSE);
  805. }
  806.  
  807.  
  808. static void usage_diff (void)
  809. {
  810.   puts (banner);
  811.   puts ("Usage:\n"
  812.         "  fst [<fst_options>] diff <file1> <file2>\n"
  813.         "Arguments:\n"
  814.         "  <file1>   Drive name, snapshot file, or CRC file (old)\n"
  815.         "  <file2>   Drive name, snapshot file, or CRC file (new)");
  816.   quit (1, FALSE);
  817. }
  818.  
  819.  
  820. static void usage_crc (void)
  821. {
  822.   puts (banner);
  823.   puts ("Usage:\n"
  824.         "  fst [<fst_options>] crc <source> <target>\n"
  825.         "Arguments:\n"
  826.         "  <source>  A drive name (eg, \"C:\")\n"
  827.         "  <target>  Name of CRC file to be written");
  828.   quit (1, FALSE);
  829. }
  830.  
  831.  
  832. /* `info' action. */
  833.  
  834. static void cmd_info (int argc, char *argv[])
  835. {
  836.   DISKIO *d;
  837.   int i;
  838.   char *e;
  839.   cyl_head_sec chs;
  840.  
  841.   i = 1;
  842.   while (i < argc)
  843.     if (strcmp (argv[i], "-c") == 0)
  844.       {
  845.         what_cluster_flag = TRUE; ++i;
  846.       }
  847.     else if (strcmp (argv[i], "-e") == 0)
  848.       {
  849.         show_eas = TRUE; ++i;
  850.       }
  851.     else if (strcmp (argv[i], "-f") == 0)
  852.       {
  853.         show_free_frag = TRUE; ++i;
  854.       }
  855.     else if (strcmp (argv[i], "-u") == 0)
  856.       {
  857.         show_unused = TRUE; ++i;
  858.       }
  859.     else
  860.       break;
  861.   if (i >= argc || argv[i][0] == '-')
  862.     usage_info ();
  863.   if (argc - i == 1)
  864.     {
  865.       a_info = TRUE;
  866.       if (what_cluster_flag || show_eas)
  867.         usage_info ();
  868.     }
  869.   else if (argc - i == 2)
  870.     {
  871.       if (argv[i+1][0] == '\\')
  872.         {
  873.           if (show_free_frag || show_unused || what_cluster_flag)
  874.             usage_info ();
  875.           a_find = TRUE; a_where = TRUE;
  876.           find_path = argv[i+1] + 1;
  877.         }
  878.       else
  879.         {
  880.           if (show_free_frag || show_unused || show_eas)
  881.             usage_info ();
  882.           errno = 0;
  883.           what_sector = strtoul (argv[i+1], &e, 0);
  884.           if (errno != 0 || e == argv[i+1] || *e != 0)
  885.             usage_info ();
  886.           a_what = TRUE;
  887.         }
  888.     }
  889.   else
  890.     usage_info ();
  891.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  892.   d = diskio_open ((PCSZ)argv[i], DIO_DISK | DIO_SNAPSHOT, FALSE);
  893.  
  894.   if (a_what && !what_cluster_flag
  895.       && diskio_cyl_head_sec (d, &chs, what_sector))
  896.     info ("Sector #%lu: Cylinder %lu, head %lu, sector %lu\n",
  897.           what_sector, chs.cyl, chs.head, chs.sec);
  898.  
  899.   do_disk (d);
  900.   diskio_close (d);
  901. }
  902.  
  903.  
  904. /* `check' action. */
  905.  
  906. static void cmd_check (int argc, char *argv[])
  907. {
  908.   DISKIO *d;
  909.   int i;
  910.  
  911.   i = 1;
  912.   while (i < argc)
  913.     if (strcmp (argv[i], "-f") == 0)
  914.       {
  915.         show_frag = TRUE; ++i;
  916.       }
  917.     else if (strcmp (argv[i], "-s") == 0)
  918.       {
  919.         show_summary = TRUE; ++i;
  920.       }
  921.     else if (strcmp (argv[i], "-m") == 0)
  922.       {
  923.         plenty_memory = TRUE; ++i;
  924.       }
  925.     else if (strcmp (argv[i], "-p") == 0)
  926.       {
  927.         check_pedantic = TRUE; ++i;
  928.       }
  929.     else if (strcmp (argv[i], "-u") == 0)
  930.       {
  931.         check_unused = TRUE; ++i;
  932.       }
  933.     else if (strcmp (argv[i], "-v") == 0)
  934.       {
  935.         verbose = TRUE; ++i;
  936.       }
  937.     else
  938.       break;
  939.   if (argc - i != 1)
  940.     usage_check ();
  941.   if (argv[i][0] == '-')
  942.     usage_check ();
  943.   a_check = TRUE;
  944.   info_file = stderr; diag_file = stdout; prog_file = stderr;
  945.   d = diskio_open ((PCSZ)argv[i], DIO_DISK | DIO_SNAPSHOT, FALSE);
  946.   do_disk (d);
  947.   diskio_close (d);
  948.   quit (0, TRUE);
  949. }
  950.  
  951.  
  952. /* Compare sectors of two snapshot files.  The operation is controlled
  953.    by WHICH:
  954.         0    compare sectors which are in both files
  955.         1    list sectors which are in the first file only
  956.         2    list sectors which are in the second file only */
  957.  
  958. static void diff_sectors (DISKIO *d1, DISKIO *d2,
  959.                           const ULONG *p1, const ULONG *p2,
  960.                           ULONG n1, ULONG n2, int which)
  961. {
  962.   int cmp;
  963.   BYTE raw1[512], raw2[512];
  964.  
  965.   if (which == 0)
  966.     list_start ("Differing sectors:");
  967.   else
  968.     list_start ("Sectors only in file %d:", which);
  969.   while (n1 != 0 || n2 != 0)
  970.     {
  971.       if (n1 == 0)
  972.         cmp = 1;
  973.       else if (n2 == 0)
  974.         cmp = -1;
  975.       else if (*p1 > *p2)
  976.         cmp = 1;
  977.       else if (*p1 < *p2)
  978.         cmp = -1;
  979.       else
  980.         cmp = 0;
  981.       switch (which)
  982.         {
  983.         case 0:                 /* Compare sectors */
  984.           if (cmp == 0)
  985.             {
  986.               read_sec (d1, raw1, *p1, 1, FALSE);
  987.               read_sec (d2, raw2, *p1, 1, FALSE);
  988.               if (memcmp (raw1, raw2, 512) != 0)
  989.                 list ("#%lu", *p1);
  990.             }
  991.           break;
  992.         case 1:
  993.           if (cmp < 0)
  994.             list ("#%lu", *p1);
  995.           break;
  996.         case 2:
  997.           if (cmp > 0)
  998.             list ("#%lu", *p2);
  999.           break;
  1000.         }
  1001.       if (cmp <= 0)
  1002.         ++p1, --n1;
  1003.       if (cmp >= 0)
  1004.         ++p2, --n2;
  1005.     }
  1006.   list_end ();
  1007. }
  1008.  
  1009.  
  1010. /* Compare sectors of a snapshot file to sectors of a disk or to a CRC
  1011.    file.  N sector numbers are passed in the array pointed to by
  1012.    ARRAY. */
  1013.  
  1014. static void compare_sectors_array (DISKIO *d1, DISKIO *d2, ULONG *array,
  1015.                                    ULONG n)
  1016. {
  1017.   BYTE raw1[512], raw2[512];
  1018.   int ok1, ok2;
  1019.   ULONG idx, secno, n1, n2;
  1020.   crc_t crc1, crc2;
  1021.  
  1022.   list_start ("Differing sectors:");
  1023.   n1 = diskio_total_sectors (d1); n2 = diskio_total_sectors (d2);
  1024.   if (diskio_type (d1) == DIO_CRC || diskio_type (d2) == DIO_CRC)
  1025.     {
  1026.       for (idx = 0; idx < n; ++idx)
  1027.         {
  1028.           secno = array[idx];
  1029.           if ((n1 != 0 && secno >= n1) || (n2 != 0 && secno >= n2))
  1030.             break;
  1031.           ok1 = crc_sec (d1, &crc1, secno);
  1032.           ok2 = crc_sec (d2, &crc2, secno);
  1033.           if (ok1 && ok2 && crc1 != crc2)
  1034.             list ("#%lu", secno);
  1035.         }
  1036.     }
  1037.   else
  1038.     {
  1039.       for (idx = 0; idx < n; ++idx)
  1040.         {
  1041.           secno = array[idx];
  1042.           if ((n1 != 0 && secno >= n1) || (n2 != 0 && secno >= n2))
  1043.             break;
  1044.           read_sec (d1, raw1, secno, 1, FALSE);
  1045.           read_sec (d2, raw2, secno, 1, FALSE);
  1046.           if (memcmp (raw1, raw2, 512) != 0)
  1047.             list ("#%lu", secno);
  1048.         }
  1049.     }
  1050.   list_end ();
  1051.   if (idx < n)
  1052.     {
  1053.       list_start ("Missing sectors in source %d:", n1 == 0 ? 2 : 1);
  1054.       for (; idx < n; ++idx)
  1055.         list ("#%lu", array[idx]);
  1056.       list_end ();
  1057.     }
  1058. }
  1059.  
  1060.  
  1061. /* Compare all sectors of two disks, two CRC files, or a disk and a
  1062.    CRC file. */
  1063.  
  1064. static void compare_sectors_all (DISKIO *d1, DISKIO *d2)
  1065. {
  1066.   BYTE raw1[512], raw2[512];
  1067.   crc_t crc1, crc2;
  1068.   int ok1, ok2;
  1069.   ULONG secno, n, n1, n2;
  1070.  
  1071.   list_start ("Differing sectors:");
  1072.   n1 = diskio_total_sectors (d1); n2 = diskio_total_sectors (d2);
  1073.   n = MIN (n1, n2);
  1074.   if (diskio_type (d1) == DIO_CRC || diskio_type (d2) == DIO_CRC)
  1075.     {
  1076.       if (diskio_type (d1) == DIO_CRC && diskio_type (d2) == DIO_CRC)
  1077.         diskio_crc_load (d1);
  1078.       for (secno = 0; secno < n; ++secno)
  1079.         {
  1080.           ok1 = crc_sec (d1, &crc1, secno);
  1081.           ok2 = crc_sec (d2, &crc2, secno);
  1082.           if (ok1 && ok2 && crc1 != crc2)
  1083.             list ("#%lu", secno);
  1084.         }
  1085.     }
  1086.   else
  1087.     {
  1088.       for (secno = 0; secno < n; ++secno)
  1089.         {
  1090.           read_sec (d1, raw1, secno, 1, FALSE);
  1091.           read_sec (d2, raw2, secno, 1, FALSE);
  1092.           if (memcmp (raw1, raw2, 512) != 0)
  1093.             list ("#%lu", secno);
  1094.         }
  1095.     }
  1096.   list_end ();
  1097.   if (n1 > n2)
  1098.     info ("First disk has more sectors than second disk\n");
  1099.   else if (n1 < n2)
  1100.     info ("Second disk has more sectors than first disk\n");
  1101. }
  1102.  
  1103.  
  1104. /* `diff' action. */
  1105.  
  1106. static void cmd_diff (int argc, char *argv[])
  1107. {
  1108.   int i;
  1109.   const char *fname1;
  1110.   const char *fname2;
  1111.   DISKIO *d1, *d2;
  1112.   ULONG *sort1, *sort2;
  1113.   ULONG n1, n2;
  1114.  
  1115.   i = 1;
  1116.   if (argc - i != 2)
  1117.     usage_diff ();
  1118.   if (argv[i][0] == '-')
  1119.     usage_diff ();
  1120.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  1121.   fname1 = argv[i+0];
  1122.   fname2 = argv[i+1];
  1123.   d1 = diskio_open ((PCSZ)fname1, DIO_DISK | DIO_SNAPSHOT | DIO_CRC, FALSE);
  1124.   d2 = diskio_open ((PCSZ)fname2, DIO_DISK | DIO_SNAPSHOT | DIO_CRC, FALSE);
  1125.   crc_build_table ();
  1126.   if (diskio_access == ACCESS_DASD
  1127.       && (diskio_type (d1) == DIO_CRC || diskio_type (d2) == DIO_CRC))
  1128.     error ("Cannot use the -d option for the `diff' action with CRC files");
  1129.   sort1 = diskio_snapshot_sort (d1);
  1130.   sort2 = diskio_snapshot_sort (d2);
  1131.   if (sort1 != NULL && sort2 != NULL)
  1132.     {
  1133.       n1 = diskio_snapshot_sectors (d1); n2 = diskio_snapshot_sectors (d2);
  1134.       for (i = 0; i <= 2; ++i)
  1135.         diff_sectors (d1, d2, sort1, sort2, n1, n2, i);
  1136.     }
  1137.   else if (sort1 != NULL)
  1138.     compare_sectors_array (d1, d2, sort1, diskio_snapshot_sectors (d1));
  1139.   else if (sort2 != NULL)
  1140.     compare_sectors_array (d1, d2, sort2, diskio_snapshot_sectors (d2));
  1141.   else
  1142.     compare_sectors_all (d1, d2);
  1143.   free (sort1); free (sort2);
  1144.   diskio_close (d1);
  1145.   diskio_close (d2);
  1146. }
  1147.  
  1148.  
  1149. /* `save' action. */
  1150.  
  1151. static void cmd_save (int argc, char *argv[])
  1152. {
  1153.   DISKIO *d;
  1154.   int i;
  1155.   const char *src_fname;
  1156.  
  1157.   i = 1;
  1158.   while (i < argc)
  1159.     if (strcmp (argv[i], "-v") == 0)
  1160.       {
  1161.         verbose = TRUE; ++i;
  1162.       }
  1163.     else
  1164.       break;
  1165.   if (argc - i != 2)
  1166.     usage_save ();
  1167.   if (argv[i][0] == '-')
  1168.     usage_save ();
  1169.   src_fname = argv[i+0];
  1170.   save_fname = argv[i+1];
  1171.   a_save = TRUE;
  1172.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  1173.   d = diskio_open ((PCSZ)src_fname, DIO_DISK | DIO_SNAPSHOT, FALSE);
  1174.   save_create (src_fname, SAVE_SNAPSHOT);
  1175.   do_disk (d);
  1176.   diskio_close (d);
  1177.   save_close ();
  1178. }
  1179.  
  1180.  
  1181. /* `restore' action. */
  1182.  
  1183. static void cmd_restore (int argc, char *argv[])
  1184. {
  1185.   DISKIO *d1, *d2;
  1186.   int i;
  1187.   const char *dst_fname;
  1188.   const char *src_fname;
  1189.   char temp, all = TRUE, *e;
  1190.   ULONG *sort, idx, sec, bad, n, secno = 0;
  1191.   char buf[10];
  1192.   BYTE data[512];
  1193.  
  1194.   i = 1; save_fname = NULL;
  1195.   while (i < argc)
  1196.     if (strncmp (argv[i], "-s=", 3) == 0)
  1197.       {
  1198.         if (argv[i][3] == 0)
  1199.           usage_restore ();
  1200.         save_fname = argv[i] + 3;
  1201.         ++i;
  1202.       }
  1203.     else
  1204.       break;
  1205.  
  1206.   if (argc - i == 2)
  1207.     {
  1208.       secno = 0; all = TRUE;
  1209.     }
  1210.   else if (argc - i == 3)
  1211.     {
  1212.       errno = 0;
  1213.       secno = strtoul (argv[i+2], &e, 0);
  1214.       if (errno != 0 || e == argv[i+2] || *e != 0)
  1215.         usage_write ();
  1216.       all = FALSE;
  1217.     }
  1218.   else
  1219.     usage_restore ();
  1220.  
  1221.   if (argv[i][0] == '-')
  1222.     usage_restore ();
  1223.   dst_fname = argv[i+0];
  1224.   src_fname = argv[i+1];
  1225.  
  1226.   printf ("Do you really want to overwrite the file system data "
  1227.           "structures\nof \"%s\" (type \"YES!\" to confirm)? ", dst_fname);
  1228.   fflush (stdout);
  1229.   if (fgets (buf, sizeof (buf), stdin) == NULL)
  1230.     quit (2, FALSE);
  1231.   if (strcmp (buf, "YES!\n") != 0)
  1232.     quit (0, FALSE);
  1233.  
  1234.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  1235.   fprintf (prog_file, "Preliminary actions...\n"); fflush (prog_file);
  1236.   temp = write_enable; write_enable = FALSE;
  1237.   ignore_lock_error = FALSE; dont_lock = FALSE;
  1238.   d2 = diskio_open ((PCSZ)src_fname, DIO_SNAPSHOT, FALSE);
  1239.   write_enable = temp;
  1240.   d1 = diskio_open ((PCSZ)dst_fname, DIO_DISK | DIO_SNAPSHOT, TRUE);
  1241.   if (save_fname != NULL)
  1242.     save_create (dst_fname, SAVE_SNAPSHOT);
  1243.  
  1244.   /* Set up a sorted list of sectors. */
  1245.  
  1246.   if (all)
  1247.     {
  1248.       sort = diskio_snapshot_sort (d2);
  1249.       n = diskio_snapshot_sectors (d2);
  1250.     }
  1251.   else
  1252.     {
  1253.       sort = &secno; n = 1;
  1254.     }
  1255.  
  1256.   /* Check the snapshot file. */
  1257.  
  1258.   for (idx = 0; idx < n; ++idx)
  1259.     read_sec (d2, data, sort[idx], 1, FALSE);
  1260.  
  1261.   /* Make a backup if requested. */
  1262.  
  1263.   if (save_fname != NULL)
  1264.     {
  1265.       a_save = TRUE;
  1266.       for (idx = 0; idx < n; ++idx)
  1267.         read_sec (d1, data, sort[idx], 1, TRUE);
  1268.       a_save = FALSE;
  1269.       save_close ();
  1270.     }
  1271.   fprintf (prog_file, "Writing...DO NOT INTERRUPT!...\n"); fflush (prog_file);
  1272.   bad = 0;
  1273.   for (idx = 0; idx < n; ++idx)
  1274.     {
  1275.       sec = sort[idx];
  1276.       read_sec (d2, data, sec, 1, FALSE);
  1277.       if (!write_sec (d1, data, sec))
  1278.         ++bad;
  1279.     }
  1280.   diskio_close (d2);
  1281.   diskio_close (d1);
  1282.   if (sort != &secno)
  1283.     free (sort);
  1284.   if (bad == 0)
  1285.     {
  1286.       fprintf (prog_file, "Done\n");
  1287.       quit (0, FALSE);
  1288.     }
  1289.   else if (bad == 1)
  1290.     {
  1291.       fprintf (prog_file, "Done, 1 sector not written\n");
  1292.       quit (2, FALSE);
  1293.     }
  1294.   else
  1295.     {
  1296.       fprintf (prog_file, "Done, %lu sectors not written\n", bad);
  1297.       quit (2, FALSE);
  1298.     }
  1299. }
  1300.  
  1301.  
  1302. /* `copy' action. */
  1303.  
  1304. static void cmd_copy (int argc, char *argv[])
  1305. {
  1306.   DISKIO *d;
  1307.   const char *src_fname;
  1308.   int i;
  1309.  
  1310.   i = 1;
  1311.   if (argc - i != 3)
  1312.     usage_copy ();
  1313.   if (argv[i][0] == '-')
  1314.     usage_copy ();
  1315.   a_find = TRUE; a_copy = TRUE;
  1316.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  1317.   src_fname = argv[i+0];
  1318.   save_fname = argv[i+2];
  1319.   d = diskio_open ((PCSZ)argv[i+0], DIO_DISK, FALSE);
  1320.   find_path = argv[i+1];
  1321.   if (*find_path == '\\')
  1322.     ++find_path;
  1323.   save_create (src_fname, SAVE_RAW);
  1324.   do_disk (d);
  1325.   save_close ();
  1326.   diskio_close (d);
  1327. }
  1328.  
  1329.  
  1330. /* `dir' action. */
  1331.  
  1332. static void cmd_dir (int argc, char *argv[])
  1333. {
  1334.   DISKIO *d;
  1335.   int i;
  1336.  
  1337.   i = 1;
  1338.   if (argc - i != 2)
  1339.     usage_dir ();
  1340.   if (argv[i][0] == '-')
  1341.     usage_dir ();
  1342.   a_find = TRUE; a_dir = TRUE;
  1343.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  1344.   d = diskio_open ((PCSZ)argv[i+0], DIO_DISK | DIO_SNAPSHOT, FALSE);
  1345.   find_path = argv[i+1];
  1346.   if (*find_path == '\\')
  1347.     ++find_path;
  1348.   do_disk (d);
  1349.   diskio_close (d);
  1350. }
  1351.  
  1352.  
  1353. /* `read' action. */
  1354.  
  1355. static void cmd_read (int argc, char *argv[])
  1356. {
  1357.   DISKIO *d;
  1358.   int i;
  1359.   ULONG n;
  1360.   const char *src_fname;
  1361.   char *e;
  1362.   char data[512];
  1363.  
  1364.   i = 1;
  1365.   if (argc - i != 3)
  1366.     usage_read ();
  1367.   if (argv[i][0] == '-')
  1368.     usage_read ();
  1369.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  1370.   src_fname = argv[i+0];
  1371.   save_fname = argv[i+1];
  1372.   d = diskio_open ((PCSZ)src_fname, DIO_DISK | DIO_SNAPSHOT, FALSE);
  1373.   errno = 0;
  1374.   n = strtoul (argv[i+2], &e, 0);
  1375.   if (errno != 0 || e == argv[i+2] || *e != 0)
  1376.     usage_read ();
  1377.   save_create (src_fname, SAVE_RAW);
  1378.   read_sec (d, data, n, 1, FALSE);
  1379.   fwrite (data, 512, 1, save_file);
  1380.   save_close ();
  1381.   diskio_close (d);
  1382. }
  1383.  
  1384.  
  1385. /* `write' action. */
  1386.  
  1387. static void cmd_write (int argc, char *argv[])
  1388. {
  1389.   DISKIO *d;
  1390.   int i, ok;
  1391.   size_t nread;
  1392.   ULONG n;
  1393.   const char *dst_fname;
  1394.   const char *src_fname;
  1395.   char *e;
  1396.   char data[512+1];             /* Extra byte for checking the file length */
  1397.   FILE *f;
  1398.  
  1399.   i = 1;
  1400.   if (argc - i != 3)
  1401.     usage_write ();
  1402.   if (argv[i][0] == '-')
  1403.     usage_write ();
  1404.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  1405.   dst_fname = argv[i+0];
  1406.   src_fname = argv[i+1];
  1407.  
  1408.   errno = 0;
  1409.   n = strtoul (argv[i+2], &e, 0);
  1410.   if (errno != 0 || e == argv[i+2] || *e != 0)
  1411.     usage_write ();
  1412.  
  1413.   f = fopen (src_fname, "rb");
  1414.   if (f == NULL)
  1415.     error ("%s: %s", src_fname, strerror (errno));
  1416.   nread = fread (data, 1, sizeof (data), f);
  1417.   if (nread == 0)
  1418.     error ("%s: %s", src_fname, strerror (errno));
  1419.   fclose (f);
  1420.   if (nread != 512)
  1421.     error ("The source file must contain exactly 512 bytes");
  1422.  
  1423.   d = diskio_open ((PCSZ)dst_fname, DIO_DISK | DIO_SNAPSHOT, TRUE);
  1424.   ok = write_sec (d, data, n);
  1425.   diskio_close (d);
  1426.   quit (ok ? 0 : 2, FALSE);
  1427. }
  1428.  
  1429.  
  1430. /* `crc' action. */
  1431.  
  1432. static void cmd_crc (int argc, char *argv[])
  1433. {
  1434.   DISKIO *d;
  1435.   int i;
  1436.   const char *src_fname;
  1437.   ULONG secno, n;
  1438.   crc_t *acrc;
  1439.  
  1440.   i = 1;
  1441.   if (argc - i != 2)
  1442.     usage_crc ();
  1443.   if (argv[i][0] == '-')
  1444.     usage_crc ();
  1445.   if (diskio_access == ACCESS_DASD)
  1446.     error ("Cannot use the -d option with the `crc' action");
  1447.   src_fname = argv[i+0];
  1448.   save_fname = argv[i+1];
  1449.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  1450.   d = diskio_open ((PCSZ)src_fname, DIO_DISK, FALSE);
  1451.   save_create (src_fname, SAVE_CRC);
  1452.   crc_build_table ();
  1453.   n = diskio_total_sectors (d);
  1454.   acrc = xmalloc (n * sizeof (*acrc));
  1455.   for (secno = 0; secno < n; ++secno)
  1456.     {
  1457.       if (!crc_sec (d, acrc + secno, secno))
  1458.         warning (1, "Sector #%lu not readable", secno);
  1459.       acrc[secno] = ULONG_TO_FS (acrc[secno]);
  1460.     }
  1461.   if (fwrite (acrc, sizeof (*acrc), n, save_file) != n)
  1462.     save_error ();
  1463.   free (acrc);
  1464.   diskio_close (d);
  1465.   save_sector_count = n;
  1466.   save_close ();
  1467. }
  1468.  
  1469.  
  1470. /* Here's where the program starts. */
  1471.  
  1472. int main (int argc, char *argv[])
  1473. {
  1474.   int i;
  1475.  
  1476.   /* Initialize variables which are not initialized to zero. */
  1477.  
  1478.   info_file = stdout; diag_file = stderr; prog_file = stderr;
  1479.   diskio_access = ACCESS_LOG_TRACK;
  1480.   removable_allowed = TRUE;
  1481.  
  1482.   /* Initialize cur_case_map. */
  1483.  
  1484.   init_cur_case_map ();
  1485.  
  1486.   /* Parse initial command line options. */
  1487.  
  1488.   i = 1;
  1489.   if (argc - i == 2 && strcmp (argv[i], "-h") == 0)
  1490.     {
  1491.       ++i; --argc;
  1492.     }
  1493.   else
  1494.     {
  1495.       while (i < argc && argv[i][0] == '-')
  1496.         if (strcmp (argv[i], "-d") == 0)
  1497.           {
  1498.             diskio_access = ACCESS_DASD; ++i;
  1499.           }
  1500.         else if (strcmp (argv[i], "-n") == 0)
  1501.           {
  1502.             ignore_lock_error = TRUE; ++i;
  1503.           }
  1504.         else if (strcmp (argv[i], "-w") == 0)
  1505.           {
  1506.             write_enable = TRUE; ++i;
  1507.           }
  1508.         else if (strcmp (argv[i], "-x") == 0)
  1509.           {
  1510.             sector_number_format = 'x'; ++i;
  1511.           }
  1512.         else if (strcmp (argv[i], "-Don't lock") == 0)
  1513.           {
  1514.             dont_lock = TRUE; ++i;
  1515.           }
  1516.         else if (strcmp (argv[i], "-FAT") == 0)
  1517.           {
  1518.             force_fs = 'f'; ++i;
  1519.           }
  1520.         else if (strcmp (argv[i], "-HPFS") == 0)
  1521.           {
  1522.             force_fs = 'h'; ++i;
  1523.           }
  1524.         else
  1525.           usage ();
  1526.       if (argc - i < 1)
  1527.         usage ();
  1528.     }
  1529.  
  1530.   /* Parse the action. */
  1531.  
  1532.   if (strcmp (argv[i], "info") == 0)
  1533.     cmd_info (argc - i, argv + i);
  1534.   else if (strcmp (argv[i], "check") == 0)
  1535.     cmd_check (argc - i, argv + i);
  1536.   else if (strcmp (argv[i], "save") == 0)
  1537.     cmd_save (argc - i, argv + i);
  1538.   else if (strcmp (argv[i], "restore") == 0)
  1539.     cmd_restore (argc - i, argv + i);
  1540.   else if (strcmp (argv[i], "diff") == 0)
  1541.     cmd_diff (argc - i, argv + i);
  1542.   else if (strcmp (argv[i], "copy") == 0)
  1543.     cmd_copy (argc - i, argv + i);
  1544.   else if (strcmp (argv[i], "dir") == 0)
  1545.     cmd_dir (argc - i, argv + i);
  1546.   else if (strcmp (argv[i], "read") == 0)
  1547.     cmd_read (argc - i, argv + i);
  1548.   else if (strcmp (argv[i], "write") == 0)
  1549.     cmd_write (argc - i, argv + i);
  1550.   else if (strcmp (argv[i], "crc") == 0)
  1551.     cmd_crc (argc - i, argv + i);
  1552.   else
  1553.     usage ();
  1554.  
  1555.   /* Done. */
  1556.  
  1557.   quit (0, FALSE);
  1558.   return 0;                     /* Keep the compiler happy */
  1559. }
  1560.