/*static char *SCCSID = "@(#)main.c	6.1 90/11/18";*/
/*static char *SCCSID = "@(#)main.c 13.4 90/01/30";*/
/***	MAIN.C - Top-level routines
 *
 *	RECOVER
 *	Gregory A. Jones
 *	Copyright 1988 Microsoft Corporation
 *
 *	Modification history:
 *	G.A. Jones	07/06/88     Original for Pinball 1.0.
 *	P.A. Williams	06/26/89     Replace doscalls.h with bsedos.h
 */

#include <setjmp.h>
#define INCL_DOS
#include "ldefs.h"
#include "ltypes.h"
#include "globals.h"
#include "pr_rec.h"

extern jmp_buf rec_error;


/***	rec_exit_error - print informative message and exit
 *
 *	rec_exit_error (code)
 *
 *	ENTRY		code - error code number, defined in LDEFS.H
 *
 *	EXIT		Does not return
 *
 *	CALLS		error_msg
 *			longjmp
 *
 *	WARNINGS	Does not return
 *
 *	EFFECTS 	Terminates program
 */
void rec_exit_error (code)
  USHORT code;
{
#ifdef TRACE
  fprintf (stderr, "rec_exit_error (%d)\n", code);
  fflush (stderr);
#endif

  if (code <= MAX_ERROR_CODE)
    error_msg (rec_error_messages [code]); /* display appropriate message */
  else {
    itoa (code, scratchbuf, 10);
    computmsg (&scratchptr, 1, REC_UNKNOWN_ERROR_STR, msgfilename, STDERR_CODE,
	       NO_RESPONSE, NULL, 0);
  }

  longjmp (rec_error, 0);
}

/***	rec_show_statistics - report how well file was recovered
 *
 *	This function is called after all file recovery is done.
 *	It displays messages indicating the number of good bytes
 *	copied, the number of bytes which were read successfully
 *	after retrying, and the number of unreadable (lost) bytes.
 *
 *	rec_show_statistics (void)
 *
 *	ENTRY		No parameters
 *
 *	EXIT		No return value
 *
 *	CALLS		out_msg
 *
 *	WARNINGS	None
 *
 *	EFFECTS 	None
 */
void rec_show_statistics (void)
{
  if (rec_good_bytes) {
    sprintf (scratchbuf, "%10ld", rec_good_bytes);
    out_msg (REC_GOOD_BYTES_STR, scratchbuf);
  }

  if (rec_retry_bytes) {
    sprintf (scratchbuf, "%10ld", rec_retry_bytes);
    out_msg (REC_RETRY_BYTES_STR, scratchbuf);
  }

  if (rec_lost_bytes) {
    sprintf (scratchbuf, "%10ld", rec_lost_bytes);
    out_msg (REC_LOST_BYTES_STR, scratchbuf);
  }
}

/***	recover - main routine
 *
 *	This function is the top-level routine for RECOVER.  It
 *	parses the command line to make sure a file name was typed,
 *	checks the existence of that file, and then recovers the
 *	file.
 *
 *	recover (argc, argv, envp)
 *
 *	ENTRY		argc - count of command line arguments
 *			argv - list of command line arguments
 *			envp - list of environment strings
 *
 *	EXIT		Return code is 0 if success, non-zero if error
 *			Specified file is recovered
 *
 *	CALLS		rec_exit_error
 *			rec_read_bit_maps
 *			rec_recover_file
 *			save_bad
 *			rec_update_bit_maps
 *			setjmp
 *
 *	WARNINGS	None
 *
 *	EFFECTS 	Copies file
 *			Adds to bad sector list
 */
USHORT far pascal _loadds recover (argc, argv, envp)
  USHORT argc;
  UCHAR **argv;
  UCHAR **envp;
{
  PUCHAR p;
  USHORT rc;

  if (setjmp (rec_error))		/* exit_error returns here */
    return (ERROR_CODE);		/* send code to the front-end prog */

#ifdef TIMESTAMPS
  printf (rec_version_str);
  printf (rec_timestamp_str, __DATE__, __TIME__);
#endif

/*
 * The user has to type RECOVER <filename>.  No other options are allowed.
 */

  if (argc != 2)			/* must have "RECOVER filename" */
    rec_exit_error (ARG_ERROR); 	/* terminate program immediately */

  if (strlen (argv [1]) == 2 && argv [1][1] == ':')
    rec_exit_error (ARG_ERROR); 	/* can't do "RECOVER d:" */

/*
 * Figure out which disk we're on, disable hard error popups,
 * and DASD Open the disk.
 */

  if (com_strchr (argv [1], ':')) {
    strcpy (disk_name, argv [1]);
    *(com_strchr (disk_name, ':') + 1) = '\0';
  }
  else {
    getcwd ((UCHAR *)disk_name, 1024);
    if (p=com_strchr (disk_name, ':'))
      *(p+1) = '\0';
  }
  com_strupr (disk_name);		/* make disk name uppercase for msgs */

  DosError (0x0000);			/* 0 => disable hard error popups */

  if (!open_disk (disk_name))
    rec_exit_error (OPEN_ERROR);

/*
 * Clear out the buffer we use to replace totally bad sectors, and try
 * to recover the file.
 */

  if ((zeros=malloc (SPB * SECSIZE)) == NULL)
    rec_exit_error (INSF_MEM_ERROR);
  memset (zeros, '\0', SPB * SECSIZE);

/*
 * Say "Press Enter to begin recovery of the file." and wait for the
 * user to hit Enter.
 */

  computmsg (NULL, 0, REC_PRESS_ENTER_STR, msgfilename, STDOUT_CODE, CR_RESPONSE,
	     NULL, 0);

/*
 * Recover the file.
 */

  if (rc=rec_recover_file (argv [1])) { /* copy the file and delete the orig. */
    computmsg (NULL, 0, rctomid (rc), "OSO001.MSG", STDERR_CODE, NO_RESPONSE,
	       NULL, 0);		/* display appropriate system msg */
    return (ERROR_CODE);		/* exit program */
  }
  free (zeros); 			/* deallocate the scratch buffer */

  rec_show_statistics ();		/* tell user how much we recovered */

/*
 * If we found any bad sectors, add them to the bad block list.  Then
 * update the bit maps to mark the bad sectors as "in use", and to
 * allocate any new blocks in the bad block chain.
 *
 * The disk has to be locked in order to change the bitmaps or bad block
 * lists.  However, we can't lock the disk for the whole RECOVER operation
 * because there are some filesystem operations which use lazy writing
 * (such as the DOSDELETE and DOSMOVE calls in rec_recover_file), and
 * unlocking the disk will dismount the volume and leave the lazy daemon
 * stranded.  So, we have to hope that the user is not running RECOVER
 * while other things are happening on the disk.  If we can't lock the
 * disk, we can't install the newly found bad sectors in the bad block
 * list, but at least we've recovered the file.
 *
 * There's also a chance that we can lock the disk, but between deleting
 * the original file and locking the disk, the (possibly bad) sectors
 * belonging to the file were re-used by the file system and the object
 * using them was closed before we locked the disk.  Rec_read_bit_maps
 * checks each bad block to see if it is in use or not.  Bad blocks which
 * have been re-used are not added to the bad block list.
 */

  if (num_bad_sectors) {		/* found some bad sectors */
    if (lock_disk ()) { 		/* we'll be doing physical I/O */
      error_msg (REC_CANT_SAVE_BAD_STR); /* can't lock -- can't update bbl */
      close_disk ();
      return (SUCCESS_CODE);		/* well, at least we recovered the file */
    }
    if (rec_read_bit_maps ()) { 	/* get current bit maps from disk */
      error_msg (REC_CANT_SAVE_BAD_STR); /* all bad blocks were re-used */
      close_disk ();
      return (SUCCESS_CODE);		/* but we did recover the file */
    }
    save_bad ();			/* add to the bad list on disk */
    rec_update_bit_maps ();		/* mark bad sectors as "in use" */
    free (bad_list);			/* deallocate the bad list */
  }

  close_disk ();

  return (SUCCESS_CODE);		/* successful exit */
  envp; 				/* we use envp to eliminate warnings. */
					/* we must have it on the call stack */
					/* because the calling program will */
					/* pull it off. */
}
