/****************************************************************/
/*								*/
/*	de_stdout.c						*/
/*								*/
/*		Displaying information from the "Disk editor".	*/
/*								*/
/****************************************************************/
/*  origination         1989-Jan-15        Terrence W. Holm	*/
/****************************************************************/


#include <minix/config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <grp.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <termcap.h>
#include <time.h>
#include <unistd.h>

#include <minix/const.h>
#include <minix/type.h>
#include "../../fs/const.h"
#include "../../fs/type.h"
#include "../../fs/inode.h"
#include <minix/fslib.h>

#include "de.h"

#define major(x) ( (x>>8) & 0377)
#define minor(x) (x & 0377)

/****************************************************************/
/*   		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;
    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 == SUPER_BLOCK )
    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 == SUPER_BLOCK  &&  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<<m)) ? "xwrxwrxwrtgu"[m] : '-' );

	if ( s->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 );
	}
      }
    }
  }
