/*
 * Copyright 1989, 1991, 1994 O'Reilly and Associates, Inc.
 * Copyright 1988 Stellar Computer, Inc.

     The X Consortium, and any party obtaining a copy of these files from
     the X Consortium, directly or indirectly, is granted, free of charge, a
     full and unrestricted irrevocable, world-wide, paid up, royalty-free,
     nonexclusive right and license to deal in this software and
     documentation files (the "Software"), including without limitation the
     rights to use, copy, modify, merge, publish, distribute, sublicense,
     and/or sell copies of the Software, and to permit persons who receive
     copies from any such party to do so.  This license includes without
     limitation a license to do the foregoing actions under any patents of
     the party supplying this software to the X Consortium.
 */
/*
 * X Version 11 Integer Programmer's Calculator
 * Written by Alan Greenspan, modified slightly by Adrian Nye.
 */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/cursorfont.h>

#include <stdio.h>

#if defined(_CH_) 
#include <termios.h>
#elif defined(SYSV) 
#include <termio.h>
#else
#include <sgtty.h>
#include <sys/ttychars.h>
#endif /* SysV*/

#include <ctype.h>
#include <pwd.h>

#include "basecalc.h"

/*
 * Programmer's calculator with 
 * number base conversions
 */
int main (argc, argv)
int argc;
register char *argv[];
{
    /* so we can use the resource manager data merging functions */
    XrmInitialize();

    /* parse command line first so we can open display, store any
     * options in a database  */
    parseOpenDisp (&argc, argv);

    /* get server defaults, program defaults, .Xdefaults, command 
     * line, etc. and merge them */
    mergeDatabases();

    /* extract values from database for use */
    extractOpts ();

    /* load font, make pixmaps, set up arrays of windows */
    initCalc ();

    /* get keyboard settings for interrupt, delete, etc. */
    initTty ();

    /* make a standard cursor */
    makeCursor ();

    /* set standard properties, create and map windows */
    makeWindows (argc, argv);

    /* get events */
    takeEvents ();

    /* bow out gracefully */
    XCloseDisplay(display);
    exit (1);
}


static char *getHomeDir( dest )
char *dest;
{
    int uid;
    /* extern char *getenv(); */
    extern int getuid();
    extern struct passwd *getpwuid();
    struct passwd *pw;
    register char *ptr;

    if ((ptr = getenv("HOME")) != NULL) {
        (void) strcpy(dest, ptr);

    } else {
        if ((ptr = getenv("USER")) != NULL) {
            pw = getpwnam(ptr);
        } else {
            uid = getuid();
            pw = getpwuid(uid);
        }
        if (pw) {
            (void) strcpy(dest, pw->pw_dir);
        } else {
            *dest = '\0';
        }
    }
    return dest;
}


/*
 * Get program's and user's defaults
 */
int mergeDatabases()
{
    XrmDatabase homeDB, serverDB, applicationDB;

    char filenamebuf[1024];
    char *filename = &filenamebuf[0];
    char *environment;
    char *classname = "Basecalc";
    char name[255];

    (void) strcpy(name, "/usr/lib/X11/app-defaults/");
    (void) strcat(name, classname);
    /* get application defaults file, if any */
    applicationDB = XrmGetFileDatabase(name);
    if(applicationDB)
      (void) XrmMergeDatabases(applicationDB, &rDB);

    /* MERGE server defaults, these are created by xrdb, loaded as a
     * property of the root window when the server initializes, and
     * loaded into the display structure on XOpenDisplay.  If not defined,
         * use .Xdefaults  */
    if (XResourceManagerString(display) != NULL) {
        serverDB = XrmGetStringDatabase(XResourceManagerString(display));
    } else {
        /* Open .Xdefaults file and merge into existing data base */
        (void) getHomeDir(filename);
        (void) strcat(filename, "/.Xdefaults");

        serverDB = XrmGetFileDatabase(filename);
    }
    XrmMergeDatabases(serverDB, &rDB);

    /* Open XENVIRONMENT file, or if not defined, the ~/.Xdefaults,
         * and merge into existing data base */
    if ((environment = getenv("XENVIRONMENT")) == NULL) {
        int len;
        environment = getHomeDir(filename);
        (void) strcat(environment, "/.Xdefaults-");
        len = strlen(environment);
        (void) gethostname(environment + len, 1024 - len);
    }
    homeDB = XrmGetFileDatabase(environment);
    XrmMergeDatabases(homeDB, &rDB);

    /* command line takes precedence over everything */
    XrmMergeDatabases(commandlineDB, &rDB);
}


/*
 * Get command line options
 */
int parseOpenDisp (argc, argv)
int *argc;
register char *argv[];
{

    XrmValue value;
    char *str_type[20];

    myDisplayName[0] = '\0';

    XrmParseCommand(&commandlineDB, opTable, opTableEntries,
            argv[0], argc, argv);

    /*
     * Check for any arguments left
     */
    if (*argc != 1) 
        Usage();

    /* get display now, because we need it to get other databases*/
    if (XrmGetResource(commandlineDB, "basecalc.display",
            "Basecalc.Display", str_type, &value) == True) {
        (void) strncpy(myDisplayName, value.addr, (int) value.size);
    }

    /*
     * Open display 
     */
    if (!(display = XOpenDisplay(myDisplayName))) {
        (void) fprintf(stderr, "%s: Can't open display '%s'\n",
                argv[0], XDisplayName(myDisplayName));
        exit(1);
    }

    screen_number = DefaultScreen(display);
    visual = DefaultVisual(display, screen_number);
    colormap = DefaultColormap(display, screen_number);
}

int extractOpts()
{
    char *str_type[20];
    char buffer[20];
    long flags;
    XrmValue value;
    int x, y, width, height;
    XColor screen_def;

    /* get geometry (actually, this is currently ignored) */
    if (XrmGetResource(rDB, "basecalc.geometry", "Basecalc.Geometry",
            str_type, &value) == True) {
        (void) strncpy(Geostr, value.addr, (int) value.size);
    } else {
        Geostr[0] = NULL;
    }

    if (XrmGetResource(rDB, "basecalc.iconGeometry", "Basecalc.IconGeometry",
            str_type, &value) == True) {
        (void) strncpy(iconGeostr, value.addr, (int) value.size);
    } else {
        iconGeostr[0] = NULL;
    }

    if (XrmGetResource(rDB, "basecalc.unsigned", "Basecalc.Unsigned",
            str_type, &value) == True)
        if (strncmp(value.addr, "False", (int) value.size) ==
                0) 
            Unsigned = False;


    if (XrmGetResource(rDB, "basecalc.base", "Basecalc.Base",
            str_type, &value) == True) {
        (void) strncpy(buffer, value.addr, (int) value.size);
        buffer[value.size] = NULL;
        Base = atoi(buffer);
    } else 
        Base = 10;

    if (XrmGetResource(rDB, "basecalc.foreground", "Basecalc.Foreground",
            str_type, &value) == True) {
        (void) strncpy(buffer, value.addr, (int) value.size);
        if (XParseColor(display, colormap, buffer,  &screen_def) ==
                0)  {
            (void) fprintf(stderr, "basecalc: fg color specification %s invalid",
                    buffer);
            foreground = BlackPixel(display, screen_number);
        }  else {
#if defined(_CH_)
            if ((visual->c_class == StaticGray) || (visual->c_class == GrayScale))
#else
            if ((visual->class == StaticGray) || (visual->class == GrayScale))
#endif

                foreground = BlackPixel(display,
                        screen_number);
            else if (XAllocColor(display, colormap,
                    &screen_def) == 0) {
                foreground = BlackPixel(display,
                        screen_number);
                (void) fprintf(stderr, "basecalc: couldn't allocate color: %s.\n",
                        buffer);
            } else
                foreground = screen_def.pixel;
        }
    } else {
        foreground = BlackPixel(display, screen_number);
    }

    if (XrmGetResource(rDB, "basecalc.background", "Basecalc.Background",
            str_type, &value) == True) {
        (void) strncpy(buffer, value.addr, (int) value.size);
        if (XParseColor(display, colormap, buffer,  &screen_def) ==
                0)  {
            (void) fprintf(stderr, "basecalc: bg color specification %s invalid",
                    buffer);
            background = WhitePixel(display, screen_number);
        }  else {
#if defined(_CH_)
            if ((visual->c_class == StaticGray) || (visual->c_class == GrayScale))
#else 
            if ((visual->class == StaticGray) || (visual->class == GrayScale))
#endif
                background = WhitePixel(display,
                        screen_number);
            else if (XAllocColor(display, colormap,
                    &screen_def) == 0) {
                background = WhitePixel(display,
                        screen_number);
                (void) fprintf(stderr, "basecalc: couldn't allocate color: %s.\n",
                        buffer);
            } else
                background = screen_def.pixel;
        }
    } else {
        background = WhitePixel(display, screen_number);
    }

    /* one last check to make sure the colors are different! */
    if (background == foreground) {
        background = WhitePixel(display, screen_number);
        foreground = BlackPixel(display, screen_number);
    }


    /*  Could add a command line option for initial state:
     *    iconOnly[0] = NULL; 
     */

    /*
     * Get window geometry info.
     */
    if (Geostr != NULL) {
        flags = XParseGeometry(Geostr,  &x, &y, &width,
                &height);
        if ((WidthValue | HeightValue) & flags)
            Usage ();
        if (XValue & flags) {
            if (XNegative & flags)
                x = DisplayWidth(display, screen_number) +
                                            x - sizehints.width;
            sizehints.flags |= USPosition;
            sizehints.x = x;
        }
        if (YValue & flags) {
            if (YNegative & flags)
                y = DisplayHeight(display, screen_number) +
                                            x - sizehints.width;
            sizehints.flags |= USPosition;
            sizehints.y = y;
        }
    }

    /*
     * Get icon geometry info.
     */
    if (iconGeostr != NULL) {
        iconGeostr[0] = '=';
        flags = XParseGeometry(iconGeostr,  &x, &y, &width,
                &height);
        if ((WidthValue | HeightValue) & flags)
            Usage ();
        if (XValue & flags) {
            if (XNegative & flags)
                x = DisplayWidth(display, screen_number) +
                                            x - iconsizehints.width;
            iconsizehints.flags |= USPosition;
            wmhints.flags |= IconPositionHint;
            wmhints.icon_x = x;
            iconsizehints.x = x;
        }
        if (YValue & flags) {
            if (YNegative & flags)
                y = DisplayHeight(display, screen_number) +
                                            x - iconsizehints.width;
            iconsizehints.flags |= USPosition;
            wmhints.flags |= IconPositionHint;
            wmhints.icon_y = y;
            iconsizehints.y = y;
        }
    }
}


/*
 * Print message to stderr and exit
 */
int Usage ()
{
    fprintf (stderr, "%s: [-iconic] [-unsigned] [-hex|x|dec|oct|binary] [-display ] [-geometry ] [-iconGeometry \n",
            calcName ? calcName : "basecalc");
    exit (1);
}


/*
 * Make a pixmap.
 */
Pixmap 
makePixmap(data, width, height)
char *data;
unsigned int width, height;
{
    Pixmap pid;

    pid = XCreatePixmapFromBitmapData(display, DefaultRootWindow(display),
            data, width, height, foreground, background, DefaultDepth(display,
            screen_number));
    return(pid);
}


/*
 * Initialize calculator options
 */
int initCalc ()
{
    register int win;
    register int found = -1;
    XGCValues values;
    extern char lgray_bits[]; 

    /* strcpy(dtext,"                               0 "); */
    strcpy(dtext,"            0 ");
    windata[0].text = dtext;

    if ((theFont = XLoadQueryFont (display, myFontName)) ==
            NULL) {
        (void) fprintf(stderr, "basecalc: can't open font %s\n",
                myFontName);
        exit(-1);
    }

    /*
     * Make the utility pixmaps.
     */
    grayPixmap = makePixmap(gray_bits, gray_width, gray_height);
    lgrayPixmap = makePixmap(lgray_bits, lgray_width, lgray_height);

    /*
     * Make the utility gc's.
     */
    values.font = theFont->fid;
    values.foreground = foreground;
    fgGC = XCreateGC(display, DefaultRootWindow(display),
            GCForeground | GCFont, &values);
    values.foreground = background;
    values.function = GXcopy;
    bgGC = XCreateGC(display, DefaultRootWindow(display),
            GCForeground | GCFont | GCFunction, &values);

    /*
     * Loop through buttons, setting disabled buttons
     * to Color Light Gray. Also, find the window
     * which corresponds to the starting dsplay base.
     * Also add ascent to y position of text.
     */
    for (win = 1; win < NBUTTONS; win++) {
        if (windata[win].type == WTYP_CONV &&  windata[win].value ==
                Base) {
            found = win;
        } else if (windata[win].type == WTYP_DIG &&  windata[win].value >=
                Base) {
            windata[win].color = disabledColor;
        } else if (windata[win].type == WTYP_SPEC && 
                windata[win].value == OPR_UNS) {
            if (Unsigned)
                windata[win].text = "U";
            else
                windata[win].text = "S";
            windata[win].color = pressedColor;
        } else
            windata[win].color = unpressedColor;
        windata[win].y += theFont->max_bounds.ascent;
    }
    windata[0].y += theFont->max_bounds.ascent;
    if (found >= 0) {
        Winbase = found;
        windata[found].color = pressedColor;
    } else {
        (void) fprintf(stderr, "basecalc: can't use base %d\n",
                Base);
        exit(-1);
    }
    windata[0].color = displayColor;
}


/*
 * Get the user's tty special chars
 * This is currently 4.2 specific.
 */
int initTty ()
{
    register struct KeyCode *KeyCodePtr;
    register int fd;
#if  defined(_CH_) 
    struct termios term;
#elif defined(SYSV) 
    struct termio term;
#else
    struct sgttyb tty;
    struct tchars tchars;
    struct ltchars ltchars;
#endif SysV

    if (!isatty(0)) {
        if ((fd = open ("/dev/console", 0)) < 0)
            return;
    } else
        fd = 0;
#if defined(SysV) || defined(_CH_) 
    (void) ioctl  (fd, TCGETA,   &term);
#else
    (void) ioctl  (fd, TIOCGETP, &tty);
    (void) ioctl  (fd, TIOCGETC, &tchars);
    (void) ioctl  (fd, TIOCGLTC, <chars);
#endif SysV
    if (fd)
        (void) close (fd);

    KeyCodePtr = KeyCodes;
#if defined(SysV) || defined(_CH_) 
    KeyCodePtr->kc_char = term.c_cc[VERASE];
    KeyCodePtr++;
    KeyCodePtr++;
    KeyCodePtr->kc_char = term.c_cc[VKILL];
    KeyCodePtr++;
    KeyCodePtr->kc_char = term.c_cc[VINTR];
    QuitChar = term.c_cc[VQUIT];
#else
    KeyCodePtr->kc_char = tty.sg_erase;
    KeyCodePtr++;
    KeyCodePtr->kc_char = ltchars.t_werasc;
    KeyCodePtr++;
    KeyCodePtr->kc_char = tty.sg_kill;
    KeyCodePtr++;
    KeyCodePtr->kc_char = tchars.t_intrc;
    QuitChar = tchars.t_quitc;
#endif SysV
}


/*
 * Make the cursor
 */
int makeCursor ()
{
    theCursor = XCreateFontCursor (display, XC_hand1);
}


/*
 * Set up the selection of events
 */
int selectEvents ()
{
    int win;

    XSelectInput (display, calcWin, KeyPressMask | KeyReleaseMask);
    XSelectInput (display, dispWin, ExposureMask);
    for (win = 1; win < NBUTTONS; win++)
        XSelectInput (display, Buttons[win].self,  ExposureMask
                |             ButtonPressMask | ButtonReleaseMask
                |             EnterWindowMask | LeaveWindowMask);
}


/*
 * Get events and process them
 */
int takeEvents ()
{
    XEvent Event;
    register int win;
    register int Pressed = False;
    register int InWindow = False;
    char buffer[10];
    register char *KeyChars = buffer;
    register int WasKeyDown = False;
    unsigned i, nbytes;
    int old_color;

    while (1) {
        if (!WasKeyDown)
            XNextEvent (display, &Event);
        else
            Event.type = KeyRelease;

        /*
         * Map keyboard events
         * to Window Events
         */
        if (Event.type == KeyPress || Event.type == KeyRelease) {
            nbytes = XLookupString (&Event, buffer,
                    sizeof(buffer), NULL, NULL);
            if (Event.type == KeyPress) {
                Event.type = ButtonPress;
                WasKeyDown = 1;
            } else {
                for (i = 0; i < 60000; i++)
                    ;
                Event.type = ButtonRelease;
            }
            if ((Event.xbutton.window =  keyToWin (KeyChars,
                    nbytes)) == None) {
                WasKeyDown = 0;
                continue;
            }
        }
        for (win = 0; win < NBUTTONS; win++)
            if (Buttons[win].self == Event.xbutton.window)
                break;
        switch (Event.type) {
        case EnterNotify:
            old_color = windata[win].color;
            break;
        case ButtonPress:
            if (windata[win].color != disabledColor) {
            Pressed = win;
            if (!WasKeyDown)
                InWindow = True;
            windata[win].color = pressedColor;
            drawButton (win, 0);
            }
            break;
        case LeaveNotify:
            if (Pressed == win) {
            InWindow = False;
            windata[win].color = old_color;
            drawButton (win, 0);
            }
            break;
            /*
        case EnterNotify:
            if (Pressed != win)
                break;
            InWindow = True;
            windata[win].color = pressedColor;
            drawButton (win, 0);
            break;
        */
        case ButtonRelease:
            if (windata[win].color == disabledColor ||
                                    Pressed != win) {
                WasKeyDown = False;
            }
            else {
            Pressed = False;
            windata[win].color = unpressedColor;
            if (WasKeyDown || InWindow)
                winPressed (win);
            WasKeyDown = False;
            InWindow = False;
            drawButton (win, 0);
            }
            break;
        case Expose:
            drawButton (win, 1);
            break;
        }
        XFlush(display);
    }
}


/*
     * Make the calculator windows
     */
int makeWindows (argc, argv)
int argc;
char *argv[];
{
    register int i;
    XSetWindowAttributes attributes;
    char *window_name = "Programmer's Calculator";
    char *icon_name = "basecalc";

    /*
     * Define the border and background for the main window.
     * - Black border and a patterned background.
     */
    attributes.border_pixel = foreground;
    attributes.background_pixmap = grayPixmap;
    /*
     * Create the main window (calculator frame) as a 
     * child of the Root Window
     */
    attributes.cursor = theCursor;
    calcWin = XCreateWindow(display, DefaultRootWindow(display),
            sizehints.x, sizehints.y, sizehints.width, sizehints.height,
            1, DefaultDepth(display, screen_number), InputOutput,
            CopyFromParent, CWBorderPixel | CWBackPixmap | CWCursor,
            &attributes);
#ifdef X11R3
    XSetStandardProperties (display, calcWin, window_name,
            icon_name, NULL, argv, argc, &sizehints);
#endif

    /*
     * Create the icon window and associate it with the calculator
     */
    iconPixmap = makePixmap(icon_bits, icon_width, icon_height);
    attributes.border_pixel = foreground;
    attributes.background_pixmap = iconPixmap;
    iconWin = XCreateWindow(display, DefaultRootWindow(display),
            iconsizehints.x, iconsizehints.y, iconsizehints.width,
            iconsizehints.height, 1, DefaultDepth(display, screen_number),
            InputOutput, CopyFromParent, CWBorderPixel | CWBackPixmap,
            &attributes);
    wmhints.icon_window = iconWin;
    wmhints.initial_state = iconOnly ? IconicState : NormalState;
    wmhints.input = True;
    wmhints.flags |= InputHint | StateHint | IconWindowHint;
#ifdef X11R3
    XSetWMHints(display, calcWin, &wmhints);
#else
    {
        XClassHint class_hints;

        /* format of the window name and icon name 
     * arguments has changed in R4 */
        XTextProperty windowName, iconName;

        /* These calls store window_name and icon_name into
     * XTextProperty structures and set their other 
     * fields properly. */
        if (XStringListToTextProperty(&window_name, 1,
                &windowName) == 0) {
            (void) fprintf( stderr, "%s: structure allocation for windowName failed.\n",
                                    argv[0]);
            exit(-1);
        }

        if (XStringListToTextProperty(&icon_name, 1, &iconName) ==
                0) {
            (void) fprintf( stderr, "%s: structure allocation for iconName failed.\n",
                                    argv[0]);
            exit(-1);
        }

        class_hints.res_name = argv[0];
        class_hints.res_class = "Basicwin";

        XSetWMProperties(display, calcWin, &windowName,
                &iconName,  argv, argc, &sizehints, &wmhints,
                            &class_hints);
    }
#endif

    /*
     * Create the buttons as subwindows. 
     */
    attributes.background_pixmap = lgrayPixmap;
    attributes.border_pixel = foreground;
    for (i = 0; i < NBUTTONS; i++)
        switch (windata[i].color) {
        case WHITE:
            Buttons[i].self = XCreateSimpleWindow(display,
                    calcWin, Buttons[i].x, Buttons[i].y,
                    Buttons[i].width, Buttons[i].height,
                    1, foreground, background);
            break;
        case BLACK:
            Buttons[i].self = XCreateSimpleWindow(display,
                    calcWin, Buttons[i].x, Buttons[i].y,
                    Buttons[i].width, Buttons[i].height,
                    1, background, foreground);
            break;
        case LIGHTGRAY:
            Buttons[i].self = XCreateWindow(display,
                    calcWin, Buttons[i].x, Buttons[i].y,
                    Buttons[i].width, Buttons[i].height,
                    1, CopyFromParent, InputOutput, CopyFromParent,
                                    CWBorderPixel | CWBackPixmap,
                    &attributes);
            break;
        }

    /*
     * The display window is distinguished
     */
    dispWin = Buttons[0].self;

    /*
     * Initialize event catching.
     */
    selectEvents ();

    /*
     * Map the calculator and sub-windows.
     */
    XMapSubwindows(display, calcWin);
    XMapWindow(display, calcWin);
}


/*
 * Draw a single button with its text
 */
int drawButton (win, exposeEvent)
int win;
int exposeEvent;
{
    register char *string;
    register int x, y;
    struct windata *winp;
    char *measure;
    XSetWindowAttributes attributes;
    unsigned long valuemask;
    GC gc;

    winp = &windata[win];
    x = winp->x;
    y = winp->y;
    string = winp->text;

    switch (windata[win].color) {
    case WHITE:
        gc = fgGC;
        attributes.background_pixel = background;
        attributes.border_pixel = foreground;
        valuemask = CWBackPixel | CWBorderPixel;
        break;
    case BLACK:
        gc = bgGC;
        attributes.background_pixel = foreground;
        attributes.border_pixel = background;
        valuemask = CWBackPixel | CWBorderPixel;
        break;
    case LIGHTGRAY:
        gc = bgGC;
        attributes.background_pixmap = lgrayPixmap;
        attributes.border_pixel = foreground;
        valuemask = CWBackPixmap | CWBorderPixel;
        break;
    }
    if (!exposeEvent) {
        XChangeWindowAttributes(display, Buttons[win].self,
                valuemask, &attributes);
        XClearWindow(display, Buttons[win].self);
    }
    XDrawString (display, Buttons[win].self, gc, x, y, string,
            strlen (string));
    if (win == 0) {
        switch (Base) {
        case 10:
        case 8:
            measure = Octmeasure;
            break;
        default:
        case 16:
        case 2:
            measure = Hexmeasure;
            break;
        }
        XDrawString (display, dispWin, gc, 7, 6, measure,
                31);
    }
}


static unsigned int LastDisp = 1;
/*
 * Do the operation corresponding to a key press
 */
int winPressed (win)
int win;
{
    register int type;

    type = windata[win].type;
    switch (type) {
    case WTYP_CONV:
        convButton (win);
        displayVal (LastDisp == 1 ? Value : Accum);
        break;
    case WTYP_DIG:
        digitButton (win);
        displayVal (Value);
        LastDisp = 1;
        break;
    case WTYP_OPER:
        if (operButton (win) == 0) {
            displayVal (Accum);
            LastDisp = 2;
        } else {
            displayVal (Value);
            LastDisp = 1;
        }
        break;
    case WTYP_SPEC:
        specButton (win);
        displayVal (LastDisp == 1 ? Value : Accum);
        LastDisp = 1;
        break;
    }
}


/*
 * Handle a conversion button
 */
int convButton (win)
int win;
{
    register int i, NewBase, Diff, Digit;
    register int HiBase, LowBase;

    NewBase = windata[win].value;
    windata[Winbase].color = unpressedColor;
    drawButton (Winbase, 0);
    windata[win].color = pressedColor;

    Diff = NewBase - Base;
    if (Diff) {
        if (NewBase > Base) {
            LowBase = Base;
            HiBase = NewBase;
        } else {
            LowBase = NewBase;
            HiBase = Base;
        }
        for (i = 1; i < NBUTTONS; i++) {
            if (windata[i].type == WTYP_DIG) {
                Digit = windata[i].value;
                if (Digit >= LowBase && Digit <
                        HiBase) {
                    if (Diff < 0)
                        windata[i].color =
                                disabledColor;
                    else
                        windata[i].color =
                                unpressedColor;
                    drawButton (i, 0);
                }
            }
        }
    }
    Winbase = win;
    Base = NewBase;
}


/*
 * Handle a digit button
 */
int
digitButton (win)
int win;
{
    register unsigned long Temp;

    if (CalcReset) {
        CalcReset = 0;
        Accum = 0;
        Value = 0;
        LastOpt = OPR_ADD;
    }
    Digit = windata[win].value;
    if (Unsigned)
        Temp = (unsigned)Value * (unsigned)Base + Digit;
    else
        Temp = Value * Base + Digit;
    if ((unsigned)Temp / Base != (unsigned)Value) {    /* OverfLow? */
        /*
         * Flash the display since the character didn't register
         */
        windata[0].color =  (displayColor == WHITE) ?
                BLACK : WHITE;
        drawButton (0, 0);
        XFlush(display);
        Delay ();
        windata[0].color = displayColor;
        drawButton (0, 0);
        return;
    }
    Value = Temp;
}


/*
 * Handle a special operator
 */
int
specButton (int win)
{
    register int oper;

    oper = windata[win].value;

    switch (oper) {
    case OPR_CLRD:
        if (LastOpt != OPR_ASGN) {
        Value = (unsigned)Value / Base;
        }
        break;
    case OPR_CLRE:
        Value = 0;
        break;
    case OPR_CLRA:
        Accum = 0;
        Value = 0;
        LastOpt = OPR_ADD;
        break;
    case OPR_UNS:
        Unsigned = !Unsigned;
        windata[win].text = Unsigned ? "U" : "S";
        windata[win].color = pressedColor;
        drawButton (win, 0);
        break;
    }
}


/*
 * Handle an operator
 */
int
operButton (int win)
{
    register int oper;

    oper = LastOpt;
    LastOpt = windata[win].value;

    CalcReset = 0;
    switch (LastOpt) {
    case OPR_NEG:
        Value = -Value;
        if ((LastOpt = oper) == OPR_ASGN)
            Accum = Value;
        return 1;
        break;
    case OPR_NOT:
        Value = ~Value;
        if ((LastOpt = oper) == OPR_ASGN)
            Accum = Value;
        return 1;
        break;
    }

    switch (oper) {
    case OPR_ADD:
        if (Unsigned)
            Accum = (unsigned)Accum + (unsigned)Value;
        else
            Accum += Value;
        break;
    case OPR_SUB:
        if (Unsigned)
            Accum = (unsigned)Accum - (unsigned)Value;
        else
            Accum -= Value;
        break;
    case OPR_MUL:
        if (Unsigned)
            Accum = (unsigned)Accum * (unsigned)Value;
        else
            Accum *= Value;
        break;
    case OPR_DIV:
        if (Value != 0) {
        if (Unsigned)
            Accum = (unsigned)Accum / (unsigned)Value;
        else
            Accum /= Value;
        }
        break;
    case OPR_MOD:
        if (Value != 0) {
        if (Unsigned)
            Accum = (unsigned)Accum % (unsigned)Value;
        else
            Accum %= Value;
        }
        break;
    case OPR_OR:
        Accum |= Value;
        break;
    case OPR_AND:
        Accum &= Value;
        break;
    case OPR_SHR:
        if (Unsigned)
            Accum = (unsigned)Accum >> (unsigned)Value;
        else
            Accum >>= Value;
        break;
    case OPR_SHL:
        if (Unsigned)
            Accum = (unsigned)Accum << (unsigned)Value;
        else
            Accum <<= Value;
        break;
    case OPR_XOR:
        Accum ^= Value;
        break;
    case OPR_ASGN:
        break;
    }
    if (LastOpt == OPR_ASGN) {
        Value = Accum;
        CalcReset = 1;
        return 1;
    }
    Value = 0;
    return 0;
}


/*
 * Display a number in the display window
 */
int
displayVal (number)
register long number;
{
    register char *Fmt;
    register char *cp;
    register int i;

    switch (Base) {
    case 16:
        Fmt = "%32x";
        break;
    case 10:
        Fmt = "%32d";
        break;
    case 8:
        Fmt = "%32o";
        break;
    case 2:
        Fmt = "%032b";
        break;
    }
    cp = windata[0].text;
    for (i = 32; --i >= 0; )
        *cp++ = ' ';
    *cp = '\0';
    Sprintf (windata[0].text, Fmt, number);
    drawButton (0, 0);
}


/*
 * Translate a key code to a corresponding window
 */
int
keyToWin (str, n)
register char *str;
register unsigned n;
{
    register int value = -1;
    register struct KeyCode *KeyCodePtr;
    register char ch;
    register int i;

    if (n > (unsigned) 0) {
        ch = *str;
        if (islower(ch) && isxdigit(ch))
            value = 10 + ch - 'a';
        else if (isdigit(ch))
            value = ch - '0';
        if (value >= 0) {
            for (i = 1; i < NBUTTONS; i++)
                if (windata[i].type == WTYP_DIG &&
                                            windata[i].value ==
                        value)
                    return Buttons[i].self;
        } else {
            /*
             * Do some translations - these should be driven
             * from the user's terminal erase, kill, etc
             */
            switch (ch) {
            case 'U':
                if (Unsigned)
                    return - 1;
                str = "S";
                n = 1;
                break;
            case 'S':
                if (!Unsigned)
                    return - 1;
                str = "U";
                n = 1;
                break;
            case '\r':
            case '\n':
                str = "=";
                n = 1;
                break;
            default:
                if (ch == QuitChar) {
                    XCloseDisplay(display);
                    exit (1);
                }
                KeyCodePtr = KeyCodes;
                while ((n = KeyCodePtr->kc_len) >
                        (unsigned) 0) {
                    if (ch == KeyCodePtr->kc_char) {
                        str = KeyCodePtr->kc_func;
                        break;
                    }
                    KeyCodePtr++;
                }
                if (n == 0)
                    n = 1;
                break;
            }
            for (i = 1; i < NBUTTONS; i++) {
                if (windata[i].type != WTYP_DIG &&
                                            strncmp (windata[i].text,
                        str, (int) n) == 0)
                    return Buttons[i].self;
            }
        }
    }
    return None;
}


/*
 * Specialized version of C Library sprintf.
 *
 * %u %d %o %x %b (binary) are recognized.
 * %0W... - where 0 means pad with zeros otherwise blanks
 *       - if W, the minimum field width is larger than
 *      - the number
 */
int
Sprintf(cp, fmt, x1)
register char *cp;
register char *fmt;
unsigned x1;
{
    register int c, b, sign;
    register char *s;
    register unsigned short fw;
    char *printInBase();
    char pad;

    while ((c = *fmt++) != '%') {
        if (c == '\0') {
            *cp = c;
            return;   /* to displayVal */
        }
        *cp++ = c;
    }
    c = *fmt++;
    if (c == '0') {
        pad = '0';
        c = *fmt++;
    } else
        pad = ' ';

    /*
     * Calculate minimum field width
     */
    fw = 0;
    while (c >= '0' && c <= '9') {
        fw = fw * 10 + (c - '0');
        c = *fmt++;
    }
    sign = 0;
    switch (c) {
    case 'x':
        b = 16;
        break;
    case 'd':
        if (!Unsigned)
            sign = 1;
        /*  falls through into 'u' case */
        b = 10;
        break;
    case 'u':
        b = 10;
        break;
    case 'o':
        b = 8;
        break;
    case 'b':
        b = 2;
        break;
    default:
        /*
         * Unknown format
         */
        b = 0;
        break;
    }
    if (b)
        cp = printInBase (cp, x1, b, fw, pad, sign);
}


/*
 * Print a number n in base b into string cp.
 * Minimum field width = fw, pad character = pad
 */
char *
printInBase (cp, n, b, fw, pad, sign)
register char *cp;
register long n;
register int b;
register int fw, pad, sign;
{
    register int i, nd, c;
    int flag;
    int plmax;
    char d[33];
    char s[] = "0123456789ABCDEF";

    c = 1;
    if (sign)
        flag = n < 0;
    else
        flag = 0;
    if (flag)
        n = (-n);
    if (b == 2)
        plmax = 32;
    else if (b == 8)
        plmax = 11;
    else if (b == 10)
        plmax = 10;
    else if (b == 16)
        plmax = 8;
    if (b == 10) {
        if (flag == 0)
            sign = 0;
        flag = 0;
    }
    for (i = 0; i < plmax; i++) {
        if (flag == 0)
            nd = (unsigned)n % b;
        else
            nd = n % b;
        if (flag) {
            nd = (b - 1) - nd + c;
            if (nd >= b) {
                nd -= b;
                c = 1;
            } else
                c = 0;
        }
        d[i] = nd;
        if (flag == 0)
            n = (unsigned)n / b;
        else
            n = n / b;
        if ((n == 0) && (flag == 0))
            break;
    }
    if (i == plmax)
        i--;
    if (sign) {
        fw--;
        if (pad == '0')
            *cp++ = '-';
    }
    if (fw > i + 1) {
        for (fw -= i + 1; fw > 0; fw--)
            *cp++ = pad;
    }
    if (sign && pad != '0')
        *cp++ = '-';
    for (; i >= 0; i--) {
        /**cp++ = "0123456789ABCDEF"[d[i]];*/
        *cp++ = s[d[i]];
    }
    *cp = '\0';
    return cp;
}


/*
 * Delay a little while
 */
int
Delay ()
{
    long tic;
    int i;
    for (tic = 0; tic < 50000; tic++)
        i=i;
}