• R/O
  • SSH
  • HTTPS

amb: Commit


Commit MetaInfo

Revision199 (tree)
Time2020-12-23 23:15:49
Authormateuszviste

Log Message

freezed version 20201223 to tags

Change Summary

Incremental Difference

--- amb/tags/amb-20201223/ptui/README.md (nonexistent)
+++ amb/tags/amb-20201223/ptui/README.md (revision 199)
@@ -0,0 +1,73 @@
1+# PTUI
2+
3+PTUI stands for "Portable Terminal UI". It is an ANSI C library that provides
4+simple terminal-handling routines that can operate on Linux, Windows and DOS.
5+
6+The prototype declarations (ptui.h) are common to all platforms, but source
7+files are not - each platform needs to build and link to the proper module:
8+
9+ptui-curse.c ncurses-based backend (Linux and Windows, requires libncurses)
10+ptui-dj.c DJGPP driver (DOS, protected mode)
11+ptui-dos.c real-time DOS driver (uses direct MDA/VGA hardware calls)
12+
13+Project's homepage: https://github.com/mateuszviste/ptui
14+
15+
16+# How to use
17+
18+Simple example:
19+
20+ ptui_init(0);
21+ ptui_putchar('H', 0x17, 0, 0);
22+ ptui_putchar('e', 0x17, 1, 0);
23+ ptui_putchar('l', 0x17, 2, 0);
24+ ptui_putchar('l', 0x17, 3, 0);
25+ ptui_putchar('o', 0x17, 4, 0);
26+ ptui_getkey(); /* wait for a key */
27+ ptui_close();
28+
29+Note about colors: each time a character needs to be output to the terminal, a
30+color attribute must be passed along. A color attribute is a single byte value
31+that encodes foreground color in its low nibble and background color in its
32+high nibble. For instance 0x17 would be white text on blue background, and
33+0x0e translates as yellow text on black background.
34+
35+
36+# Dependencies
37+
38+On non-DOS platforms, this library requires ncursesw. One needs to ensure that
39+his program is linked to ncursesw (possibly tinfo, too, depending on your
40+distribution and packaging). Typically, this would be something like that:
41+
42+ cc hello.c ptui-ncurses.c -lncursesw -ltinfo
43+
44+
45+# Contact
46+
47+If you'd like to get in contact with this library's author, you will find
48+instructions how to do so on his personal homepage: http://mateusz.viste.fr
49+
50+
51+# License
52+
53+PTUI is published under the MIT license.
54+
55+Copyright (C) 2013-2020 Mateusz Viste
56+
57+Permission is hereby granted, free of charge, to any person obtaining a copy
58+of this software and associated documentation files (the "Software"), to deal
59+in the Software without restriction, including without limitation the rights
60+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
61+copies of the Software, and to permit persons to whom the Software is
62+furnished to do so, subject to the following conditions:
63+
64+The above copyright notice and this permission notice shall be included in all
65+copies or substantial portions of the Software.
66+
67+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
73+SOFTWARE.
--- amb/tags/amb-20201223/ptui/ptui-dj.c (nonexistent)
+++ amb/tags/amb-20201223/ptui/ptui-dj.c (revision 199)
@@ -0,0 +1,104 @@
1+/*
2+ * PTUI stands for "Portable Terminal UI". It is an ANSI C library that
3+ * provides simple terminal-handling routines that can operate on Linux,
4+ * Windows and DOS.
5+ *
6+ * Copyright (C) 2013-2020 Mateusz Viste
7+ *
8+ * Permission is hereby granted, free of charge, to any person obtaining a
9+ * copy of this software and associated documentation files (the "Software"),
10+ * to deal in the Software without restriction, including without limitation
11+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12+ * and/or sell copies of the Software, and to permit persons to whom the
13+ * Software is furnished to do so, subject to the following conditions:
14+ *
15+ * The above copyright notice and this permission notice shall be included in
16+ * all copies or substantial portions of the Software.
17+ *
18+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24+ * DEALINGS IN THE SOFTWARE.
25+ */
26+
27+#include <conio.h>
28+#include <pc.h> /* ScreenRows() */
29+
30+#include "ptui.h" /* include self for control */
31+
32+
33+int ptui_hascolor(void) {
34+ if (ScreenMode() == 7) return(0);
35+ return(1);
36+}
37+
38+
39+/* inits the UI subsystem */
40+int ptui_init(int flags) {
41+ return(0);
42+}
43+
44+
45+void ptui_close(void) {
46+}
47+
48+
49+int ptui_getrowcount(void) {
50+ return(ScreenRows());
51+}
52+
53+
54+int ptui_getcolcount(void) {
55+ return(ScreenCols());
56+}
57+
58+
59+void ptui_cls(void) {
60+ clrscr();
61+}
62+
63+
64+void ptui_puts(const char *str) {
65+ cprintf("%s\r\n", str);
66+}
67+
68+
69+void ptui_locate(int x, int y) {
70+ ScreenSetCursor(y, x);
71+}
72+
73+
74+void ptui_putchar(int c, int attr, int x, int y) {
75+ ScreenPutChar(c, attr, x, y);
76+}
77+
78+void ptui_putchar_rep(int c, int attr, int x, int y, int r) {
79+ while (r--) ScreenPutChar(c, attr, x++, y);
80+}
81+
82+int ptui_getkey(void) {
83+ return(getkey());
84+}
85+
86+
87+int ptui_kbhit(void) {
88+ return(kbhit());
89+}
90+
91+
92+void ptui_cursor_show(void) {
93+ _setcursortype(_NORMALCURSOR);
94+}
95+
96+
97+void ptui_cursor_hide(void) {
98+ _setcursortype(_NOCURSOR);
99+}
100+
101+
102+void ptui_refresh(void) {
103+ /* nothing here, we draw directly to video memory already */
104+}
--- amb/tags/amb-20201223/ptui/ptui-dos.c (nonexistent)
+++ amb/tags/amb-20201223/ptui/ptui-dos.c (revision 199)
@@ -0,0 +1,259 @@
1+/*
2+ * PTUI stands for "Portable Terminal UI". It is an ANSI C library that
3+ * provides simple terminal-handling routines that can operate on Linux,
4+ * Windows and DOS.
5+ *
6+ * Copyright (C) 2013-2020 Mateusz Viste
7+ *
8+ * Permission is hereby granted, free of charge, to any person obtaining a
9+ * copy of this software and associated documentation files (the "Software"),
10+ * to deal in the Software without restriction, including without limitation
11+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12+ * and/or sell copies of the Software, and to permit persons to whom the
13+ * Software is furnished to do so, subject to the following conditions:
14+ *
15+ * The above copyright notice and this permission notice shall be included in
16+ * all copies or substantial portions of the Software.
17+ *
18+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24+ * DEALINGS IN THE SOFTWARE.
25+ */
26+
27+#include <dos.h>
28+
29+#include "ptui.h" /* include self for control */
30+
31+static unsigned char far *vmem; /* video memory pointer (beginning of page 0) */
32+static int term_width = 0, term_height = 0;
33+static int cursor_start = 0, cursor_end = 0; /* remember the cursor's shape */
34+static unsigned short videomode = 0;
35+static int mousedetected, mousehidden;
36+static unsigned int lastmouse_x, lastmouse_y;
37+static int lastmouse_btn = -1;
38+
39+
40+/* returns 1 if color is supported, 0 otherwise */
41+int ptui_hascolor(void) {
42+ if (videomode == 7) return(0); /* MDA/herc mode */
43+ return(1);
44+}
45+
46+/* inits the UI subsystem */
47+int ptui_init(int flags) {
48+ union REGS regs;
49+ regs.h.ah = 0x0F; /* get current video mode */
50+ int86(0x10, &regs, &regs);
51+ videomode = regs.h.al;
52+ term_width = regs.h.ah; /* int10,F provides number of columns in AH */
53+ /* read screen length from BIOS at 0040:0084 */
54+ term_height = (*(unsigned char far *) MK_FP(0x40, 0x84)) + 1;
55+ if (term_height < 10) term_height = 25; /* assume 25 rows if weird value */
56+ /* select the correct VRAM address */
57+ if (videomode == 7) { /* MDA/HERC mode */
58+ vmem = MK_FP(0xB000, 0); /* B000:0000 video memory addess */
59+ } else {
60+ vmem = MK_FP(0xB800, 0); /* B800:0000 video memory address */
61+ }
62+ /* get cursor shape */
63+ regs.h.ah = 3;
64+ regs.h.bh = 0;
65+ int86(0x10, &regs, &regs);
66+ cursor_start = regs.h.ch;
67+ cursor_end = regs.h.cl;
68+ /* enable MOUSE? */
69+ mousedetected = 0;
70+ if (flags & PTUI_ENABLE_MOUSE) {
71+ union REGS r;
72+ /* TODO INT 33 might be unitialised on old machines: I should first check
73+ * that INT 33 is not pointing at 0000h:0000h */
74+ r.x.ax = 0;
75+ int86(0x33, &r, &r);
76+ if (r.x.ax == 0xffffu) {
77+ mousedetected = 1;
78+ mousehidden = 1;
79+ ptui_mouseshow(1); /* DOS MOUSE is hidden by default, make sure to make it visible */
80+ }
81+ }
82+ return(0);
83+}
84+
85+void ptui_close(void) {
86+ /* reset mouse driver if present */
87+ if (mousedetected) {
88+ union REGS r;
89+ r.x.ax = 0;
90+ int86(0x33, &r, &r);
91+ mousedetected = 0;
92+ }
93+}
94+
95+static void cursor_set(int startscanline, int endscanline) {
96+ union REGS regs;
97+ regs.h.ah = 0x01;
98+ regs.h.al = videomode; /* RBIL says some BIOSes require video mode in AL */
99+ regs.h.ch = startscanline;
100+ regs.h.cl = endscanline;
101+ int86(0x10, &regs, &regs);
102+}
103+
104+int ptui_getrowcount(void) {
105+ return(term_height);
106+}
107+
108+
109+int ptui_getcolcount(void) {
110+ return(term_width);
111+}
112+
113+
114+void ptui_cls(void) {
115+ union REGS regs;
116+ regs.x.ax = 0x0600; /* Scroll window up, entire window */
117+ regs.h.bh = 0x07; /* Attribute to write to screen */
118+ regs.h.bl = 0;
119+ regs.x.cx = 0x0000; /* Upper left */
120+ regs.h.dh = term_height - 1;
121+ regs.h.dl = term_width - 1; /* Lower right */
122+ int86(0x10, &regs, &regs);
123+ ptui_locate(0, 0);
124+}
125+
126+
127+void ptui_puts(const char *str) {
128+ union REGS regs;
129+ /* display the string one character at a time */
130+ while (*str != 0) {
131+ regs.h.ah = 0x02;
132+ regs.h.dl = *str;
133+ int86(0x21, &regs, &regs);
134+ str++;
135+ }
136+ /* write a CR/LF pair to screen */
137+ regs.h.ah = 0x02; /* DOS 1+ - WRITE CHARACTER TO STDOUT */
138+ regs.h.dl = '\r';
139+ int86(0x21, &regs, &regs);
140+ regs.h.ah = 0x02; /* DOS 1+ - WRITE CHARACTER TO STDOUT */
141+ regs.h.dl = '\n';
142+ int86(0x21, &regs, &regs);
143+}
144+
145+
146+void ptui_locate(int column, int row) {
147+ union REGS regs;
148+ regs.h.ah = 0x02;
149+ regs.h.bh = 0;
150+ regs.h.dh = row;
151+ regs.h.dl = column;
152+ int86(0x10, &regs, &regs);
153+}
154+
155+
156+void ptui_putchar(int c, int attr, int x, int y) {
157+ unsigned char far *p;
158+ p = vmem + ((y * term_width + x) << 1);
159+ *p++ = c;
160+ *p = attr;
161+}
162+
163+
164+void ptui_putchar_rep(int c, int attr, int x, int y, int r) {
165+ unsigned char far *p;
166+ p = vmem + ((y * term_width + x) << 1);
167+ while (r--) {
168+ *p++ = c;
169+ *p++ = attr;
170+ }
171+}
172+
173+
174+void ptui_mouseshow(int status) {
175+ union REGS r;
176+ if (mousedetected == 0) return;
177+ if ((status == 0) && (mousehidden == 1)) return; /* already hidden, do nothing */
178+ if ((status != 0) && (mousehidden == 0)) return; /* already shown, do nothing */
179+ if (status == 0) {
180+ r.x.ax = 2; /* hide mouse cursor */
181+ mousehidden = 1;
182+ } else {
183+ r.x.ax = 1; /* show mouse cursor */
184+ mousehidden = 0;
185+ }
186+ int86(0x33, &r, &r);
187+}
188+
189+
190+int ptui_getmouse(unsigned int *x, unsigned int *y) {
191+ int r = lastmouse_btn;
192+ if (r < 0) return(-1);
193+ *x = lastmouse_x;
194+ *y = lastmouse_y;
195+ lastmouse_btn = -1;
196+ return(r);
197+}
198+
199+
200+int ptui_getkey(void) {
201+ union REGS regs;
202+ /* if mouse support enabled, then do not block on key wait, instead
203+ * poll alternatively mouse and keyboard in a busy loop + INT 28h */
204+ if (mousedetected) {
205+ for (;;) {
206+ regs.x.ax = 6;
207+ regs.x.bx = 0; /* 0=look for left button ; 1=right button ; 2=middle button */
208+ int86(0x33, &regs, &regs);
209+ if (regs.x.bx > 0) {
210+ lastmouse_x = regs.x.cx / 8; /* coordinates returned by the mouse driver are relative */
211+ lastmouse_y = regs.x.dx / 8; /* to a "virtual graphic mode" where each character is 8x8 */
212+ lastmouse_btn = 0;
213+ return(PTUI_MOUSE); /* special "key" that means "mouse click" */
214+ }
215+ /* no mouse click - so maybe keyboard? */
216+ if (ptui_kbhit() != 0) break;
217+ /* neither keyb nor mouse event found - call the idle int and do again */
218+ int86(0x28, &regs, &regs);
219+ }
220+ }
221+ regs.h.ah = 0x08;
222+ int86(0x21, &regs, &regs);
223+ if (regs.h.al != 0) return(regs.h.al);
224+ /* extended key, read again */
225+ regs.h.ah = 0x08;
226+ int86(0x21, &regs, &regs);
227+ return(0x100 | regs.h.al);
228+}
229+
230+
231+int ptui_kbhit(void) {
232+ union REGS regs;
233+ regs.h.ah = 0x0b; /* DOS 1+ - GET STDIN STATUS */
234+ int86(0x21, &regs, &regs);
235+ return(regs.h.al);
236+}
237+
238+
239+void ptui_cursor_show(void) {
240+ if (cursor_start == 0) return;
241+ cursor_set(cursor_start, cursor_end); /* unhide the cursor */
242+}
243+
244+
245+void ptui_cursor_hide(void) {
246+ cursor_set(0x2F, 0x0E); /* hide the cursor */
247+ /* the 'start position' of cursor_set() is a bitfield:
248+ *
249+ * Bit(s) Description (Table 00013)
250+ * 7 should be zero
251+ * 6,5 cursor blink (00=normal, 01=invisible)
252+ * 4-0 topmost scan line containing cursor
253+ */
254+}
255+
256+
257+void ptui_refresh(void) {
258+ /* nothing here, we draw directly to video memory already */
259+}
--- amb/tags/amb-20201223/ptui/ptui-ncurses.c (nonexistent)
+++ amb/tags/amb-20201223/ptui/ptui-ncurses.c (revision 199)
@@ -0,0 +1,270 @@
1+/*
2+ * PTUI stands for "Portable Terminal UI". It is an ANSI C library that
3+ * provides simple terminal-handling routines that can operate on Linux,
4+ * Windows and DOS.
5+ *
6+ * Copyright (C) 2013-2020 Mateusz Viste
7+ *
8+ * Permission is hereby granted, free of charge, to any person obtaining a
9+ * copy of this software and associated documentation files (the "Software"),
10+ * to deal in the Software without restriction, including without limitation
11+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12+ * and/or sell copies of the Software, and to permit persons to whom the
13+ * Software is furnished to do so, subject to the following conditions:
14+ *
15+ * The above copyright notice and this permission notice shall be included in
16+ * all copies or substantial portions of the Software.
17+ *
18+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24+ * DEALINGS IN THE SOFTWARE.
25+ */
26+
27+
28+#define _XOPEN_SOURCE_EXTENDED
29+
30+#include <locale.h>
31+#include <ncursesw/curses.h>
32+#include <stdio.h> /* this one contains the NULL definition */
33+#include <string.h>
34+
35+#include "ptui.h" /* include self for control */
36+
37+
38+/* mouse-related global variables */
39+static int lastclick_btn = -1;
40+static unsigned short lastclick_x, lastclick_y;
41+
42+
43+static attr_t getorcreatecolor(int col) {
44+ static attr_t DOSPALETTE[256] = {0};
45+ static int lastcolid = 0;
46+ /* if color doesn't exist yet, create it */
47+ if (DOSPALETTE[col] == 0) {
48+ unsigned long DOSCOLORS[16] = { COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE, COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE };
49+ if (col & 0x80) { /* bright background */
50+ init_pair(++lastcolid, DOSCOLORS[col >> 4], DOSCOLORS[col & 0xf]);
51+ DOSPALETTE[col] = COLOR_PAIR(lastcolid) | WA_BOLD | WA_REVERSE;
52+ } else if (col & 0x08) { /* bright foreground */
53+ init_pair(++lastcolid, DOSCOLORS[col & 0x0f], DOSCOLORS[col >> 4]);
54+ DOSPALETTE[col] = COLOR_PAIR(lastcolid) | A_BOLD;
55+ } else { /* no bright nothing */
56+ init_pair(++lastcolid, DOSCOLORS[col & 0x0f], DOSCOLORS[col >> 4]);
57+ /* init_pair(col+1, COLOR_BLUE, COLOR_MAGENTA); */
58+ DOSPALETTE[col] = COLOR_PAIR(lastcolid) | A_NORMAL;
59+ }
60+ }
61+
62+ return(DOSPALETTE[col]);
63+}
64+
65+
66+/* returns 0 on monochrome terminals, 1 on color terminals */
67+int ptui_hascolor(void) {
68+ if (has_colors() == TRUE) return(1);
69+ return(0);
70+}
71+
72+
73+/* inits the UI subsystem */
74+int ptui_init(int flags) {
75+ setlocale(LC_ALL, "");
76+ if (initscr() == NULL) return(-1); /* returns a ptr to stdscr on success */
77+ start_color();
78+ raw();
79+ noecho();
80+ keypad(stdscr, TRUE); /* capture arrow keys */
81+ timeout(100); /* getch blocks for 50ms max */
82+ set_escdelay(50); /* ESC should wait for 50ms max */
83+ nonl(); /* allow ncurses to detect KEY_ENTER */
84+ /* enable MOUSE? */
85+ if (flags & PTUI_ENABLE_MOUSE) {
86+ mousemask(BUTTON1_RELEASED, NULL);
87+ }
88+ return(0);
89+}
90+
91+
92+void ptui_close(void) {
93+ endwin();
94+}
95+
96+
97+int ptui_getrowcount(void) {
98+ return(getmaxy(stdscr));
99+}
100+
101+
102+int ptui_getcolcount(void) {
103+ return(getmaxx(stdscr));
104+}
105+
106+
107+void ptui_cls(void) {
108+ int x, y;
109+ int maxrows, maxcols;
110+ clear();
111+ getmaxyx(stdscr, maxrows, maxcols);
112+ attron(0);
113+ for (y = 0; y < maxrows; y++) {
114+ for (x = 0; x < maxcols; x++) {
115+ mvaddch(y, x, ' ');
116+ }
117+ }
118+ attroff(0);
119+ /* bkgd(COLOR_PAIR(colattr)); */
120+ move(0,0);
121+ ptui_refresh();
122+}
123+
124+
125+void ptui_puts(const char *str) {
126+ puts(str);
127+}
128+
129+
130+void ptui_locate(int x, int y) {
131+ move(y, x);
132+ ptui_refresh();
133+}
134+
135+
136+void ptui_putchar(int wchar, int attr, int x, int y) {
137+ int oldx, oldy;
138+ cchar_t t;
139+
140+ memset(&t, 0, sizeof(t));
141+
142+ /* remember cursor position to restore it afterwards */
143+ getyx(stdscr, oldy, oldx);
144+
145+ t.attr = getorcreatecolor(attr);
146+ t.chars[0] = wchar;
147+ mvadd_wch(y, x, &t);
148+
149+ /* restore cursor to its initial location */
150+ move(oldy, oldx);
151+}
152+
153+
154+void ptui_putchar_rep(int wchar, int attr, int x, int y, int r) {
155+ int oldx, oldy;
156+ cchar_t t;
157+
158+ memset(&t, 0, sizeof(t));
159+
160+ /* remember cursor position to restore it afterwards */
161+ getyx(stdscr, oldy, oldx);
162+
163+ t.attr = getorcreatecolor(attr);
164+ t.chars[0] = wchar;
165+ while (r--) mvadd_wch(y, x++, &t);
166+
167+ /* restore cursor to its initial location */
168+ move(oldy, oldx);
169+}
170+
171+
172+int ptui_getmouse(unsigned int *x, unsigned *y) {
173+ int r = lastclick_btn;
174+ if (lastclick_btn < 0) return(-1);
175+ *x = lastclick_x;
176+ *y = lastclick_y;
177+ lastclick_btn = -1;
178+ return(r);
179+}
180+
181+
182+int ptui_getkey(void) {
183+ int res;
184+
185+ for (;;) {
186+ res = getch();
187+ if (res == KEY_MOUSE) {
188+ MEVENT event;
189+ if (getmouse(&event) == OK) {
190+ if (event.bstate & BUTTON1_RELEASED) {
191+ lastclick_btn = 0;
192+ lastclick_x = event.x;
193+ lastclick_y = event.y;
194+ return(PTUI_MOUSE);
195+ }
196+ }
197+ continue; /* ignore invalid mouse events */
198+ }
199+ if (res != ERR) break; /* ERR means "no input available yet" */
200+ }
201+
202+ /* either ESC or ALT+some key */
203+ if (res == 27) {
204+ res = getch();
205+ if (res == ERR) return(27);
206+ /* else this is an ALT+something combination */
207+ switch (res) {
208+ case 'f': return(0x121);
209+ case 'h': return(0x123);
210+ case 'j': return(0x124);
211+ case 's': return(0x11F);
212+ case 'u': return(0x116);
213+ }
214+ }
215+
216+ switch (res) {
217+ case KEY_LEFT: return(0x14B);
218+ case KEY_RIGHT: return(0x14D);
219+ case KEY_UP: return(0x148);
220+ case KEY_DOWN: return(0x150);
221+ case KEY_BACKSPACE: return(8);
222+ case KEY_ENTER: return(13);
223+ case KEY_PPAGE: return(0x149); /* PAGEUP */
224+ case KEY_NPAGE: return(0x151); /* PGDOWN */
225+ case KEY_HOME: return(0x147);
226+ case KEY_END: return(0x14F);
227+ case KEY_DC: return(0x153); /* DEL */
228+ case KEY_F(1): return(0x13b);
229+ case KEY_F(2): return(0x13c);
230+ case KEY_F(3): return(0x13d);
231+ case KEY_F(4): return(0x13e);
232+ case KEY_F(5): return(0x13f);
233+ case KEY_F(6): return(0x140);
234+ case KEY_F(7): return(0x141);
235+ case KEY_F(8): return(0x142);
236+ case KEY_F(9): return(0x143);
237+ case KEY_F(10): return(0x144);
238+ default: return(res); /* return the scancode as-is otherwise */
239+ }
240+}
241+
242+
243+int ptui_kbhit(void) {
244+ int tmp;
245+ timeout(0);
246+ tmp = getch();
247+ timeout(100);
248+ if (tmp == ERR) return(0);
249+ ungetch(tmp);
250+ return(1);
251+}
252+
253+
254+void ptui_mouseshow(int status) {
255+}
256+
257+
258+void ptui_cursor_show(void) {
259+ curs_set(1);
260+}
261+
262+
263+void ptui_cursor_hide(void) {
264+ curs_set(0);
265+}
266+
267+
268+void ptui_refresh(void) {
269+ refresh();
270+}
--- amb/tags/amb-20201223/ptui/ptui.h (nonexistent)
+++ amb/tags/amb-20201223/ptui/ptui.h (revision 199)
@@ -0,0 +1,97 @@
1+/*
2+ * PTUI stands for "Portable Terminal UI". It is an ANSI C library that
3+ * provides simple terminal-handling routines that can operate on Linux,
4+ * Windows and DOS.
5+ *
6+ * Copyright (C) 2013-2020 Mateusz Viste
7+ *
8+ * Permission is hereby granted, free of charge, to any person obtaining a
9+ * copy of this software and associated documentation files (the "Software"),
10+ * to deal in the Software without restriction, including without limitation
11+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12+ * and/or sell copies of the Software, and to permit persons to whom the
13+ * Software is furnished to do so, subject to the following conditions:
14+ *
15+ * The above copyright notice and this permission notice shall be included in
16+ * all copies or substantial portions of the Software.
17+ *
18+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24+ * DEALINGS IN THE SOFTWARE.
25+ */
26+
27+#ifndef ptui_h_sentinel
28+#define ptui_h_sentinel
29+
30+/* inits the UI subsystem, 0 on success, non-zero otherwise
31+ * flags may accept following (possibly OR-ed) flags:
32+ * PTUI_ENABLE_MOUSE - enable mouse support if available */
33+int ptui_init(int flags);
34+
35+/* returns 1 if terminal has color capability, 0 otherwise
36+ * this must be called only AFTER ptui_init() */
37+int ptui_hascolor(void);
38+
39+void ptui_close(void);
40+
41+/* returns the number of rows of current text mode */
42+int ptui_getrowcount(void);
43+
44+/* returns the number of columns of current text mode */
45+int ptui_getcolcount(void);
46+
47+/* clear the screen */
48+void ptui_cls(void);
49+
50+/* print a string on screen, and go to next line */
51+void ptui_puts(const char *str);
52+
53+/* Set the position (zero-based) of the cursor on screen */
54+void ptui_locate(int x, int y);
55+
56+/* Put a char directly on screen, without playing with the cursor. Coordinates are zero-based.
57+ * This may be a "wide" (unicode) codepoint on platforms that support unicode. */
58+void ptui_putchar(int c, int attr, int x, int y);
59+
60+/* same as ptui_putchar() but draws the character count times (be careful
61+ * about line overflow - count chars should never go out of screen!) */
62+void ptui_putchar_rep(int c, int attr, int x, int y, int count);
63+
64+/* waits for a key to be pressed and returns it. ALT+keys have 0x100 added to
65+ * them. this may also report a "PTUI_MOUSE" key in case of a mouse click,
66+ * in such case call ptui_getmouse() to fetch the details about last click) */
67+int ptui_getkey(void);
68+
69+/* returns 0 if no key is awaiting in the keyboard buffer, non-zero otherwise */
70+int ptui_kbhit(void);
71+
72+/* if mouse support is enabled, set mouse cursor to be:
73+ * status == 0 -> hidden
74+ * statis != 0 -> not hidden (default)
75+ * NOTE: this works only on the MSDOS platform */
76+void ptui_mouseshow(int status);
77+
78+/* fetches the coordinates of the last mouse click. returns -1 if nothing is
79+ * pending. */
80+int ptui_getmouse(unsigned int *x, unsigned int *y);
81+
82+/* makes the cursor visible */
83+void ptui_cursor_show(void);
84+
85+/* hides the cursor */
86+void ptui_cursor_hide(void);
87+
88+/* tell the UI library to render the screen (ignored on platforms that perform immediate rendering) */
89+void ptui_refresh(void);
90+
91+
92+/* some public definitions used by PTUI */
93+
94+#define PTUI_ENABLE_MOUSE 1 /* may be passed to ptui_init() */
95+#define PTUI_MOUSE 0x200 /* returned by ptui_getkey() to advertise a mouse event */
96+
97+#endif
--- amb/tags/amb-20201223/Makefile (nonexistent)
+++ amb/tags/amb-20201223/Makefile (revision 199)
@@ -0,0 +1,13 @@
1+#
2+# Linux Makefile
3+#
4+
5+CFLAGS = -Wall -Wextra -O3 -pedantic -std=c89 -DUNICODEOUTPUT
6+LDLIBS = -lncursesw -ltinfo
7+
8+all: amb
9+
10+amb: amb.o ptui/ptui-ncurses.o
11+
12+clean:
13+ rm -f *.o amb ptui/*.o
--- amb/tags/amb-20201223/amb.c (nonexistent)
+++ amb/tags/amb-20201223/amb.c (revision 199)
@@ -0,0 +1,1079 @@
1+/*
2+ * amb is a reader of AMB (Ancient Machine Book) files. MIT license.
3+ *
4+ * Copyright (C) 2020 Mateusz Viste
5+ *
6+ * Permission is hereby granted, free of charge, to any person obtaining a
7+ * copy of this software and associated documentation files (the "Software"),
8+ * to deal in the Software without restriction, including without limitation
9+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10+ * and/or sell copies of the Software, and to permit persons to whom the
11+ * Software is furnished to do so, subject to the following conditions:
12+ *
13+ * The above copyright notice and this permission notice shall be included in
14+ * all copies or substantial portions of the Software.
15+ *
16+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22+ * DEALINGS IN THE SOFTWARE.
23+ */
24+
25+/* program version */
26+#define PVER "20201223"
27+
28+#ifdef REALDOS
29+/* real-mode DOS specific (OpenWatcom and Turbo C) */
30+#include <dos.h>
31+#else
32+#include <stdio.h> /* DOS code does not rely on stdio */
33+#endif
34+
35+#include <ctype.h> /* tolower() */
36+#include <stdlib.h>
37+#include <string.h>
38+
39+#include "ptui/ptui.h"
40+
41+#ifndef VLINE /* vertical line used as left border of the screen */
42+ #ifdef UNICODEOUTPUT
43+ #define VLINE 0x2502 /* unicode BOX DRAWINGS LIGHT VERTICAL LINE (note that the "HEAVY" variant is not available in the default Windows XP console) */
44+ #else
45+ #define VLINE '|'
46+ #endif
47+#endif
48+
49+#ifndef ARRUP /* 'up' arrow */
50+ #ifdef UNICODEOUTPUT
51+ #define ARRUP 0x2191 /* unicode UPWARDS ARROW */
52+ #else
53+ #define ARRUP '^'
54+ #endif
55+#endif
56+
57+#ifndef ARRDW /* 'down' arrow */
58+ #ifdef UNICODEOUTPUT
59+ #define ARRDW 0x2193 /* unicode DOWNWARDS ARROW */
60+ #else
61+ #define ARRDW 'V'
62+ #endif
63+#endif
64+
65+/* how many history locations to keep in memory */
66+#ifndef HISTORYSIZE
67+ #define HISTORYSIZE 16
68+#endif
69+
70+/* uncomment to print on screen keycodes of unhandled key presses (debug only) */
71+/* #define PRINT_KEYCODES */
72+
73+
74+enum AMB_COL {
75+ AMB_TITLEBAR = 0,
76+ AMB_LBORDER = 1,
77+ AMB_RBORDER = 2,
78+ AMB_RBORDERC = 3,
79+ AMB_WARN = 4
80+};
81+
82+enum AMA_MODE {
83+ AMA_NORMAL = 6, /* values 0..5 are reserved for program's colors (see above) */
84+ AMA_BORING = 7,
85+ AMA_LINK = 8,
86+ AMA_HEAD = 9,
87+ AMA_LINK_CURSOROVER = 10,
88+ AMA_NOTICE = 11,
89+ AMA_HIDDEN = 12 /* this MUST be last! (used to size the colscheme array) */
90+};
91+
92+
93+/* actions returned by display_ama() */
94+enum ACTION {
95+ ACTION_NONE,
96+ ACTION_LOADPAGE,
97+ ACTION_PREV,
98+ ACTION_QUIT
99+};
100+
101+
102+#ifdef REALDOS
103+
104+typedef unsigned short FILEHANDLE;
105+#define FILEHANDLE_NONE 0xffffu
106+
107+static FILEHANDLE FOPEN(char *fname) {
108+ union REGS r;
109+ r.h.ah = 0x3D; /* DOS 2+ OPEN EXISTING FILE */
110+ r.h.al = 0;
111+ r.x.dx = FP_OFF(fname);
112+ int86(0x21, &r, &r);
113+ if (r.x.cflag != 0) return(FILEHANDLE_NONE);
114+ return(r.x.ax);
115+}
116+
117+static unsigned short FREAD(void far *buf, unsigned short readlen, FILEHANDLE handle) {
118+ union REGS r;
119+ struct SREGS sr;
120+ r.h.ah = 0x3f; /* DOS 2+ read from file or device */
121+ r.x.bx = handle;
122+ r.x.cx = readlen;
123+ sr.ds = FP_SEG(buf);
124+ r.x.dx = FP_OFF(buf);
125+ int86x(0x21, &r, &r, &sr);
126+ if (r.x.cflag != 0) return(0);
127+ return(r.x.ax);
128+}
129+
130+static void FCLOSE(FILEHANDLE h) {
131+ union REGS r;
132+ r.h.ah = 0x3E; /* DOS 2+ CLOSE FILE BY HANDLE */
133+ r.x.bx = h;
134+ int86(0x21, &r, &r);
135+}
136+
137+static void FSEEK(FILEHANDLE h, unsigned long offset) {
138+ union REGS r;
139+ r.h.ah = 0x42; /* DOS 2+ SET CURRENT FILE POSITION */
140+ r.h.al = 0; /* origin offset is start of file */
141+ r.x.bx = h;
142+ r.x.cx = (unsigned short)(offset >> 16); /* offset high word */
143+ r.x.dx = (unsigned short)(offset & 0xffffu); /* offset low word */
144+ int86(0x21, &r, &r);
145+}
146+
147+/* use the DOS call to avoid pulling in puts() and friends */
148+static void PUTS(char *s) {
149+ const char crlf[] = "\r\n$";
150+ union REGS r;
151+ int i;
152+ for (i = 0; s[i] != 0; i++); /* replace the NULL terminator by a $ */
153+ s[i] = '$';
154+ r.h.ah = 0x09;
155+ r.x.dx = FP_OFF(s);
156+ int86(0x21, &r, &r);
157+ s[i] = 0; /* replace back the NULL terminator */
158+ /* output a crl/lf now */
159+ r.h.ah = 0x09;
160+ r.x.dx = FP_OFF(crlf);
161+ int86(0x21, &r, &r);
162+}
163+
164+#else
165+
166+typedef FILE* FILEHANDLE;
167+#define FILEHANDLE_NONE NULL
168+#define FOPEN(x) fopen(x, "rb")
169+#define FREAD(b, l, h) fread(b, 1, l, h)
170+#define FCLOSE(x) fclose(x);
171+#define FSEEK(h, o) fseek(h, o, SEEK_SET)
172+#define PUTS(x) puts(x)
173+
174+#define far /* replace far with nothing */
175+
176+#endif
177+
178+/* unicode platforms use a map table to match codepage bytes into unicode glyphs */
179+#ifdef UNICODEOUTPUT
180+static unsigned short UNICODEMAP[256];
181+#endif
182+
183+
184+/* this is like strcpy(), but allows destination to be a far pointer */
185+static void mystrcpy(char far *dst, char *src) {
186+ int i;
187+ for (i = 0;; i++) {
188+ dst[i] = src[i];
189+ if (src[i] == 0) break;
190+ }
191+}
192+
193+
194+#ifdef REALDOS
195+/* allocates a 64K buffer and returns a ptr to it or NULL on failure */
196+static void far *ALLOC64K(void) {
197+ /* NOTE OpenWatcom's malloc() is unable to allocate a full 64K block due
198+ * to OpenWatcom's book-keeping overhead, that's why a direct DOS call is
199+ * performed instead */
200+ union REGS r;
201+ r.h.ah = 0x48;
202+ r.x.bx = 4096; /* request 4096 paragraphs (16 bytes each, ie. 64K total) */
203+ int86(0x21, &r, &r); /* CF should be clear, AX = segment of allocated block */
204+ if (r.x.cflag != 0) return(NULL);
205+ return(MK_FP(r.x.ax, 0));
206+}
207+#endif
208+
209+
210+/* returns the length of a link target (ie. content until a ':' character) */
211+static int linktargetlen(const char far *s) {
212+ int r;
213+ for (r = 0;; r++) {
214+ switch (s[r]) {
215+ case 0:
216+ case '\n':
217+ return(r);
218+ case ':':
219+ return(r+1);
220+ }
221+ }
222+}
223+
224+
225+/* returns number of bytes before next escape code (%) or end of line (\n or 0) */
226+static int bytesuntilesc(const char far *s) {
227+ int r;
228+ for (r = 0;; r++) {
229+ switch (s[r]) {
230+ case 0:
231+ case '\n':
232+ case '%':
233+ return(r);
234+ }
235+ }
236+}
237+
238+
239+/* display a warning. line2 is optional. */
240+static void warn(const char *line1, const char *line2, const unsigned char *colscheme) {
241+ int y = (ptui_getrowcount() / 2) - 2;
242+ int len;
243+ int x;
244+ int t;
245+
246+ len = strlen(line1);
247+ if (line2 != NULL) {
248+ int l2 = strlen(line2);
249+ if (l2 > len) len = l2;
250+ }
251+ /* draw a nice box */
252+ x = 38 - (len / 2);
253+ ptui_putchar_rep(' ', colscheme[AMB_WARN], x, y, len + 4);
254+ y++;
255+ ptui_putchar_rep(' ', colscheme[AMB_WARN], x, y, len + 4);
256+ /* print line 1 */
257+ for (t = 0; line1[t] != 0; t++) ptui_putchar(line1[t], colscheme[AMB_WARN], x + 2 + t, y);
258+ y++;
259+ /* print line 2, if defined */
260+ if (line2 != NULL) {
261+ ptui_putchar_rep(' ', colscheme[AMB_WARN], x, y, len + 4);
262+ for (t = 0; line2[t] != 0; t++) ptui_putchar(line2[t], colscheme[AMB_WARN], x + 2 + t, y);
263+ y++;
264+ }
265+ /* end of box */
266+ ptui_putchar_rep(' ', colscheme[AMB_WARN], x, y, len + 4);
267+}
268+
269+
270+/* display a line of AMA text, until first LF, CR or NULL terminator
271+ * returns amount of consumed bytes */
272+static unsigned short display_ama_line(const char far *l, int linenum, const unsigned char *colscheme, int cursorx, int cursory, const char far **linktarget) {
273+ enum AMA_MODE curmode = AMA_NORMAL;
274+ int waitformode = 0;
275+ int displayedlen = 0;
276+ int consumedbytes = 0;
277+
278+ for (;; consumedbytes++) {
279+ /* ignore \r */
280+ if (l[consumedbytes] == '\r') continue;
281+ /* too much data already */
282+ /* end of line? */
283+ if ((l[consumedbytes] == '\n') || (l[consumedbytes] == 0)) {
284+ /* print the rest of the on-screen line, if any left, using the normal mode color */
285+ if (displayedlen < 78) {
286+ ptui_putchar_rep(' ', colscheme[AMA_NORMAL], 1 + displayedlen, linenum, 78 - displayedlen);
287+ displayedlen = 78;
288+ }
289+ if (l[consumedbytes] == '\n') consumedbytes++; /* skip \n so next call gets next line */
290+ return(consumedbytes);
291+ }
292+ /* am I waiting for a mode (after %)? */
293+ if (waitformode) {
294+ waitformode = 0;
295+ switch (l[consumedbytes]) { /* what kind of control code did I get? */
296+ case 'h': /* heading */
297+ curmode = AMA_HEAD;
298+ break;
299+ case 'b': /* boring text */
300+ curmode = AMA_BORING;
301+ break;
302+ case 't': /* normal text */
303+ curmode = AMA_NORMAL;
304+ break;
305+ case '!': /* warning/notice */
306+ curmode = AMA_NOTICE;
307+ break;
308+ case 'l': /* link */
309+ curmode = AMA_LINK;
310+ {
311+ const char far *target = l + consumedbytes + 1;
312+ consumedbytes += linktargetlen(l + consumedbytes + 1);
313+ if ((cursory == linenum) && (cursorx > displayedlen)) {
314+ if (cursorx < displayedlen + bytesuntilesc(l + consumedbytes)) {
315+ curmode = AMA_LINK_CURSOROVER;
316+ *linktarget = target;
317+ }
318+ }
319+ }
320+ break;
321+ case '%': /* percent */
322+ if (displayedlen < 78) ptui_putchar('%', colscheme[curmode], 1 + displayedlen++, linenum);
323+ break;
324+ default: /* unknown */
325+ break;
326+ }
327+
328+ continue;
329+ }
330+ /* is this a control code? */
331+ if (l[consumedbytes] == '%') {
332+ waitformode = 1;
333+ continue;
334+ }
335+ /* actual content */
336+ if (displayedlen < 78) {
337+#ifdef UNICODEOUTPUT
338+ ptui_putchar(UNICODEMAP[(unsigned char)(l[consumedbytes])], colscheme[curmode], 1 + displayedlen++, linenum);
339+#else
340+ ptui_putchar(l[consumedbytes], colscheme[curmode], 1 + displayedlen++, linenum);
341+#endif
342+ }
343+ }
344+}
345+
346+
347+/* checks two filenames for match. filenames are up to 12 characters.
348+ * case-insensitive. returns 0 on match, non-zero otherwise. */
349+static int f12match(const char *f1, const char *f2) {
350+ int i;
351+ for (i = 0; i < 12; i++) {
352+ if (tolower(f1[i]) != tolower(f2[i])) return(1);
353+ if (f1[i] == 0) return(0);
354+ }
355+ return(0);
356+}
357+
358+
359+/* looks into the AMB container and returns the slot number at which the
360+ * file is present. sets errflag on error. */
361+static unsigned short ama_findslot(const char *fname, unsigned short fcount, FILEHANDLE fd, int *errflag) {
362+ char fentry[20]; /* only looking at the fname so signed char is ok */
363+ unsigned short i;
364+
365+ for (i = 0; i < fcount; i++) {
366+ /* read file entries until fname is found */
367+ FSEEK(fd, 4 + 2 + (i * 20));
368+ if (FREAD(fentry, 20, fd) != 20) goto ERR_QUIT;
369+ /* is this the file I am looking for? */
370+ if (f12match(fentry, fname) == 0) break;
371+ }
372+ if (i == fcount) goto ERR_QUIT; /* NOT FOUND */
373+ *errflag = 0;
374+ return(i);
375+
376+ ERR_QUIT:
377+ *errflag = 1;
378+ return(0);
379+}
380+
381+
382+static unsigned short consumecontrol(const char far *body) {
383+ unsigned short i = 0;
384+ if (body[i] != '%') return(0);
385+ i++;
386+ switch (body[i]) {
387+ case 'l':
388+ i += linktargetlen(body + i);
389+ break;
390+ case '\n':
391+ case '\0':
392+ break;
393+ default:
394+ i++;
395+ break;
396+ }
397+ return(i);
398+}
399+
400+
401+/* returns line number where next link is, or 0xffff if no link could be found */
402+static unsigned short find_next_link(const char far *body_org, const char far *displine_first_ptr, unsigned short displine_first, int *cursorx, int cursory) {
403+ const char far *body;
404+ unsigned short i, t, curline;
405+ int toplookout = 0; /* information whether or not I looked from top yet */
406+
407+ /* jump to line where the cursor is now */
408+ body = displine_first_ptr;
409+ curline = displine_first;
410+ i = 0; /* counts all data characters */
411+ for (; curline != (displine_first + cursory - 1); i++) {
412+ if (body[i] == 0) break;
413+ if (body[i] == '\n') curline++;
414+ }
415+
416+ /* skip cursorx displayable bytes (or entire line) */
417+ t = 0; /* counts displayable characters */
418+ for (;;) {
419+ unsigned short bytes = bytesuntilesc(body + i);
420+ t += bytes;
421+ i += bytes;
422+ if (t >= *cursorx) break;
423+ if ((body[i] == '\n') || (body[i] == 0)) {
424+ t = 0;
425+ break;
426+ }
427+ if (body[i] == '%') { /* skip control codes */
428+ if (body[i + 1] == '%') t++;
429+ i += consumecontrol(body + i);
430+ continue;
431+ }
432+ }
433+ /* look for nearest %l */
434+ AGAIN:
435+ for (;;) {
436+ unsigned short bytes = bytesuntilesc(body + i);
437+ t += bytes;
438+ i += bytes;
439+ if ((body[i] == '%') && (body[i + 1] == 'l')) {
440+ *cursorx = t + 1;
441+ return(curline);
442+ }
443+ if (body[i] == '\n') {
444+ t = 0;
445+ i++;
446+ curline++;
447+ continue;
448+ }
449+ if (body[i] == 0) break;
450+ if (body[i] == '%') { /* skip control codes */
451+ if (body[i + 1] == '%') t++;
452+ i += consumecontrol(body + i);
453+ }
454+ }
455+ /* if I'm here, then no link could be found - look again, but this time through the entire document */
456+ if (toplookout != 0) return(0xffffu);
457+ toplookout = 1;
458+ body = body_org;
459+ curline = 0;
460+ t = 0;
461+ i = 0;
462+ goto AGAIN;
463+}
464+
465+
466+/* returns the length of line, in bytes */
467+static unsigned short linelen(const char far *ptr) {
468+ unsigned short r;
469+ for (r = 0;; r++) {
470+ if (ptr[r] == '\n') return(r + 1);
471+ if (ptr[r] == 0) return(r);
472+ }
473+}
474+
475+
476+static enum ACTION display_ama(const char far *body, const unsigned char *colscheme, unsigned short *newtarget, FILEHANDLE fd, unsigned short ambfcount, int esc_quits) {
477+ int curline;
478+ unsigned short displine_first = 0;
479+ unsigned short displine_last;
480+ unsigned short lastcontentline;
481+ int emptylines_bottom; /* number of empty lines displayed at the bottom of the document */
482+ int key;
483+ int totlinescount = 0;
484+ const char far *ptr;
485+ int cursorx = 1, cursory = 1;
486+ const char far *linkover; /* pointer to link currently cursor-over-ed */
487+ unsigned short refreshonlylines[2] = {0xffffu, 0xffffu};
488+ int fileposcursor; /* y-position of the right-edge file cursor */
489+
490+ /* one-time thing: count the number of lines in the file */
491+ for (ptr = body; *ptr != 0; ptr++) if (*ptr == '\n') totlinescount++;
492+
493+ ptr = NULL;
494+
495+ AGAIN:
496+ displine_last = displine_first + ptui_getrowcount() - 2;
497+ linkover = NULL;
498+ curline = displine_first;
499+ emptylines_bottom = 0;
500+ ptui_locate(cursorx, cursory);
501+
502+ /* compute file position and draw right border */
503+ {
504+ int i;
505+ int range = ptui_getrowcount() - 3; /* 3 = titlebar + top and bottom arrows */
506+ if ((displine_first + cursory - 1 == 0) || (totlinescount == 0)) {
507+ fileposcursor = 2;
508+ } else if (displine_first + cursory - 1 >= totlinescount) {
509+ fileposcursor = range + 1;
510+ } else {
511+ fileposcursor = (((displine_first + cursory - 1) * range) / totlinescount) + 2;
512+ }
513+ for (i = 1; i < ptui_getrowcount(); i++) {
514+ if (i == fileposcursor) {
515+ ptui_putchar(' ', colscheme[AMB_RBORDERC], 79, i);
516+ } else {
517+ ptui_putchar(' ', colscheme[AMB_RBORDER], 79, i);
518+ }
519+ }
520+ /* draw arrows */
521+ ptui_putchar(ARRUP, colscheme[AMB_RBORDER], 79, 1);
522+ ptui_putchar(ARRDW, colscheme[AMB_RBORDER], 79, range + 2);
523+ }
524+
525+ if (ptr == NULL) { /* no position in cache, find it */
526+ /* skip lines until curline = displine_first */
527+ ptr = body;
528+ curline = 0;
529+ while (curline < displine_first) {
530+ if (*ptr == '\n') curline++;
531+ if (*ptr == 0) break;
532+ ptr++;
533+ }
534+ }
535+
536+ /* display lines until displine_last or end of document */
537+ {
538+ const char far *vptr = ptr;
539+ for (;;) {
540+ if (*vptr == 0) break; /* end of document */
541+ if ((refreshonlylines[0] != 0xffffu) && (refreshonlylines[0] != curline) && (refreshonlylines[1] != curline)) {
542+ vptr += linelen(vptr);
543+ } else {
544+ vptr += display_ama_line(vptr, 1 + curline - displine_first, colscheme, cursorx, cursory, &linkover);
545+ }
546+ if (curline > displine_last) break; /* screen bottom reached */
547+ curline++;
548+ }
549+ /* remember last line where cursor presence is allowed */
550+ lastcontentline = curline - displine_first + 1;
551+ }
552+
553+ /* relocate cursor if it is out of content screen now (could happen after a pgdown event) */
554+ if (cursory > lastcontentline) {
555+ cursory = lastcontentline;
556+ ptui_locate(cursorx, cursory);
557+ }
558+
559+ /* draw rest of display lines as empty space */
560+ for (; curline <= displine_last; curline++) {
561+ ptui_putchar_rep(' ', colscheme[AMA_NORMAL], 1, 1 + curline - displine_first, 78);
562+ emptylines_bottom++;
563+ }
564+ ptui_refresh();
565+
566+ /* pre-set refreshonly variables to "refresh all screen" */
567+ refreshonlylines[0] = 0xffffu;
568+ refreshonlylines[1] = 0xffffu;
569+
570+ ptui_mouseshow(1);
571+ WAITEVENT:
572+ key = ptui_getkey();
573+ if (key == PTUI_MOUSE) { /* mouse event */
574+ unsigned int x, y;
575+ if ((ptui_getmouse(&x, &y) == 0) && (x > 0) && (y > 0) && (x < 79) && (y <= lastcontentline)) {
576+ if ((cursorx == (int)x) && (cursory == (int)y) && (linkover != NULL)) {
577+ key = 0x0d;
578+ } else {
579+ refreshonlylines[0] = displine_first + cursory - 1;
580+ refreshonlylines[1] = displine_first + y - 1;
581+ }
582+ cursorx = x;
583+ cursory = y;
584+ } else if ((x == 79) && (y > 0)) { /* cursor bar click */
585+ if ((y == 1) && (displine_first > 0)) {
586+ displine_first--;
587+ ptr = NULL; /* displine_first ptr needs to be recomputed now */
588+ } else if (((int)y == ptui_getrowcount() - 1) && (emptylines_bottom == 0)) {
589+ displine_first++;
590+ ptr = NULL; /* displine_first ptr needs to be recomputed now */
591+ } else if ((int)y > fileposcursor) {
592+ key = 0x151; /* simulate pgdown */
593+ } else if ((int)y < fileposcursor) {
594+ key = 0x149; /* simulate pgup */
595+ }
596+ } else {
597+ goto WAITEVENT; /* ignore mouse clicks on invalid screen areas and do not waste time on screen refresh */
598+ }
599+ }
600+ ptui_mouseshow(0); /* hide mouse to avoid "mouse droppings" in DOS (glitches due to direct VRAM access) */
601+
602+ switch (key) {
603+ case 0x1b: /* ESCAPE */
604+ if (esc_quits) {
605+ warn("PRESS ESC AGAIN TO QUIT", NULL, colscheme);
606+ if (ptui_getkey() == 0x1b) return(ACTION_QUIT);
607+ } else {
608+ return(ACTION_PREV);
609+ }
610+ break;
611+ case 0x148: /* UP */
612+ if (cursory > 1) {
613+ refreshonlylines[0] = displine_first + cursory - 1;
614+ cursory--;
615+ refreshonlylines[1] = displine_first + cursory - 1;
616+ } else { /* scroll document, if possible */
617+ if (displine_first > 0) {
618+ displine_first--;
619+ ptr = NULL; /* displine_first ptr needs to be recomputed now */
620+ } else {
621+ refreshonlylines[0] = 0;
622+ }
623+ }
624+ break;
625+ case 0x149: /* PGUP */
626+ if (displine_first >= (ptui_getrowcount() - 2)) {
627+ displine_first -= ptui_getrowcount() - 2;
628+ ptr = NULL; /* displine_first ptr needs to be recomputed now */
629+ } else {
630+ displine_first = 0;
631+ refreshonlylines[0] = displine_first + cursory - 1;
632+ cursory = 1;
633+ refreshonlylines[0] = displine_first + cursory - 1;
634+ ptr = NULL; /* displine_first ptr needs to be recomputed now */
635+ }
636+ break;
637+ case 0x150: /* DOWN */
638+ if (cursory < lastcontentline) {
639+ refreshonlylines[0] = displine_first + cursory - 1;
640+ cursory++;
641+ refreshonlylines[1] = displine_first + cursory - 1;
642+ } else { /* scroll document down, if possible */
643+ if (emptylines_bottom == 0) {
644+ displine_first++;
645+ ptr = NULL; /* displine_first ptr needs to be recomputed now */
646+ } else {
647+ refreshonlylines[0] = 0;
648+ }
649+ }
650+ break;
651+ case 0x151: /* PGDOWN */
652+ if (emptylines_bottom == 0) {
653+ displine_first += (ptui_getrowcount() - 2);
654+ ptr = NULL; /* displine_first ptr needs to be recomputed now */
655+ } else { /* move cursor to last line with content */
656+ refreshonlylines[0] = displine_first + cursory - 1;
657+ cursory = lastcontentline;
658+ refreshonlylines[1] = displine_first + cursory - 1;
659+ }
660+ break;
661+ case 0x14D: /* RIGHT */
662+ if (cursorx < 78) {
663+ cursorx++;
664+ refreshonlylines[0] = displine_first + cursory - 1;
665+ }
666+ break;
667+ case 0x14B: /* LEFT */
668+ if (cursorx > 1) {
669+ cursorx--;
670+ refreshonlylines[0] = displine_first + cursory - 1;
671+ }
672+ break;
673+ case 0x09: /* TAB */
674+ {
675+ unsigned short line;
676+ line = find_next_link(body, ptr, displine_first, &cursorx, cursory);
677+ if (line != 0xffffu) {
678+ if (line < displine_first) {
679+ displine_first = line;
680+ cursory = 1;
681+ /* if not line 0, then offset display one line down (nicer) */
682+ if (displine_first > 0) {
683+ displine_first--;
684+ cursory++;
685+ }
686+ ptr = NULL; /* displine_first ptr needs to be recomputed now */
687+ /* conditions below means "scroll the page down if next link is not on the same line as the current cursor position and it is either at the last displayed line or below" */
688+ } else if ((line != displine_first + cursory - 1) && (line >= displine_first + ptui_getrowcount() - 2)) {
689+ displine_first = line - (ptui_getrowcount() - 3);
690+ cursory = ptui_getrowcount() - 2;
691+ ptr = NULL; /* displine_first ptr needs to be recomputed now */
692+ } else {
693+ refreshonlylines[0] = displine_first + cursory - 1;
694+ cursory = (line - displine_first) + 1;
695+ refreshonlylines[1] = displine_first + cursory - 1;
696+ }
697+ }
698+ break;
699+ }
700+ case 0x0d: /* ENTER */
701+ if (linkover != NULL) {
702+ char fname[13];
703+ int i;
704+ /* copy linkover into fname, up to 12 chars */
705+ for (i = 0; i < 12; i++) {
706+ if (linkover[i] == 0) break;
707+ if (linkover[i] == ':') break;
708+ if (linkover[i] == '\n') break;
709+ if (linkover[i] == '\r') break;
710+ fname[i] = linkover[i];
711+ }
712+ fname[i] = 0;
713+ /* resolve fname into newtarget */
714+ *newtarget = ama_findslot(fname, ambfcount, fd, &i);
715+ if (i == 0) return(ACTION_LOADPAGE);
716+ /* findslot() failed to locate the file */
717+ warn("NOT FOUND:", fname, colscheme);
718+ ptui_getkey();
719+ } else {
720+ refreshonlylines[0] = 0;
721+ }
722+ break;
723+ default:
724+#ifdef PRINT_KEYCODES
725+ ptui_locate(1, 1);
726+ printf("key = 0x%02x\n", key);
727+#endif
728+ break;
729+ }
730+ goto AGAIN;
731+}
732+
733+
734+/* returns length of file on success and fills fname with file name (unless fname is NULL), returns 0 otherwise */
735+static unsigned short ama_load(char far *mem, unsigned short slot, char *fname, FILEHANDLE fd) {
736+ unsigned long ama_offset;
737+ unsigned short ama_len;
738+ unsigned char fentry[20]; /* must be unsigned! */
739+
740+ /* read file entry */
741+ ama_offset = slot; /* doing a multi-step calculation to be sure not to overflow a short */
742+ ama_offset *= 20;
743+ ama_offset += 6; /* AMB header is 4 + AMB filescount is 2 */
744+ FSEEK(fd, ama_offset);
745+ if (FREAD(fentry, 20, fd) != 20) goto ERR_CORRUPT;
746+
747+ /* get props */
748+ ama_offset = fentry[15];
749+ ama_offset <<= 8;
750+ ama_offset |= fentry[14];
751+ ama_offset <<= 8;
752+ ama_offset |= fentry[13];
753+ ama_offset <<= 8;
754+ ama_offset |= fentry[12];
755+ ama_len = fentry[17];
756+ ama_len <<= 8;
757+ ama_len |= fentry[16];
758+ /* copy filename */
759+ if (fname != NULL) {
760+ memcpy(fname, fentry, 12);
761+ fname[12] = 0;
762+ }
763+
764+ /* reposition to start of ama file inside archive and read it */
765+ FSEEK(fd, ama_offset);
766+ if (FREAD(mem, ama_len, fd) != ama_len) goto ERR_CORRUPT;
767+ mem[ama_len] = 0; /* terminate with a NULL byte */
768+ return(ama_len);
769+
770+ ERR_CORRUPT:
771+ mystrcpy(mem, "%!ERROR: FILE IS CORRUPTED");
772+ return(0);
773+}
774+
775+
776+static void draw_titlebar_curfile(const char *amafname, const unsigned char *col) {
777+ int i = 66; /* start drawing at offset 66 (left of this is done by draw_titlebar_title) */
778+ int fnamelen;
779+ int off;
780+ /* count how many bytes there is in amafname until end of string or '.' */
781+ for (fnamelen = 0; (amafname[fnamelen] != '.') && (amafname[fnamelen] != 0); fnamelen++);
782+ /* add some padding so fname is right-aligned */
783+ for (; i < (77 - fnamelen); i++) ptui_putchar(' ', col[AMB_TITLEBAR], i, 0);
784+ /* ama fname - display it now (right-aligned to screen) */
785+ ptui_putchar('[', col[AMB_TITLEBAR], i++, 0);
786+ for (off = i; (i - off) < fnamelen; i++) {
787+ ptui_putchar(amafname[i - off], col[AMB_TITLEBAR], i, 0);
788+ }
789+ ptui_putchar(']', col[AMB_TITLEBAR], i++, 0);
790+}
791+
792+
793+static void draw_titlebar_title(const char far *ambtitle, const unsigned char *col) {
794+ int i = 0, off;
795+ ptui_putchar(' ', col[AMB_TITLEBAR], i++, 0);
796+ for (off = i; ambtitle[i - off] != 0; i++) {
797+ if (ambtitle[i - off] == '\r') break;
798+ if (ambtitle[i - off] == '\n') break;
799+ if (i - off > 64) break;
800+#ifdef UNICODEOUTPUT
801+ ptui_putchar(UNICODEMAP[(unsigned char)(ambtitle[i - off])], col[AMB_TITLEBAR], i, 0);
802+#else
803+ ptui_putchar(ambtitle[i - off], col[AMB_TITLEBAR], i, 0);
804+#endif
805+ }
806+ /* add padding until column 79 */
807+ for (; i < 80; i++) ptui_putchar(' ', col[AMB_TITLEBAR], i, 0);
808+}
809+
810+
811+static void draw_leftborder(const unsigned char *colscheme) {
812+ int i;
813+ for (i = ptui_getrowcount() - 1; i > 0; i--) {
814+ ptui_putchar(VLINE, colscheme[AMB_LBORDER], 0, i);
815+ }
816+}
817+
818+
819+/* add a new entry to history */
820+static void history_push(unsigned short *history, int *history_cur, unsigned short newentry) {
821+ int i, cur;
822+ cur = *history_cur;
823+ /* make room if history buff full */
824+ if (cur == HISTORYSIZE) {
825+ for (i = 1; i < HISTORYSIZE; i++) history[i - 1] = history[i];
826+ cur--;
827+ }
828+ /* append new entry to list and inc history_cur (and return it) */
829+ history[cur] = newentry;
830+ *history_cur = cur + 1;
831+}
832+
833+
834+static unsigned short history_pop(unsigned short *history, int *history_cur, unsigned short defaultslot) {
835+ int cur;
836+ cur = *history_cur;
837+ if (cur == 0) return(defaultslot);
838+ cur--;
839+ *history_cur = cur;
840+ return(history[cur]);
841+}
842+
843+
844+enum COLSCHEMES {
845+ COLSCH_MONO,
846+ COLSCH_COLOR
847+};
848+
849+
850+static void loadcolscheme(unsigned char *colscheme, enum COLSCHEMES id) {
851+ switch (id) {
852+ case COLSCH_COLOR:
853+ colscheme[AMB_TITLEBAR] = 0x70;
854+ colscheme[AMB_LBORDER] = 0x17;
855+ colscheme[AMB_RBORDER] = 0x70;
856+ colscheme[AMB_RBORDERC] = 0x00;
857+ colscheme[AMB_WARN] = 0x4e;
858+ colscheme[AMA_LINK_CURSOROVER] = 0x21;
859+ colscheme[AMA_NORMAL] = 0x17;
860+ colscheme[AMA_BORING] = 0x18;
861+ colscheme[AMA_LINK] = 0x12;
862+ colscheme[AMA_HEAD] = 0x1F;
863+ colscheme[AMA_NOTICE] = 0x60;
864+ break;
865+ case COLSCH_MONO:
866+ colscheme[AMB_TITLEBAR] = 0x70;
867+ colscheme[AMB_LBORDER] = 0x07;
868+ colscheme[AMB_RBORDER] = 0x07;
869+ colscheme[AMB_RBORDERC] = 0x70;
870+ colscheme[AMB_WARN] = 0x70;
871+ colscheme[AMA_LINK_CURSOROVER] = 0xf0;
872+ colscheme[AMA_NORMAL] = 0x07;
873+ colscheme[AMA_BORING] = 0x07;
874+ colscheme[AMA_LINK] = 0x0f;
875+ colscheme[AMA_HEAD] = 0x01; /* MDA-compatible cards render this as "underlined" */
876+ colscheme[AMA_NOTICE] = 0x70;
877+ break;
878+ }
879+}
880+
881+
882+int main(int argc, char **argv) {
883+ FILEHANDLE fd = FILEHANDLE_NONE;
884+ char *errmsg = NULL;
885+ unsigned short filescount;
886+ unsigned char colscheme[AMA_HIDDEN];
887+ enum ACTION action;
888+ int ptui_inited = 0;
889+ unsigned short history[HISTORYSIZE];
890+ int history_cur = 0;
891+ unsigned short curslot;
892+ unsigned short index_slot;
893+ int errflag;
894+#ifdef REALDOS
895+ static char staticbuff[50 * 1024u]; /* can't have full 64K in a COM file, sorry */
896+ char far *ama = staticbuff;
897+#else
898+ static char ama[65536u];
899+#endif
900+
901+ if ((argc < 2) || (argc > 3) || (argv[1][0] == '/')) {
902+ errmsg = "usage: amb file.amb [chaptername]";
903+ goto EARLY_QUIT;
904+ }
905+
906+ fd = FOPEN(argv[1]);
907+ if (fd == FILEHANDLE_NONE) {
908+ errmsg = "ERROR: failed to open file";
909+ goto EARLY_QUIT;
910+ }
911+
912+ if ((FREAD(ama, 4, fd) != 4) || (ama[0] != 'A') || (ama[1] != 'M') || (ama[2] != 'B') || (ama[3] != '1') || (FREAD(ama, 2, fd) != 2)) {
913+ errmsg = "ERROR: invalid file format";
914+ goto EARLY_QUIT;
915+ }
916+
917+ filescount = ama[1];
918+ filescount <<= 8;
919+ filescount |= ama[0];
920+
921+ if (filescount == 0) {
922+ errmsg = "ERROR: AMB archive is empty";
923+ goto EARLY_QUIT;
924+ }
925+
926+ /* look how big the biggest file is in the AMB archive, so I know whether
927+ * or not I need to allocate a "big" memory buffer (16-bit DOS only)
928+ * NOTE: I never free the allocated block, DOS does that at program's end */
929+#ifdef REALDOS
930+ {
931+ unsigned short i;
932+ unsigned short biggest = 0;
933+ unsigned short sz;
934+ for (i = filescount; i != 0; i--) {
935+ if (FREAD(ama, 20, fd) != 20) {
936+ }
937+ sz = (unsigned char)(ama[17]);
938+ sz <<= 8;
939+ sz |= (unsigned char)(ama[16]);
940+ if (sz > biggest) biggest = sz;
941+ }
942+ /* do I need a buffer bigger than my static buffer? */
943+ if (biggest >= sizeof(staticbuff)) {
944+ ama = ALLOC64K();
945+ if (ama == NULL) {
946+ errmsg = "ERROR: out of memory";
947+ goto EARLY_QUIT;
948+ }
949+ }
950+ }
951+#endif
952+
953+ /* look for specific chapter if user asked for it */
954+ if (argc == 3) {
955+ char userchap[16];
956+ int i;
957+ for (i = 0; i < 13; i++) {
958+ userchap[i] = argv[2][i];
959+ if ((userchap[i] == 0) || (userchap[i] == '.')) break;
960+ }
961+ /* append the .ama extension */
962+ userchap[i++] = '.';
963+ userchap[i++] = 'a';
964+ userchap[i++] = 'm';
965+ userchap[i++] = 'a';
966+ userchap[i++] = 0;
967+ /* look for the exact thing that user asked for, if not found try again with appending .ama */
968+ index_slot = ama_findslot(argv[2], filescount, fd, &errflag);
969+ if (errflag != 0) {
970+ index_slot = ama_findslot(userchap, filescount, fd, &errflag);
971+ if (errflag != 0) {
972+ errmsg = "ERROR: requested chapter not found";
973+ goto EARLY_QUIT;
974+ }
975+ }
976+ } else { /* locate the default index slot */
977+ index_slot = ama_findslot("index.ama", filescount, fd, &errflag);
978+ if (errflag != 0) {
979+ errmsg = "ERROR: this AMB file does not contain an index.ama";
980+ goto EARLY_QUIT;
981+ }
982+ }
983+
984+ /* on unicode-aware platforms set up the CP->unicode map table */
985+#ifdef UNICODEOUTPUT
986+{
987+ /* first 128 entries are normal ascii */
988+ unsigned short i;
989+ for (i = 0; i < 128; i++) UNICODEMAP[i] = i;
990+ for (i = 128; i < 256; i++) UNICODEMAP[i] = 0xfffdu; /* preload high-ascii with the unicode 'replacement' character */
991+ /* does this AMB feature a unicode.map? */
992+ i = ama_findslot("unicode.map", filescount, fd, &errflag);
993+ if (errflag == 0) {
994+ ama_load(ama, i, NULL, fd);
995+ for (i = 0; i < 128; i++) UNICODEMAP[128 + i] = ((unsigned char)ama[i * 2]) | (((unsigned char)ama[i * 2 + 1]) << 8) ;
996+ }
997+}
998+#endif
999+
1000+ /* file is open, memory is allocated, index found: all seems fine */
1001+ if (ptui_init(PTUI_ENABLE_MOUSE) != 0) {
1002+ PUTS("ERROR: UI initialization failed");
1003+ goto EARLY_QUIT;
1004+ }
1005+ ptui_inited = 1; /* remember that ptui is inited to close it later */
1006+ ptui_mouseshow(0); /* hide mouse by default -- will be shown by display_ama() */
1007+
1008+ /* init color scheme */
1009+ if (ptui_hascolor()) {
1010+ loadcolscheme(colscheme, COLSCH_COLOR);
1011+ } else {
1012+ loadcolscheme(colscheme, COLSCH_MONO);
1013+ }
1014+
1015+ action = ACTION_LOADPAGE;
1016+ curslot = index_slot;
1017+
1018+ /* draw the title bar */
1019+ {
1020+ unsigned short titslot;
1021+ titslot = ama_findslot("title", filescount, fd, &errflag);
1022+ if (errflag == 0) {
1023+ ama_load(ama, titslot, NULL, fd);
1024+ draw_titlebar_title(ama, colscheme);
1025+ } else { /* if no title found, use the amb filename */
1026+ draw_titlebar_title(argv[1], colscheme);
1027+ }
1028+ }
1029+
1030+ /* draw the left border */
1031+ draw_leftborder(colscheme);
1032+
1033+ /* main loop */
1034+ for (;;) {
1035+ unsigned short newslot;
1036+ switch (action) {
1037+ case ACTION_PREV:
1038+ case ACTION_LOADPAGE:
1039+ {
1040+ char fname[13];
1041+ ama_load(ama, curslot, fname, fd);
1042+ draw_titlebar_curfile(fname, colscheme);
1043+ }
1044+ break;
1045+ case ACTION_QUIT:
1046+ goto EARLY_QUIT;
1047+ case ACTION_NONE:
1048+ /* no-op */
1049+ break;
1050+ }
1051+
1052+ action = display_ama(ama, colscheme, &newslot, fd, filescount, ((history_cur == 0) && (curslot == index_slot))?1:0);
1053+
1054+ if (action == ACTION_PREV) {
1055+ curslot = history_pop(history, &history_cur, index_slot);
1056+ } else if ((action == ACTION_LOADPAGE) && (curslot != newslot)) { /* push to history only if page is different */
1057+ history_push(history, &history_cur, curslot);
1058+ curslot = newslot;
1059+ }
1060+ }
1061+
1062+ /* exit */
1063+
1064+ EARLY_QUIT:
1065+ if (ptui_inited) {
1066+ ptui_cls();
1067+ ptui_close();
1068+ }
1069+ if (fd != FILEHANDLE_NONE) FCLOSE(fd);
1070+ if (errmsg != NULL) {
1071+ PUTS(errmsg);
1072+ return(1);
1073+ }
1074+
1075+ PUTS("AMB ver " PVER);
1076+ PUTS("Have a nice day!");
1077+
1078+ return(0);
1079+}
--- amb/tags/amb-20201223/amb.txt (nonexistent)
+++ amb/tags/amb-20201223/amb.txt (revision 199)
@@ -0,0 +1,98 @@
1+
2+ ----------------------------------------- "All that is gold does not glitter
3+ | AMB: ANCIENT MACHINES BOOK READER | all that is long does not last
4+ | Copyright (C) Mateusz Viste | All that is old does not wither
5+ | home page: http://amb.osdn.io | not all that is over is past."
6+ ----------------------------------------- [J. R. R. Tolkien]
7+
8+
9+AMB is a reader of eponymous AMB (Ancient Machine Book) files. AMB stands for
10+"Ancient Machine's Book". It is an extremely lightweight file format meant to
11+store any kind of hypertext documentation that may be comfortably viewed even
12+on the most ancient PCs: technical manuals, books, etc. Think of it as a retro
13+equivalent of a *.CHM help file.
14+AMB is a multiplatform tool, available for Linux, Windows and real mode DOS.
15+It is designed for minimum hardware requirements - the DOS version needs an
16+IBM PC-compatible machine with DOS 2+ and 64 KiB of available RAM.
17+
18+
19+=== USAGE ====================================================================
20+
21+Start the AMB reader with the following syntax:
22+
23+ AMB FILE.AMB [chaptername]
24+
25+FILE.AMB is the AMB book that you wish to read, while the optional chaptername
26+argument allows to specify the name of the chapter you'd like AMB to start
27+from (otherwise it will use the default "index" page).
28+
29+Once loaded, you may navigate through the book using a mouse or the keyboard:
30+
31+ UP/DOWN move the cursor up/down (scrolls the document when necessary)
32+ LEFT/RIGHT move the cursor left/right on the current line
33+ PGUP/PGDOWN move one page up or down
34+ TAB jump to next link
35+ ESC either move to previous page or (if no previous page) quit
36+ ENTER follow the currently highlighted link
37+
38+When using a mouse, you can scroll the document by clicking on the right
39+cursor bar and follow links by clicking twice on them.
40+
41+
42+=== CONTACT ==================================================================
43+
44+The author of this software is Mateusz Viste. Should you wish to contact him
45+regarding this, or any other of his programs, you will find instructions how
46+to do so on his personal homepage at http://mateusz.viste.fr
47+
48+
49+=== CHANGELOG ================================================================
50+
51+AMB ver 20201223:
52+ - jump to a specific chapter when specified as command line argument
53+ - mouse support
54+ - optimized display routines (refresh only lines that may have changed)
55+ - optimized next-link-lookup (tab) so it is fast even on a 4 MHz machines
56+ - optimized memory usage
57+ - reloading same page does not insert a new history position
58+ - ported to the Windows platform (compiled with Mingw)
59+
60+AMB ver 20201218:
61+ - unicode output (on platforms that support it)
62+ - support for "notice" AMA tags (%!)
63+ - tab key jumps to next link
64+ - monochrome scheme is used on colorless terminals
65+
66+AMB ver 20201205:
67+ - pgup/pgdown handling
68+ - cursor is not allowed to be moved past the last line of content
69+ - bad links trigger an error message instead of loading a default file
70+ - confirmation is asked before quitting
71+
72+AMB ver 20201203:
73+ - first public release
74+
75+
76+=== LICENSE ==================================================================
77+
78+This software is published under the terms of the MIT license.
79+
80+Copyright (C) 2020 Mateusz Viste
81+
82+Permission is hereby granted, free of charge, to any person obtaining a copy
83+of this software and associated documentation files (the "Software"), to deal
84+in the Software without restriction, including without limitation the rights
85+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
86+copies of the Software, and to permit persons to whom the Software is
87+furnished to do so, subject to the following conditions:
88+
89+The above copyright notice and this permission notice shall be included in all
90+copies or substantial portions of the Software.
91+
92+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
93+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
94+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
95+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
96+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
97+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
98+SOFTWARE.
Show on old repository browser