tholm@uvicctr.UUCP (Terrence W. Holm) (05/18/88)
EFTH Minix report #13 - May 1988 - ic(1) [part 2 of 3] echo x - ic.c gres '^X' '' > ic.c << '/' X/****************************************************************/ X/* */ X/* ic.c */ X/* */ X/* The main loop of the "Integer Calculator". */ X/* */ X/****************************************************************/ X/* origination 1988-Apr-6 T. Holm */ X/* added Exec_Shell() 1988-Apr-11 T. Holm */ X/* added "s+" 1988-Apr-18 T. Holm */ X/* added cmd line args 1988-May-13 T. Holm */ X/****************************************************************/ X X X X X X X#include <stdio.h> X#include <signal.h> X X#include "ic.h" X X X X Xstatic char copyright[] = { "ic(tm) (c) Terrence W. Holm 1988" }; X X X X X/****************************************************************/ X/* */ X/* main() */ X/* */ X/* Initialize. Enter the main processing loop. */ X/* */ X/****************************************************************/ X X Xmain( argc, argv ) X int argc; X char *argv[]; X X { X ic_state state; /* This state record is passed */ X /* to most subroutines */ X X Init_State( &state ); X X X state.scratch_pad = (FILE *) NULL; /* No 'w' command yet */ X X X X Init_Getc( argc, argv ); /* Refs to command line args */ X X X X if ( Init_Termcap() == NULL ) X { X fprintf( stderr, "ic requires a termcap entry\n" ); X exit( 1 ); X } X X X Save_Term(); /* Save terminal characteristics */ X X X /* The following conditional is always TRUE under Minix, */ X /* this is a bug. */ X X if ( signal( SIGINT, SIG_IGN ) != SIG_IGN ) X { X signal( SIGINT, Sigint ); X signal( SIGQUIT, Sigint ); X } X X X Set_Term(); /* Setup terminal characteristics */ X X X X Draw_Screen( &state ); X X while (1) X { X int rc = Process( &state, Get_Char() ); X X if ( rc == EOF ) X break; X X if ( rc == ERROR ) X putchar( BELL ); X } X X X Reset_Term(); /* Restore terminal characteristics */ X X exit( OK ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Init_State() */ X/* */ X/* Initialize the state record. */ X/* */ X/****************************************************************/ X X XInit_State( s ) X ic_state *s; X X { X s->stack[0] = 0; X s->stack_size = 1; X s->register_mask = 0x000; X s->last_tos = 0; X s->mode = LAST_WAS_ENTER; X s->input_base = DECIMAL; X s->output_base = DECIMAL; X } X X X X X X X X X/****************************************************************/ X/* */ X/* Sigint() */ X/* */ X/* Terminate the program on an interrupt (^C) */ X/* or quit (^\) signal. */ X/* */ X/****************************************************************/ X X XSigint() X X { X Reset_Term(); /* Restore terminal characteristics */ X X exit( 1 ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Process( state, input_char ) */ X/* */ X/* Determine the function requested by the */ X/* input character. Returns OK, EOF or ERROR. */ X/* */ X/****************************************************************/ X X Xint Process( s, c ) X ic_state *s; X int c; X X { X switch ( c ) X { X case '0' : X case '1' : X case '2' : X case '3' : X case '4' : X case '5' : X case '6' : X case '7' : X case '8' : X case '9' : X X return( Enter_Numeric( s, (int) c - '0' ) ); X X X case 'a' : X case 'b' : X case 'c' : X case 'd' : X case 'e' : X case 'f' : X X return( Enter_Numeric( s, (int) c - 'a' + 10 ) ); X X X case 'h' : case '?' : /* Help */ X X Draw_Help_Screen(); X X Get_Char(); X X Draw_Screen( s ); X return( OK ); X X X case 'i' : /* Set input base */ X X { X int numeral; X X Draw_Prompt( "Base?" ); X X numeral = Get_Base( Get_Char() ); X X Erase_Prompt(); X X if ( numeral == ERROR || numeral == ASCII ) X return( ERROR ); X X s->input_base = numeral; X s->mode = LAST_WAS_FUNCTION; X X Draw_Screen( s ); X return( OK ); X } X X X case 'l' : case ESC_PGDN : /* Get last tos value */ X X if ( s->mode != LAST_WAS_ENTER ) X Push( s ); X X s->stack[0] = s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case 'm' : /* Invoke a Minix shell */ X X Reset_Term(); X X Exec_Shell(); X X Set_Term(); X X Draw_Screen( s ); X return( OK ); X X X case 'o' : /* Set output base */ X X { X int numeral; X X Draw_Prompt( "Base?" ); X X numeral = Get_Base( Get_Char() ); X X Erase_Prompt(); X X if ( numeral == ERROR ) X return( ERROR ); X X s->output_base = numeral; X s->mode = LAST_WAS_FUNCTION; X X Draw_Screen( s ); X return( OK ); X } X X X case 'p' : case ESC_DOWN : /* Pop: Roll down stack */ X X { X long int temp = s->stack[0]; X int i; X X for ( i = 0; i < s->stack_size-1; ++i ) X s->stack[i] = s->stack[i+1]; X X s->stack[ s->stack_size-1 ] = temp; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X } X X X case 'q' : case ESC_END : /* Quit */ X case EOF : case CTRL_D : X X return( EOF ); X X X case 'r' : case ESC_LEFT : /* Recall from register */ X X { X int numeral; X X Draw_Prompt( "Register?" ); X X numeral = Get_Char() - '0'; X X Erase_Prompt(); X X if ( numeral < 0 || numeral >= REGISTERS || X ((1 << numeral) & s->register_mask) == 0 ) X return( ERROR ); X X if ( s->mode != LAST_WAS_ENTER ) X Push( s ); X X s->stack[0] = s->registers[numeral]; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X } X X X case 's' : case ESC_RIGHT : /* Store in register, */ X /* or accumulate if */ X /* "s+" is typed. */ X X { X int c; X int numeral; X X X Draw_Prompt( "Register?" ); X X c = Get_Char(); X X if ( c == ESC_PLUS ) X c = '+'; /* Allow keypad '+' */ X X X if ( c == '+' ) X { X Draw_Prompt( "Accumulator?" ); X numeral = Get_Char() - '0'; X } X else X numeral = c - '0'; X X Erase_Prompt(); X X X if ( numeral < 0 || numeral >= REGISTERS ) X return( ERROR ); X X X if ( c != '+' || (s->register_mask & (1 << numeral)) == 0 ) X { X s->register_mask |= 1 << numeral; X s->registers[numeral] = s->stack[0]; X } X else X s->registers[numeral] += s->stack[0]; X X X s->mode = LAST_WAS_FUNCTION; X X Draw_Registers( s ); X return( OK ); X } X X X case 't' : /* Translate from ASCII */ X X { X long int numeral; X X Draw_Prompt( "Character?" ); X X numeral = (long int) Getc(); X X Erase_Prompt(); X X if ( s->mode != LAST_WAS_ENTER ) X Push( s ); X X s->stack[0] = numeral; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X } X X X case 'w' : case ESC_PGUP : /* Write tos to a file */ X X { X if ( (int) s->scratch_pad == NULL ) X { X /* Try to open a scratch pad file */ X X strcpy( s->file_name, "./pad" ); X X if ( (s->scratch_pad=fopen(s->file_name,"w")) == NULL ) X { X /* Unsuccessful, try in /tmp */ X char *id; X X strcpy( s->file_name, "/tmp/pad_" ); X X if ( (id=cuserid(NULL)) == NULL ) X return( ERROR ); X X strcat( s->file_name, id ); X X if ( (s->scratch_pad=fopen(s->file_name,"w")) == NULL ) X return( ERROR ); X } X X Draw_Screen( s ); X } X X X /* We have a successfully opened file */ X X Print_Number( s->scratch_pad, s->stack[0], s->output_base ); X putc( '\n', s->scratch_pad ); X fflush( s->scratch_pad ); X X s->mode = LAST_WAS_FUNCTION; X X return( OK ); X } X X X case 'x' : case ESC_UP : /* Exchange top of stack */ X X { X long int temp = s->stack[0]; X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->stack[0] = s->stack[1]; X s->stack[1] = temp; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X } X X X case 'z' : case ESC_HOME: /* Clear all */ X X Init_State( s ); X X Draw_Screen( s ); X return( OK ); X X X case BS : case DEL : /* Clear top of stack */ X X s->stack[0] = 0; X X s->mode = LAST_WAS_ENTER; X X Draw_Top_of_Stack( s ); X return( OK ); X X X case '\n' : /* Enter */ X X Push( s ); X X s->mode = LAST_WAS_ENTER; X X Draw_Stack( s ); X return( OK ); X X X case '.' : /* Change sign */ X X s->last_tos = s->stack[0]; X X s->stack[0] = - s->stack[0]; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Top_of_Stack( s ); X return( OK ); X X X case '+' : case ESC_PLUS : /* Add */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] += s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '-' : case ESC_MINUS : /* Subtract */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] -= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '*' : /* Multiply */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] *= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '/' : /* Divide */ X X if ( s->stack_size < 2 || s->stack[0] == 0 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] /= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '%' : case ESC_5 : /* Remainder */ X X if ( s->stack_size < 2 || s->stack[0] == 0 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] %= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '~' : /* Not */ X X s->last_tos = s->stack[0]; X X s->stack[0] = ~ s->stack[0]; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Top_of_Stack( s ); X return( OK ); X X X case '&' : /* And */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] &= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '|' : /* Or */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] |= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '^' : /* Exclusive-or */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] ^= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X default: X return( ERROR ); X } X } X X X X X X X X/****************************************************************/ X/* */ X/* Enter_Numeric( state, numeral ) */ X/* */ X/* A numeral (0 to 15) has been typed. */ X/* If a number is currently being entered */ X/* then shift it over and add this to the */ X/* display. If the last operation was a function */ X/* then push up the stack first. If the last */ X/* key was "ENTER", then clear out the top of */ X/* the stack and put the numeral there. */ X/* */ X/* Returns OK or ERROR. */ X/* */ X/****************************************************************/ X X Xint Enter_Numeric( s, numeral ) X ic_state *s; X int numeral; X X { X if ( numeral >= s->input_base ) X return( ERROR ); X X X switch ( s->mode ) X { X case LAST_WAS_FUNCTION : X Push( s ); X s->stack[0] = numeral; X Draw_Stack( s ); X break; X X case LAST_WAS_NUMERIC : X s->stack[0] = s->stack[0] * s->input_base + numeral; X Draw_Top_of_Stack( s ); X break; X X case LAST_WAS_ENTER : X s->stack[0] = numeral; X Draw_Top_of_Stack( s ); X break; X X default: X fprintf( stderr, "Internal failure (mode)\n" ); X Sigint(); X } X X s->mode = LAST_WAS_NUMERIC; X X return( OK ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Push( state ) */ X/* */ X/* Push up the stack one level. */ X/* */ X/****************************************************************/ X X XPush( s ) X ic_state *s; X X { X int i; X X if ( s->stack_size == STACK_SIZE ) X --s->stack_size; X X for ( i = s->stack_size; i > 0; --i ) X s->stack[i] = s->stack[i-1]; X X ++s->stack_size; X } X X X X X X X X X/****************************************************************/ X/* */ X/* Pop( state ) */ X/* */ X/* Pop the stack down one level. */ X/* This routine is only called with */ X/* the stack size > 1. */ X/* */ X/****************************************************************/ X X XPop( s ) X ic_state *s; X X { X int i; X X for ( i = 0; i < s->stack_size-1; ++i ) X s->stack[i] = s->stack[i+1]; X X --s->stack_size; X } X X X X X X X X X/****************************************************************/ X/* */ X/* Exec_Shell() */ X/* */ X/* Fork off a sub-process to exec() the shell. */ X/* */ X/****************************************************************/ X X XExec_Shell() X X { X int pid = fork(); X X if ( pid == -1 ) X return; X X X if ( pid == 0 ) X { X /* The child process */ X X extern char **environ; X char *shell = getenv( "SHELL" ); X X if ( shell == NULL ) X shell = "/bin/sh"; X X execle( shell, shell, 0, environ ); X X perror( shell ); X exit( 127 ); X } X X X /* The parent process: ignore signals, wait for sub-process */ X X signal( SIGINT, SIG_IGN ); X signal( SIGQUIT, SIG_IGN ); X X { X int status; X int w; X X while ( (w=wait(&status)) != pid & w != -1 ); X } X X signal( SIGINT, Sigint ); X signal( SIGQUIT, Sigint ); X X return; X } / -------------------------------------------------------------------- Edwin L. Froese (in London for the month) Terrence W. Holm {uw-beaver,ubc-cs}!uvicctr!sirius!tholm
tholm@uvicctr.UUCP (Terrence W. Holm) (05/18/88)
EFTH Minix report #13 - May 1988 - ic(1) [part 3 of 3] echo x - ic_input.c gres '^X' '' > ic_input.c << '/' X/****************************************************************/ X/* */ X/* ic_input.c */ X/* */ X/* Character input routines for the */ X/* "Integer Calculator". */ X/* */ X/****************************************************************/ X/* origination 1988-Apr-7 T. Holm */ X/* added cmd line args 1988-May-13 T. Holm */ X/****************************************************************/ X X X X#include <stdio.h> X#include <ctype.h> X#include <sgtty.h> X X#include "ic.h" X X X X Xstatic struct sgttyb saved_mode; Xstatic struct tchars saved_chars; X X X X X X X/****************************************************************/ X/* */ X/* Save_Term() */ X/* */ X/* Save the current terminal characteristics. */ X/* */ X/****************************************************************/ X X XSave_Term() X X { X ioctl( 0, TIOCGETP, &saved_mode ); X ioctl( 0, TIOCGETC, &saved_chars ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Set_Term() */ X/* */ X/* Set up the terminal characteristics for ic. */ X/* */ X/****************************************************************/ X X XSet_Term() X X { X struct sgttyb ic_mode; X struct tchars ic_chars; X X ic_mode = saved_mode; X ic_chars = saved_chars; X X X /* No tab expansion, no echo, cbreak mode */ X X ic_mode.sg_flags = ic_mode.sg_flags & ~XTABS & ~ECHO | CBREAK; X X X /* Change the interrupt character to ^C, ignore ^S & ^Q */ X X ic_chars.t_intrc = '\003'; X ic_chars.t_startc = '\377'; X ic_chars.t_stopc = '\377'; X X ioctl( 0, TIOCSETP, &ic_mode ); X ioctl( 0, TIOCSETC, &ic_chars ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Reset_Term() */ X/* */ X/* Restore the terminal characteristics. */ X/* */ X/****************************************************************/ X X XReset_Term() X X { X ioctl( 0, TIOCSETP, &saved_mode ); X ioctl( 0, TIOCSETC, &saved_chars ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Get_Char() */ X/* */ X/* Return the next input character. Upper case */ X/* is mapped to lower case. Escape sequences */ X/* are mapped to special codes (msb set). */ X/* */ X/****************************************************************/ X X Xint Get_Char() X X { X int c; X X X /* fflush() used because Minix does not automatically */ X /* flush the output. */ X X fflush( stdout ); X X X if ( (c = Getc()) == EOF ) X return( EOF ); X X c &= 0x7f; X X X if ( isupper(c) ) X return( tolower(c) ); X X X if ( c == ESCAPE ) X if ( (c=Getc()) != '[' ) X { X ungetc( c, stdin ); X return( ESCAPE ); X } X else X { X c = Getc() | 0x80; X X if ( c == ESC_HOME || c == ESC_UP || c == ESC_PGUP || X c == ESC_LEFT || c == ESC_5 || c == ESC_RIGHT || X c == ESC_END || c == ESC_DOWN || c == ESC_PGDN || X c == ESC_PLUS || c == ESC_MINUS ) X return( c ); X else X return( ESCAPE ); X } X X X return( c ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Init_Getc( argc, argv ) */ X/* */ X/* Give Getc() references to the command line */ X/* arguments. */ X/* */ X/****************************************************************/ X X X Xstatic int args_remaining; Xstatic char **args_pointer; Xstatic int args_index; X X X XInit_Getc( argc, argv ) X int argc; X char *argv[]; X X { X args_remaining = argc - 1; X args_pointer = &argv[1]; X args_index = 0; X } X X X X X X X X X/****************************************************************/ X/* */ X/* Getc() */ X/* */ X/* Get the next input character from the command */ X/* line if there is some more, else from stdin. */ X/* */ X/****************************************************************/ X X Xint Getc() X X { X int c; X X if ( args_remaining > 0 ) X if ( (c = args_pointer[ 0 ][ args_index++ ]) == '\0' ) X { X --args_remaining; X ++args_pointer; X args_index = 0; X X if ( args_remaining > 0 ) X return( '\n' ); X } X else X return( c ); X X return( getchar() ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Get_Base( character ) */ X/* */ X/* Return an appropriate base number for the */ X/* given character code. Used by 'i' and 'o'. */ X/* */ X/****************************************************************/ X X Xint Get_Base( code ) X char code; X X { X switch ( code ) X { X case 'h' : return( HEXADECIMAL ); X X case 'd' : return( DECIMAL ); X X case 'o' : return( OCTAL ); X X case 'b' : return( BINARY ); X X case 'a' : return( ASCII ); X X default : return( ERROR ); X } X } / echo x - ic_output.c gres '^X' '' > ic_output.c << '/' X/****************************************************************/ X/* */ X/* ic_output.c */ X/* */ X/* Output routines for the "Integer Calculator". */ X/* */ X/****************************************************************/ X/* origination 1988-Apr-6 T. Holm */ X/****************************************************************/ X X X X X#include <stdio.h> X X#include "ic.h" X X X X X X X/****************************************************************/ X/* Code for handling termcap */ X/****************************************************************/ X X X#define TC_BUFFER 1024 /* Size of termcap(3) buffer */ X#define TC_STRINGS 200 /* Enough room for cm,cl,so,se */ X X X X Xstatic char *Tmove; /* (cm) - Format for tgoto */ Xstatic char *Tclr_all; /* (cl) - String to clear screen */ Xstatic char *Treverse; /* (so) - String to reverse mode */ Xstatic char *Tnormal; /* (se) - String to undo reverse */ X X X X X X X X/****************************************************************/ X/* */ X/* Init_Termcap() */ X/* */ X/* Initializes the external variables for the */ X/* current terminal. */ X/* */ X/* NULL is returned on error conditions. */ X/* */ X/****************************************************************/ X X XInit_Termcap() X { X char *term; X char buffer[ TC_BUFFER ]; X static char strings[ TC_STRINGS ]; X char *s = &strings[0]; X X term = getenv( "TERM" ); X X if ( term == NULL ) X return( NULL ); X X if ( tgetent( buffer, term ) != 1 ) X return( NULL ); X X X X if ( (Tmove=tgetstr( "cm", &s )) == NULL ) X return( NULL ); X X if ( (Tclr_all=tgetstr( "cl", &s )) == NULL ) X return( NULL ); X X if ( (Treverse=tgetstr( "so", &s )) == NULL ) X { X Treverse = s; X *s = '\0'; X ++s; X } X X if ( (Tnormal=tgetstr( "se", &s )) == NULL ) X { X Tnormal = s; X *s = '\0'; X ++s; X } X X return( EOF ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Move( column, line ) */ X/* */ X/* Use the termcap string to move the cursor. */ X/* */ X/****************************************************************/ X X XMove( column, line ) X int column; X int line; X X { X Puts( tgoto( Tmove, column, line ) ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Puts( string ) */ X/* */ X/* Write the given termcap string to the standard */ X/* output device. */ X/* */ X/****************************************************************/ X X XPuts( string ) X char *string; X X { X int Putchar(); X X tputs( string, 1, Putchar ); X } X X XPutchar( c ) X char c; X X { X putchar( c ); X } X X X X X X X X X/****************************************************************/ X/* Output routines */ X/****************************************************************/ X X X X X/****************************************************************/ X/* */ X/* Draw_Help_Screen() */ X/* */ X/****************************************************************/ X X XDraw_Help_Screen() X X { X Puts( Tclr_all ); /* Clear the screen */ X X printf( "\n\n " ); X Puts( Treverse ); X printf( "IC COMMANDS" ); X Puts( Tnormal ); X printf( "\n\n\n" ); X X printf( " h Help ENTER Push stack\n" ); X printf( " i Input base (h d o b) DEL Clear entry\n" ); X printf( " PGDN l Last top of stack\n" ); X printf( " m Minix shell . Change sign\n" ); X printf( " o Output base (h d o b a) + Add\n" ); X printf( " DOWN p Pop stack - Subtract\n" ); X printf( " END q Quit * Multiply\n" ); X printf( " LEFT r Recall (0-9) / Divide\n" ); X printf( " RIGHT s Store [+] (0-9) %% Remainder\n" ); X printf( " t Translate (char) ~ Not\n" ); X printf( " PGUP w Write top to scratch pad & And\n" ); X printf( " UP x Exchange top of stack | Or\n" ); X printf( " HOME z Zero all state ^ Exclusive-or\n\n\n" ); X X printf( "\n\nPress a key to continue..." ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Prompt( string ) */ X/* */ X/* Write a message in the "wait" area. */ X/* */ X/****************************************************************/ X X XDraw_Prompt( string ) X char *string; X X { X Move( WAIT_COLUMN, WAIT_LINE ); X X Puts( Treverse ); X printf( string ); X Puts( Tnormal ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Erase_Prompt() */ X/* */ X/* Erase the message in the "wait" area. */ X/* */ X/****************************************************************/ X XErase_Prompt() X X { X Move( WAIT_COLUMN, WAIT_LINE ); X X printf( " " ); X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Screen( state ) */ X/* */ X/* Redraw everything. */ X/* */ X/****************************************************************/ X X XDraw_Screen( s ) X ic_state *s; X X { X Puts( Tclr_all ); /* Clear the screen */ X X Draw_Stack( s ); X Draw_Registers( s ); X X X Move( STATUS_COLUMN, STATUS_LINE ); X X printf( "Input base = %2d ", s->input_base ); X X if ( s->output_base == ASCII ) X printf( "Output is ASCII " ); X else X printf( "Output base = %2d ", s->output_base ); X X if ( (int) s->scratch_pad != NULL ) X printf( "Scratch file = %s", s->file_name ); X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Stack( state ) */ X/* */ X/* Redraw the stack. */ X/* */ X/****************************************************************/ X X XDraw_Stack( s ) X ic_state *s; X X { X int i; X X for ( i = STACK_SIZE-1; i >= 0; --i ) X { X Move( STACK_COLUMN, STACK_LINE + STACK_SIZE - 1 - i ); X X if ( i >= s->stack_size ) X printf( "%*c", s->output_base == BINARY ? 32 : 17, ' ' ); X else X Print_Number( stdout, s->stack[i], s->output_base ); X } X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Registers( state ) */ X/* */ X/* Redraw the registers. Note that only registers */ X/* in use are displayed. A register only drops */ X/* out of use after a 'z' command, which will */ X/* explicitly clear the display, thus we never */ X/* have to "wipe off" a value, as the */ X/* Draw_Stack() routine must. */ X/* */ X/****************************************************************/ X X XDraw_Registers( s ) X ic_state *s; X X { X int i; X X for ( i = 0; i < REGISTERS; ++i ) X { X if ( (1 << i) & s->register_mask ) X { X Move( REG_COLUMN, REG_LINE + i ); X Print_Number( stdout, s->registers[i], s->output_base ); X printf( " (r%1d)", i ); X } X } X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Top_of_Stack( state ) */ X/* */ X/* Redraw only the entry on the top of the stack. */ X/* */ X/****************************************************************/ X X XDraw_Top_of_Stack( s ) X ic_state *s; X X { X Move( STACK_COLUMN, STACK_LINE + STACK_SIZE - 1 ); X X Print_Number( stdout, s->stack[0], s->output_base ); X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Print_Number( stream, number, output_base ) */ X/* */ X/* Output the "number" to "stream" in the */ X/* specified "output_base". */ X/* */ X/****************************************************************/ X X XPrint_Number( stream, number, output_base ) X FILE *stream; X long int number; X int output_base; X X { X switch ( output_base ) X { X case HEXADECIMAL : fprintf( stream, "%12X", number ); X break; X X X case DECIMAL : fprintf( stream, "%12D", number ); X break; X X X case OCTAL : fprintf( stream, "%12O", number ); X break; X X X case BINARY : { X unsigned long int mask; X char pad = ' '; X X for ( mask = 0x80000000; mask > 1; mask >>= 1 ) X putc( ( mask & number ) ? (pad = '0', '1') : pad, stream ); X X putc( ( 0x01 & number ) ? '1' : '0', stream ); X X break; X } X X X case ASCII : { X char c = number & 0x7f; X X if ( (number & ~ 0x7fL) == 0 ) X fprintf( stream, "%15c", ' ' ); X else X fprintf( stream, "%12X + ", number & ~ 0x7fL ); X X if ( c < ' ' ) X fprintf( stream, "^%c", c + '@' ); X else if ( c == ' ' ) X fprintf( stream, "sp" ); X else if ( c < 0x7f ) X fprintf( stream, " %c", c ); X else X fprintf( stream, "^?" ); X X break; X } X X default : fprintf( stderr, "Internal failure (output base)\n" ); X Sigint(); X } X } / -------------------------------------------------------------------- Edwin L. Froese (in London for the month) Terrence W. Holm {uw-beaver,ubc-cs}!uvicctr!sirius!tholm
kenf@aplcen.UUCP (7784) (05/20/88)
The following hardware combination works with Minix: Compaq Portable with nec v20, Seagate st-251 hard drive and Western Digital WD1002a-WX1 Hard Disk Controller. A few hints, use the Western Digital superbios to low level format the disk. Do so dynamically and use value of 821 for the write precomp and reduced write current cylinder. The drive has 820 cylinders and 6 heads. defaults work for all else. After the low level format I used Disk Manager tm which came with the Seagate to partition the drive. Use the DM /m for manual and use just the partition menu. DO NOT USE DISK MANAGER TO INITALIZE THE DISK. This wipes out all the good parameters that the controller put out there and the Minix HD driver is going to look for. I set the disk up with partition one set for Minix at 10 meg. 2 and 3 become dos drives d and e, and partition 4 is the dos bootable, c. Don't forget to tell the low level format program where the bad tracks are. As I said, this all worked for me, and now Minix talks to my hard disk :-)} Ken Firestone kenf@aplcen
holm@ubc-bdcvax.UUCP (Terrence W. Holm) (02/02/89)
EFTH MINIX report #66 - February 1989 - ic(1) Ic(1) is an integer calculator. It was originally posted as EFTH report #13 (May 1988). I find it very useful for bin/oct/dec/hex conversions and perhaps others have also. I am giving everyone who originally missed it another chance to download it. Terrence W. Holm holm@bdc.ubc.ca ---------------------------------------------------------- echo x - Makefile gres '^X' '' > Makefile << '/' XCFLAGS = -F -T. XICOBJ = ic.s ic_input.s ic_output.s X Xic: $(ICOBJ) X cc -T. $(ICOBJ) -o ic X chmem =5000 ic X X$(ICOBJ): ic.h X Xprint: X pr ic.h ic.c ic_input.c ic_output.c\ X Makefile | lpr / echo x - ic.1 gres '^X' '' > ic.1 << '/' XNAME X ic(1) - integer calculator X XSYNOPSIS X ic [ args ] ... X XDESCRIPTION X This is a simple RPN calculator, used for small calculations X and base conversions. All calculations are done using 32 bit X integers. X X The standard input is usually a keyboard and the standard output X requires a device with a "termcap" entry. X X The program starts by interpreting any <args> as commands, where X the separation between arguments is considered to be the same as X the ENTER key. For example, "ic 692 784+". After reading the X arguments input is from the keyboard. X X X STACK OPERATIONS X X The operation of this program is similar to an RPN calculator. X A six level stack is used. The ENTER key pushes the stack up one X level. For example, "12+5" is entered as "12 ENTER 5 +". X X The top two entries on the stack are exchanged by the 'x' X command, and the stack is rolled down one (popped) by the X 'p' key. X X The top of the stack may be cleared by pressing the back-space X key. The whole stack and the registers are initialized by a 'z'. X X X NUMERIC ENTRY X X The input and output bases are initially decimal, but they may X be changed using the 'i' and 'o' commands. The 'i' command changes X both bases, but the 'o' command changes just the output base. X These commands take a one character argument of 'h', 'd', 'o' or X 'b' to change to Hexadecimal, Decimal, Octal or Binary. While the X input base is hexadecimal the letters 'a' through 'f' are used X to represent the decimal values 10 through 15. X X When the input base is decimal: multiply, divide and remainder X are signed, otherwise they are performed unsigned. X X The output base may also be changed to ASCII ('a'), this causes X the least significant 7 bits of a value to be displayed as a X character. To input an ASCII value the translate ('t') command X may be used, it accepts one character as its argument. X X X CALCULATIONS X X The arithmetic operations supported are: Negate ('.'), Add ('+'), X Subtract ('-'), Multiply ('*'), Divide ('/'), and Remainder ('%'). X The logical operations available are: Not ('~'), And ('&'), Or ('|'), X and Exclusive-or ('^'). X X After one of these operations the last top of stack value is X saved. It may be restored by pressing 'l' (L). X X X SAVING RESULTS X X Ten temporary registers are available. The Store ('s') command X followed by a digit ('0'..'9') will copy the top of the stack X to the specified register. The Recall ('r') command pushes the X contents of a register onto the top of the stack. X X If the Store command is followed by a '+' preceding the digit, then X the top of the stack will be added to the specified "accumulator" X register. X X Values may also be written to a file. The 'w' command writes the X top of the stack, using the current output base, to a file called X "pad" in the current directory. If the user does not have write X access to the current directory then the file "/tmp/pad_$USER" is X used as the scratch pad. The scratch pad file is erased on the X first use of the 'w' command within each new invocation of "ic". X X X LEAVING THE PROGRAM X X The Quit ('q') key causes an immediate exit. (End of file on X standard input, the SIGINT and the SIGQUIT interrupts also terminate X the program.) X X The 'm' command temporarily leaves "ic" by invoking the Minix "sh" X shell as a sub-process. X X For help while using "ic", hit the 'h' key. If an erroneous key X is pressed the bell will sound. X X X COMMAND SUMMARY X X Note that many commands have an alternative key-code available X on the extended AT keyboard. This aids entry by including most X commands on the right side of the keyboard. X X ENTER Enter (push up) X BS (DEL) Clear top of stack X X h Help X i Input base (h, d, o, b) X l (PGDN) Last top of stack X m Minix shell X o Output base (h, d, o, b, a) X p (DOWN) Pop stack (roll down) X q (END) Quit X r (LEFT) Recall (0-9) X s (RIGHT) Store [+] (0-9) X t Translate (char) X w (PGUP) Write top of stack to scratch pad X x (UP) Exchange top of stack X z (HOME) Zero all state X X . Change sign X + (+) Add X - (-) Subtract X * Multiply X / Divide X % (sh/5) Remainder X X ~ Not X & And X | Or X ^ Exclusive-or X XFILES X ./pad X /tmp/pad_$USER X XSEE ALSO X dc(1), expr(1) X XAUTHOR X Terrence W. Holm / echo x - ic.c gres '^X' '' > ic.c << '/' X/****************************************************************/ X/* */ X/* ic.c */ X/* */ X/* The main loop of the "Integer Calculator". */ X/* */ X/****************************************************************/ X/* origination 1988-Apr-6 Terrence W. Holm */ X/* added Exec_Shell() 1988-Apr-11 Terrence W. Holm */ X/* added "s+" 1988-Apr-18 Terrence W. Holm */ X/* added cmd line args 1988-May-13 Terrence W. Holm */ X/* 'i' also does 'o' 1988-May-28 Terrence W. Holm */ X/* if ~dec:unsigned *%/ 1988-Jul-10 Terrence W. Holm */ X/****************************************************************/ X X X X X X X#include <stdio.h> X#include <signal.h> X X#include "ic.h" X X X X Xstatic char copyright[] = { "ic (c) Terrence W. Holm 1988" }; X X X X X/****************************************************************/ X/* */ X/* main() */ X/* */ X/* Initialize. Enter the main processing loop. */ X/* */ X/****************************************************************/ X X Xmain( argc, argv ) X int argc; X char *argv[]; X X { X ic_state state; /* This state record is passed */ X /* to most subroutines */ X X Init_State( &state ); X X X state.scratch_pad = (FILE *) NULL; /* No 'w' command yet */ X X X X Init_Getc( argc, argv ); /* Refs to command line args */ X X X X if ( Init_Termcap() == NULL ) X { X fprintf( stderr, "ic requires a termcap entry\n" ); X exit( 1 ); X } X X X Save_Term(); /* Save terminal characteristics */ X X X if ( signal( SIGINT, SIG_IGN ) != SIG_IGN ) X { X signal( SIGINT, Sigint ); X signal( SIGQUIT, Sigint ); X } X X X Set_Term(); /* Setup terminal characteristics */ X X X X Draw_Screen( &state ); X X while (1) X { X int rc = Process( &state, Get_Char() ); X X if ( rc == EOF ) X break; X X if ( rc == ERROR ) X putchar( BELL ); X } X X X Reset_Term(); /* Restore terminal characteristics */ X X exit( OK ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Init_State() */ X/* */ X/* Initialize the state record. */ X/* */ X/****************************************************************/ X X XInit_State( s ) X ic_state *s; X X { X s->stack[0] = 0; X s->stack_size = 1; X s->register_mask = 0x000; X s->last_tos = 0; X s->mode = LAST_WAS_ENTER; X s->input_base = DECIMAL; X s->output_base = DECIMAL; X } X X X X X X X X X/****************************************************************/ X/* */ X/* Sigint() */ X/* */ X/* Terminate the program on an interrupt (^C) */ X/* or quit (^\) signal. */ X/* */ X/****************************************************************/ X X XSigint() X X { X Reset_Term(); /* Restore terminal characteristics */ X X exit( 1 ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Process( state, input_char ) */ X/* */ X/* Determine the function requested by the */ X/* input character. Returns OK, EOF or ERROR. */ X/* */ X/****************************************************************/ X X Xint Process( s, c ) X ic_state *s; X int c; X X { X switch ( c ) X { X case '0' : X case '1' : X case '2' : X case '3' : X case '4' : X case '5' : X case '6' : X case '7' : X case '8' : X case '9' : X X return( Enter_Numeric( s, (int) c - '0' ) ); X X X case 'a' : X case 'b' : X case 'c' : X case 'd' : X case 'e' : X case 'f' : X X return( Enter_Numeric( s, (int) c - 'a' + 10 ) ); X X X case 'h' : case '?' : /* Help */ X X Draw_Help_Screen(); X X Get_Char(); X X Draw_Screen( s ); X return( OK ); X X X case 'i' : /* Set i/p and o/p base */ X X { X int numeral; X X Draw_Prompt( "Base?" ); X X numeral = Get_Base( Get_Char() ); X X Erase_Prompt(); X X if ( numeral == ERROR || numeral == ASCII ) X return( ERROR ); X X s->input_base = numeral; X s->output_base = numeral; X s->mode = LAST_WAS_FUNCTION; X X Draw_Screen( s ); X return( OK ); X } X X X case 'l' : case ESC_PGDN : /* Get last tos value */ X X if ( s->mode != LAST_WAS_ENTER ) X Push( s ); X X s->stack[0] = s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case 'm' : /* Invoke a Minix shell */ X X Reset_Term(); X X Exec_Shell(); X X Set_Term(); X X Draw_Screen( s ); X return( OK ); X X X case 'o' : /* Set output base */ X X { X int numeral; X X Draw_Prompt( "Base?" ); X X numeral = Get_Base( Get_Char() ); X X Erase_Prompt(); X X if ( numeral == ERROR ) X return( ERROR ); X X s->output_base = numeral; X s->mode = LAST_WAS_FUNCTION; X X Draw_Screen( s ); X return( OK ); X } X X X case 'p' : case ESC_DOWN : /* Pop: Roll down stack */ X X { X long int temp = s->stack[0]; X int i; X X for ( i = 0; i < s->stack_size-1; ++i ) X s->stack[i] = s->stack[i+1]; X X s->stack[ s->stack_size-1 ] = temp; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X } X X X case 'q' : case ESC_END : /* Quit */ X case EOF : case CTRL_D : X X return( EOF ); X X X case 'r' : case ESC_LEFT : /* Recall from register */ X X { X int numeral; X X Draw_Prompt( "Register?" ); X X numeral = Get_Char() - '0'; X X Erase_Prompt(); X X if ( numeral < 0 || numeral >= REGISTERS || X ((1 << numeral) & s->register_mask) == 0 ) X return( ERROR ); X X if ( s->mode != LAST_WAS_ENTER ) X Push( s ); X X s->stack[0] = s->registers[numeral]; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X } X X X case 's' : case ESC_RIGHT : /* Store in register, */ X /* or accumulate if */ X /* "s+" is typed. */ X X { X int c; X int numeral; X X X Draw_Prompt( "Register?" ); X X c = Get_Char(); X X if ( c == ESC_PLUS ) X c = '+'; /* Allow keypad '+' */ X X X if ( c == '+' ) X { X Draw_Prompt( "Accumulator?" ); X numeral = Get_Char() - '0'; X } X else X numeral = c - '0'; X X Erase_Prompt(); X X X if ( numeral < 0 || numeral >= REGISTERS ) X return( ERROR ); X X X if ( c != '+' || (s->register_mask & (1 << numeral)) == 0 ) X { X s->register_mask |= 1 << numeral; X s->registers[numeral] = s->stack[0]; X } X else X s->registers[numeral] += s->stack[0]; X X X s->mode = LAST_WAS_FUNCTION; X X Draw_Registers( s ); X return( OK ); X } X X X case 't' : /* Translate from ASCII */ X X { X long int numeral; X X Draw_Prompt( "Character?" ); X X numeral = (long int) Getc(); X X Erase_Prompt(); X X if ( s->mode != LAST_WAS_ENTER ) X Push( s ); X X s->stack[0] = numeral; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X } X X X case 'w' : case ESC_PGUP : /* Write tos to a file */ X X { X if ( (int) s->scratch_pad == NULL ) X { X /* Try to open a scratch pad file */ X X strcpy( s->file_name, "./pad" ); X X if ( (s->scratch_pad=fopen(s->file_name,"w")) == NULL ) X { X /* Unsuccessful, try in /tmp */ X char *id; X X strcpy( s->file_name, "/tmp/pad_" ); X X if ( (id=cuserid(NULL)) == NULL ) X return( ERROR ); X X strcat( s->file_name, id ); X X if ( (s->scratch_pad=fopen(s->file_name,"w")) == NULL ) X return( ERROR ); X } X X Draw_Screen( s ); X } X X X /* We have a successfully opened file */ X X Print_Number( s->scratch_pad, s->stack[0], s->output_base ); X putc( '\n', s->scratch_pad ); X fflush( s->scratch_pad ); X X s->mode = LAST_WAS_FUNCTION; X X return( OK ); X } X X X case 'x' : case ESC_UP : /* Exchange top of stack */ X X { X long int temp = s->stack[0]; X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->stack[0] = s->stack[1]; X s->stack[1] = temp; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X } X X X case 'z' : case ESC_HOME: /* Clear all */ X X Init_State( s ); X X Draw_Screen( s ); X return( OK ); X X X case BS : case DEL : /* Clear top of stack */ X X s->stack[0] = 0; X X s->mode = LAST_WAS_ENTER; X X Draw_Top_of_Stack( s ); X return( OK ); X X X case '\n' : /* Enter */ X X Push( s ); X X s->mode = LAST_WAS_ENTER; X X Draw_Stack( s ); X return( OK ); X X X case '.' : /* Change sign */ X X s->last_tos = s->stack[0]; X X s->stack[0] = - s->stack[0]; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Top_of_Stack( s ); X return( OK ); X X X case '+' : case ESC_PLUS : /* Add */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] += s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '-' : case ESC_MINUS : /* Subtract */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] -= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '*' : /* Multiply */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X if ( s->input_base == DECIMAL ) X s->stack[0] *= s->last_tos; X else X s->stack[0] = (long int) X ( UNS(s->stack[0]) * UNS(s->last_tos) ); X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '/' : /* Divide */ X X if ( s->stack_size < 2 || s->stack[0] == 0 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X if ( s->input_base == DECIMAL ) X s->stack[0] /= s->last_tos; X else X s->stack[0] = (long int) X ( UNS(s->stack[0]) / UNS(s->last_tos) ); X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '%' : case ESC_5 : /* Remainder */ X X if ( s->stack_size < 2 || s->stack[0] == 0 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X if ( s->input_base == DECIMAL ) X s->stack[0] %= s->last_tos; X else X s->stack[0] = (long int) X ( UNS(s->stack[0]) % UNS(s->last_tos) ); X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '~' : /* Not */ X X s->last_tos = s->stack[0]; X X s->stack[0] = ~ s->stack[0]; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Top_of_Stack( s ); X return( OK ); X X X case '&' : /* And */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] &= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '|' : /* Or */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] |= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X case '^' : /* Exclusive-or */ X X if ( s->stack_size < 2 ) X return( ERROR ); X X s->last_tos = s->stack[0]; X X Pop( s ); X X s->stack[0] ^= s->last_tos; X X s->mode = LAST_WAS_FUNCTION; X X Draw_Stack( s ); X return( OK ); X X X default: X return( ERROR ); X } X } X X X X X X X X X/****************************************************************/ X/* */ X/* Enter_Numeric( state, numeral ) */ X/* */ X/* A numeral (0 to 15) has been typed. */ X/* If a number is currently being entered */ X/* then shift it over and add this to the */ X/* display. If the last operation was a function */ X/* then push up the stack first. If the last */ X/* key was "ENTER", then clear out the top of */ X/* the stack and put the numeral there. */ X/* */ X/* Returns OK or ERROR. */ X/* */ X/****************************************************************/ X X Xint Enter_Numeric( s, numeral ) X ic_state *s; X int numeral; X X { X if ( numeral >= s->input_base ) X return( ERROR ); X X X switch ( s->mode ) X { X case LAST_WAS_FUNCTION : X Push( s ); X s->stack[0] = numeral; X Draw_Stack( s ); X break; X X case LAST_WAS_NUMERIC : X s->stack[0] = s->stack[0] * s->input_base + numeral; X Draw_Top_of_Stack( s ); X break; X X case LAST_WAS_ENTER : X s->stack[0] = numeral; X Draw_Top_of_Stack( s ); X break; X X default: X fprintf( stderr, "Internal failure (mode)\n" ); X Sigint(); X } X X s->mode = LAST_WAS_NUMERIC; X X return( OK ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Push( state ) */ X/* */ X/* Push up the stack one level. */ X/* */ X/****************************************************************/ X X XPush( s ) X ic_state *s; X X { X int i; X X if ( s->stack_size == STACK_SIZE ) X --s->stack_size; X X for ( i = s->stack_size; i > 0; --i ) X s->stack[i] = s->stack[i-1]; X X ++s->stack_size; X } X X X X X X X X X/****************************************************************/ X/* */ X/* Pop( state ) */ X/* */ X/* Pop the stack down one level. */ X/* This routine is only called with */ X/* the stack size > 1. */ X/* */ X/****************************************************************/ X X XPop( s ) X ic_state *s; X X { X int i; X X for ( i = 0; i < s->stack_size-1; ++i ) X s->stack[i] = s->stack[i+1]; X X --s->stack_size; X } X X X X X X X X X/****************************************************************/ X/* */ X/* Exec_Shell() */ X/* */ X/* Fork off a sub-process to exec() the shell. */ X/* */ X/****************************************************************/ X X XExec_Shell() X X { X int pid = fork(); X X if ( pid == -1 ) X return; X X X if ( pid == 0 ) X { X /* The child process */ X X extern char **environ; X char *shell = getenv( "SHELL" ); X X if ( shell == NULL ) X shell = "/bin/sh"; X ------------------------------------------------------------------
holm@ubc-bdcvax.UUCP (Terrence W. Holm) (02/02/89)
--------------------------------------------------------------- X execle( shell, shell, (char *) 0, environ ); X X perror( shell ); X exit( 127 ); X } X X X /* The parent process: ignore signals, wait for sub-process */ X X signal( SIGINT, SIG_IGN ); X signal( SIGQUIT, SIG_IGN ); X X { X int status; X int w; X X while ( (w=wait(&status)) != pid && w != -1 ); X } X X signal( SIGINT, Sigint ); X signal( SIGQUIT, Sigint ); X X return; X } / echo x - ic.h gres '^X' '' > ic.h << '/' X/****************************************************************/ X/* */ X/* ic.h */ X/* */ X/* Definitions for the "Integer Calculator". */ X/* */ X/****************************************************************/ X/* origination 1988-Apr-6 Terrence W. Holm */ X/****************************************************************/ X X X X X/****************************************************************/ X/* */ X/* ic(1) */ X/* */ X/* This is a simple RPN calculator, used for small calculations */ X/* and base conversions. All calculations are done using 32 bit */ X/* integers. */ X/* */ X/* Commands are available for stack operations, saving results, */ X/* arithmetic and logical operations, and escaping to the Minix */ X/* shell. */ X/* */ X/* The program requires termcap(3), but otherwise should run */ X/* under all Unix (tm) variants. */ X/* */ X/* See the ic(1) man page. */ X/* */ X/****************************************************************/ X X X X X/****************************************************************/ X/* */ X/* ic Copyright Terrence W. Holm 1988 */ X/* */ X/* This program was written for users of the Minix operating */ X/* system, and in the spirit of other public domain software */ X/* written for said system, this source code is made available */ X/* at no cost to everyone. */ X/* */ X/* This program (one .h, three .c and a "man" page) may be */ X/* copied and/or modified subject to (1) no charge must be */ X/* made for distribution, other than for the medium, (2) all */ X/* modified sources must be clearly marked as such, (3) all */ X/* sources must carry this copyright. */ X/* */ X/****************************************************************/ X X X X X/****************************************************************/ X/* */ X/* files */ X/* */ X/* ic.h Definitions */ X/* ic.c The main loop */ X/* ic_input.c Character input routines */ X/* ic_output.c Output routines */ X/* */ X/* ic.1 "Man" page */ X/* Makefile For "make" */ X/* */ X/****************************************************************/ X X X X X X#define UNS(x) ((unsigned long)(x)) X X#define STACK_SIZE 6 /* Max # of levels */ X#define REGISTERS 10 /* Registers 0 to 9 */ X X X#define LAST_WAS_ENTER 1 /* Numeric input modes */ X#define LAST_WAS_NUMERIC 2 X#define LAST_WAS_FUNCTION 3 X X X#define ASCII -1 /* Input and output */ X#define BINARY 2 /* modes */ X#define OCTAL 8 X#define DECIMAL 10 X#define HEXADECIMAL 16 X X X#define OK 0 /* Return codes */ X#define ERROR 1 X X X X X X X#define CTRL_D '\004' /* ASCII ^D */ X#define BELL '\007' /* ASCII bell code */ X#define BS '\010' /* ASCII back space */ X#define ESCAPE '\033' /* ASCII escape code */ X#define DEL '\177' /* ASCII delete code */ X X X X X /* Input escape codes generated by the Minix console. */ X /* Format: ESC [ X. Shows character equivalent for ic. */ X X#define ESC_HOME 'H' + 0x80 /* z */ X#define ESC_UP 'A' + 0x80 /* x */ X#define ESC_PGUP 'V' + 0x80 /* w */ X#define ESC_LEFT 'D' + 0x80 /* r */ X#define ESC_5 'G' + 0x80 /* % */ X#define ESC_RIGHT 'C' + 0x80 /* s */ X#define ESC_END 'Y' + 0x80 /* q */ X#define ESC_DOWN 'B' + 0x80 /* p */ X#define ESC_PGDN 'U' + 0x80 /* l */ X#define ESC_PLUS 'T' + 0x80 /* + */ X#define ESC_MINUS 'S' + 0x80 /* - */ X X X X X /* Move positions for the output display. */ X X#define STACK_COLUMN 4 X#define STACK_LINE 7 X#define REG_COLUMN STACK_COLUMN+36 X#define REG_LINE 3 X#define STATUS_COLUMN 6 X#define STATUS_LINE 0 X#define WAIT_COLUMN 0 X#define WAIT_LINE 14 X X X X X X Xtypedef struct ic_state /* State of int. calc. */ X { X long int stack[ STACK_SIZE ]; /* The stack */ X long int registers[ REGISTERS ]; /* The registers */ X X int stack_size; /* Current size (>= 1) */ X int register_mask; /* In use bit mask */ X X long int last_tos; /* For 'L' command */ X X int mode; /* Last key type. See */ X /* LAST_WAS_ENTER, etc */ X X int input_base; /* Current i/o base, */ X int output_base; /* ASCII, BINARY, etc */ X X FILE *scratch_pad; /* For 'w' command */ X char file_name[20]; /* "pad" or "/tmp/pad" */ X } ic_state; X X X X X X Xint Sigint(); Xint Process(); Xint Enter_Numeric(); Xint Get_Char(); Xint Get_Base(); Xchar *getenv(); Xchar *tgetstr(); Xchar *tgoto(); Xchar *cuserid(); XFILE *fopen(); / echo x - ic_input.c gres '^X' '' > ic_input.c << '/' X/****************************************************************/ X/* */ X/* ic_input.c */ X/* */ X/* Character input routines for the */ X/* "Integer Calculator". */ X/* */ X/****************************************************************/ X/* origination 1988-Apr-7 Terrence W. Holm */ X/* added cmd line args 1988-May-13 Terrence W. Holm */ X/****************************************************************/ X X X X#include <stdio.h> X#include <ctype.h> X#include <sgtty.h> X X#include "ic.h" X X X X Xstatic struct sgttyb saved_mode; Xstatic struct tchars saved_chars; X X X X X X X/****************************************************************/ X/* */ X/* Save_Term() */ X/* */ X/* Save the current terminal characteristics. */ X/* */ X/****************************************************************/ X X XSave_Term() X X { X ioctl( 0, TIOCGETP, &saved_mode ); X ioctl( 0, TIOCGETC, &saved_chars ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Set_Term() */ X/* */ X/* Set up the terminal characteristics for ic. */ X/* */ X/****************************************************************/ X X XSet_Term() X X { X struct sgttyb ic_mode; X struct tchars ic_chars; X X ic_mode = saved_mode; X ic_chars = saved_chars; X X X /* No tab expansion, no echo, cbreak mode */ X X ic_mode.sg_flags = ic_mode.sg_flags & ~XTABS & ~ECHO | CBREAK; X X X /* Change the interrupt character to ^C, ignore ^S & ^Q */ X X ic_chars.t_intrc = '\003'; X ic_chars.t_startc = '\377'; X ic_chars.t_stopc = '\377'; X X ioctl( 0, TIOCSETP, &ic_mode ); X ioctl( 0, TIOCSETC, &ic_chars ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Reset_Term() */ X/* */ X/* Restore the terminal characteristics. */ X/* */ X/****************************************************************/ X X XReset_Term() X X { X ioctl( 0, TIOCSETP, &saved_mode ); X ioctl( 0, TIOCSETC, &saved_chars ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Get_Char() */ X/* */ X/* Return the next input character. Upper case */ X/* is mapped to lower case. Escape sequences */ X/* are mapped to special codes (msb set). */ X/* */ X/****************************************************************/ X X Xint Get_Char() X X { X int c; X X X /* fflush() used because Minix does not automatically */ X /* flush the output. */ X X fflush( stdout ); X X X if ( (c = Getc()) == EOF ) X return( EOF ); X X c &= 0x7f; X X X if ( isupper(c) ) X return( tolower(c) ); X X X if ( c == ESCAPE ) X if ( (c=Getc()) != '[' ) X { X ungetc( c, stdin ); X return( ESCAPE ); X } X else X { X c = Getc() | 0x80; X X if ( c == ESC_HOME || c == ESC_UP || c == ESC_PGUP || X c == ESC_LEFT || c == ESC_5 || c == ESC_RIGHT || X c == ESC_END || c == ESC_DOWN || c == ESC_PGDN || X c == ESC_PLUS || c == ESC_MINUS ) X return( c ); X else X return( ESCAPE ); X } X X X return( c ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Init_Getc( argc, argv ) */ X/* */ X/* Give Getc() references to the command line */ X/* arguments. */ X/* */ X/****************************************************************/ X X X Xstatic int args_remaining; Xstatic char **args_pointer; Xstatic int args_index; X X X XInit_Getc( argc, argv ) X int argc; X char *argv[]; X X { X args_remaining = argc - 1; X args_pointer = &argv[1]; X args_index = 0; X } X X X X X X X X X/****************************************************************/ X/* */ X/* Getc() */ X/* */ X/* Get the next input character from the command */ X/* line if there is some more, else from stdin. */ X/* */ X/****************************************************************/ X X Xint Getc() X X { X int c; X X if ( args_remaining > 0 ) X if ( (c = args_pointer[ 0 ][ args_index++ ]) == '\0' ) X { X --args_remaining; X ++args_pointer; X args_index = 0; X X if ( args_remaining > 0 ) X return( '\n' ); X } X else X return( c ); X X return( getchar() ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Get_Base( character ) */ X/* */ X/* Return an appropriate base number for the */ X/* given character code. Used by 'i' and 'o'. */ X/* */ X/****************************************************************/ X X Xint Get_Base( code ) X char code; X X { X switch ( code ) X { X case 'h' : return( HEXADECIMAL ); X X case 'd' : return( DECIMAL ); X X case 'o' : return( OCTAL ); X X case 'b' : return( BINARY ); X X case 'a' : return( ASCII ); X X default : return( ERROR ); X } X } / echo x - ic_output.c gres '^X' '' > ic_output.c << '/' X/****************************************************************/ X/* */ X/* ic_output.c */ X/* */ X/* Output routines for the "Integer Calculator". */ X/* */ X/****************************************************************/ X/* origination 1988-Apr-6 Terrence W. Holm */ X/****************************************************************/ X X X X X#include <stdio.h> X X#include "ic.h" X X X X X X X/****************************************************************/ X/* Code for handling termcap */ X/****************************************************************/ X X X#define TC_BUFFER 1024 /* Size of termcap(3) buffer */ X#define TC_STRINGS 200 /* Enough room for cm,cl,so,se */ X X X X Xstatic char *Tmove; /* (cm) - Format for tgoto */ Xstatic char *Tclr_all; /* (cl) - String to clear screen */ Xstatic char *Treverse; /* (so) - String to reverse mode */ Xstatic char *Tnormal; /* (se) - String to undo reverse */ X X X X X X X X/****************************************************************/ X/* */ X/* Init_Termcap() */ X/* */ X/* Initializes the external variables for the */ X/* current terminal. */ X/* */ X/* NULL is returned on error conditions. */ X/* */ X/****************************************************************/ X X XInit_Termcap() X { X char *term; X char buffer[ TC_BUFFER ]; X static char strings[ TC_STRINGS ]; X char *s = &strings[0]; X X term = getenv( "TERM" ); X X if ( term == NULL ) X return( NULL ); X X if ( tgetent( buffer, term ) != 1 ) X return( NULL ); X X X X if ( (Tmove=tgetstr( "cm", &s )) == NULL ) X return( NULL ); X X if ( (Tclr_all=tgetstr( "cl", &s )) == NULL ) X return( NULL ); X X if ( (Treverse=tgetstr( "so", &s )) == NULL ) X { X Treverse = s; X *s = '\0'; X ++s; X } X X if ( (Tnormal=tgetstr( "se", &s )) == NULL ) X { X Tnormal = s; X *s = '\0'; X ++s; X } X X return( EOF ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Move( column, line ) */ X/* */ X/* Use the termcap string to move the cursor. */ X/* */ X/****************************************************************/ X X XMove( column, line ) X int column; X int line; X X { X Puts( tgoto( Tmove, column, line ) ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Puts( string ) */ X/* */ X/* Write the given termcap string to the standard */ X/* output device. */ X/* */ X/****************************************************************/ X X XPuts( string ) X char *string; X X { X int Putchar(); X X tputs( string, 1, Putchar ); X } X X XPutchar( c ) X char c; X X { X putchar( c ); X } X X X X X X X X X/****************************************************************/ X/* Output routines */ X/****************************************************************/ X X X X X/****************************************************************/ X/* */ X/* Draw_Help_Screen() */ X/* */ X/****************************************************************/ X X XDraw_Help_Screen() X X { X Puts( Tclr_all ); /* Clear the screen */ X X printf( "\n\n " ); X Puts( Treverse ); X printf( "IC COMMANDS" ); X Puts( Tnormal ); X printf( "\n\n\n" ); X X printf( " h Help ENTER Push stack\n" ); X printf( " i Input base (h d o b) DEL Clear entry\n " ); X printf( " PGDN l Last top of stack\n" ); X printf( " m Minix shell . Change sign\n " ); X printf( " o Output base (h d o b a) + Add\n" ); X printf( " DOWN p Pop stack - Subtract\n" ) ; X printf( " END q Quit * Multiply\n" ) ; X printf( " LEFT r Recall (0-9) / Divide\n" ); X printf( " RIGHT s Store [+] (0-9) %% Remainder\n" ); X printf( " t Translate (char) ~ Not\n" ); X printf( " PGUP w Write top to scratch pad & And\n" ); X printf( " UP x Exchange top of stack | Or\n" ); X printf( " HOME z Zero all state ^ Exclusive-or\ n\n\n" ); X X printf( "\n\nPress a key to continue..." ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Prompt( string ) */ X/* */ X/* Write a message in the "wait" area. */ X/* */ X/****************************************************************/ X X XDraw_Prompt( string ) X char *string; X X { X Move( WAIT_COLUMN, WAIT_LINE ); X X Puts( Treverse ); X printf( string ); X Puts( Tnormal ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Erase_Prompt() */ X/* */ X/* Erase the message in the "wait" area. */ X/* */ X/****************************************************************/ X XErase_Prompt() X X { X Move( WAIT_COLUMN, WAIT_LINE ); X X printf( " " ); X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Screen( state ) */ X/* */ X/* Redraw everything. */ X/* */ X/****************************************************************/ X X XDraw_Screen( s ) X ic_state *s; X X { X Puts( Tclr_all ); /* Clear the screen */ X X Draw_Stack( s ); X Draw_Registers( s ); X X X Move( STATUS_COLUMN, STATUS_LINE ); X X printf( "Input base = %2d ", s->input_base ); X X if ( s->output_base == ASCII ) X printf( "Output is ASCII " ); X else X printf( "Output base = %2d ", s->output_base ); X X if ( (int) s->scratch_pad != NULL ) X printf( "Scratch file = %s", s->file_name ); X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Stack( state ) */ X/* */ X/* Redraw the stack. */ X/* */ X/****************************************************************/ X X XDraw_Stack( s ) X ic_state *s; X X { X int i; X X for ( i = STACK_SIZE-1; i >= 0; --i ) X { X Move( STACK_COLUMN, STACK_LINE + STACK_SIZE - 1 - i ); X X if ( i >= s->stack_size ) X printf( "%*c", s->output_base == BINARY ? 32 : 17, ' ' ); X else X Print_Number( stdout, s->stack[i], s->output_base ); X } X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Registers( state ) */ X/* */ X/* Redraw the registers. Note that only registers */ X/* in use are displayed. A register only drops */ X/* out of use after a 'z' command, which will */ X/* explicitly clear the display, thus we never */ X/* have to "wipe off" a value, as the */ X/* Draw_Stack() routine must. */ X/* */ X/****************************************************************/ X X XDraw_Registers( s ) X ic_state *s; X X { X int i; X X for ( i = 0; i < REGISTERS; ++i ) X { X if ( (1 << i) & s->register_mask ) X { X Move( REG_COLUMN, REG_LINE + i ); X Print_Number( stdout, s->registers[i], s->output_base ); X printf( " (r%1d)", i ); X } X } X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Draw_Top_of_Stack( state ) */ X/* */ X/* Redraw only the entry on the top of the stack. */ X/* */ X/****************************************************************/ X X XDraw_Top_of_Stack( s ) X ic_state *s; X X { X Move( STACK_COLUMN, STACK_LINE + STACK_SIZE - 1 ); X X Print_Number( stdout, s->stack[0], s->output_base ); X X Move( WAIT_COLUMN, WAIT_LINE ); X } X X X X X X X X X/****************************************************************/ X/* */ X/* Print_Number( stream, number, output_base ) */ X/* */ X/* Output the "number" to "stream" in the */ X/* specified "output_base". */ X/* */ X/****************************************************************/ X X XPrint_Number( stream, number, output_base ) X FILE *stream; X long int number; X int output_base; X X { X switch ( output_base ) X { X case HEXADECIMAL : fprintf( stream, "%12lx", number ); X break; X X X case DECIMAL : fprintf( stream, "%12ld", number ); X break; X X X case OCTAL : fprintf( stream, "%12lo", number ); X break; X X X case BINARY : { X unsigned long int mask; X char pad = ' '; X X for ( mask = 0x80000000; mask > 1; mask >>= 1 ) X putc( ( mask & number ) ? (pad = '0', '1') : pad, stream ); X X putc( ( 0x01 & number ) ? '1' : '0', stream ); X X break; X } X X X case ASCII : { X char c = number & 0x7f; X X if ( (number & ~ 0x7fL) == 0 ) X fprintf( stream, "%15c", ' ' ); X else X fprintf( stream, "%12lx + ", number & ~ 0x7fL ); X X if ( c < ' ' ) X fprintf( stream, "^%c", c + '@' ); X else if ( c == ' ' ) X fprintf( stream, "sp" ); X else if ( c < 0x7f ) X fprintf( stream, " %c", c ); X else X fprintf( stream, "^?" ); X X break; X } X X default : fprintf( stderr, "Internal failure (output base)\n" ); X Sigint(); X } X } / ----------------------------------------------------------