/****************************************************************/ /* */ /* de_stdout.c */ /* */ /* Displaying information from the "Disk editor". */ /* */ /****************************************************************/ /* origination 1989-Jan-15 Terrence W. Holm */ /****************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../servers/fs/const.h" #include "../../servers/fs/type.h" #include "../../servers/fs/inode.h" #include #include "de.h" #ifndef major #define major(x) ( (x>>8) & 0377) #define minor(x) (x & 0377) #endif /****************************************************************/ /* Code for handling termcap */ /****************************************************************/ #define TC_BUFFER 1024 /* Size of termcap(3) buffer */ #define TC_STRINGS 200 /* Enough room for cm,cl,so,se */ static char *Tmove; /* (cm) - Format for tgoto */ static char *Tclr_all; /* (cl) - Clear screen */ static char *Treverse; /* (so) - Start reverse mode */ static char *Tnormal; /* (se) - End reverse mode */ char Kup = 0; /* (ku) - Up arrow key */ char Kdown = 0; /* (kd) - Down arrow key */ char Kleft = 0; /* (kl) - Left arrow key */ char Kright = 0; /* (kr) - Right arrow key */ _PROTOTYPE(void Goto , (int column , int line )); _PROTOTYPE(void Block_Type , (de_state *s )); _PROTOTYPE(void Draw_Words , (de_state *s )); _PROTOTYPE(void Draw_Info , (de_state *s )); _PROTOTYPE(void Draw_Block , (char *block )); _PROTOTYPE(void Draw_Map , (char *block , int max_bits )); _PROTOTYPE(void Draw_Offset , (de_state *s )); _PROTOTYPE(void Word_Pointers , (off_t old_addr , off_t new_addr )); _PROTOTYPE(void Block_Pointers , (off_t old_addr , off_t new_addr )); _PROTOTYPE(void Map_Pointers , (off_t old_addr , off_t new_addr )); _PROTOTYPE(void Print_Number , (Word_t number , int output_base )); _PROTOTYPE(void Draw_Zone_Numbers , (de_state *s , struct inode *inode , int zindex , int zrow )); /****************************************************************/ /* */ /* Init_Termcap() */ /* */ /* Initializes the external variables for the */ /* current terminal. */ /* */ /****************************************************************/ int Init_Termcap() { char *term; char buffer[ TC_BUFFER ]; static char strings[ TC_STRINGS ]; char *s = &strings[0]; char *Kcode; term = getenv( "TERM" ); if ( term == NULL ) return( 0 ); if ( tgetent( buffer, term ) != 1 ) return( 0 ); if ( (Tmove = tgetstr( "cm", &s )) == NULL ) return( 0 ); if ( (Tclr_all = tgetstr( "cl", &s )) == NULL ) return( 0 ); if ( (Treverse = tgetstr( "so", &s )) == NULL ) { Treverse = Tnormal = s; *s = '\0'; ++s; } else if ( (Tnormal = tgetstr( "se", &s )) == NULL ) return( 0 ); /* See if there are single character arrow key codes */ if ( (Kcode = tgetstr( "ku", &s )) != NULL && strlen( Kcode ) == 1 ) Kup = Kcode[0]; if ( (Kcode = tgetstr( "kd", &s )) != NULL && strlen( Kcode ) == 1 ) Kdown = Kcode[0]; if ( (Kcode = tgetstr( "kl", &s )) != NULL && strlen( Kcode ) == 1 ) Kleft = Kcode[0]; if ( (Kcode = tgetstr( "kr", &s )) != NULL && strlen( Kcode ) == 1 ) Kright = Kcode[0]; return( 1 ); } /****************************************************************/ /* */ /* Goto( column, line ) */ /* */ /* Use the termcap string to move the cursor. */ /* */ /****************************************************************/ void Goto( column, line ) int column; int line; { fputs( tgoto( Tmove, column, line ), stdout ); } /****************************************************************/ /* Output routines */ /****************************************************************/ /****************************************************************/ /* */ /* Draw_Help_Screen() */ /* */ /****************************************************************/ void Draw_Help_Screen( s ) de_state *s; { int down; int right; switch ( s->mode ) { case WORD : down = 2; right = 32; break; case BLOCK : down = 64; right = 1; break; case MAP : down = 256; right = 4; break; } printf( "%s ", Tclr_all ); printf( "%sDE COMMANDS%s\r\n\n\n", Treverse, Tnormal ); printf( " PGUP b Back one block h Help\r\n" ); printf( " PGDN f Forward one block q Quit\r\n" ); printf( " HOME B Goto first block m Minix shell\r\n" ); printf( " END F Goto last block\r\n" ); printf( " v Visual mode (w b m)\r\n" ); printf( " g Goto specified block o Output base (h d o b)\r\n" ); printf( " G Goto block indirectly\r\n" ); printf( " i Goto i-node c Change file name\r\n" ); printf( " I Filename to i-node w Write ASCII block\r\n" ); printf( " W Write block exactly\r\n" ); printf( " / Search\r\n" ); printf( " n Next occurrence x Extract lost entry\r\n" ); printf( " p Previous address X Extract lost blocks\r\n" ); printf( " s Store word\r\n" ); printf( " UP u Move back %d bytes\r\n", down ); printf( " DOWN d Move forward %d bytes\r\n", down ); printf( " LEFT l Move back %d byte%s\r\n", right, right == 1 ? "" : "s" ); printf( " RIGHT r Move forward %d byte%s\r\n\n\n", right, right == 1 ? "" : "s" ); } /****************************************************************/ /* */ /* Wait_For_Key() */ /* */ /* The user must press a key to continue. */ /* */ /****************************************************************/ void Wait_For_Key() { Draw_Prompt( "Press a key to continue..." ); Get_Char(); } /****************************************************************/ /* */ /* Draw_Prompt( string ) */ /* */ /* Write a message in the "prompt" area. */ /* */ /****************************************************************/ void Draw_Prompt( string ) char *string; { Goto( PROMPT_COLUMN, PROMPT_LINE ); printf( "%s%s%s ", Treverse, string, Tnormal ); } /****************************************************************/ /* */ /* Erase_Prompt() */ /* */ /* Erase the message in the "prompt" area. */ /* */ /****************************************************************/ void Erase_Prompt() { Goto( PROMPT_COLUMN, PROMPT_LINE ); printf( "%77c", ' ' ); Goto( PROMPT_COLUMN, PROMPT_LINE ); } /****************************************************************/ /* */ /* Draw_Screen( state ) */ /* */ /* Redraw everything, except pointers. */ /* */ /****************************************************************/ void Draw_Screen( s ) de_state *s; { fputs( Tclr_all, stdout ); Draw_Strings( s ); Block_Type( s ); switch ( s->mode ) { case WORD : Draw_Words( s ); Draw_Info( s ); break; case BLOCK : Draw_Block( s->buffer ); break; case MAP : { int max_bits = 2 * K; /* Don't display the bits after the end */ /* of the i-node or zone bit maps. */ if ( s->block == 2 + s->inode_maps - 1 ) max_bits = (int) (s->inodes_in_map - CHAR_BIT * K * (ino_t) (s->inode_maps - 1) - CHAR_BIT * (ino_t) (s->offset & ~ MAP_MASK)); else if ( s->block == 2 + s->inode_maps + s->zone_maps - 1 ) max_bits = (int) (s->zones_in_map - CHAR_BIT * K * (zone_t) (s->zone_maps - 1) - CHAR_BIT * (zone_t) (s->offset & ~ MAP_MASK)); if ( max_bits < 0 ) max_bits = 0; Draw_Map( &s->buffer[ s->offset & ~ MAP_MASK ], max_bits ); break; } } } /****************************************************************/ /* */ /* Draw_Strings( state ) */ /* */ /* The first status line contains the device name, */ /* the current write file name (if one is open) */ /* and the current search string (if one has */ /* been defined). */ /* */ /* Long strings are truncated. */ /* */ /****************************************************************/ void Draw_Strings( s ) de_state *s; { int len; int i; Goto( STATUS_COLUMN, STATUS_LINE ); printf( "Device %s= %-14.14s ", s->device_mode == O_RDONLY ? "" : "(w) ", s->device_name ); switch ( s->magic ) { case SUPER_MAGIC : printf( "V1 file system "); break; case SUPER_REV : printf( "V1-bytes-swapped file system (?) "); break; case SUPER_V2 : printf( "V2 file system "); break; case SUPER_V2_REV : printf( "V2-bytes-swapped file system (?) "); break; case SUPER_V3 : printf( "V3 file system "); break; default : printf( "not a Minix file system "); break; } len = strlen( s->file_name ); if ( len == 0 ) printf( "%29s", " " ); else if ( len <= 20 ) printf( "File = %-20s ", s->file_name ); else printf( "File = ...%17.17s ", s->file_name + len - 17 ); len = strlen( s->search_string ); if ( len == 0 ) printf( "%20s", " " ); else { printf( "Search = " ); if ( len <= 11 ) { for ( i = 0; i < len; ++i ) Print_Ascii( s->search_string[ i ] ); for ( ; i < 11; ++i ) putchar( ' ' ); } else { for ( i = 0; i < 8; ++i ) Print_Ascii( s->search_string[ i ] ); printf( "..." ); } } } /****************************************************************/ /* */ /* Block_Type( state ) */ /* */ /* Display the current block type. */ /* */ /****************************************************************/ void Block_Type( s ) de_state *s; { Goto( STATUS_COLUMN, STATUS_LINE + 1 ); printf( "Block = %5u of %-5u ", s->block, s->zones ); if ( !s->is_fs ) return; if ( s->block == BOOT_BLOCK ) printf( "Boot block" ); else if ( s->block == 1 ) printf( "Super block" ); else if ( s->block < 2 + s->inode_maps ) printf( "I-node bit map" ); else if ( s->block < 2 + s->inode_maps + s->zone_maps ) printf( "Zone bit map" ); else if ( s->block < s->first_data ) printf( "I-nodes" ); else printf( "Data block (%sin use)", In_Use( (bit_t) (s->block - (s->first_data - 1)), s->zone_map ) ? "" : "not " ); } /****************************************************************/ /* */ /* Draw_Words( state ) */ /* */ /* Draw a page in word format. */ /* */ /****************************************************************/ void Draw_Words( s ) de_state *s; { int line; int addr = s->offset & ~ PAGE_MASK; for ( line = 0; line < 16; ++line, addr += 2 ) { Goto( BLOCK_COLUMN, BLOCK_LINE + line ); printf( "%5d ", addr ); Print_Number( *( (word_t *) &s->buffer[ addr ] ), s->output_base ); } Goto( BLOCK_COLUMN + 64, BLOCK_LINE ); printf( "(base %d)", s->output_base ); } /****************************************************************/ /* */ /* Draw_Info( state ) */ /* */ /* Add information to a page drawn in word format. */ /* The routine recognizes the super block, inodes, */ /* executables and "ar" archives. If the current */ /* page is not one of these, then ASCII characters */ /* are printed from the data words. */ /* */ /****************************************************************/ char *super_block_info[] = { "number of inodes", "V1 number of zones", "inode bit map blocks", "zone bit map blocks", "first data zone", "blocks per zone shift & flags", "maximum file size", "", "magic number", "fsck magic number", "V2 number of zones" }; void Draw_Info( s ) de_state *s; { int i; int page = s->offset >> PAGE_SHIFT; dev_t dev; if ( s->is_fs && s->block == 1 && page == 0 ) for ( i = 0; i < 11; ++i ) { Goto( INFO_COLUMN, INFO_LINE + i ); printf( "%s", super_block_info[ i ] ); } else if ( s->is_fs && s->block >= s->first_data - s->inode_blocks && s->block < s->first_data ) { struct inode core_inode; d1_inode *dip1; d2_inode *dip2; struct inode *inode = &core_inode; int special = 0; int m; struct passwd *user; struct group *grp; dip1 = (d1_inode *) &s->buffer[ s->offset & ~ PAGE_MASK ]; dip2 = (d2_inode *) &s->buffer[ s->offset & ~ PAGE_MASK & ~ (V2_INODE_SIZE-1) ]; conv_inode( inode, dip1, dip2, READING, s->magic ); user = getpwuid( inode->i_uid ); grp = getgrgid( inode->i_gid ); if ( s->magic != SUPER_MAGIC && page & 1 ) { Draw_Zone_Numbers( s, inode, 2, 0 ); return; } Goto( INFO_COLUMN, INFO_LINE ); switch( inode->i_mode & S_IFMT ) { case S_IFDIR : printf( "directory " ); break; case S_IFCHR : printf( "character " ); special = 1; break; case S_IFBLK : printf( "block " ); special = 1; break; case S_IFREG : printf( "regular " ); break; #ifdef S_IFIFO case S_IFIFO : printf( "fifo " ); break; #endif #ifdef S_IFLNK case S_IFLNK : printf( "symlink " ); break; #endif #ifdef S_IFSOCK case S_IFSOCK: printf( "socket " ); break; #endif default : printf( "unknown " ); } for ( m = 11; m >= 0; --m ) putchar( (inode->i_mode & (1<magic == SUPER_MAGIC ) { /* V1 file system */ Goto( INFO_COLUMN, INFO_LINE + 1 ); printf( "user %s", user ? user->pw_name : "" ); Goto( INFO_COLUMN, INFO_LINE + 2 ); printf( "file size %lu", inode->i_size ); Goto( INFO_COLUMN, INFO_LINE + 4 ); printf( "m_time %s", ctime( &inode->i_mtime ) ); Goto( INFO_COLUMN, INFO_LINE + 6 ); printf( "links %d, group %s", inode->i_nlinks, grp ? grp->gr_name : "" ); Draw_Zone_Numbers( s, inode, 0, 7 ); } else { /* V2 file system, even page. */ Goto( INFO_COLUMN, INFO_LINE + 1 ); printf( "links %d ", inode->i_nlinks); Goto( INFO_COLUMN, INFO_LINE + 2 ); printf( "user %s", user ? user->pw_name : "" ); Goto( INFO_COLUMN, INFO_LINE + 3 ); printf( "group %s", grp ? grp->gr_name : "" ); Goto( INFO_COLUMN, INFO_LINE + 4 ); printf( "file size %lu", inode->i_size ); Goto( INFO_COLUMN, INFO_LINE + 6 ); printf( "a_time %s", ctime( &inode->i_atime ) ); Goto( INFO_COLUMN, INFO_LINE + 8 ); printf( "m_time %s", ctime( &inode->i_mtime ) ); Goto( INFO_COLUMN, INFO_LINE + 10 ); printf( "c_time %s", ctime( &inode->i_ctime ) ); Draw_Zone_Numbers( s, inode, 0, 12 ); } if ( special ) { Goto( INFO_COLUMN, INFO_LINE + 7 ); dev = (dev_t) inode->i_zone[0]; printf( "major %d, minor %d", major(dev), minor(dev) ); } } else /* Print ASCII characters for each byte in page */ { char *p = &s->buffer[ s->offset & ~ PAGE_MASK ]; for ( i = 0; i < 16; ++i ) { Goto( INFO_COLUMN, INFO_LINE + i ); Print_Ascii( *p++ ); Print_Ascii( *p++ ); } if ( s->block >= s->first_data && page == 0 ) { unsigned magic = ((s->buffer[1] & 0xff) << 8) | (s->buffer[0] & 0xff); unsigned second = ((s->buffer[3] & 0xff) << 8) | (s->buffer[2] & 0xff); /* Is this block the start of an executable file? */ if ( magic == (unsigned) A_OUT ) { Goto( INFO_COLUMN, INFO_LINE ); printf( "executable" ); Goto( INFO_COLUMN, INFO_LINE + 1 ); if ( second == (unsigned) SPLIT ) printf( "separate I & D" ); else printf( "combined I & D" ); } } } } /****************************************************************/ /* */ /* Draw_Block( block ) */ /* */ /* Redraw a 1k block in character format. */ /* */ /****************************************************************/ void Draw_Block( block ) char *block; { int line; int column; int reverse = 0; int msb_flag = 0; for ( line = 0; line < 16; ++line ) { Goto( BLOCK_COLUMN, BLOCK_LINE + line ); for ( column = 0; column < 64; ++column ) { char c = *block++; if ( c & 0x80 ) { msb_flag = 1; c &= 0x7f; } if ( c >= ' ' && c < DEL ) { if ( reverse ) { fputs( Tnormal, stdout ); reverse = 0; } putchar( c ); } else { if ( ! reverse ) { fputs( Treverse, stdout ); reverse = 1; } putchar( c == DEL ? '?' : '@' + c ); } } /* end for ( column ) */ } /* end for ( line ) */ if ( reverse ) { fputs( Tnormal, stdout ); reverse = 0; } if ( msb_flag ) { Goto( BLOCK_COLUMN + 68, BLOCK_LINE + 6 ); fputs( "(MSB)", stdout ); } } /****************************************************************/ /* */ /* Draw_Map( block, max_bits ) */ /* */ /* Redraw a block in a bit map format. */ /* Display min( max_bits, 2048 ) bits. */ /* */ /* The 256 bytes in "block" are displayed from */ /* top to bottom and left to right. Bit 0 of */ /* a byte is towards the top of the screen. */ /* */ /* Special graphic codes are used to generate */ /* two "bits" per character position. So a 16 */ /* line by 64 column display is 32 "bits" by */ /* 64 "bits". Or 4 bytes by 64 bytes. */ /* */ /****************************************************************/ void Draw_Map( block, max_bits ) char *block; int max_bits; { int line; int column; int bit_count = 0; for ( line = 0; line < 16; ++line ) { char *p = &block[ (line & 0xC) >> 2 ]; int shift = (line & 0x3) << 1; Goto( BLOCK_COLUMN, BLOCK_LINE + line ); for ( column = 0; column < 64; ++column, p += 4 ) { char c = (*p >> shift) & 0x3; int current_bit = ((p - block) << 3) + shift; /* Don't display bits past "max_bits" */ if ( current_bit >= max_bits ) break; /* If "max_bits" occurs in between the two bits */ /* I am trying to display as one character, then */ /* zero off the high-order bit. */ if ( current_bit + 1 == max_bits ) c &= 1; switch ( c ) { case 0 : putchar( BOX_CLR ); break; case 1 : putchar( BOX_TOP ); ++bit_count; break; case 2 : putchar( BOX_BOT ); ++bit_count; break; case 3 : putchar( BOX_ALL ); bit_count += 2; break; } } /* end for ( column ) */ } /* end for ( line ) */ Goto( BLOCK_COLUMN + 68, BLOCK_LINE + 6 ); printf( "(%d)", bit_count ); } /****************************************************************/ /* */ /* Draw_Pointers( state ) */ /* */ /* Redraw the pointers and the offset field. */ /* The rest of the screen stays intact. */ /* */ /****************************************************************/ void Draw_Pointers( s ) de_state *s; { Draw_Offset( s ); switch ( s->mode ) { case WORD : Word_Pointers( s->last_addr, s->address ); break; case BLOCK : Block_Pointers( s->last_addr, s->address ); break; case MAP : Map_Pointers( s->last_addr, s->address ); break; } Goto( PROMPT_COLUMN, PROMPT_LINE ); } /****************************************************************/ /* */ /* Draw_Offset( state ) */ /* */ /* Display the offset in the current buffer */ /* and the relative position if within a map */ /* or i-node block. */ /* */ /****************************************************************/ void Draw_Offset( s ) de_state *s; { Goto( STATUS_COLUMN, STATUS_LINE + 2 ); printf( "Offset = %5d ", s->offset ); if ( s->block < 2 ) return; if ( s->block < 2 + s->inode_maps ) { long bit = (s->address - 2 * K) * 8; if ( bit < s->inodes_in_map ) printf( "I-node %ld of %d ", bit, s->inodes ); else printf( "(padding) " ); } else if ( s->block < 2 + s->inode_maps + s->zone_maps ) { long bit = (s->address - (2 + s->inode_maps) * K) * 8; if ( bit < s->zones_in_map ) printf( "Block %ld of %u ", bit + s->first_data - 1, s->zones ); else printf( "(padding) " ); } else if ( s->block < s->first_data ) { bit_t node = (s->address - (2 + s->inode_maps + s->zone_maps) * K) / s->inode_size + 1; if ( node <= s->inodes ) printf( "I-node %lu of %lu (%sin use) ", (unsigned long) node, (unsigned long) s->inodes, In_Use( node, s->inode_map ) ? "" : "not " ); else printf( "(padding) " ); } } /****************************************************************/ /* */ /* Word_Pointers( old_addr, new_addr ) */ /* */ /* Block_Pointers( old_addr, new_addr ) */ /* */ /* Map_Pointers( old_addr, new_addr ) */ /* */ /* Redraw the index pointers for a each type */ /* of display. The pointer at "old_addr" is */ /* erased and a new pointer is positioned */ /* for "new_addr". This makes the screen */ /* update faster and more pleasant for the user. */ /* */ /****************************************************************/ void Word_Pointers( old_addr, new_addr ) off_t old_addr; off_t new_addr; { int from = ( (int) old_addr & PAGE_MASK ) >> 1; int to = ( (int) new_addr & PAGE_MASK ) >> 1; Goto( BLOCK_COLUMN - 2, BLOCK_LINE + from ); putchar( ' ' ); Goto( BLOCK_COLUMN - 2, BLOCK_LINE + to ); putchar( '>' ); } void Block_Pointers( old_addr, new_addr ) off_t old_addr; off_t new_addr; { int from = (int) old_addr & ~K_MASK; int to = (int) new_addr & ~K_MASK; Goto( BLOCK_COLUMN - 2, BLOCK_LINE + from / 64 ); putchar( ' ' ); Goto( BLOCK_COLUMN - 2, BLOCK_LINE + to / 64 ); putchar( '>' ); Goto( BLOCK_COLUMN + from % 64, BLOCK_LINE + 17 ); putchar( ' ' ); Goto( BLOCK_COLUMN + to % 64, BLOCK_LINE + 17 ); putchar( '^' ); } void Map_Pointers( old_addr, new_addr ) off_t old_addr; off_t new_addr; { int from = ( (int) old_addr & MAP_MASK ) >> 2; int to = ( (int) new_addr & MAP_MASK ) >> 2; Goto( BLOCK_COLUMN + from, BLOCK_LINE + 17 ); putchar( ' ' ); Goto( BLOCK_COLUMN + to, BLOCK_LINE + 17 ); putchar( '^' ); } /****************************************************************/ /* */ /* Print_Number( number, output_base ) */ /* */ /* Output "number" in the output base. */ /* */ /****************************************************************/ void Print_Number( number, output_base ) word_t number; int output_base; { switch ( output_base ) { case 16 : printf( "%5x", number ); break; case 10 : printf( "%7u", number ); break; case 8 : printf( "%7o", number ); break; case 2 : { unsigned int mask; char pad = ' '; for ( mask = 0x8000; mask > 1; mask >>= 1 ) putchar( (mask & number) ? (pad = '0', '1') : pad ); putchar( (0x01 & number) ? '1' : '0' ); break; } default : Error( "Internal fault (output_base)" ); } } /****************************************************************/ /* */ /* Print_Ascii( char ) */ /* */ /* Display a character in reverse mode if it */ /* is not a normal printable ASCII character. */ /* */ /****************************************************************/ void Print_Ascii( c ) char c; { c &= 0x7f; if ( c < ' ' ) printf( "%s%c%s", Treverse, '@' + c, Tnormal ); else if ( c == DEL ) printf( "%s?%s", Treverse, Tnormal ); else putchar( c ); } /****************************************************************/ /* */ /* Warning( text, arg1, arg2 ) */ /* */ /* Display a message for 2 seconds. */ /* */ /****************************************************************/ #if __STDC__ void Warning( const char *text, ... ) #else void Warning( text ) char *text; #endif { va_list argp; printf( "%c%s", BELL, Tclr_all ); Goto( WARNING_COLUMN, WARNING_LINE ); printf( "%s Warning: ", Treverse ); va_start( argp, text ); vprintf( text, argp ); va_end( argp ); printf( " %s", Tnormal ); fflush(stdout); /* why does everyone forget this? */ sleep( 2 ); } void Draw_Zone_Numbers( s, inode, zindex, zrow ) de_state *s; struct inode *inode; int zindex; int zrow; { static char *plurals[] = { "", "double ", "triple " }; zone_t zone; for ( ; zrow < 16; ++zindex, zrow += s->zone_num_size / sizeof (word_t) ) { Goto( INFO_COLUMN, INFO_LINE + zrow ); if ( zindex < s->ndzones ) printf( "zone %d", zindex ); else printf( "%sindirect", plurals[ zindex - s->ndzones ] ); if ( s->magic != SUPER_MAGIC ) { zone = inode->i_zone[ zindex ]; if ( zone != (word_t) zone ) { Goto( INFO_COLUMN + 16, INFO_LINE + zrow ); printf("%ld", (long) zone ); } } } }