1335 lines
24 KiB
C
Executable file
1335 lines
24 KiB
C
Executable file
/****************************************************************/
|
|
/* */
|
|
/* 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 <sys/wait.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#undef ERROR /* arrgghh, errno.h has this pollution */
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <minix/const.h>
|
|
#include <minix/type.h>
|
|
#include "../../servers/fs/const.h"
|
|
#include "../../servers/fs/type.h"
|
|
#include "../../servers/fs/inode.h"
|
|
|
|
#include "de.h"
|
|
|
|
static char copyright[] = "de (c) Terrence W. Holm 1989";
|
|
|
|
|
|
_PROTOTYPE(void Push , (de_state *s ));
|
|
_PROTOTYPE(int Get_Base , (int *base ));
|
|
_PROTOTYPE(int Get_Filename , (de_state *s ));
|
|
_PROTOTYPE(int Get_Count , (char *units , unsigned long *result ));
|
|
_PROTOTYPE(void Exec_Shell , (void));
|
|
_PROTOTYPE(void Sigint , (int));
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/* */
|
|
/* main() */
|
|
/* */
|
|
/* Initialize. Handle the "-r" recovery option if */
|
|
/* specified, else enter the main processing loop. */
|
|
/* */
|
|
/****************************************************************/
|
|
|
|
|
|
void main( argc, argv )
|
|
int argc;
|
|
char *argv[];
|
|
|
|
{
|
|
static de_state s; /* it is safer not to put it on the stack
|
|
* and some things probably now rely on zero
|
|
* initialization
|
|
*/
|
|
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" );
|
|
Warning( "The (partial) last block will not be accessible" );
|
|
}
|
|
}
|
|
|
|
|
|
/* 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) * s.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 = (unsigned) (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 long block;
|
|
|
|
if ( Get_Count( "Block?", &block ) )
|
|
{
|
|
if ( block >= s->zones )
|
|
{
|
|
Warning( "Block number too large" );
|
|
return( REDRAW );
|
|
}
|
|
|
|
Push( s );
|
|
|
|
s->address = (off_t) block * K;
|
|
|
|
return( REDRAW );
|
|
}
|
|
else
|
|
return( ERROR );
|
|
}
|
|
|
|
|
|
case 'G' : /* Goto block indirect */
|
|
|
|
{
|
|
unsigned block = *( (word_t *) &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 */
|
|
|
|
{
|
|
unsigned long 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 = (off_t) (s->first_data - s->inode_blocks) * K
|
|
+ (off_t) (inode - 1) * s->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) * s->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 long 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 ) )
|
|
{
|
|
if ( word != (word_t) word )
|
|
{
|
|
Warning( "Word is more than 16 bits" );
|
|
return( REDRAW );
|
|
}
|
|
Write_Word( s, (word_t) 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' : {
|
|
/* Assume user knows if map mode is possible
|
|
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) * s->inode_size;
|
|
}
|
|
|
|
return( REDRAW );
|
|
}
|
|
|
|
|
|
case 'X' : /* eXtract lost blocks */
|
|
|
|
{
|
|
int rc;
|
|
|
|
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 ( 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;
|
|
unsigned long *result;
|
|
|
|
{
|
|
char *number;
|
|
|
|
Draw_Prompt( units );
|
|
|
|
number = Get_Line();
|
|
|
|
if ( number == NULL || number[0] == '\0' )
|
|
return( 0 );
|
|
|
|
errno = 0;
|
|
*result = strtoul( number, (char **) NULL, 0 );
|
|
return( errno == 0 );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/* */
|
|
/* In_Use( bit, map ) */
|
|
/* */
|
|
/* Is the bit set in the map? */
|
|
/* */
|
|
/****************************************************************/
|
|
|
|
|
|
int In_Use( bit, map )
|
|
bit_t bit;
|
|
bitchunk_t *map;
|
|
|
|
{
|
|
return( map[ (int) (bit / (CHAR_BIT * sizeof (bitchunk_t))) ] &
|
|
(1 << ((unsigned) bit % (CHAR_BIT * sizeof (bitchunk_t)))) );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/* */
|
|
/* 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" );
|
|
|
|
#ifdef S_IFLNK
|
|
if ( lstat( filename, &file_stat ) == -1 )
|
|
#else
|
|
if ( stat( filename, &file_stat ) == -1 )
|
|
#endif
|
|
{
|
|
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(n)
|
|
int n;
|
|
{
|
|
Reset_Term(); /* Restore terminal characteristics */
|
|
|
|
putchar( '\n' );
|
|
|
|
exit( 1 );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/* */
|
|
/* Error( text, ... ) */
|
|
/* */
|
|
/* Print an error message on stderr. */
|
|
/* */
|
|
/****************************************************************/
|
|
|
|
|
|
#if __STDC__
|
|
void Error( const char *text, ... )
|
|
#else
|
|
void Error( text )
|
|
char *text;
|
|
#endif
|
|
|
|
{
|
|
va_list argp;
|
|
|
|
Reset_Term();
|
|
|
|
fprintf( stderr, "\nde: " );
|
|
va_start( argp, text );
|
|
vfprintf( stderr, text, argp );
|
|
va_end( argp );
|
|
if ( errno != 0 )
|
|
fprintf( stderr, ": %s", strerror( errno ) );
|
|
fprintf( stderr, "\n" );
|
|
|
|
exit( 1 );
|
|
}
|