|
|
1.1 root 1: /****************************** Module Header ******************************\
2: * Module Name: calc.c - Calc application
3: *
4: * OS/2 Presentation Manager version of Calc, ported from Windows version
5: *
6: * Created by Microsoft Corporation, 1987
7: *
8: \***************************************************************************/
9:
10: #define INCL_WIN
11: #define INCL_GPI
12: #define INCL_DEV
13: #define INCL_ERRORS
14: #define INCL_DOSPROCESS
15: #define INCL_DOSSEMAPHORES
16: #define INCL_DOSNLS
17: #include <os2.h>
18: #include <string.h>
19: #include <stdlib.h>
20: #include <stdio.h>
21: #include "calc.h"
22:
23: /************* GLOBAL VARIABLES */
24:
25: char lastkey, currkey;
26: char szCalcClass[] = "Calculator";
27: char szTitle[30];
28: char szreg1[20], szreg2[20], szmem[20], szregx[20];
29: /* hope 20 is enough for kanji error string */
30: char szErrorString[20], szPlusMinus[2];
31: short charwidth, charheight;
32: int aspectx, aspecty, nchszstr;
33: extern BOOL fError;
34: BOOL fValueInMemory = FALSE;
35: BOOL fMDown = FALSE;
36: UCHAR mScan = 0;
37:
38: #define TOLOWER(x) ( (((x) >= 'A') && ((x) <= 'Z')) ? (x)|0x20 : (x))
39: #define WIDTHCONST 28
40: #define CXCHARS 37
41: #define CYCHARS 13
42:
43: HAB hab;
44: HDC hdcLocal; /* Local used for button bitmap */
45: HPS hpsLocal;
46: HDC hdcSqr; /* Sqr used for square-root bitmap */
47: HPS hpsSqr;
48: HBITMAP hbmLocal, hbmSqr;
49: HMQ hmqCalc;
50: HWND hwndCalc, hwndMenu;
51: HWND hwndCalcFrame;
52: HPOINTER hptrFinger;
53:
54: DEVOPENSTRUC dop = /* used by DevOpenDC */
55: {
56: NULL, "DISPLAY", NULL, NULL, NULL, NULL, NULL, NULL, NULL
57: };
58:
59: static char keys[25] = /* keyboard keys */
60: {
61: '\271', '0', '.', '\261', '+', '=',
62: '\272', '1', '2', '3', '-', 'c',
63: '\273', '4', '5', '6', '*', '%',
64: '\274', '7', '8', '9', '/', 'q',
65: NULL
66: };
67:
68: static char dkeys[25] = /* 4th key is plusminus */
69: {
70: ' ', '0', '.', '+', '+', '=',
71: ' ', '1', '2', '3', '-', 'C',
72: ' ', '4', '5', '6', '*', '%',
73: ' ', '7', '8', '9', '/', ' ',
74: NULL
75: };
76:
77: /************* PROCEDURE DECLARATIONS */
78:
79: MPARAM EXPENTRY AboutDlgProc(HWND, USHORT, MPARAM, MPARAM);
80: BOOL CalcInit(VOID);
81: VOID CalcPaint( HWND, HPS);
82: extern BOOL CalcTextOut( HPS, INT, INT, PCH, INT);
83: MRESULT EXPENTRY CalcWndProc(HWND, USHORT, MPARAM, MPARAM);
84: VOID cdecl main(VOID);
85: VOID DataXCopy( VOID);
86: VOID DataXPaste( VOID);
87: VOID DrawNumbers( HPS);
88: VOID Evaluate(BYTE);
89: BOOL FlashSqr( HPS, NPWPOINT);
90: VOID FlipKey(HPS, int, int);
91: VOID FrameKey( HPS, INT, INT);
92: VOID InitCalc( VOID);
93: BOOL InterpretChar( CHAR);
94: VOID ProcessKey(HWND, WPOINT *);
95: BOOL PSInit( VOID);
96: char Translate(WPOINT *);
97: VOID UpdateDisplay( VOID);
98:
99:
100:
101: /********************************************************************
102: Write the appropriate number or error string to the display area
103: and mark memory-in-use if appropriate.
104: */
105:
106: VOID UpdateDisplay()
107: {
108: int len, size;
109: HPS hps;
110: RECTL rcl;
111:
112: len = strlen(szreg1);
113: size = ((szreg1[0] == '-') ? 10 : 9); /* check for leading minus sign */
114: if (strchr(szreg1, '.'))
115: size++;
116: hps = WinGetPS(hwndCalc);
117:
118: rcl.xLeft = (6 * charwidth);
119: rcl.yBottom = 1050 * charheight / 100;
120: rcl.xRight = rcl.xLeft + (12 * charwidth);
121: rcl.yTop = rcl.yBottom + charheight;
122: WinFillRect(hps, &rcl, CLR_WHITE); /* paint display area white */
123:
124: if (fError) /* display error or number */
125: {
126: CalcTextOut( hps, 17 * charwidth - charwidth * 5,
127: 1050 * charheight / 100,
128: (PSZ)"Error", 5);
129: }
130: else
131: {
132: CalcTextOut(hps, 18 * charwidth - (charwidth * min(len,size)),
133: 1050 * charheight / 100, (PCH)szreg1, min(len, size));
134: }
135:
136: if (fValueInMemory) /* little black square shows mem use */
137: {
138: rcl.xLeft = (6 * charwidth);
139: rcl.yBottom = 1050 * charheight / 100;
140: rcl.xRight = rcl.xLeft + (charwidth / 2);
141: rcl.yTop = rcl.yBottom + (charheight / 2);
142: WinFillRect(hps, &rcl, CLR_BLACK);
143: }
144: WinReleasePS(hps);
145: }
146:
147:
148: /**********************************************************************
149: Display helpful info
150: */
151:
152: MPARAM EXPENTRY AboutDlgProc(hwnd, msg, mp1, mp2)
153: HWND hwnd;
154: USHORT msg;
155: MPARAM mp1;
156: MPARAM mp2;
157: {
158: if (msg == WM_COMMAND)
159: {
160: WinDismissDlg(hwnd, TRUE);
161: return(MPFROMSHORT(TRUE));
162: }
163: else return(WinDefDlgProc(hwnd, msg, mp1, mp2));
164: }
165:
166:
167: /**********************************************************************
168: General initialization
169: */
170:
171: BOOL CalcInit()
172: {
173: hab = WinInitialize(0);
174:
175: hmqCalc = WinCreateMsgQueue( hab, 0);
176:
177: if (!WinRegisterClass( hab, szCalcClass, CalcWndProc, CS_SIZEREDRAW, 0))
178: return(FALSE);
179:
180: hptrFinger = WinLoadPointer(HWND_DESKTOP, (HMODULE)NULL, IDP_FINGER);
181:
182: WinLoadString(NULL, NULL, 1, 30, (PSZ)szTitle);
183: WinLoadString(NULL, NULL, 2, 20, (PSZ)szErrorString);
184: WinLoadString(NULL, NULL, 3, 2, (PSZ)szPlusMinus);
185:
186: InitCalc(); /* arithmetic initialization */
187:
188: return(TRUE);
189: }
190:
191: /**********************************************************************
192: main procedure
193: */
194:
195: VOID cdecl main()
196: {
197: QMSG qmsg;
198: ULONG ctlData;
199:
200: if (!CalcInit()) { /* general initialization */
201: WinAlarm(HWND_DESKTOP, 0xffff);
202: goto exit;
203: }
204:
205: if (!PSInit()) { /* presentation spaces & bitmaps */
206: WinAlarm(HWND_DESKTOP, 0xffff);
207: goto exit;
208: }
209:
210: ctlData = FCF_TITLEBAR | FCF_MINBUTTON | FCF_SYSMENU | FCF_MENU;
211: hwndCalcFrame = WinCreateStdWindow(HWND_DESKTOP,
212: FS_ACCELTABLE | FS_BORDER | FS_ICON,
213: &ctlData, szCalcClass, szTitle, 0L,
214: (HMODULE)NULL, IDR_CALC,
215: (HWND FAR *)&hwndCalc);
216:
217: WinSetWindowPos(hwndCalcFrame, (HWND)NULL, 2, 2, CXCHARS * charwidth,
218: CYCHARS * charheight +
219: (SHORT)WinQuerySysValue(HWND_DESKTOP, SV_CYTITLEBAR) +
220: (SHORT)WinQuerySysValue(HWND_DESKTOP, SV_CYMENU),
221: SWP_MOVE | SWP_SIZE);
222: WinShowWindow(hwndCalcFrame, TRUE);
223: WinSetFocus(HWND_DESKTOP, hwndCalc);
224:
225: while (WinGetMsg( hab, (PQMSG)&qmsg, NULL, 0, 0))
226: WinDispatchMsg( hab, (PQMSG)&qmsg);
227:
228: exit: /* clean up */
229: if (hdcSqr) /* square-root bitmap */
230: {
231: GpiDestroyPS( hpsSqr);
232: if (hbmSqr)
233: GpiDeleteBitmap( hbmSqr);
234: }
235:
236: if (hdcLocal) /* keypad button */
237: {
238: GpiDestroyPS( hpsLocal);
239: if (hbmLocal)
240: GpiDeleteBitmap( hbmLocal);
241: }
242:
243: WinDestroyWindow(hwndCalcFrame);
244:
245: WinDestroyMsgQueue(hmqCalc);
246: WinTerminate(hab);
247:
248: DosExit(EXIT_PROCESS, 0); /* exit without error */
249: }
250:
251:
252: /*************************************************************************
253: Calc Window Procedure
254: */
255:
256: MRESULT EXPENTRY CalcWndProc(hwnd, msg, mp1, mp2)
257: HWND hwnd;
258: USHORT msg;
259: MPARAM mp1;
260: MPARAM mp2;
261: {
262: HPS hps;
263: RECTL rclPaint;
264: WPOINT pt;
265: BOOL fClip;
266: USHORT fi;
267:
268: switch (msg)
269: {
270: case WM_CREATE:
271: /* Set up this global first thing in case we need it elsewhere */
272: hwndCalc = hwnd;
273: break;
274:
275: case WM_DESTROY:
276: WinDestroyPointer(hptrFinger);
277: GpiDestroyPS( hpsSqr);
278: GpiDeleteBitmap( hbmSqr);
279: GpiDestroyPS( hpsLocal);
280: GpiDeleteBitmap( hbmLocal);
281: break;
282:
283: case WM_INITMENU:
284: fClip = FALSE;
285: if (WinOpenClipbrd(NULL))
286: {
287: fClip = WinQueryClipbrdFmtInfo(NULL, CF_TEXT, (USHORT FAR *)&fi);
288: WinCloseClipbrd(NULL);
289: }
290: WinSendMsg((HWND)mp2, MM_SETITEMATTR,
291: (MPARAM) MAKELONG(CMD_PASTE, TRUE),
292: (MPARAM) MAKELONG(MIA_DISABLED, fClip ? 0 : MIA_DISABLED));
293: break;
294:
295: case WM_PAINT:
296: hps = WinBeginPaint(hwnd, NULL, &rclPaint);
297: CalcPaint( hwnd, hps); /* re-draw calculator */
298: WinEndPaint(hps);
299: break;
300:
301: case WM_COMMAND:
302: if (fError)
303: break;
304: switch(LOUSHORT(mp1))
305: {
306: case CMD_COPY:
307: DataXCopy(); /* copy to clipboard */
308: break;
309: case CMD_PASTE:
310: DataXPaste(); /* paste from clipboard */
311: break;
312: case CMD_EXIT:
313: WinPostMsg(hwndCalcFrame, WM_QUIT, 0L, 0L);
314: break;
315: case CMD_ABOUT:
316: WinDlgBox(HWND_DESKTOP, hwndCalcFrame, (PFNWP)AboutDlgProc, NULL,
317: 1, (PSZ)NULL);
318: break;
319: }
320: break;
321:
322: case WM_MOUSEMOVE:
323: WinSetPointer(HWND_DESKTOP, hptrFinger);
324: break;
325:
326: case WM_BUTTON1DOWN:
327: pt.x = LOUSHORT(mp1);
328: pt.y = HIUSHORT(mp1);
329: ProcessKey(hwndCalc, &pt);
330: goto dwp;
331: break;
332:
333: case WM_CHAR:
334: if (SHORT1FROMMP(mp1) & KC_KEYUP)
335: {
336: if (CHAR4FROMMP(mp1) == mScan)
337: fMDown = FALSE; /* 'm' key went up */
338: }
339: else if (SHORT1FROMMP(mp1) & KC_CHAR)
340: {
341: if (InterpretChar(CHAR1FROMMP(mp2)))
342: UpdateDisplay();
343: else if ((CHAR1FROMMP(mp2)=='m') || (CHAR1FROMMP(mp2)=='M'))
344: {
345: mScan = CHAR4FROMMP(mp1); /* save 'm' key scan code */
346: fMDown = TRUE; /* 'm' key went down */
347: }
348: }
349: break;
350:
351: case WM_ACTIVATE:
352: if (HIUSHORT(mp1))
353: WinSetFocus(HWND_DESKTOP, hwndCalc);
354: break;
355:
356: case WM_SETFOCUS:
357: if ((HWNDFROMMP(mp1)==hwndCalc) && !mp2);
358: fMDown = FALSE; /* since we are losing focus */
359: break;
360:
361: dwp:
362: default:
363: return(WinDefWindowProc(hwnd, msg, mp1, mp2));
364: break;
365: }
366: return(0L);
367: }
368:
369:
370: /*************************************************************************
371: translate & interpret keys (ie. locate in logical keyboard)
372: */
373:
374: BOOL InterpretChar(ch)
375: register CHAR ch;
376: {
377: BOOL fDone;
378: CHAR *chstep;
379: register int n;
380: HPS hps;
381:
382: fDone = FALSE;
383: chstep = keys;
384: switch (ch)
385: {
386: case 'n':
387: ch = szPlusMinus[0];
388: break;
389: case 27: /* xlate Escape into 'c' */
390: ch = 'c';
391: break;
392: case '\r': /* xlate Enter into '=' */
393: ch = '=';
394: break;
395: }
396:
397: if (fMDown) /* Do memory keys */
398: {
399: switch (ch)
400: {
401: case 'c':
402: case 'C':
403: ch = '\274';
404: break;
405: case 'r':
406: case 'R':
407: ch = '\273';
408: break;
409: case '+':
410: ch = '\272';
411: break;
412: case '-':
413: ch = '\271';
414: break;
415: }
416: }
417:
418: while (!fDone && *chstep)
419: {
420: if (*chstep++ == ch)
421: fDone = TRUE; /* char found in logical keyboard */
422: }
423: if (fDone)
424: {
425: lastkey = currkey;
426: n = chstep - keys - 1;
427: hps = WinGetPS(hwndCalc);
428: FlipKey(hps, n/6, n%6);
429: WinReleasePS(hps);
430: Evaluate(keys[n]);
431: }
432: return (fDone);
433: }
434:
435:
436: /*************************************************************************
437: briefly reverse the shading on one of the keys
438: */
439:
440: VOID FlipKey(hps, i, j)
441: HPS hps;
442: int i,j;
443: {
444: RECTL rcl;
445:
446: rcl.xLeft = (j * 6 * charwidth) + (14 * charwidth / 10);
447: rcl.yBottom = (165 * charheight / 100) + (2 * i * charheight);
448: rcl.xRight = rcl.xLeft + (11 * charwidth / 3);
449: rcl.yTop = rcl.yBottom + (7 * charheight / 4);
450: WinInvertRect(hps, &rcl);
451: DosSleep(50L);
452: WinInvertRect(hps, &rcl);
453: }
454:
455:
456: /*************************************************************************
457: compute whether a point is over a button and flash the button if so
458: */
459:
460: BOOL FlashSqr(hps, ppt)
461: HPS hps;
462: NPWPOINT ppt;
463: {
464: register int i;
465: register int j;
466: BOOL fDone;
467:
468: /* find x range */
469: fDone = FALSE;
470: j = 0;
471: i = 3;
472: while (!fDone && j<6)
473: {
474: if (ppt->x < (j * 6 * charwidth) + (14 * charwidth / 10) +
475: (11*charwidth/3))
476: {
477: if (ppt->x > (j * 6 * charwidth) + (14 * charwidth / 10))
478: fDone = TRUE;
479: else
480: return FALSE;
481: }
482: else
483: j++;
484: }
485: if (!fDone)
486: return FALSE;
487: fDone = FALSE;
488: while (!fDone && i >= 0)
489: {
490: if (ppt->y > ((165 * charheight / 100) + (2 * i * charheight)))
491: {
492: if (ppt->y < ((165 * charheight / 100) + (2 * i * charheight) +
493: (7 * charheight / 4)))
494: fDone = TRUE;
495: else
496: return FALSE;
497: }
498: else
499: i--;
500: }
501: if (!fDone)
502: return FALSE;
503: ppt->x = i;
504: ppt->y = j;
505: FlipKey(hps, i, j);
506: return TRUE;
507: }
508:
509:
510: /*************************************************************************
511: which key is point on?
512: */
513:
514: char Translate(ppt)
515: WPOINT *ppt;
516: {
517: register int offset;
518: register char result;
519:
520: offset = ppt->x * 6 + ppt->y;
521: result = keys[offset];
522: return result;
523: }
524:
525:
526: /*************************************************************************
527: invoke flashing, point-to-key translation, and result-display update
528: */
529:
530: VOID ProcessKey(hwnd, ppt)
531: HWND hwnd;
532: WPOINT *ppt;
533: {
534: HPS hps;
535: register BOOL fFlashed;
536:
537: hps = WinGetPS(hwnd);
538: lastkey = currkey;
539: fFlashed = FlashSqr(hps, ppt);
540: WinReleasePS(hps);
541:
542: if (fFlashed)
543: Evaluate((BYTE)Translate(ppt));
544: UpdateDisplay();
545: }
546:
547:
548: /*************************************************************************
549: draw a blank key
550: */
551:
552: VOID FrameKey(hps, i, j)
553: HPS hps;
554: int i, j;
555: {
556: POINTL aptl[3];
557:
558: aptl[0].x = (j * 6 * charwidth) + (14 * charwidth / 10);
559: aptl[0].y = (165 * charheight / 100) + (2 * i * charheight);
560: aptl[1].x = (11 * charwidth / 3) + (aptl[0].x);
561: aptl[1].y = (7 * charheight / 4) + (aptl[0].y);
562: aptl[2].x = 0;
563: aptl[2].y = 0;
564: GpiBitBlt( hps, hpsLocal, 3L, aptl, ROP_SRCCOPY, BBO_IGNORE);
565: }
566:
567:
568: /*************************************************************************
569: draw the keys and fill in numbers
570: */
571:
572: VOID DrawNumbers(hps)
573: HPS hps;
574: {
575: register int i,j;
576:
577: /* Draw the keys and fill in the numbers we can */
578: for (i = 0; i < 4; i++)
579: {
580: for (j = 0; j < 6; j++)
581: {
582: FrameKey(hps, i, j);
583: CalcTextOut(hps,
584: (j * 6 * charwidth) + (WIDTHCONST * charwidth / 10),
585: (charheight) + ( i * 2 * charheight) + (charheight),
586: (PSZ)((char *)dkeys + (i * 6) + j), 1);
587: }
588: }
589: }
590:
591:
592: /*************************************************************************
593: redraw the whole calculator
594: */
595:
596: VOID CalcPaint( hwnd, hps)
597: HWND hwnd;
598: HPS hps;
599: {
600: RECTL rclDst;
601: CHARBUNDLE cbnd;
602: register int x1, y1;
603:
604: WinQueryWindowRect( hwnd, &rclDst);
605: WinFillRect( hps, &rclDst, CLR_GREEN);
606:
607: DrawNumbers(hps);
608: CalcTextOut(hps, x1 = (11 * charwidth / 5) + 1, y1 = 2 * charheight,
609: (PSZ)"M-", 2);
610: CalcTextOut(hps, x1, y1 + 2 * charheight, (PSZ)"M+", 2);
611: CalcTextOut(hps, x1, y1 + 4 * charheight, (PSZ)"MR", 2);
612: CalcTextOut(hps, x1, y1 + 6 * charheight, (PSZ)"MC", 2);
613:
614: /* Draw the minus of the plus/minus button */
615: cbnd.usBackMixMode = FM_LEAVEALONE;
616: GpiSetAttrs( hps, PRIM_CHAR, CBB_BACK_MIX_MODE, 0L, &cbnd);
617: x1 = (3 * 6 * charwidth) + (WIDTHCONST * charwidth / 10);
618: CalcTextOut( hps, x1, y1 - charheight / 3, (PSZ)"-", 1);
619:
620: /* Draw the square root bitmap */
621: rclDst.xLeft = 160 * charwidth / 5;
622: rclDst.yBottom = 31 * charheight / 4;
623: rclDst.xRight = rclDst.xLeft + 2 * charwidth;
624: rclDst.yTop = rclDst.yBottom + (3 * charheight / 2);
625: WinDrawBitmap( hps, hbmSqr, NULL, (PPOINTL)&rclDst, CLR_WHITE, CLR_BLACK, DBM_STRETCH);
626:
627: UpdateDisplay();
628: }
629:
630:
631: /*************************************************************************
632: initialize the bitmaps for a blank key and for the square-root sign
633: */
634: BOOL PSInit()
635: {
636: HPS hps;
637: FONTMETRICS fntmet;
638: POINTL ptl;
639: SIZEL sizl;
640: BITMAPINFOHEADER bmp;
641: POINTL aptl[4];
642: LONG alCaps[2];
643:
644: hps = WinGetPS( HWND_DESKTOP);
645: GpiQueryFontMetrics( hps, (LONG)sizeof(FONTMETRICS), &fntmet);
646: charheight = (SHORT)(fntmet.lEmHeight);
647: charwidth = (SHORT)(fntmet.lAveCharWidth);
648: WinReleasePS( hps);
649:
650: hdcLocal = DevOpenDC( hab, OD_MEMORY, "*", 3L, (PDEVOPENDATA)&dop, NULL);
651: if( !hdcLocal)
652: return(FALSE);
653:
654: sizl.cx = sizl.cy = 0L;
655: hpsLocal = GpiCreatePS( hab, hdcLocal, &sizl, PU_PELS | GPIT_MICRO
656: | GPIA_ASSOC);
657:
658: hdcSqr = DevOpenDC( hab, OD_MEMORY, "*", 3L, (PDEVOPENDATA)&dop, NULL);
659: if( !hdcSqr)
660: return(FALSE);
661:
662: sizl.cx = sizl.cy = 0L;
663: hpsSqr = GpiCreatePS( hab, hdcSqr, &sizl, PU_PELS | GPIT_MICRO
664: | GPIA_ASSOC);
665: hbmSqr = GpiLoadBitmap( hpsSqr, NULL, IDB_SQR, 0L, 0L);
666:
667: bmp.cx = 11 * charwidth / 3;
668: bmp.cy = charheight * 2;
669: DevQueryCaps( hdcLocal, CAPS_COLOR_PLANES, 2L, alCaps);
670: bmp.cPlanes = (USHORT)alCaps[0];
671: bmp.cBitCount = (USHORT)alCaps[1];
672: hbmLocal = GpiCreateBitmap( hpsLocal, &bmp, 0L, NULL, NULL);
673: if( !hbmLocal )
674: return(FALSE);
675: GpiSetBitmap( hpsLocal, hbmLocal);
676:
677: /* Fill in the DC with CALC's background color */
678: aptl[0].x = aptl[0].y = 0;
679: aptl[1].x = 11 * charwidth / 3;
680: aptl[1].y = 7 * charheight / 4;
681: aptl[2].x = aptl[2].y = 0;
682: aptl[3].x = aptl[1].x;
683: aptl[3].y = aptl[1].y;
684: GpiSetColor( hpsLocal, CLR_GREEN);
685: GpiBitBlt( hpsLocal, NULL, 2L, aptl, ROP_PATCOPY, BBO_IGNORE);
686:
687: /* Draw the rounded rect in the local PS */
688: ptl.x = 0;
689: ptl.y = 0;
690: GpiSetCurrentPosition( hpsLocal, &ptl);
691: ptl.x = (11 * charwidth / 3) - 1;
692: ptl.y = (7 * charheight / 4) - 1;
693: GpiSetColor( hpsLocal, CLR_WHITE);
694: GpiBox( hpsLocal, DRO_FILL, &ptl, (LONG)charwidth,
695: (LONG)(charheight/2));
696: ptl.x = 0;
697: ptl.y = 0;
698: GpiSetCurrentPosition( hpsLocal, &ptl);
699: ptl.x = (11 * charwidth / 3) - 1;
700: ptl.y = (7 * charheight / 4) - 1;
701: GpiSetColor( hpsLocal, CLR_BLACK);
702: GpiBox( hpsLocal, DRO_OUTLINE, &ptl, (LONG)charwidth,
703: (LONG)(charheight/2));
704: return( TRUE);
705: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.