/* 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(void) { 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 < dp && 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': winsize++; if(winsize == 3) winsize = 0; free_pixmaps(); shutdown_x(); setup_x(); load_pixmaps(); show_value(); break; } } 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 < n; i++) key(s[i]); } void button(int b, int x, int y) { char k; if(b == 2) { paste(); return; } x = (x - 2) / (24 * scale_factor); if(x < 0) x = 0; if(x > 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); }