/* Copyright 1998 DJ Delorie Distributed under the terms of the GNU GPL http://www.delorie.com/store/hcalc/ Revisions copyright 2007, Theodore Kilgore More revisions copyright 2023, B. Watson */ #include "hcalc.h" #include #include #include #include #include char pending_op = 0; int making_number = 0; int base = 10; int quiet = 0; int showing_err = 0; #define MAXIN 40 char input_buf[MAXIN]; int iptr; double value, saved, stored=0; void paste(void); void copy(void); int number_is_ok(double n) { return (fpclassify(n) & (FP_ZERO | FP_NORMAL )); } void bell(void) { if(!quiet) XBell(display, 0); } void convert_number() { char *ip = input_buf; double scale = 1; int sign = +1; if (*ip == '-') sign = -1; value = 0; while (*++ip) { if (*ip == '.') break; if (*ip >= '0' && *ip <= '9') { value *= base; value += *ip-'0'; } if (*ip >= 'a' && *ip <= 'f') { value *= base; value += *ip-'a'+10; } } if (*ip) while (*++ip) { if (*ip >= '0' && *ip <= '9') { scale *= base; value += (*ip-'0')/scale; } if (*ip >= 'a' && *ip <= 'f') { scale *= base; value += (*ip-'a'+10)/scale; } } value *= sign; } void show_value() { char tmp[25], *tp; char commas[40], *cp, *dp; double v = value; if(!number_is_ok(value)) { set_string("Err"); bell(); showing_err = 1; saved = value = 0; /* so it doesn't get saved on exit! */ return; } if (base == 2) { int q = (long long)v & 0xffffffffL; set_bits(q); return; } tmp[0] = ' '; if (v < 0) { tmp[0] = '-'; v = -v; } if (base == 10) { sprintf(tmp+1, "%.14G", v); if (strchr(tmp+1, 'E')) sprintf(tmp+1, "%.9G", v); if (tmp[14] == '.') tmp[14] = 0; } else { static char tohex[] = "0123456789ABCDEF"; long long ll = (long long)v; char *revptr; tp = tmp+1; if (base == 16) { *tp++ = '0'; *tp++ = 'x'; } else if (base == 8) *tp++ = '0'; revptr = tp; do { *tp++ = tohex[ll%base]; ll /= base; } while (ll); *tp-- = 0; while (revptr < tp) { char t = *revptr; *revptr = *tp; *tp = t; tp--; revptr++; } } cp = commas+40; tp = tmp+strlen(tmp); dp = strchr(tmp, '.'); if (dp == 0) dp = tp; *--cp = 0; while (tp>=tmp) { *--cp = *tp--; switch (base) { case 10: if (isdigit(cp[0]) && isdigit(cp[1]) && isdigit(cp[2]) && tp=tmp && isdigit(*tp)) *--cp = ','; break; case 16: if (isxdigit(cp[0]) && isxdigit(cp[1]) && isxdigit(cp[2]) && isxdigit(cp[3]) && tp>=tmp && isxdigit(*tp)) *--cp = ','; break; } } if (strlen(cp) > 15) set_string(tmp); else set_string(cp); } void end_number() { if (!making_number) return; making_number = 0; iptr = 0; switch (pending_op) { case '+': value = saved + value; break; case '-': value = saved - value; break; case '*': value = saved * value; break; case '/': value = saved / value; break; case '%': value = (long long)saved % (long long)value; break; case '&': value = (long long)saved & (long long)value; break; case '|': value = (long long)saved | (long long)value; break; case '^': value = (long long)saved ^ (long long)value; break; case 'S': /* Shift. Positive means <<, negative means >> */ if (value < 0) value = (long long)saved >> (long long)(-value); else value = (long long)saved << (long long)value; break; } saved = value; pending_op = 0; show_value(); } void start_number() { if (making_number) return; iptr = 1; input_buf[0] = ' '; input_buf[1] = 0; making_number = 1; } void key(char c) { int v = c; /* printf("key_number 0x%x\n", v); */ if(showing_err) { switch(c) { /* list the keys that should be still active in error state */ case 27: case 'C': case 'Q': case 17: break; default: bell(); return; } } switch (c) { case 27: case 'C': showing_err = 0; making_number = 0; iptr = 0; pending_op = 0; value = saved = 0; set_string(""); show_value(); break; case 'u': if (making_number) { making_number = 0; set_string(""); } break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': v = c - 39; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': v -= '0'; if (v >= base || iptr == MAXIN-1) bell(); else { start_number(); input_buf[iptr++] = c; input_buf[iptr] = 0; convert_number(); show_value(); } break; case '.': if (strchr(input_buf, '.')) { bell(); break; } case ',': if (iptr == 1 || iptr == MAXIN-1) bell(); else { start_number(); input_buf[iptr++] = c; input_buf[iptr] = 0; convert_number(); show_value(); } break; case 8: case 127: if (iptr <= 1) bell(); else { input_buf[--iptr] = 0; convert_number(); show_value(); } break; case '_': /* The +/- key */ case 'i': if (making_number) { if (input_buf[0] == '-') input_buf[0] = ' '; else input_buf[0] = '-'; convert_number(); show_value(); } else { value *= -1.0; saved *= -1.0; show_value(); } break; case 'D': end_number(); base = 10; show_value(); break; case 'H': end_number(); base = 16; show_value(); break; case 'O': end_number(); base = 8; show_value(); break; case 'B': end_number(); base = 2; show_value(); break; case 'x': /* Allow 'x' on keyboard to be used to enter '*' */ c = '*'; case '+': case '-': case '*': case '/': case '%': case '^': case '&': case '|': case 'S': case '=': end_number(); pending_op = c; break; case 13: case 10: end_number(); pending_op = '='; break; case '~': /* Invert bits (one's complement) */ end_number(); value = ~ (long long)value; saved = value; show_value(); break; case '@': /* Two's complement */ end_number(); { unsigned int i = ((unsigned int)value) & 0xffffffff; value = ~i + 1; } saved = value; show_value(); break; case '<': /* SHL by one bit only */ /* Modified by Keith Meehl to fix bit shift bug*/ end_number(); value = (long long)value << 1; saved = value; pending_op = 0; show_value(); break; case '>': /* SHR by one bit only */ /* Modified by Keith Meehl to fix bit shift bug*/ end_number(); value = (long long)value >> 1; saved = value; pending_op = 0; show_value(); break; case '[': /* STO */ stored = value; break; case ']': /* RCL */ value = stored; show_value(); making_number = 1; iptr = 1; input_buf[0] = ' '; break; case '}': /* SUM */ stored += value; break; case 'P': /* click on the display itself */ break; case 3: /* ^C */ copy(); break; case 22: /* ^V */ paste(); break; case 'q': quiet = !quiet; break; case 'Q': case 17: exit(0); case 'z': { /* dirty hack method of changing window size: re-execute ourselves with appropriate argument. TODO: see if this leaks memory, fds, etc */ extern char *self; char *args[4]; args[0] = self; switch(winsize) { case 0: args[1] = "-medium"; break; case 1: args[1] = "-large"; break; case 2: args[1] = "-small"; break; } args[2] = NULL; /* note that atexit() functions do NOT get called on execv() */ save_config(); XCloseDisplay(display); execv(self, args); } } } static char *bmap[] = { "PPPP\033", /* mouse copy and paste, CLR */ "DHOB\010", /* DEC, HEX, OCT, BIN, DEL */ "[]}<>", /* STO, RCL, SUM, <<, >> */ "Sdef/", /* SHF, D E F / */ "~abc*", /* INV, */ "|789-", /* OR, */ "&456+", /* AND, */ "^123=", /* XOR, */ "u0._=" /* CE, 0, . , +/-, = */ }; void copy(void) { XSetSelectionOwner(display, XA_PRIMARY, window, event.xbutton.time); } void paste(void) { XConvertSelection(display, XA_PRIMARY, XA_STRING, paste_atom, window, event.xbutton.time); } void complete_paste(unsigned char *s, int n) { int i; for (i=0; i 4) x = 4; y = (y-1)/(16 * scale_factor); if (y < 0) y = 0; if (y > 8) y = 8; k = bmap[y][x]; if (k == 27 && b == 3) exit(0); if (k == 'P' && b == 1) copy(); else if (k == 'P' && b != 1) paste(); else if(k == '/' && b != 1) key('%'); else if(k == '~' && b != 1) key('@'); else key(k); }