text: /****************************************************************/
  /*								*/
  /*	de.c							*/
  /*								*/
  /*		Main loop of the "Disk editor".			*/
  /*								*/
  /****************************************************************/
  /*  origination         1989-Jan-15        Terrence W. Holm	*/
  /****************************************************************/
  
  
  #include <minix/config.h>
  #include <sys/types.h>
  #include <sys/dir.h>
  #include <sys/stat.h>
  #include <ctype.h>
  #include <fcntl.h>
  #include <limits.h>
  #include <signal.h>
  #include <string.h>
  #include <unistd.h>
  #include <stdio.h>
  
  #include <minix/type.h>
  #include "../../fs/const.h"
  #include "../../fs/type.h"
  
  #include "de.h"
  
  static char copyright[] = { "de  (c) Terrence W. Holm 1989" };
  
  
  
  /****************************************************************/
  /*								*/
  /*	main()							*/
  /*								*/
  /*		Initialize. Handle the "-r" recovery option if	*/
  /*		specified, else enter the main processing loop.	*/
  /*								*/
  /****************************************************************/
  
  
  void main( argc, argv )
    int   argc;
    char *argv[];
  
    {
    de_state s;
    char *command_name = argv[0];
    int   recover = 0;
  
  
    s.device_mode = O_RDONLY;
  
  
    /*  Parse arguments  */
  
    if ( argc == 3  &&  strcmp( argv[1], "-r" ) == 0 )
      {
      recover = 1;
      --argc;
      ++argv;
      }
    else if ( argc == 3  &&  strcmp( argv[1], "-w" ) == 0 )
      {
      s.device_mode = O_RDWR;
      --argc;
      ++argv;
      }
  
    if ( argc != 2  ||  *argv[1] == '-' )
      {
      fprintf( stderr, "Usage: %s [-w] /dev/device\n", command_name );
      fprintf( stderr, "       %s -r lost_file_name\n", command_name );
      exit( 1 );
      }
  
  
    /*  Set the effective id to the real id. This eliminates	*/
    /*  any increase in privilege done by a set-uid bit on the	*/
    /*  executable file. We want to be "root" for recovering 	*/
    /*  files, because we must be able to read the device.	*/
    /*  However, in normal usage, de(1) should not let just 	*/
    /*  anyone look at a file system, thus we drop the privilege.	*/
    /*								*/
    /*  NOTE: There is a security hole when using "-r" with a	*/
    /*  set-uid de(1). Do not use set-uid root if there is any	*/
    /*  way to externally access your Minix system.		*/
  
    if ( ! recover )
      {
      setuid( getuid() );
      setgid( getgid() );
      }
  
  
    /*  Set terminal characteristics, and ^C interrupt handler  */
  
    Save_Term();
  
    if ( signal( SIGINT, SIG_IGN ) != SIG_IGN )
      {
      signal( SIGINT,  Sigint );
      signal( SIGQUIT, Sigint );
      }
  
    Set_Term();
  
    if ( ! Init_Termcap() )
      Error( "Requires a termcap entry" );
  
  
  
    /*  Get the device file name. If recovering, also open an output file.  */
  
    if ( recover )
      {
      char *dir_name;
      char *file_name;
      struct stat device_stat;
      struct stat tmp_stat;
  
      /*  Split the path name into a directory and a file name.  */
  
      if ( strlen(argv[1]) > MAX_STRING )
        Error( "Path name too long" );
  
      if ( ! Path_Dir_File( argv[1], &dir_name, &file_name ) )
        Error( "Recover aborted" );
  
      /*  Find the device holding the directory.  */
  
      if ( (s.device_name = File_Device( dir_name )) == NULL )
        Error( "Recover aborted" );
  
  
      /*  The output file will be in /tmp with the same file name.  */
  
      strcpy( s.file_name, TMP );
      strcat( s.file_name, "/" );
      strcat( s.file_name, file_name );
  
  
      /*  Make sure /tmp is not on the same device as the file we	   */
      /*  are trying to recover (we don't want to use up the free	   */
      /*  i-node and blocks before we get a chance to recover them). */
  
      if ( stat( s.device_name, &device_stat ) == -1 )
        Error( "Can not stat(2) device %s", s.device_name );
  
      if ( stat( TMP, &tmp_stat ) == -1 )
        Error( "Can not stat(2) directory %s", TMP );
  
      if ( device_stat.st_rdev == tmp_stat.st_dev )
        Error( "Will not recover files on the same device as %s", TMP );
  
      if ( access( s.file_name, F_OK ) == 0 )
        Error( "Will not overwrite file %s", s.file_name );
  
  
      /*  Open the output file.  */
  
      if ( (s.file_f = fopen( s.file_name, "w" )) == NULL )
        Error( "Can not open file %s", s.file_name );
  
      /*  Don't let anyone else look at the recovered file  */
  
      chmod( s.file_name, 0700 );
  
      /*  If running as root then change the owner of the  */
      /*  restored file. If not running as root then the   */
      /*  chown(2) will fail.				 */
  
      chown( s.file_name, getuid(), getgid() );
      }
    else
      {
      s.device_name = argv[1];
      s.file_name[ 0 ] = '\0';
      }
  
  
    /*  Open the device file.  */
  
    {
    struct stat device_stat;
    off_t size;
  
    if ( stat( s.device_name, &device_stat ) == -1 )
      Error( "Can not find file %s", s.device_name );
  
    if ( (device_stat.st_mode & S_IFMT) != S_IFBLK  &&
         (device_stat.st_mode & S_IFMT) != S_IFREG )
      Error( "Can only edit block special or regular files" );
  
  
    if ( (s.device_d = open( s.device_name, s.device_mode )) == -1 )
      Error( "Can not open %s", s.device_name );
  
    if ( (size = lseek( s.device_d, 0L, SEEK_END )) == -1 )
      Error( "Error seeking %s", s.device_name );
  
    if ( size % K != 0 )
      Warning( "Device size is not a multiple of 1024" );
    }
  
  
    /*  Initialize the rest of the state record  */
  
    s.mode = WORD;
    s.output_base = 10;
    s.search_string[ 0 ] = '\0';
  
    {
    int i;
  
    for ( i = 0;  i < MAX_PREV;  ++i )
      {
      s.prev_addr[ i ] = 0L;
      s.prev_mode[ i ] = WORD;
      }
    }
  
  
    sync();
  
    Read_Super_Block( &s );
  
    Read_Bit_Maps( &s );
  
    s.address = 0L;
  
  
  
    /*  Recover mode basically performs an 'x' and an 'X'  */
  
    if ( recover )
      {
      ino_t inode = Find_Deleted_Entry( &s, argv[1] );
      off_t size;
  
      if ( inode == 0 )
        {
        unlink( s.file_name );
        Error( "Recover aborted" );
        }
  
      s.address = ( (long) s.first_data - s.inode_blocks ) * K
  		      + (long) (inode - 1) * INODE_SIZE;
  
      Read_Block( &s, s.buffer );
  
  
      /*  Have found the lost i-node, now extract the blocks.  */
  
      if ( (size = Recover_Blocks( &s )) == -1L )
        {
        unlink( s.file_name );
        Error( "Recover aborted" );
        }
  
      Reset_Term();
  
      printf( "Recovered %ld bytes, written to file %s\n", size, s.file_name );
  
      exit( 0 );
      }
  
  
    /*  Enter the main loop, first time redraw the screen  */
    {
    int rc = REDRAW;
  
  
    do
      {
      if ( rc == REDRAW )
        {
        Read_Block( &s, s.buffer );
        Draw_Screen( &s );
        s.last_addr = s.address;
        Draw_Pointers( &s );
        }
  
      else if ( rc == REDRAW_POINTERS )
        {
        s.offset = s.address & ~ K_MASK;
        Draw_Pointers( &s );
        }
  
      else if ( rc == ERROR )
        {
        Erase_Prompt();
        putchar( BELL );
        }
      } while ( (rc = Process( &s, Arrow_Esc(Get_Char()) )) != EOF );
    }
  
  
    /*  If there is an open output file that was never written to  */
    /*  then remove its directory entry. This occurs when no 'w' 	 */
    /*  or 'W' command occurred between a 'c' command and exiting	 */
    /*  the program.						 */
  
    if ( s.file_name[0] != '\0'  &&  ! s.file_written )
      unlink( s.file_name );
  
  
    Reset_Term();	   /*  Restore terminal characteristics  */
  
    exit( 0 );
    }
  
  
  
  /****************************************************************/
  /*								*/
  /*	Get_Base( base )					*/
  /*								*/
  /*		Get a new base value.				*/
  /*		Returns REDRAW or ERROR.			*/
  /*								*/
  /****************************************************************/
  
  
  
  int Get_Base( base )
    int *base;
    {
  	switch ( Get_Char() )
  	  {
  	  case 'h' :	*base = 16;
  			break;
  
  	  case 'd' :	*base = 10;
  			break;
  
  	  case 'o' :	*base = 8;
  			break;
  
  	  case 'b' :	*base = 2;
  			break;
  
  	  default  :	return( ERROR );
  	  }
  
  		return( REDRAW );
    }
  
  
  
  /****************************************************************/
  /*								*/
  /*	Process( state, input_char )				*/
  /*								*/
  /*		Determine the function requested by the 	*/
  /*		input character. Returns OK, REDRAW,		*/
  /*		REDRAW_POINTERS,  ERROR or EOF.			*/
  /*								*/
  /****************************************************************/
  
  
  int Process( s, c )
    de_state  *s;
    int  c;
  
    {
    switch ( c )
      {
      case 'b' :				/*  Back up one block	*/
      case ESC_PGUP :
  
  		if ( s->address == 0 )
  		  return( ERROR );
  
  		s->address = (s->address - K) & K_MASK;
  
  		return( REDRAW );
  
  
      case 'B' :				/*  Back up to home	*/
      case ESC_HOME :
  
  		if ( s->address == 0 )
  		  return( OK );
  
  		Push( s );
  
  		s->address = 0L;
  
  		return( REDRAW );
  
  
      case 'c' :				/*  Change file name	*/
  
  		{
  		int rc = Get_Filename( s );
  
  		return( rc == OK ? REDRAW : rc );
  		}
  
  
      case 'd' :				/*  Down		*/
      case ESC_DOWN :
  
  		{
  		s->last_addr = s->address;
  
  		switch ( s->mode )
  		  {
  		  case WORD :	s->address += 2;
  
  				if ( (s->address & PAGE_MASK) == 0 )
  				  return( REDRAW );
  
  				return( REDRAW_POINTERS );
  
  		  case BLOCK :	s->address += 64;
  
  				if ( (s->last_addr & K_MASK) !=
  				     (s->address   & K_MASK) )
  				  return( REDRAW );
  
  				return( REDRAW_POINTERS );
  
  		  case MAP :	s->address += 256;
  
  				return( REDRAW );
  
  		  default :	Error( "Internal fault (mode)" );
  		  }
  		}
  
  
      case 'f' :				/*  Forward one block	*/
      case ' ' :
      case ESC_PGDN :
  
  		if ( s->block == s->device_size - 1 )
  		  return( ERROR );
  
  		s->address = (s->address + K) & K_MASK;
  
  		return( REDRAW );
  
  
      case 'F' :				/*  Forward to end	*/
      case ESC_END :
  
  		{
  		off_t  last_block = ( (long) s->device_size - 1 ) * K;
  
  		if ( s->address == last_block )
  		  return( OK );
  
  		Push( s );
  
  		s->address = last_block;
  
  		return( REDRAW );
  		}
  
  
      case 'g' :				/*  Goto block		*/
  
  		{
  		unsigned block;
  
  		if ( Get_Count( "Block?", &block ) )
  		  {
  		  if ( block >= s->zones )
  		    {
  		    Warning( "Block number too large" );
  		    return( REDRAW );
  		    }
  
  		  Push( s );
  
  		  s->address = (long) block * K;
  
  		  return( REDRAW );
  		  }
  		else
  		  return( ERROR );
  		}
  
  
      case 'G' :				/*  Goto block indirect	*/
  
  		{
  		unsigned block = *( (unsigned short *)
  				    &s->buffer[ s->offset ] );
  
  		if ( s->mode != WORD )
  		  {
  		  Warning( "Must be in visual mode \"word\"" );
  		  return( REDRAW );
  		  }
  
  		if ( block >= s->zones )
  		  {
  		  Warning( "Block number too large" );
  		  return( REDRAW );
  		  }
  
  		Push( s );
  
  		s->mode = BLOCK;
  		s->address = (long) block * K;
  
  		return( REDRAW );
  		}
  
  
      case 'h' :				/*  Help		*/
      case '?' :
  
  		Draw_Help_Screen( s );
  
  		Wait_For_Key();
  
  		return( REDRAW );
  
  
      case 'i' :				/*  Goto i-node		*/
  
  		{
  		ino_t inode;
  
  		if ( Get_Count( "I-node?", &inode ) )
  		  {
  		  if ( inode < 1  || inode > s->inodes )
  		    {
  		    Warning( "Illegal i-node number" );
  		    return( REDRAW );
  		    }
  
  		  Push( s );
  
  		  s->mode = WORD;
  		  s->address = ( (long) s->first_data - s->inode_blocks ) * K
  				  + (long) (inode - 1) * INODE_SIZE;
  
  		  return( REDRAW );
  		  }
  		else
  		  return( ERROR );
  		}
  
  
      case 'I' :				/*  Filename to i-node	*/
  
  		{
  		ino_t inode;
  		char *filename;
  
  		Draw_Prompt( "File name?" );
  
  		filename = Get_Line();
  
  		if ( filename == NULL  ||  filename[0] == '\0' )
  		  return( ERROR );
  
  		inode = Find_Inode( s, filename );
  
  		if ( inode )
  		  {
  		  Push( s );
  
  		  s->mode = WORD;
  		  s->address = ( (long) s->first_data - s->inode_blocks ) * K
  				  + (long) (inode - 1) * INODE_SIZE;
  		  }
  
  		return( REDRAW );
  		}
  
  
      case 'l' :				/*  Left		*/
      case ESC_LEFT :
  
  		{
  		s->last_addr = s->address;
  
  		switch ( s->mode )
  		  {
  		  case WORD :	s->address = s->address - 32;
  
  				return( REDRAW );
  
  		  case BLOCK :	s->address -= 1;
  
  				if ( (s->last_addr & K_MASK) !=
  				     (s->address   & K_MASK) )
  				  return( REDRAW );
  
  				return( REDRAW_POINTERS );
  
  		  case MAP :	s->address -= 4;
  
  				if ( (s->last_addr & ~ MAP_MASK) !=
  				     (s->address   & ~ MAP_MASK) )
  				  return( REDRAW );
  
  				return( REDRAW_POINTERS );
  
  		  default :	Error( "Internal fault (mode)" );
  		  }
  		}
  
  
      case 'm' :				/*  Invoke a Minix shell */
  
  		Reset_Term();
  
  		Exec_Shell();
  
  		Set_Term();
  
  		return( REDRAW );
  
  
      case 'n' :				/*  Search for next	*/
  
  		{
  		off_t addr;
  
  		if ( s->search_string[0] == '\0' )
  		  {
  		  Warning( "No search string defined" );
  		  return( REDRAW );
  		  }
  
  		Draw_Prompt( "Searching..." );
  
  		if ( (addr = Search( s, s->search_string )) == -1L )
  		  {
  		  Warning( "Search string not found" );
  
  		  Wait_For_Key();
  
  		  return( REDRAW );
  		  }
  
  		Push( s );
  		s->address = addr;
  
  		return( REDRAW );
  		}
  
  
      case 'o' :				/*  Set output base	*/
  
  		Draw_Prompt( "Output base?" );
  
  		return( Get_Base( &s->output_base ) );
  
  
      case 'p' :				/*  Previous address	*/
  
  		{
  		int  i;
  
  		s->address = s->prev_addr[ 0 ];
  		s->mode    = s->prev_mode[ 0 ];
  
    		for ( i = 0;  i < MAX_PREV - 1;  ++i )
  		  {
      		  s->prev_addr[ i ] = s->prev_addr[ i + 1 ];
  		  s->prev_mode[ i ] = s->prev_mode[ i + 1 ];
  		  }
  
  		return( REDRAW );
  		}
  
  
      case 'q' :				/*  Quit		 */
      case EOF :
      case CTRL_D :
  
  		return( EOF );
  
  
      case 'r' :				/*  Right		*/
      case ESC_RIGHT :
  
  		{
  		s->last_addr = s->address;
  
  		switch ( s->mode )
  		  {
  		  case WORD :	s->address += 32;
  
  				return( REDRAW );
  
  		  case BLOCK :	s->address += 1;
  
  				if ( (s->last_addr & K_MASK) !=
  				     (s->address   & K_MASK) )
  				  return( REDRAW );
  
  				return( REDRAW_POINTERS );
  
  		  case MAP :	s->address += 4;
  
  				if ( (s->last_addr & ~ MAP_MASK) !=
  				     (s->address   & ~ MAP_MASK) )
  				  return( REDRAW );
  
  				return( REDRAW_POINTERS );
  
  		  default :	Error( "Internal fault (mode)" );
  		  }
  		}
  
      case 's' :				/*  Store word		*/
  
  		{
  		unsigned word;
  
  		if ( s->mode != WORD )
  		  {
  		  Warning( "Must be in visual mode \"word\"" );
  		  return( REDRAW );
  		  }
  
  		if ( s->device_mode == O_RDONLY )
  		  {
  		  Warning( "Use -w option to open device for writing" );
  		  return( REDRAW );
  		  }
  
  		if ( Get_Count( "Store word?", &word ) )
  		  {
  		  Write_Word( s, word );
  
  		  return( REDRAW );
  		  }
  		else
  		  return( ERROR );
  		}
  
  
      case 'u' :				/*  Up			*/
      case ESC_UP :
  
  		{
  		s->last_addr = s->address;
  
  		switch ( s->mode )
  		  {
  		  case WORD :	s->address -= 2;
  
  				if ( (s->last_addr & PAGE_MASK) == 0 )
  				  return( REDRAW );
  
  				return( REDRAW_POINTERS );
  
  		  case BLOCK :	s->address -= 64;
  
  				if ( (s->last_addr & K_MASK) !=
  				     (s->address   & K_MASK) )
  				  return( REDRAW );
  
  				return( REDRAW_POINTERS );
  
  		  case MAP :	s->address -= 256;
  
  				return( REDRAW );
  
  		  default :	Error( "Internal fault (mode)" );
  		  }
  		}
  
  
      case 'v' :				/*  Visual mode		*/
  
  		Draw_Prompt( "Visual mode?" );
  
  		switch ( Get_Char() )
  		  {
  		  case 'w' :	s->mode = WORD;
  				break;
  
  		  case 'b' :	s->mode = BLOCK;
  				break;
  
  		  case 'm' :	{
  				char *tty = ttyname( 0 );
  
  				if ( tty == NULL  ||
  				    strcmp( tty, "/dev/tty0" ) != 0 )
  				  Warning( "Must be at console" );
  				else
  				  s->mode = MAP;
  
  				break;
  				}
  
  		  default  :	return( ERROR );
  		  }
  
  		return( REDRAW );
  
  
      case 'w' :				/*  Write ASCII block	*/
  
  		if ( s->file_name[0] == '\0' )
  		  {
  		  int  rc = Get_Filename( s );
  
  		  if ( rc != OK )
  		    return( rc );
  		  }
  
  		/*  We have a successfully opened file  */
  
  		/*  Eliminate non-ASCII characters	*/
  		{
  		int i;
  		char buf[ K ];
  		char *from = s->buffer;
  		char *to = buf;
  
  		for ( i = 0;  i < K;  ++i, ++from )
  		  {
  		  *to = *from & 0x7f;
  
  		  if ( *to != '\0'  &&  *to != '\177' )
  		    ++to;
  		  }
  
  		if ( fwrite( buf, 1, (int)(to - buf), s->file_f ) != to - buf )
  		  Warning( "Problem writing out buffer" );
  
  		s->file_written = 1;
  
  		return( REDRAW );
  		}
  
  
      case 'W' :				/*  Write block exactly	*/
  
  		if ( s->file_name[0] == '\0' )
  		  {
  		  int  rc = Get_Filename( s );
  
  		  if ( rc != OK )
  		    return( rc );
  		  }
  
  		/*  We have a successfully opened file  */
  
  		if ( fwrite( s->buffer, 1, K, s->file_f ) != K )
  		  Warning( "Problem writing out buffer" );
  
  		s->file_written = 1;
  
  		return( REDRAW );
  
  
      case 'x' :				/*  eXtract lost entry	*/
  
  		{
  		ino_t inode;
  		char *filename;
  
  		Draw_Prompt( "Lost file name?" );
  
  		filename = Get_Line();
  
  		if ( filename == NULL  ||  filename[0] == '\0' )
  		  return( ERROR );
  
  		inode = Find_Deleted_Entry( s, filename );
  
  		if ( inode )
  		  {
  		  Push( s );
  
  		  s->mode = WORD;
  		  s->address = ( (long) s->first_data - s->inode_blocks ) * K
  				  + (long) (inode - 1) * INODE_SIZE;
  		  }
  
  		return( REDRAW );
  		}
  
  
      case 'X' :				/*  eXtract lost blocks	*/
  
  		{
  		int  rc;
  		off_t size;
  
  		if ( s->mode != WORD )
  		  {
  		  Warning( "Must be in visual mode \"word\"" );
  		  return( REDRAW );
  		  }
  
  
  		/*  Force a new output file name.  */
  
  		if ( (rc = Get_Filename( s )) != OK )
  		  return( rc );
  
  
  		Draw_Strings( s );
  
  		Erase_Prompt();
  		Draw_Prompt( "Recovering..." );
  
  		if ( (size = Recover_Blocks( s )) == -1L )
  		  unlink( s->file_name );
  
  		/*  Force closure of output file.  */
  
  		fclose( s->file_f );
  		s->file_name[ 0 ] = '\0';
  
  		return( REDRAW );
  		}
  
  
      case '/' :				/*  Search		*/
      case ESC_PLUS :
  
  		{
  		off_t addr;
  		char *string;
  
  		Draw_Prompt( "Search string?" );
  
  		string = Get_Line();
  
  		if ( string == NULL )
  		  return( ERROR );
  
  		if ( string[0] != '\0' )
  		  {
  		  strcpy( s->search_string, string );
  		  Draw_Strings( s );
  		  }
  
  		else if ( s->search_string[0] == '\0' )
  		  {
  		  Warning( "No search string defined" );
  		  return( REDRAW );
  		  }
  
  		Erase_Prompt();
  		Draw_Prompt( "Searching..." );
  
  		if ( (addr = Search( s, s->search_string )) == -1L )
  		  {
  		  Warning( "Search string not found" );
  
  		  Wait_For_Key();
  
  		  return( REDRAW );
  		  }
  
  		Push( s );
  
  		s->mode = BLOCK;
  		s->address = addr;
  
  		return( REDRAW );
  		}
  
  
      default:
  		return( ERROR );
      }
    }
  
  
  
  
  
  
  /****************************************************************/
  /*								*/
  /*	Push( state )						*/
  /*								*/
  /*		Push current address and mode, used by the	*/
  /*		commands B, F, g, G, i, I, n, x and /.  This	*/
  /*		information is popped by the 'p' command.	*/
  /*								*/
  /****************************************************************/
  
  
  void Push( s )
    de_state *s;
  
    {
    int  i;
  
    for ( i = MAX_PREV - 1;  i > 0;  --i )
      {
      s->prev_addr[ i ] = s->prev_addr[ i - 1 ];
      s->prev_mode[ i ] = s->prev_mode[ i - 1 ];
      }
  
    s->prev_addr[ 0 ] = s->address;
    s->prev_mode[ 0 ] = s->mode;
    }
  
  
  
  
  
  
  /****************************************************************/
  /*								*/
  /*	Get_Filename( state )					*/
  /*								*/
  /*		Read and check a filename.			*/
  /*								*/
  /****************************************************************/
  
  
  int Get_Filename( s )
    de_state *s;
  
    {
    char *filename;
    char *name;
    FILE *f;
  
    Draw_Prompt( "File name?" );
  
    filename = Get_Line();
  
    if ( filename == NULL  ||  filename[0] == '\0' )
      return( ERROR );
  
  
    for ( name = filename;  *name != '\0';  ++name )
      if ( ! isgraph( *name ) )
        {
        Warning( "File name contains non-graphic characters" );
        return( REDRAW );
        }
  
  
    if ( access( filename, F_OK ) == 0 )
      {
      Warning( "Will not overwrite file %s", filename );
      return( REDRAW );
      }
  
    if ( (f = fopen( filename, "w" )) == NULL )
      {
      Warning( "Can not open file %s", filename );
      return( REDRAW );
      }
  
    /*  If there is already an open output file then  */
    /*  close it. If it was never written to then	    */
    /*  remove its directory entry.		    */
  
    if ( s->file_name[0] != '\0' )
      {
      if ( ! s->file_written )
        unlink( s->file_name );
  
      fclose( s->file_f );
      }
  
    strcpy( s->file_name, filename );
    s->file_f = f;
    s->file_written = 0;
  
    return( OK );
    }
  
  
  
  
  
  
  /****************************************************************/
  /*								*/
  /*	Get_Count()						*/
  /*								*/
  /*		Read and check a number. Returns non-zero	*/
  /*		if successful.					*/
  /*								*/
  /****************************************************************/
  
  
  int Get_Count( units, result )
    char *units;
    int  *result;
  
    {
    char *number;
  
    Draw_Prompt( units );
  
    number = Get_Line();
  
    if ( number == NULL  ||  number[0] == '\0' )
      return( 0 );
  
    return( Str_Int( number, result ) );
    }
  
  
  
  
  
  
  /****************************************************************/
  /*								*/
  /*	Str_Int( string, &result )				*/
  /*								*/
  /*		Convert "string" to an int. Returns non-zero	*/
  /*		if successful. Format: [-][0][x]{0-9}*		*/
  /*								*/
  /****************************************************************/
  
  
  int Str_Int( str, result )
    char *str;
    int  *result;
  
    {
    int negative = 0;
    int base = 10;
    int total = 0;
    char c;
  
    while ( *str == ' ' )
      ++str;
  
    if ( *str == '-' )
      {
      ++str;
      negative = 1;
      }
  
    if ( *str == '0' )
      {
      ++str;
      base = 8;
      }
  
    if ( *str == 'x'  ||  *str == 'X' )
      {
      ++str;
      base = 16;
      }
  
    if ( *str == '\0'  &&  base != 8 )
      return( 0 );
  
    while ( (c = *str++) != '\0' )
      {
      if ( c >= '0'  &&  c <= '7' )
  	total = total * base + c - '0';
      else if ( isdigit( c )  &&  base >= 10 )
          total = total * base + c - '0';
      else if ( isxdigit( c )  &&  base == 16 )
          total = total * base + c - 'A'  + 10;
      else
  	return( 0 );
      }
  
    *result = negative ? -total : total;
    return( 1 );
    }
  
  
  
  
  
  
  /****************************************************************/
  /*								*/
  /*	In_Use( bit, map )					*/
  /*								*/
  /*		Is the bit set in the map?			*/
  /*								*/
  /****************************************************************/
  
  
  int In_Use( bit, map )
    int bit;
    unsigned *map;
  
    {
    return( map[(unsigned) bit / (CHAR_BIT * sizeof(int))] &
  	  (1 << ((unsigned) bit % (CHAR_BIT * sizeof(int)))) );
    }
  
  
  
  
  
  
  /****************************************************************/
  /*								*/
  /*	Find_Inode( state, filename )				*/
  /*								*/
  /*		Find the i-node for the given file name.	*/
  /*								*/
  /****************************************************************/
  
  
  ino_t Find_Inode( s, filename )
    de_state *s;
    char *filename;
  
    {
    struct stat device_stat;
    struct stat file_stat;
    ino_t inode;
  
  
    if ( fstat( s->device_d, &device_stat ) == -1 )
      Error( "Can not fstat(2) file system device" );
  
    if ( stat( filename, &file_stat ) == -1 )
      {
      Warning( "Can not find file %s", filename );
      return( 0 );
      }
  
    if ( device_stat.st_rdev != file_stat.st_dev )
      {
      Warning( "File is not on device %s", s->device_name );
      return( 0 );
      }
  
  
    inode = file_stat.st_ino;
  
    if ( inode < 1  || inode > s->inodes )
      {
      Warning( "Illegal i-node number" );
      return( 0 );
      }
  
    return( inode );
    }
  
  
  
  
  
  
  /****************************************************************/
  /*								*/
  /*	Exec_Shell()						*/
  /*								*/
  /*		Fork off a sub-process to exec() the shell.	*/
  /*								*/
  /****************************************************************/
  
  
  void Exec_Shell()
  
    {
    int pid = fork();
  
    if ( pid == -1 )
      return;
  
  
    if ( pid == 0 )
      {
      /*  The child process  */
  
      extern char **environ;
      char *shell  =  getenv( "SHELL" );
  
      if ( shell == NULL )
        shell = "/bin/sh";
  
      execle( shell, shell, (char *) 0, environ );
  
      perror( shell );
      exit( 127 );
      }
  
  
    /*  The parent process: ignore signals, wait for sub-process	*/
  
    signal( SIGINT,  SIG_IGN );
    signal( SIGQUIT, SIG_IGN );
  
    {
    int  status;
    int  w;
  
    while ( (w=wait(&status)) != pid  &&  w != -1 );
    }
  
    signal( SIGINT,  Sigint );
    signal( SIGQUIT, Sigint );
  
    return;
    }
  
  
  
  
  
  
  /****************************************************************/
  /*								*/
  /*	Sigint()						*/
  /*								*/
  /*		Terminate the program on an interrupt (^C)	*/
  /*		or quit (^\) signal.				*/
  /*								*/
  /****************************************************************/
  
  
  void Sigint()
  
    {
    Reset_Term();		/*  Restore terminal characteristics	*/
  
    putchar( '\n' );
  
    exit( 1 );
    }
  
  
  
  
  
  
  /****************************************************************/
  /*								*/
  /*	Error( text, arg1, arg2 )				*/
  /*								*/
  /*		Print an error message on stderr.		*/
  /*								*/
  /****************************************************************/
  
  
  void Error( text, arg1, arg2 )
    char *text;
    char *arg1;
    char *arg2;
  
    {
    Reset_Term();
  
    fprintf( stderr, "\nde: " );
    fprintf( stderr, text, arg1, arg2 );
    fprintf( stderr, "\n" );
  
    exit( 1 );
    }
  