/*****************************************************************/ /* */ /* ic.c */ /* */ /* The main loop of the "Integer Calculator". */ /* */ /*****************************************************************/ /* origination 1988-Apr-6 Terrence W. Holm */ /* added Exec_Shell() 1988-Apr-11 Terrence W. Holm */ /* added "s+" 1988-Apr-18 Terrence W. Holm */ /* added cmd line args 1988-May-13 Terrence W. Holm */ /* 'i' also does 'o' 1988-May-28 Terrence W. Holm */ /* if ~dec:unsigned *%/ 1988-Jul-10 Terrence W. Holm */ /*****************************************************************/ #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include "ic.h" static char copyright[] = {"ic (c) Terrence W. Holm 1988"}; /****************************************************************/ /* */ /* main() */ /* */ /* Initialize. Enter the main processing loop. */ /* */ /****************************************************************/ int main(argc, argv) int argc; char *argv[]; { ic_state state; /* This state record is passed to most procs */ Init_State(&state); state.scratch_pad = (FILE *) NULL; /* No 'w' command yet */ Init_Getc(argc, argv); /* Refs to command line args */ if (Init_Termcap() == 0) { fprintf(stderr, "ic requires a termcap entry\n"); exit(1); } Save_Term(); /* Save terminal characteristics */ if (signal(SIGINT, SIG_IGN) != SIG_IGN) { signal(SIGINT, Sigint); signal(SIGQUIT, Sigint); } Set_Term(); /* Setup terminal characteristics */ Draw_Screen(&state); while (1) { int rc = Process(&state, Get_Char()); if (rc == EOF) break; if (rc == ERROR) putchar(BELL); } Reset_Term(); /* Restore terminal characteristics */ exit(OK); } /****************************************************************/ /* */ /* Init_State() */ /* */ /* Initialize the state record. */ /* */ /****************************************************************/ void Init_State(s) ic_state *s; { s->stack[0] = 0; s->stack_size = 1; s->register_mask = 0x000; s->last_tos = 0; s->mode = LAST_WAS_ENTER; s->input_base = DECIMAL; s->output_base = DECIMAL; } /*****************************************************************/ /* */ /* Sigint() */ /* */ /* Terminate the program on an interrupt (^C) */ /* or quit (^\) signal. */ /* */ /*****************************************************************/ void Sigint(sig) int sig; { Reset_Term(); /* Restore terminal characteristics */ exit(1); } /*****************************************************************/ /* */ /* Process( state, input_char ) */ /* */ /* Determine the function requested by the */ /* input character. Returns OK, EOF or ERROR. */ /* */ /******************************************************************/ int Process(s, c) ic_state *s; int c; { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return(Enter_Numeric(s, (int) c - '0')); case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return(Enter_Numeric(s, (int) c - 'a' + 10)); case 'h': case '?': /* Help */ Draw_Help_Screen(); Get_Char(); Draw_Screen(s); return(OK); case 'i': /* Set i/p and o/p base */ { int numeral; Draw_Prompt("Base?"); numeral = Get_Base(Get_Char()); Erase_Prompt(); if (numeral == ERROR || numeral == ASCII) return(ERROR); s->input_base = numeral; s->output_base = numeral; s->mode = LAST_WAS_FUNCTION; Draw_Screen(s); return(OK); } case 'l': case ESC_PGDN: /* Get last tos value */ if (s->mode != LAST_WAS_ENTER) Push(s); s->stack[0] = s->last_tos; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); case 'm': /* Invoke a Minix shell */ Reset_Term(); Exec_Shell(); Set_Term(); Draw_Screen(s); return(OK); case 'o': /* Set output base */ { int numeral; Draw_Prompt("Base?"); numeral = Get_Base(Get_Char()); Erase_Prompt(); if (numeral == ERROR) return(ERROR); s->output_base = numeral; s->mode = LAST_WAS_FUNCTION; Draw_Screen(s); return(OK); } case 'p': case ESC_DOWN: /* Pop: Roll down stack */ { long int temp = s->stack[0]; int i; for (i = 0; i < s->stack_size - 1; ++i) s->stack[i] = s->stack[i + 1]; s->stack[s->stack_size - 1] = temp; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); } case 'q': case ESC_END: /* Quit */ case EOF: case CTRL_D: return(EOF); case 'r': case ESC_LEFT: /* Recall from register */ { int numeral; Draw_Prompt("Register?"); numeral = Get_Char() - '0'; Erase_Prompt(); if (numeral < 0 || numeral >= REGISTERS || ((1 << numeral) & s->register_mask) == 0) return(ERROR); if (s->mode != LAST_WAS_ENTER) Push(s); s->stack[0] = s->registers[numeral]; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); } case 's': case ESC_RIGHT: /* Store in register, */ /* Or accumulate if "s+" is typed */ { int c; int numeral; Draw_Prompt("Register?"); c = Get_Char(); if (c == ESC_PLUS) c = '+'; /* Allow keypad '+' */ if (c == '+') { Draw_Prompt("Accumulator?"); numeral = Get_Char() - '0'; } else numeral = c - '0'; Erase_Prompt(); if (numeral < 0 || numeral >= REGISTERS) return(ERROR); if (c != '+' || (s->register_mask & (1 << numeral)) == 0) { s->register_mask |= 1 << numeral; s->registers[numeral] = s->stack[0]; } else s->registers[numeral] += s->stack[0]; s->mode = LAST_WAS_FUNCTION; Draw_Registers(s); return(OK); } case 't': /* Translate from ASCII */ { long int numeral; Draw_Prompt("Character?"); numeral = (long int) Getc(); Erase_Prompt(); if (s->mode != LAST_WAS_ENTER) Push(s); s->stack[0] = numeral; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); } case 'w': case ESC_PGUP: /* Write tos to a file */ { if (s->scratch_pad == (FILE *) NULL) { /* Try to open a scratch pad file */ strcpy(s->file_name, "./pad"); if ((s->scratch_pad = fopen(s->file_name,"w")) ==NULL){ /* Unsuccessful, try in /tmp */ char *id; strcpy(s->file_name, "/tmp/pad_"); if ((id = cuserid(NULL)) == NULL) return(ERROR); strcat(s->file_name, id); if ((s->scratch_pad = fopen(s->file_name, "w")) == NULL) return(ERROR); } Draw_Screen(s); } /* We have a successfully opened file */ Print_Number(s->scratch_pad, s->stack[0], s->output_base); putc('\n', s->scratch_pad); fflush(s->scratch_pad); s->mode = LAST_WAS_FUNCTION; return(OK); } case 'x': case ESC_UP: /* Exchange top of stack */ { long int temp = s->stack[0]; if (s->stack_size < 2) return(ERROR); s->stack[0] = s->stack[1]; s->stack[1] = temp; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); } case 'z': case ESC_HOME: /* Clear all */ Init_State(s); Draw_Screen(s); return(OK); case BS: case DEL: /* Clear top of stack */ s->stack[0] = 0; s->mode = LAST_WAS_ENTER; Draw_Top_of_Stack(s); return(OK); case '\n': /* Enter */ Push(s); s->mode = LAST_WAS_ENTER; Draw_Stack(s); return(OK); case '.': /* Change sign */ s->last_tos = s->stack[0]; s->stack[0] = -s->stack[0]; s->mode = LAST_WAS_FUNCTION; Draw_Top_of_Stack(s); return(OK); case '+': case ESC_PLUS: /* Add */ if (s->stack_size < 2) return(ERROR); s->last_tos = s->stack[0]; Pop(s); s->stack[0] += s->last_tos; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); case '-': case ESC_MINUS: /* Subtract */ if (s->stack_size < 2) return(ERROR); s->last_tos = s->stack[0]; Pop(s); s->stack[0] -= s->last_tos; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); case '*': /* Multiply */ if (s->stack_size < 2) return(ERROR); s->last_tos = s->stack[0]; Pop(s); if (s->input_base == DECIMAL) s->stack[0] *= s->last_tos; else s->stack[0] = (long int) (UNS(s->stack[0]) * UNS(s->last_tos)); s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); case '/': /* Divide */ if (s->stack_size < 2 || s->stack[0] == 0) return(ERROR); s->last_tos = s->stack[0]; Pop(s); if (s->input_base == DECIMAL) s->stack[0] /= s->last_tos; else s->stack[0] = (long int) (UNS(s->stack[0]) / UNS(s->last_tos)); s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); case '%': case ESC_5: /* Remainder */ if (s->stack_size < 2 || s->stack[0] == 0) return(ERROR); s->last_tos = s->stack[0]; Pop(s); if (s->input_base == DECIMAL) s->stack[0] %= s->last_tos; else s->stack[0] = (long int) (UNS(s->stack[0]) % UNS(s->last_tos)); s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); case '~': /* Not */ s->last_tos = s->stack[0]; s->stack[0] = ~s->stack[0]; s->mode = LAST_WAS_FUNCTION; Draw_Top_of_Stack(s); return(OK); case '&': /* And */ if (s->stack_size < 2) return(ERROR); s->last_tos = s->stack[0]; Pop(s); s->stack[0] &= s->last_tos; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); case '|': /* Or */ if (s->stack_size < 2) return(ERROR); s->last_tos = s->stack[0]; Pop(s); s->stack[0] |= s->last_tos; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); case '^': /* Exclusive-or */ if (s->stack_size < 2) return(ERROR); s->last_tos = s->stack[0]; Pop(s); s->stack[0] ^= s->last_tos; s->mode = LAST_WAS_FUNCTION; Draw_Stack(s); return(OK); default: return(ERROR); } } /*****************************************************************/ /* */ /* Enter_Numeric( state, numeral ) */ /* */ /* A numeral (0 to 15) has been typed. */ /* If a number is currently being entered */ /* then shift it over and add this to the */ /* display. If the last operation was a function */ /* then push up the stack first. If the last */ /* key was "ENTER", then clear out the top of */ /* the stack and put the numeral there. */ /* */ /* Returns OK or ERROR. */ /* */ /*****************************************************************/ int Enter_Numeric(s, numeral) ic_state *s; int numeral; { if (numeral >= s->input_base) return(ERROR); switch (s->mode) { case LAST_WAS_FUNCTION: Push(s); s->stack[0] = numeral; Draw_Stack(s); break; case LAST_WAS_NUMERIC: s->stack[0] = s->stack[0] * s->input_base + numeral; Draw_Top_of_Stack(s); break; case LAST_WAS_ENTER: s->stack[0] = numeral; Draw_Top_of_Stack(s); break; default: fprintf(stderr, "Internal failure (mode)\n"); Sigint(0); } s->mode = LAST_WAS_NUMERIC; return(OK); } /*****************************************************************/ /* */ /* Push( state ) */ /* */ /* Push up the stack one level. */ /* */ /*****************************************************************/ void Push(s) ic_state *s; { int i; if (s->stack_size == STACK_SIZE) --s->stack_size; for (i = s->stack_size; i > 0; --i) s->stack[i] = s->stack[i - 1]; ++s->stack_size; } /*****************************************************************/ /* */ /* Pop( state ) */ /* */ /* Pop the stack down one level. */ /* This routine is only called with */ /* the stack size > 1. */ /* */ /*****************************************************************/ void Pop(s) ic_state *s; { int i; for (i = 0; i < s->stack_size - 1; ++i) s->stack[i] = s->stack[i + 1]; --s->stack_size; } /****************************************************************/ /* */ /* Exec_Shell() */ /* */ /* Fork off a sub-process to exec() the shell. */ /* */ /****************************************************************/ void Exec_Shell() { int pid = fork(); if (pid == -1) return; if (pid == 0) { /* The child process */ extern char **environ; char *shell = getenv("SHELL"); if (shell == NULL) shell = "/bin/sh"; execle(shell, shell, (char *) 0, environ); perror(shell); exit(127); } /* The parent process: ignore signals, wait for sub-process */ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); { int status; int w; while ((w = wait(&status)) != pid && w != -1); } signal(SIGINT, Sigint); signal(SIGQUIT, Sigint); return; }