├── .gitignore
├── Makefile
├── README.md
├── SECURITY.md
├── atfuncs
├── Makefile
├── atfuncs.c
├── atfuncs.h
├── date.c
├── product.c
├── system.c
└── weekday.c
├── binutils.sh
├── coff.h
├── coffsyrup.c
├── dist
├── Makefile
├── control
└── lotus123r3.spec
├── draw.c
├── draw.h
├── extract.sh
├── filemap.c
├── filemap.h
├── filename.c
├── forceplt.s
├── globalize.lst
├── graphics.c
├── graphics.h
├── gzip.sh
├── import.c
├── keymap
├── Makefile
├── keymap.bt
├── keymap.c
├── keymap.h
└── lmbcs.h
├── kfun.h
├── l123set.cf
├── localize.lst
├── lotdefs.h
├── loterrs.h
├── lotfuncs.h
├── lottypes.h
├── main.c
├── patch.c
├── redefine.lst
├── res
├── Makefile
├── README.md
├── hlp.c
├── l123txt3.txt
└── resgen.c
├── src
├── display.c
├── invalid.c
└── showme.c
├── symbols.lst
├── test
├── macutil.sh
├── runtests.sh
├── smpfiles.txt
├── testcases
│ └── bug103.wk3
└── testutil.sh
├── ttydraw
├── COPYING
├── Makefile
├── attr.c
├── box.c
├── cacastub.h
├── canvas.c
├── charset.c
├── codec.h
├── config.h
├── conic.c
├── drawtest.c
├── frame.c
├── line.c
├── string.c
├── transfrm.c
├── triangle.c
├── ttydraw.h
└── ttyint.h
├── undefine.lst
├── unixterm.h
├── unixtypes.h
└── wrappers.c
/.gitignore:
--------------------------------------------------------------------------------
1 | *.a
2 | *.o
3 | *.sw?
4 | *.deb
5 | *.IMG
6 | *.cmarkers
7 | *.idb
8 | *.ri
9 | .cache
10 | orig
11 | share
12 | real-ld
13 | objcopy
14 | objdump
15 | ld
16 | coffsyrup
17 | drawtest
18 | 123
19 | binutils-*
20 | gzip-*
21 | keymap/keymap
22 | res/resgen
23 | dist/lotus123r3_*_i386/
24 | compile_commands.json
25 | gunzip
26 | gzip
27 | zcat
28 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | BFD_INP_TARGET = coff-i386
2 | BFD_OUT_TARGET = coff-i386
3 | OBJCOPY_FLAGS = --wildcard --localize-symbols=localize.lst --globalize-symbols=globalize.lst --redefine-syms=redefine.lst $(shell cat symbols.lst)
4 | OBJCOPY_FILES = localize.lst globalize.lst redefine.lst undefine.lst symbols.lst
5 | OPTFLAGS = -O2
6 | CFLAGS = -freg-struct-return -W -Wall -m32 $(OPTFLAGS) -fno-stack-protector
7 | CPPFLAGS = -I. -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 -D_GNU_SOURCE -I atfuncs -I ttydraw -Wno-unused-parameter
8 | ASFLAGS = --32
9 | LDFLAGS = $(CFLAGS) -lc -B. -Wl,-b,$(BFD_OUT_TARGET) -no-pie
10 | LDLIBS = -lncurses -ltinfo -lm
11 | PATH := .:$(PATH)
12 | prefix = /usr/local
13 |
14 | # The list of terminals we generate keymaps for by default.
15 | KEYMAPS = xterm rxvt-unicode
16 | KEYMAPS := $(KEYMAPS) $(patsubst %,%-256color,$(KEYMAPS))
17 | KEYMAPS += screen.xterm-256color vt100 $(TERM)
18 |
19 | define BFD_TARGET_ERROR
20 | Your version of binutils was compiled without coff-i386 target support.
21 | You can try running ./binutils.sh to build a version that does support it.
22 | endef
23 |
24 | export BFD_TARGET_ERROR
25 |
26 | .PHONY: clean check distclean install uninstall
27 |
28 | all: check 123 keymaps resources
29 | @size 123
30 |
31 | debug: OPTFLAGS=-ggdb3 -O0
32 | debug: all
33 |
34 | check:
35 | @objdump --info | egrep -q '^coff-i386$$' || (echo "$$BFD_TARGET_ERROR"; false)
36 |
37 | orig/123.o:
38 | @echo You need to run the extract.sh script to get the 1-2-3 files.
39 | @false
40 |
41 | 123.o: orig/123.o $(OBJCOPY_FILES) | coffsyrup
42 | objcopy -I $(BFD_INP_TARGET) -O $(BFD_OUT_TARGET) $(OBJCOPY_FLAGS) $< $@
43 | coffsyrup $@ $(@:.o=.tmp.o) $$(cat undefine.lst)
44 | mv $(@:.o=.tmp.o) $@
45 |
46 | dl_init.o: orig/dl_init.o
47 | objcopy -I $(BFD_INP_TARGET) -O $(BFD_OUT_TARGET) $(OBJCOPY_FLAGS) $< $@
48 |
49 | ttydraw/ttydraw.a:
50 | $(MAKE) -C ttydraw OPTFLAGS="$(OPTFLAGS)"
51 |
52 | atfuncs/atfuncs.a:
53 | $(MAKE) -C atfuncs OPTFLAGS="$(OPTFLAGS)"
54 |
55 | 123OBJS=src/showme.o \
56 | src/invalid.o \
57 | src/display.o
58 |
59 | bin/123: 123.o dl_init.o main.o wrappers.o patch.o filemap.o graphics.o draw.o filename.o import.o $(123OBJS) | ttydraw/ttydraw.a atfuncs/atfuncs.a forceplt.o
60 | @mkdir -p $(@D)
61 | $(CC) forceplt.o $(CFLAGS) $(LDFLAGS) $^ -Wl,--whole-archive,ttydraw/ttydraw.a,atfuncs/atfuncs.a,--no-whole-archive -o $@ $(LDLIBS)
62 |
63 | 123: bin/123
64 | @ln -fs $^ $@
65 |
66 | keymap/keymap:
67 | $(MAKE) -C keymap
68 |
69 | # This generates the keymaps in a seperate directory based on the first letter.
70 | $(sort $(KEYMAPS)): keymap/keymap
71 | mkdir -p share/lotus/keymaps/$(shell printf "%c" $@)
72 | -keymap/keymap $@ > share/lotus/keymaps/$(shell printf "%c" $@)/$@
73 |
74 | keymaps: $(KEYMAPS)
75 |
76 | resources:
77 | $(MAKE) -C res
78 | @cp res/l123txt3.ri share/lotus/123.v10/ri/USA-English/
79 |
80 | clean:
81 | $(RM) *.o src/*.o coffsyrup 123
82 | $(RM) vgcore.* core.* core
83 | $(RM) -r bin share/lotus/keymaps
84 | $(MAKE) -C ttydraw clean
85 | $(MAKE) -C atfuncs clean
86 | $(MAKE) -C keymap clean
87 | $(MAKE) -C res clean
88 |
89 | distclean: clean
90 | $(MAKE) -C dist clean
91 | ./gzip.sh clean
92 | ./binutils.sh clean
93 | ./extract.sh clean
94 |
95 | install: all
96 | install -Dm 755 "bin/123" "$(prefix)/bin/123"
97 | install -Dm 644 "share/man/man1/123.1" "$(prefix)/share/man/man1/123.1"
98 | install -Dm 644 "share/lotus/etc/l123set.cf" "$(prefix)/share/lotus/etc/l123set.cf"
99 | find "share/lotus/keymaps" -type f -exec install -Dm 644 {} "$(prefix)/{}" \;
100 | install -Dm 644 "share/lotus/123.v10/sysV386/lotus.bcf" "$(prefix)/share/lotus/123.v10/sysV386/lotus.bcf"
101 | install -Dm 644 "share/lotus/123.v10/sysV386/lib/wyse50-lts123" "$(prefix)/share/lotus/123.v10/sysV386/lib/wyse50-lts123"
102 | find "share/lotus/123.v10/cbd" -type f -exec install -Dm 644 {} "$(prefix)/{}" \;
103 | find "share/lotus/123.v10/fonts" -type f -exec install -Dm 644 {} "$(prefix)/{}" \;
104 | find "share/lotus/123.v10/hlp" -type f -exec install -Dm 644 {} "$(prefix)/{}" \;
105 | find "share/lotus/123.v10/keymaps" -type f -exec install -Dm 644 {} "$(prefix)/{}" \;
106 | find "share/lotus/123.v10/pbd" -type f -exec install -Dm 644 {} "$(prefix)/{}" \;
107 | find "share/lotus/123.v10/ri" -type f -exec install -Dm 644 {} "$(prefix)/{}" \;
108 | find "share/lotus/123.v10/smpfiles" -type f -exec install -Dm 644 {} "$(prefix)/{}" \;
109 |
110 | uninstall:
111 | $(RM) "$(prefix)/bin/123"
112 | $(RM) "$(prefix)/share/man/man1/123.1"
113 | $(RM) -r "$(prefix)/share/lotus"
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lotus 1-2-3 for Linux
2 |
3 | This is a native port of Lotus 1-2-3 Release 3 to Linux. There's an article documenting how this is possible
4 | [here](https://lock.cmpxchg8b.com/linux123.html).
5 |
6 | 
7 |
8 | ## Screenshots
9 |
10 | Here are some screenshots of Lotus 1-2-3 on Linux.
11 |
12 | |  |  |  | 
13 | |--|--|--|--|
14 | | Interactive, live-updating ASCII-art charts. | Context sensitive help. | Highly configurable graphs. | Work with multiple sheets.
15 |
16 | See more screenshots and gifs in the [wiki](https://github.com/taviso/123elf/wiki/Getting-Started).
17 |
18 | ## Building
19 |
20 | ### Dependencies
21 |
22 | First, you need a version of binutils that is compiled with `coff-i386` target
23 | support. You can check like this:
24 |
25 | ```
26 | $ objdump --info | grep coff-i386
27 | ```
28 |
29 | > Note: Most distributions do not enable this for some reason.
30 |
31 | Run the included `binutils.sh` to download and compile a version of binutils known to work for this.
32 |
33 | Secondly, you need a copy of Lotus 1-2-3 for UNIX, you can download it
34 | [here](https://archive.org/details/123-unix). Just place the raw disk images in
35 | the build directory and run `extract.sh`.
36 |
37 | Finally, just run `make`.
38 |
39 | #### Packages
40 |
41 | The following packages are required
42 |
43 | | Ubuntu | Fedora | Debian (bookworm) | Ubuntu (bionic)
44 | | ------------------- | ------------------- | ------------------- | -------------------
45 | | build-essential | glibc-devel.i686 | build-essential | build-essential
46 | | gcc-multilib | libgcc.i686 | gcc-multilib | gcc-multilib
47 | | lib32ncurses-dev | ncurses-static.i686 | lib32ncurses-dev | libncurses-dev:i386
48 |
49 | ## Installing
50 |
51 | Run `make install` to install into `/usr/local`.
52 |
53 | Run `make install prefix=/my/prefix` to install into a custom prefix.
54 |
55 | Run `make uninstall` or `make uninstall prefix=/my/prefix` to uninstall.
56 |
57 | ## Running
58 |
59 | Just run `./123` in the project directory after building, or, if you installed it, run `123`.
60 |
61 | ### Getting Started
62 |
63 | There is a quick start guide in the wiki [here](https://github.com/taviso/123elf/wiki/Getting-Started), and the full manual can be seen here [here](https://archive.org/details/lotus-1-2-3-release-3.1-reference/Lotus%201-2-3%20Release%203.1%20-%20Tutorial).
64 |
65 | There is a man page in `share/man/man1/123.1` that describes the command line options.
66 |
67 | Lotus 1-2-3 has context sensitive online help, you can press F1 at most times to see some hints.
68 |
69 | > Note: You use the / key to open the 123 menu!
70 |
71 | If you've used any spreadsheet before, you should be able to get started quickly. Functions use `@` instead of `=`, but the common functions like `@SUM`, `@AVG`, `@INDEX`, and even `@HLOOKUP` all work as you would expect.
72 |
73 | ## FAQ
74 |
75 | - Q. How do I quit 123?
76 |
77 | If the status indicator in the top right says `READY`, try `/Quit Yes`.
78 |
79 | If it doesn't say `READY` (it might say `ERROR`, `HELP` `POINT`, `MENU` or
80 | something else), try hitting Esc until it goes back to `READY`.
81 |
82 | - Q. I get the error 'invalid compressed data--code out of range'.
83 |
84 | You have a *very* old gzip with broken lzw/pack support. You can try running
85 | the `gzip.sh` script to build a more recent gzip, then rerun the extract
86 | script.
87 |
88 | See the full [FAQ](https://github.com/taviso/123elf/wiki/FAQ) for more.
89 |
90 | ## Bugs
91 |
92 | - ~~The keyboard map seems to be incomplete~~ (XTerm compatible terminals should be [working](https://github.com/taviso/123elf/wiki/Keybindings), please test others!)
93 | - ~~Graphs don't work yet~~ (Partially working!, see [#5](https://github.com/taviso/123elf/issues/5)).
94 | - ~~Printing doesn't work yet~~ (Print to file works, print to lpr is being worked on, see [#50](https://github.com/taviso/123elf/issues/50)).
95 | - `STEP` mode does not display the current step (Nearly working! see [#101](https://github.com/taviso/123elf/issues/101)).
96 | - There is limited i18n support (We're working on it, see [#73](https://github.com/taviso/123elf/issues/73)).
97 | - File an issue if you notice something, there are probably lots of minor issues that can be fixed!
98 |
99 |
100 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Macros
4 | By default, 1-2-3 allows [AutoExec macros](https://github.com/taviso/123elf/wiki/Getting-Started#autoexec-macros)
5 | in worksheets. 1-2-3 macros are powerful, they can run shell commands with `{SYSTEM}`, read and write
6 | arbitrary files with `{OPEN}`, and so on.
7 |
8 | However, you can disable AutoExec macros via `/Worksheet Global Default Autoexec No Update`.
9 |
10 | > I am thinking of changing this default before we reach a first release, see [#27](https://github.com/taviso/123elf/issues/27).
11 |
12 | If you disable AutoExec then in *theory* it's safe to open untrusted
13 | worksheets -- ***but*** this software hasn't been maintained for over 30 years,
14 | and may contain security bugs!
15 |
16 | ## Sealed Files
17 |
18 | There is a 1-2-3 feature called "Sealing", accessible via `/File Admin Seal`. In theory, this requires
19 | a password to alter protected ranges (see `/Worksheet Global Prot`) or to view hidden ranges (see `/Worksheet Hide Enable`, among various others).
20 |
21 | This is not a supported security feature, the password is stored in the file in plaintext.
22 |
23 | ## Reporting a Vulnerability
24 |
25 | It is possible to fix bugs by redirecting unsafe functions to new safe versions, so we will
26 | make a best effort to fix vulnerabilities if you [report](https://github.com/taviso/123elf/issues/new)
27 | them!
28 |
29 |
--------------------------------------------------------------------------------
/atfuncs/Makefile:
--------------------------------------------------------------------------------
1 | CPPFLAGS=-I..
2 | CFLAGS=-freg-struct-return -m32 $(OPTFLAGS) -fno-stack-protector
3 | LDFLAGS=$(CFLAGS)
4 | LDLIBS=
5 |
6 | .PHONY: clean
7 |
8 | all: atfuncs.a
9 |
10 | atfuncs.a: date.o weekday.o product.o atfuncs.o system.o
11 | $(AR) r $@ $^
12 |
13 | clean:
14 | rm -f *.a *.o
15 |
--------------------------------------------------------------------------------
/atfuncs/atfuncs.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "lottypes.h"
6 | #include "lotfuncs.h"
7 | #include "lotdefs.h"
8 | #include "atfuncs.h"
9 |
10 | void init_at_funcs()
11 | {
12 | // Setup any @functions here.
13 | functions[AT_WEEKDAY] = (atfunc_t) at_weekday;
14 | functions[AT_PRODUCT] = (atfunc_t) at_product;
15 | functions[AT_SYSTEM] = (atfunc_t) at_system;
16 |
17 | // You can pass 1 or more parameters to @PRODUCT().
18 | fn_numargs[AT_PRODUCT] = AT_ARGS_VARIABLE | 1;
19 | fn_numargs[AT_SYSTEM] = 1;
20 | }
21 |
--------------------------------------------------------------------------------
/atfuncs/atfuncs.h:
--------------------------------------------------------------------------------
1 | #ifndef __ATFUNCS_H
2 | #define __ATFUNCS_H
3 |
4 | #define AT_ARGS_VARIABLE 0x80
5 |
6 | // Apply an operation to a RANGE.
7 | extern int16_t range_scan_tos(void (*op)());
8 |
9 | // Various operations.
10 | extern void op_add();
11 | extern void op_mul();
12 |
13 | // Stack operations.
14 | extern int16_t date_valid(int16_t dateval[3]);
15 | extern int16_t encode_date(int16_t *datenums);
16 |
17 | extern int16_t check_three_numbers();
18 | extern int16_t check_one_number();
19 | extern int16_t check_one_string();
20 | extern int16_t get_integer();
21 | extern int16_t push_integer(uint16_t val);
22 | extern int16_t push_one();
23 | extern int16_t push_zero();
24 | extern int16_t push_na();
25 | extern int16_t drop_one_push_err();
26 | extern int16_t drop_one_push_stack_string(char *str);
27 | extern char * peek_string(int16_t n);
28 | extern void mod_real_d();
29 | extern void int_real_d();
30 | extern void drop_one();
31 | extern void swap_TOS();
32 |
33 | // Replaced previously unimplemented functions
34 | #define AT_CALL AT_PRODUCT
35 | #define AT_TERM2 AT_SYSTEM
36 |
37 | enum {
38 | AT_NA,
39 | AT_ERR,
40 | AT_ABS,
41 | AT_INT,
42 | AT_SQRT,
43 | AT_LOG,
44 | AT_LN,
45 | AT_PI,
46 | AT_SIN,
47 | AT_COS,
48 | AT_TAN,
49 | AT_ATAN2,
50 | AT_ATAN,
51 | AT_ASIN,
52 | AT_ACOS,
53 | AT_EXP,
54 | AT_MOD,
55 | AT_CHOOSE,
56 | AT_ISNA,
57 | AT_ISERR,
58 | AT_FALSE,
59 | AT_TRUE,
60 | AT_RAND,
61 | AT_DATE,
62 | AT_NOW,
63 | AT_PMT,
64 | AT_PV,
65 | AT_FV,
66 | AT_IF,
67 | AT_DAY,
68 | AT_MONTH,
69 | AT_YEAR,
70 | AT_ROUND,
71 | AT_TIME,
72 | AT_HOUR,
73 | AT_MINUTE,
74 | AT_SECOND,
75 | AT_ISNUMBER,
76 | AT_ISSTRING,
77 | AT_LENGTH,
78 | AT_VALUE,
79 | AT_STRING,
80 | AT_MID,
81 | AT_CHAR,
82 | AT_CODE,
83 | AT_FIND,
84 | AT_DATEVALUE,
85 | AT_TIMEVALUE,
86 | AT_CELLPOINTER,
87 | AT_SUM,
88 | AT_AVG,
89 | AT_COUNT,
90 | AT_MIN,
91 | AT_MAX,
92 | AT_VLOOKUP,
93 | AT_NPV,
94 | AT_VAR,
95 | AT_STD,
96 | AT_IRR,
97 | AT_HLOOKUP,
98 | AT_DSUM,
99 | AT_DAVG,
100 | AT_DCOUNT,
101 | AT_DMIN,
102 | AT_DMAX,
103 | AT_DVAR,
104 | AT_DSTD,
105 | AT_INDEX,
106 | AT_COLS,
107 | AT_ROWS,
108 | AT_REPEAT,
109 | AT_UPPER,
110 | AT_LOWER,
111 | AT_LEFT,
112 | AT_RIGHT,
113 | AT_REPLACE,
114 | AT_PROPER,
115 | AT_CELL,
116 | AT_TRIM,
117 | AT_CLEAN,
118 | AT_S,
119 | AT_N,
120 | AT_EXACT,
121 | AT_PRODUCT,
122 | AT_AT,
123 | AT_RATE,
124 | AT_TERM,
125 | AT_CTERM,
126 | AT_SLN,
127 | AT_SYD,
128 | AT_DDB,
129 | AT_SPLFUNCS,
130 | AT_SHEETS,
131 | AT_INFO,
132 | AT_SUMPRODUCT,
133 | AT_ISRANGE,
134 | AT_DGET,
135 | AT_DQUERY,
136 | AT_COORD,
137 | AT_MATCH,
138 | AT_TODAY,
139 | AT_VDB,
140 | AT_DVARS,
141 | AT_DSTDS,
142 | AT_VARS,
143 | AT_STDS,
144 | AT_D360,
145 | AT_BLANK,
146 | AT_ISAPP,
147 | AT_ISAAF,
148 | AT_WEEKDAY,
149 | AT_DATEDIF,
150 | AT_RANK,
151 | AT_NUMBERSTRING,
152 | AT_DATESTRING,
153 | AT_DECIMAL,
154 | AT_HEX,
155 | AT_DB,
156 | AT_PMTI,
157 | AT_SPI,
158 | AT_FULLP,
159 | AT_HALFP,
160 | AT_PUREAVG,
161 | AT_PURECOUNT,
162 | AT_PUREMAX,
163 | AT_PUREMIN,
164 | AT_PURESTD,
165 | AT_PUREVAR,
166 | AT_PURESTDS,
167 | AT_PUREVARS,
168 | AT_PMT2,
169 | AT_PV2,
170 | AT_FV2,
171 | AT_SYSTEM,
172 | AT_NUM_FUNCS,
173 | };
174 |
175 | typedef int16_t (*atfunc_t)(void);
176 |
177 | extern atfunc_t functions[AT_NUM_FUNCS];
178 | extern int8_t fn_numargs[AT_NUM_FUNCS];
179 |
180 | int16_t at_date();
181 | int16_t at_weekday();
182 | int16_t at_system();
183 | int16_t at_product(int16_t cnt);
184 |
185 | void init_at_funcs();
186 |
187 | #endif
188 |
--------------------------------------------------------------------------------
/atfuncs/date.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "lottypes.h"
6 | #include "lotfuncs.h"
7 | #include "lotdefs.h"
8 | #include "atfuncs.h"
9 |
10 | int16_t at_date()
11 | {
12 | int16_t result;
13 | int16_t datenums[3];
14 |
15 | result = check_three_numbers();
16 |
17 | if (result)
18 | {
19 | datenums[2] = get_integer(); // Day
20 | datenums[1] = get_integer(); // Month
21 | datenums[0] = get_integer(); // Year
22 |
23 | // If this is a four digit year, adjust it to work with the lotus @DATE
24 | // syntax by changing it to an offset from 1900. This should work for
25 | // years up to 2100.
26 | if (datenums[0] > 999) {
27 | datenums[0] -= 1900;
28 | }
29 | return encode_date(datenums);
30 | }
31 |
32 | return result;
33 | }
34 |
--------------------------------------------------------------------------------
/atfuncs/product.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "lottypes.h"
6 | #include "lotfuncs.h"
7 | #include "lotdefs.h"
8 | #include "atfuncs.h"
9 |
10 | int16_t at_product(int16_t cnt)
11 | {
12 | int16_t result;
13 |
14 | push_one();
15 |
16 | while (cnt--) {
17 | swap_TOS();
18 | result = range_scan_tos(op_mul);
19 | }
20 |
21 | return result;
22 | }
23 |
--------------------------------------------------------------------------------
/atfuncs/system.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "lottypes.h"
8 | #include "lotfuncs.h"
9 | #include "lotdefs.h"
10 | #include "atfuncs.h"
11 |
12 | int16_t at_system()
13 | {
14 | FILE *cmd;
15 | char buf[512] = {0};
16 |
17 | if (!check_one_string()) {
18 | return 0;
19 | }
20 |
21 | // @SYSTEM() is only permitted when autoexec is enabled.
22 | if (get_allow_autoexec() == false) {
23 | drop_one();
24 | push_na();
25 | return 1;
26 | }
27 |
28 | cmd = popen(peek_string(0), "r");
29 |
30 | if (cmd == NULL) {
31 | return drop_one_push_err();
32 | }
33 |
34 | // We can read at most 512 bytes of output.
35 | fscanf(cmd, "%511[^\n]", buf);
36 |
37 | // We return @ERR if the command returned failure.
38 | if (pclose(cmd) != EXIT_SUCCESS) {
39 | return drop_one_push_err();
40 | }
41 |
42 | // Return the output as a string.
43 | return drop_one_push_stack_string(buf);
44 | }
45 |
--------------------------------------------------------------------------------
/atfuncs/weekday.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "lottypes.h"
6 | #include "lotfuncs.h"
7 | #include "lotdefs.h"
8 | #include "atfuncs.h"
9 |
10 | int16_t at_weekday()
11 | {
12 | if (!check_one_number()) {
13 | return 0;
14 | }
15 |
16 | push_integer(7);
17 |
18 | // Find the day number.
19 | mod_real_d();
20 | int_real_d();
21 | return 1;
22 | }
23 |
--------------------------------------------------------------------------------
/binutils.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | BINUTILS_DL="https://ftp.gnu.org/gnu/binutils"
4 | BINUTILS_XZ="binutils-2.38.tar.xz"
5 | BINUTILS_DIR="$(basename $BINUTILS_XZ .tar.xz)"
6 | BINUTILS_URL="$BINUTILS_DL/$BINUTILS_XZ"
7 | ORIG_DIR="$(pwd)"
8 |
9 | # Dont continue on error.
10 | set -e
11 |
12 | # Help text.
13 | if [ "$1" = '-h' ]; then
14 | echo >&2 "Usage: $0 [clean]"
15 | echo >&2
16 | echo >&2 'Specify "clean" to remove binutils, otherwise it will be downloaded and built.'
17 | exit 1
18 | fi
19 |
20 | # Optional cleanup if requested.
21 | if [ "$1" = 'clean' ]; then
22 | rm -fv objcopy objdump ld "$BINUTILS_XZ"
23 | rm -rfv "$BINUTILS_DIR"
24 | exit
25 | fi
26 |
27 | # Extract binutils.
28 | if [ ! -d "$BINUTILS_DIR" ]; then
29 |
30 | # Download binutils.
31 | if [ ! -f "$BINUTILS_XZ" ]; then
32 | wget "$BINUTILS_URL"
33 | fi
34 |
35 | tar xf "$BINUTILS_XZ"
36 | fi
37 |
38 | # Compile binutils.
39 | if [ ! -x "$BINUTILS_DIR/binutils/objcopy" ]; then
40 | cd "$BINUTILS_DIR"
41 | ./configure --enable-targets=i386-pc-elf32 \
42 | --disable-gas \
43 | --disable-libctf \
44 | --disable-plugins \
45 | --disable-gprof \
46 | --enable-compressed-debug-sections=none
47 |
48 | make all-ld -j$(nproc) MAKEINFO=true
49 | fi
50 |
51 | # Copy compiled binaries to working directory.
52 | copy() {
53 | test ! -x "$2" && cp -v "$1" "$2"
54 | }
55 |
56 | cd "$ORIG_DIR"
57 | copy "$BINUTILS_DIR/binutils/objcopy" objcopy
58 | copy "$BINUTILS_DIR/binutils/objdump" objdump
59 | copy "$BINUTILS_DIR/ld/ld-new" ld
60 |
--------------------------------------------------------------------------------
/coff.h:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
2 | #ifndef __dj_include_coff_h_
3 | #define __dj_include_coff_h_
4 |
5 | #ifdef __cplusplus
6 | extern "C" {
7 | #endif
8 |
9 | #ifndef __dj_ENFORCE_ANSI_FREESTANDING
10 |
11 | #ifndef __STRICT_ANSI__
12 |
13 | /* #ifndef _POSIX_SOURCE */
14 | #if 1
15 |
16 | /*** coff information for Intel 386/486. */
17 |
18 | /********************** FILE HEADER **********************/
19 |
20 | struct external_filehdr {
21 | unsigned short f_magic; /* magic number */
22 | unsigned short f_nscns; /* number of sections */
23 | unsigned int f_timdat; /* time & date stamp */
24 | unsigned int f_symptr; /* file pointer to symtab */
25 | unsigned int f_nsyms; /* number of symtab entries */
26 | unsigned short f_opthdr; /* sizeof(optional hdr) */
27 | unsigned short f_flags; /* flags */
28 | };
29 |
30 |
31 | /* Bits for f_flags:
32 | * F_RELFLG relocation info stripped from file
33 | * F_EXEC file is executable (no unresolved external references)
34 | * F_LNNO line numbers stripped from file
35 | * F_LSYMS local symbols stripped from file
36 | * F_AR32WR file has byte ordering of an AR32WR machine (e.g. vax)
37 | */
38 |
39 | #define F_RELFLG (0x0001)
40 | #define F_EXEC (0x0002)
41 | #define F_LNNO (0x0004)
42 | #define F_LSYMS (0x0008)
43 |
44 |
45 |
46 | #define I386MAGIC 0x14c
47 | #define I386AIXMAGIC 0x175
48 | #define I386BADMAG(x) (((x).f_magic!=I386MAGIC) && (x).f_magic!=I386AIXMAGIC)
49 |
50 |
51 | #define FILHDR struct external_filehdr
52 | #define FILHSZ sizeof(FILHDR)
53 |
54 | /********************** AOUT "OPTIONAL HEADER" **********************/
55 |
56 |
57 | typedef struct
58 | {
59 | unsigned short magic; /* type of file */
60 | unsigned short vstamp; /* version stamp */
61 | unsigned int tsize; /* text size in bytes, padded to FW bdry*/
62 | unsigned int dsize; /* initialized data " " */
63 | unsigned int bsize; /* uninitialized data " " */
64 | unsigned int entry; /* entry pt. */
65 | unsigned int text_start; /* base of text used for this file */
66 | unsigned int data_start; /* base of data used for this file */
67 | }
68 | AOUTHDR;
69 |
70 |
71 | typedef struct gnu_aout {
72 | unsigned int info;
73 | unsigned int tsize;
74 | unsigned int dsize;
75 | unsigned int bsize;
76 | unsigned int symsize;
77 | unsigned int entry;
78 | unsigned int txrel;
79 | unsigned int dtrel;
80 | } GNU_AOUT;
81 |
82 | #define AOUTSZ (sizeof(AOUTHDR))
83 |
84 | #define OMAGIC 0404 /* object files, eg as output */
85 | #define ZMAGIC 0413 /* demand load format, eg normal ld output */
86 | #define STMAGIC 0401 /* target shlib */
87 | #define SHMAGIC 0443 /* host shlib */
88 |
89 |
90 | /********************** SECTION HEADER **********************/
91 |
92 |
93 | struct external_scnhdr {
94 | char s_name[8]; /* section name */
95 | unsigned int s_paddr; /* physical address, aliased s_nlib */
96 | unsigned s_vaddr; /* virtual address */
97 | unsigned s_size; /* section size */
98 | unsigned s_scnptr; /* file ptr to raw data for section */
99 | unsigned s_relptr; /* file ptr to relocation */
100 | unsigned s_lnnoptr; /* file ptr to line numbers */
101 | unsigned short s_nreloc; /* number of relocation entries */
102 | unsigned short s_nlnno; /* number of line number entries*/
103 | unsigned int s_flags; /* flags */
104 | };
105 |
106 | #define SCNHDR struct external_scnhdr
107 | #define SCNHSZ sizeof(SCNHDR)
108 |
109 | /*
110 | * names of "special" sections
111 | */
112 | #define _TEXT ".text"
113 | #define _DATA ".data"
114 | #define _BSS ".bss"
115 | #define _COMMENT ".comment"
116 | #define _LIB ".lib"
117 |
118 | /*
119 | * s_flags "type"
120 | */
121 | #define STYP_TEXT (0x0020) /* section contains text only */
122 | #define STYP_DATA (0x0040) /* section contains data only */
123 | #define STYP_BSS (0x0080) /* section contains bss only */
124 |
125 | /********************** LINE NUMBERS **********************/
126 |
127 | /* 1 line number entry for every "breakpointable" source line in a section.
128 | * Line numbers are grouped on a per function basis; first entry in a function
129 | * grouping will have l_lnno = 0 and in place of physical address will be the
130 | * symbol table index of the function name.
131 | */
132 | struct external_lineno {
133 | union {
134 | unsigned int l_symndx __attribute__((packed)); /* function name symbol index, iff l_lnno == 0 */
135 | unsigned int l_paddr __attribute__((packed)); /* (physical) address of line number */
136 | } l_addr;
137 | unsigned short l_lnno; /* line number */
138 | };
139 |
140 |
141 | #define LINENO struct external_lineno
142 | #define LINESZ sizeof(LINENO)
143 |
144 |
145 | /********************** SYMBOLS **********************/
146 |
147 | #define E_SYMNMLEN 8 /* # characters in a symbol name */
148 | #define E_FILNMLEN 14 /* # characters in a file name */
149 | #define E_DIMNUM 4 /* # array dimensions in auxiliary entry */
150 |
151 | struct external_syment
152 | {
153 | union {
154 | char e_name[E_SYMNMLEN];
155 | struct {
156 | unsigned int e_zeroes __attribute__((packed));
157 | unsigned int e_offset __attribute__((packed));
158 | } e;
159 | } e;
160 | unsigned int e_value __attribute__((packed));
161 | short e_scnum;
162 | unsigned short e_type;
163 | unsigned char e_sclass;
164 | unsigned char e_numaux;
165 | };
166 |
167 | #define N_BTMASK (0xf)
168 | #define N_TMASK (0x30)
169 | #define N_BTSHFT (4)
170 | #define N_TSHIFT (2)
171 |
172 | union external_auxent {
173 | struct {
174 | unsigned int x_tagndx __attribute__((packed)); /* str, un, or enum tag indx */
175 | union {
176 | struct {
177 | unsigned short x_lnno; /* declaration line number */
178 | unsigned short x_size; /* str/union/array size */
179 | } x_lnsz;
180 | unsigned int x_fsize __attribute__((packed)); /* size of function */
181 | } x_misc;
182 | union {
183 | struct { /* if ISFCN, tag, or .bb */
184 | unsigned int x_lnnoptr __attribute__((packed)); /* ptr to fcn line # */
185 | unsigned int x_endndx __attribute__((packed)); /* entry ndx past block end */
186 | } x_fcn;
187 | struct { /* if ISARY, up to 4 dimen. */
188 | unsigned short x_dimen[E_DIMNUM];
189 | } x_ary;
190 | } x_fcnary;
191 | unsigned short x_tvndx; /* tv index */
192 | } x_sym;
193 |
194 | union {
195 | char x_fname[E_FILNMLEN];
196 | struct {
197 | unsigned int x_zeroes __attribute__((packed));
198 | unsigned int x_offset __attribute__((packed));
199 | } x_n;
200 | } x_file;
201 |
202 | struct {
203 | unsigned int x_scnlen __attribute__((packed)); /* section length */
204 | unsigned short x_nreloc; /* # relocation entries */
205 | unsigned short x_nlinno; /* # line numbers */
206 | } x_scn;
207 |
208 | struct {
209 | unsigned int x_tvfill __attribute__((packed)); /* tv fill value */
210 | unsigned short x_tvlen; /* length of .tv */
211 | unsigned short x_tvran[2]; /* tv range */
212 | } x_tv; /* info about .tv section (in auxent of symbol .tv)) */
213 |
214 |
215 | };
216 |
217 | #define SYMENT struct external_syment
218 | #define SYMESZ sizeof(SYMENT)
219 | #define AUXENT union external_auxent
220 | #define AUXESZ sizeof(AUXENT)
221 |
222 |
223 | # define _ETEXT "etext"
224 |
225 |
226 | /* Relocatable symbols have number of the section in which they are defined,
227 | or one of the following: */
228 |
229 | #define N_UNDEF ((short)0) /* undefined symbol */
230 | #define N_ABS ((short)-1) /* value of symbol is absolute */
231 | #define N_DEBUG ((short)-2) /* debugging symbol -- value is meaningless */
232 | #define N_TV ((short)-3) /* indicates symbol needs preload transfer vector */
233 | #define P_TV ((short)-4) /* indicates symbol needs postload transfer vector*/
234 |
235 | /*
236 | * Type of a symbol, in low N bits of the word
237 | */
238 | #define T_NULL 0
239 | #define T_VOID 1 /* function argument (only used by compiler) */
240 | #define T_CHAR 2 /* character */
241 | #define T_SHORT 3 /* short integer */
242 | #define T_INT 4 /* integer */
243 | #define T_LONG 5 /* long integer */
244 | #define T_FLOAT 6 /* floating point */
245 | #define T_DOUBLE 7 /* double word */
246 | #define T_STRUCT 8 /* structure */
247 | #define T_UNION 9 /* union */
248 | #define T_ENUM 10 /* enumeration */
249 | #define T_MOE 11 /* member of enumeration*/
250 | #define T_UCHAR 12 /* unsigned character */
251 | #define T_USHORT 13 /* unsigned short */
252 | #define T_UINT 14 /* unsigned integer */
253 | #define T_ULONG 15 /* unsigned long */
254 | #define T_LNGDBL 16 /* long double */
255 |
256 | /*
257 | * derived types, in n_type
258 | */
259 | #define DT_NON (0) /* no derived type */
260 | #define DT_PTR (1) /* pointer */
261 | #define DT_FCN (2) /* function */
262 | #define DT_ARY (3) /* array */
263 |
264 | #define BTYPE(x) ((x) & N_BTMASK)
265 |
266 | #define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
267 | #define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
268 | #define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
269 | #define ISTAG(x) ((x)==C_STRTAG||(x)==C_UNTAG||(x)==C_ENTAG)
270 | #define DECREF(x) ((((x)>>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK))
271 |
272 | /********************** STORAGE CLASSES **********************/
273 |
274 | /* This used to be defined as -1, but now n_sclass is unsigned. */
275 | #define C_EFCN 0xff /* physical end of function */
276 | #define C_NULL 0
277 | #define C_AUTO 1 /* automatic variable */
278 | #define C_EXT 2 /* external symbol */
279 | #define C_STAT 3 /* static */
280 | #define C_REG 4 /* register variable */
281 | #define C_EXTDEF 5 /* external definition */
282 | #define C_LABEL 6 /* label */
283 | #define C_ULABEL 7 /* undefined label */
284 | #define C_MOS 8 /* member of structure */
285 | #define C_ARG 9 /* function argument */
286 | #define C_STRTAG 10 /* structure tag */
287 | #define C_MOU 11 /* member of union */
288 | #define C_UNTAG 12 /* union tag */
289 | #define C_TPDEF 13 /* type definition */
290 | #define C_USTATIC 14 /* undefined static */
291 | #define C_ENTAG 15 /* enumeration tag */
292 | #define C_MOE 16 /* member of enumeration */
293 | #define C_REGPARM 17 /* register parameter */
294 | #define C_FIELD 18 /* bit field */
295 | #define C_AUTOARG 19 /* auto argument */
296 | #define C_LASTENT 20 /* dummy entry (end of block) */
297 | #define C_BLOCK 100 /* ".bb" or ".eb" */
298 | #define C_FCN 101 /* ".bf" or ".ef" */
299 | #define C_EOS 102 /* end of structure */
300 | #define C_FILE 103 /* file name */
301 | #define C_LINE 104 /* line # reformatted as symbol table entry */
302 | #define C_ALIAS 105 /* duplicate tag */
303 | #define C_HIDDEN 106 /* ext symbol in dmert public lib */
304 |
305 | /********************** RELOCATION DIRECTIVES **********************/
306 |
307 |
308 |
309 | struct external_reloc {
310 | unsigned int r_vaddr __attribute__((packed));
311 | unsigned int r_symndx __attribute__((packed));
312 | unsigned short r_type;
313 | };
314 |
315 |
316 | #define RELOC struct external_reloc
317 | #define RELSZ sizeof(RELOC)
318 |
319 | #define RELOC_REL32 20 /* 32-bit PC-relative address */
320 | #define RELOC_ADDR32 6 /* 32-bit absolute address */
321 |
322 | #define DEFAULT_DATA_SECTION_ALIGNMENT 4
323 | #define DEFAULT_BSS_SECTION_ALIGNMENT 4
324 | #define DEFAULT_TEXT_SECTION_ALIGNMENT 4
325 | /* For new sections we haven't heard of before */
326 | #define DEFAULT_SECTION_ALIGNMENT 4
327 |
328 | #endif /* !_POSIX_SOURCE */
329 | #endif /* !__STRICT_ANSI__ */
330 | #endif /* !__dj_ENFORCE_ANSI_FREESTANDING */
331 |
332 | #ifndef __dj_ENFORCE_FUNCTION_CALLS
333 | #endif /* !__dj_ENFORCE_FUNCTION_CALLS */
334 |
335 | #ifdef __cplusplus
336 | }
337 | #endif
338 |
339 | #endif /* !__dj_include_coff_h_ */
340 |
--------------------------------------------------------------------------------
/dist/Makefile:
--------------------------------------------------------------------------------
1 | PKGNAME = lotus123r3
2 | VERSION = 1.0
3 | REV = 4
4 |
5 | all: $(PKGNAME)_$(VERSION)-$(REV)_i386.deb
6 |
7 | %.deb: control
8 | mkdir -m0755 -p $(@:.deb=)/DEBIAN
9 | cp control $(@:.deb=)/DEBIAN
10 | $(MAKE) -C .. install prefix="$(CURDIR)/$(@:.deb=)/usr"
11 | dpkg-deb --build --root-owner-group $(@:.deb=)
12 |
13 | clean:
14 | $(RM) *.deb
15 | $(RM) -r $(PKGNAME)_$(VERSION)-$(REV)_i386
16 |
--------------------------------------------------------------------------------
/dist/control:
--------------------------------------------------------------------------------
1 | Package: lotus123r3
2 | Version: 1.0
3 | Architecture: i386
4 | Maintainer: Tavis Ormandy
5 | Description: Create, modify, and process financial or scientific models.
6 | Homepage: https://github.com/taviso/123elf
7 | Depends: lib32tinfo6:amd64 | libtinfo6:i386, libc6-i386:amd64 | libc6:i386, lib32ncurses6:amd64 | libncurses6:i386
8 |
--------------------------------------------------------------------------------
/dist/lotus123r3.spec:
--------------------------------------------------------------------------------
1 | Name: lotus123r3
2 | Version: 1.0.0rc3
3 | Release: 4%{?dist}
4 | Summary: Create, modify, and process financial or scientific models.
5 | License: Abandonware
6 | URL: https://github.com/taviso/123elf
7 | Source0: https://github.com/taviso/123elf/archive/refs/tags/v%{version}.tar.gz
8 | Source1: https://archive.org/compress/123-unix/formats=ISO%20IMAGE&file=/123-unix.zip
9 | Source2: https://ftp.gnu.org/gnu/binutils/binutils-2.38.tar.xz
10 | Requires: ncurses-libs(x86-32) glibc(x86-32)
11 |
12 | %description
13 | The 123 command is a spreadsheet application for UNIX-based systems that can
14 | be used in interactive mode to create and modify financial and scientific
15 | models.
16 |
17 | %global debug_package %{nil}
18 |
19 | %prep
20 | %setup -q -n 123elf-%{version} -a 1 -a 2
21 |
22 | %build
23 | ./extract.sh
24 | ./binutils.sh
25 | make
26 |
27 | %install
28 | make install prefix=%{buildroot}/%{_prefix}
29 |
30 | %files
31 | %{_prefix}/share/lotus
32 | %{_prefix}/share/man/man1/123.1.gz
33 | %{_prefix}/bin/123
34 |
35 | %changelog
36 | * Sun Jul 10 2022 Tavis Ormandy
37 | - New release.
38 | - Make specfile version neutral.
39 |
40 | * Thu Jun 16 2022 Tavis Ormandy
41 | - Initial Version
42 |
--------------------------------------------------------------------------------
/draw.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "lottypes.h"
4 | #include "lotdefs.h"
5 | #include "ttydraw.h"
6 |
7 | // The canvas used for drawing ascii-art graphics.
8 | extern caca_canvas_t *cv;
9 |
10 | void exprt_scan_linx(int x, int y, int width, int attr)
11 | {
12 | caca_set_attr(cv, attr);
13 | caca_draw_thin_line(cv, x, y, x + width, y);
14 | refresh();
15 | return;
16 | }
17 |
18 | void exprt_fill_rect(int x, int y, int width, int height, int attr)
19 | {
20 | caca_set_attr(cv, attr);
21 | caca_fill_box(cv, x, y, width, height, ' ');
22 | refresh();
23 | return;
24 | }
25 |
26 | void exprt_thin_diag_line(int x1, int y1, int x2, int y2, int attr)
27 | {
28 | caca_set_attr(cv, attr);
29 | caca_draw_thin_line(cv, x1, y1, x2, y2);
30 | refresh();
31 | return;
32 | }
33 |
34 | void exprt_thin_vert_line(int x, int y, int height, int attr)
35 | {
36 | caca_set_attr(cv, attr);
37 | caca_draw_thin_line(cv, x, y, x, y + height);
38 | refresh();
39 | return;
40 | }
41 |
42 | // This is called for cross hatching, pattern fills.
43 | void exprt_shade_rect(struct POINT origin,
44 | struct POINT dim,
45 | PATT *fillpat,
46 | uint16_t fillcolor)
47 | {
48 | caca_set_attr(cv, fillcolor);
49 | caca_fill_box(cv, origin.ptx, origin.pty, dim.ptx, dim.pty, fillpat->pattptr[0]);
50 | refresh();
51 | return;
52 | }
53 |
54 | void exprt_fill_scan_list()
55 | {
56 | return;
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/draw.h:
--------------------------------------------------------------------------------
1 | #ifndef __DRAW_H
2 | #define __DRAW_H
3 |
4 | // The canvas used for drawing ascii-art graphics.
5 | extern caca_canvas_t *cv;
6 |
7 | void exprt_scan_linx(int x, int y, int width, int attr);
8 | void exprt_fill_rect(int x, int y, int width, int height, int attr);
9 | void exprt_thin_diag_line(int x1, int y1, int x2, int y2, int attr);
10 | void exprt_thin_vert_line(int x, int y, int height, int attr);
11 | void exprt_shade_rect(int a, int b, int c, int d, void *e, int f);
12 | void exprt_fill_scan_list();
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/extract.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # Extract the necessary files from Lotus 1-2-3 UNIX
4 | #
5 | BASE="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
6 | IMG="${BASE}"
7 | ROOT="${BASE}/share"
8 | ORIG="${BASE}/orig"
9 | ETC="${ROOT}/lotus/etc"
10 | LOTUS="${ROOT}/lotus/123.v10"
11 |
12 | CPIO="$(command -v cpio)"
13 | TAR="$(command -v tar)"
14 |
15 | if [ "$1" = 'clean' ]; then
16 | rm -rfv "${ROOT}" "${ORIG}" "${ETC}"
17 | exit
18 | fi
19 |
20 | # Use any tools placed in this directory first.
21 | export PATH="${BASE}:$PATH"
22 |
23 | if ! test -f "${IMG}/123UNIX1.IMG" \
24 | -a -f "${IMG}/123UNIX2.IMG" \
25 | -a -f "${IMG}/123UNIX3.IMG" \
26 | -a -f "${IMG}/123UNIX4.IMG" \
27 | -a -f "${IMG}/123UNIX5.IMG"; then
28 | echo >&2 'You need to download the original 1-2-3 UNIX IMG files.'
29 | echo >&2 'They are available here: https://archive.org/download/123-unix'
30 | exit 1
31 | fi
32 |
33 | mkdir "${ROOT}"
34 | mkdir "${ORIG}"
35 |
36 | cd "${ROOT}"
37 | echo '==> Extracting 123UNIX1.IMG tar archive'
38 | "${TAR}" xvf "${IMG}/123UNIX1.IMG"
39 | echo '==> Extracting 123UNIX2.IMG cpio archive'
40 | "${CPIO}" -idv < "${IMG}/123UNIX2.IMG"
41 | echo '==> Extracting 123UNIX3.IMG cpio archive'
42 | "${CPIO}" -idv < "${IMG}/123UNIX3.IMG"
43 | echo '==> Seeking into 123UNIX4.IMG to extract cpio archive'
44 | dd if="${IMG}/123UNIX4.IMG" skip=1 bs=550536 | "${CPIO}" -idv
45 | echo '==> Extracting 123UNIX5.IMG cpio archive'
46 | "${CPIO}" -idv < "${IMG}/123UNIX5.IMG"
47 | cd - > /dev/null
48 |
49 | echo '==> Reconstructing object file'
50 |
51 | if ! cat "${LOTUS}"/sysV386/lib/123.o.z_1 "${LOTUS}"/sysV386/lib/123.o.z_2 | zcat > "${ORIG}/123.o"; then
52 | echo >&2 'Failed to decompress object files.'
53 |
54 | echo >&2 'If you see the message "code out of range", gzip is too old.'
55 | echo >&2 'You can try running ./gzip.sh to build a recent gzip that is known to work.'
56 | rm -f "${ORIG}/123.o"
57 | exit 1
58 | fi
59 |
60 | echo '==> Uncompressing .z files'
61 | find "${ROOT}" -iname '*.z' -exec gunzip --force {} \;
62 |
63 | echo '==> Uncompressing and copying remaining object files'
64 | cp "${LOTUS}"/sysV386/lib/*.o "${ORIG}/"
65 |
66 | echo '==> Copying the banner template over'
67 | install -vDm644 "${ROOT}/usr/tmp/lotus_install/123/banner" "${LOTUS}/ri/USA-English/123ban.ri"
68 |
69 | echo '==> Copying default config file'
70 | install -vDm644 l123set.cf "${ETC}/l123set.cf"
71 |
72 | echo '==> Copying man page'
73 | install -vDm644 "${ROOT}/lotus/man/man1/123.1" "${ROOT}/man/man1/123.1"
74 |
--------------------------------------------------------------------------------
/filemap.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | // Figure out where our runtime files are located.
10 | static const char *get_lotus_runtimefile(const char *file)
11 | {
12 | static char *lotusdir;
13 | static char exepath[PATH_MAX];
14 | static char *exedir;
15 | static char filepath[PATH_MAX];
16 | static char localpath[PATH_MAX];
17 |
18 | // Cache this path so it only has to be looked up once.
19 | if (lotusdir == NULL) {
20 | if (readlink("/proc/self/exe", exepath, PATH_MAX) == -1) {
21 | err(EXIT_FAILURE, "Failed to determine the lotus root directory");
22 | }
23 | // Figure out the containing directory from the exe path.
24 | exedir = dirname(exepath);
25 | snprintf(localpath, PATH_MAX, "%s/%s", exedir, "../share/lotus");
26 | lotusdir = localpath;
27 | }
28 |
29 | // Append the requested filename, obviously this is not reentrant, but 123
30 | // does not use threads.
31 | snprintf(filepath, PATH_MAX, "%s/%s", lotusdir, file);
32 | return filepath;
33 | }
34 |
35 | // This routine examines a path requested by Lotus, and will
36 | // map it to something more suitable for use on Linux.
37 | const char * map_unix_pathname(const char *unixpath)
38 | {
39 | const char *filename = strrchr(unixpath, '/');
40 |
41 | if (*unixpath == '\0')
42 | return unixpath;
43 |
44 | // Check if we just need to substitute in the root path.
45 | if (strncmp(unixpath + 1, "{LOTUSROOT}", strlen("{LOTUSROOT}")) == 0) {
46 | return get_lotus_runtimefile(unixpath + 1 + strlen("{LOTUSROOT}"));
47 | }
48 |
49 | if (filename == NULL)
50 | return unixpath;
51 |
52 | if (strcmp(filename, "/.l123set") == 0) {
53 | // This is the 123 configuration file, if a user has not created one
54 | // we can map it to the default configuration file instead, which
55 | // is the directory where 123 is located.
56 | if (access(unixpath, F_OK) != 0) {
57 | return get_lotus_runtimefile("etc/l123set.cf");
58 | }
59 | }
60 |
61 | // No mapping required
62 | return unixpath;
63 | }
64 |
--------------------------------------------------------------------------------
/filemap.h:
--------------------------------------------------------------------------------
1 | #ifndef __FILEMAP_H
2 | #define __FILEMAP_H
3 |
4 | const char * map_unix_pathname(const char *unixpath);
5 |
6 | #endif
7 |
--------------------------------------------------------------------------------
/filename.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "loterrs.h"
9 | #include "lottypes.h"
10 | #include "lotdefs.h"
11 | #include "lotfuncs.h"
12 |
13 | // This breaks the specified pathname into the PATHNAME structure, which
14 | // contains offsets of drive, directory, filename, extension, and so on.
15 | //
16 | // This routine handles LMBCS encoded filesnames in DOS mode only.
17 | // In DOS mode, '\\' or '/' are valid directory seperators, and will be
18 | // converted as requested.
19 | //
20 | // Originally, this routine required filenames on UNIX to be under 14
21 | // characters, but this rewrite removes that limitation, see #82.
22 | //
23 | // - base is an optional base directory to assume is prepended to pathname.
24 | // - extension is a file extension to append.
25 | // - resource..
26 | // - If set, expandpath will expand globs.
27 | // - If set, checklen will validate all components are within limits.
28 | uint16_t file_name_split(struct PATHNAME *pstruct,
29 | char *pathname,
30 | char *root,
31 | char *extension,
32 | int16_t *resource,
33 | int16_t expandpath,
34 | int16_t checklen)
35 | {
36 | char *p;
37 | unsigned int mbcs;
38 | char *prepended_root_after_drv;
39 | char *dirsep;
40 | char *extptr;
41 | int extlen;
42 | int pathend;
43 | uint16_t *fname_content_xlt_tbl;
44 | int16_t pathtype;
45 | uint16_t diroff_1;
46 | uint16_t diroff;
47 | uint16_t offsetofext;
48 | uint16_t result;
49 | char *prepended_root;
50 | char *path_after_drive;
51 | char *lastspace;
52 | char *lastdirptr;
53 | char *filenamestart;
54 | char *fileext;
55 | char *resourcematch;
56 | char reducedpath[PATH_MAX];
57 |
58 | result = 0;
59 |
60 | // The translation table for lmbcs encoded filenames.
61 | fname_content_xlt_tbl = get_fname_content_xlt_tbl(0, 2);
62 |
63 | // If caller requested, validate lengths.
64 | if (checklen && (result = check_lengths(pathname))) {
65 | return result;
66 | }
67 |
68 | // Reset the cracked filename structure.
69 | file_name_clear(pstruct);
70 |
71 | // Validate parameters.
72 | if (!pathname ||!*pathname )
73 | return 0;
74 |
75 | // Skip past any leading blanks.
76 | while (*pathname == ' ')
77 | ++pathname;
78 |
79 | p = pathname;
80 | fileext = NULL;
81 | lastdirptr = NULL;
82 | lastspace = NULL;
83 | path_after_drive = NULL;
84 | resourcematch = NULL;
85 | pathtype = 0; // pathtype 2 = resource?
86 |
87 | // If in DOS mode, check if this looks like a drive.
88 | if (pathname[1] == ':' && file_mode != FILE_MODE_UNIX) {
89 | p = pathname + 2;
90 | path_after_drive = pathname + 2;
91 | }
92 |
93 | // This translates lmbcs codepoints
94 | if (!file_name_reduce(reducedpath, p))
95 | return LOTERR_FILENAME_INVALID;
96 |
97 | // Now check through the whole path for any special charaters.
98 | while (*p) {
99 | switch (*p) {
100 | case ' ':
101 | resourcematch = p;
102 | lastspace = p;
103 | if (!resource_substr_match(0xC28u, &resourcematch))
104 | pathtype = 2;
105 | break;
106 | case '.':
107 | // There can't be two extensions in DOS mode.
108 | if (file_mode != FILE_MODE_UNIX && checklen && fileext)
109 | return LOTERR_FILENAME_INVALID;
110 | // Otherwise, the last . begins the extension.
111 | fileext = p;
112 | break;
113 | case '/':
114 | // The last directory.
115 | lastdirptr = p;
116 | break;
117 | case '<':
118 | case '>':
119 | // These are not permitted in filenames because it's part of the
120 | // linked file range syntax, e.g. <>A1..A5
121 | return LOTERR_FILENAME_INVALID;
122 | case '\\':
123 | // In DOS mode, this is an acceptable dirseperator, otherwise
124 | // it's just a normal char on UNIX.
125 | if (file_mode != FILE_MODE_UNIX)
126 | lastdirptr = p;
127 | break;
128 | default:
129 | // For all other characters, maybe do lmbcs translation.
130 | if (file_mode != FILE_MODE_UNIX) {
131 | mbcs = peek_next_mbcs(p);
132 | if ( mbcs > 0xFF || mbcs <= 0x1F || !fname_content_xlt_tbl[mbcs - 32] )
133 | return LOTERR_FILENAME_INVALID;
134 | }
135 | break;
136 | }
137 |
138 | // If a resource request
139 | if (pathtype == 2)
140 | break;
141 |
142 | // Skip over this char (possibly mb char)
143 | p += char_size(*p);
144 | }
145 |
146 | // Now we've found the extension, filename, and dirname
147 | if (lastspace) {
148 | resourcematch = lastspace;
149 | if (!resource_substr_match(0xC2Bu, &resourcematch) && !peek_next_mbcs(resourcematch)) {
150 | pathtype = 1;
151 | p = lastspace;
152 | }
153 | }
154 |
155 | if (resource)
156 | *resource = pathtype;
157 |
158 | // Is the file extension part of the filename?
159 | if (lastdirptr && fileext && lastdirptr > fileext)
160 | fileext = 0;
161 |
162 | prepended_root_after_drv = 0;
163 | prepended_root = NULL;
164 |
165 | if (file_mode != FILE_MODE_UNIX) {
166 | // This is an optional "root" to prepend
167 | if (root) {
168 | // Check if it's a drive
169 | prepended_root = root;
170 | if (root[1] == ':') {
171 | prepended_root = root + 2;
172 | prepended_root_after_drv = root + 2;
173 | }
174 | }
175 | if (path_after_drive) {
176 | pstruct->diroff = path_after_drive - pathname;
177 | memcpy(pstruct->str, pathname, (path_after_drive - pathname));
178 | pathname = path_after_drive;
179 | } else if (prepended_root_after_drv) {
180 | pstruct->diroff = prepended_root_after_drv - root;
181 | memcpy(pstruct->str, root, pstruct->diroff);
182 | }
183 | }
184 | else {
185 | prepended_root = root;
186 | pstruct->diroff = 0;
187 | path_after_drive = 0;
188 | }
189 | pstruct->start = 0;
190 | diroff_1 = pstruct->diroff;
191 | if (lastdirptr) {
192 | filenamestart = lastdirptr + 1;
193 | pstruct->filenameoff = filenamestart - pathname;
194 | memcpy(&pstruct->str[diroff_1], pathname, (filenamestart - pathname));
195 | pathname = filenamestart;
196 | }
197 | else if (root) {
198 | if (!path_after_drive) {
199 | pstruct->filenameoff = strlen(prepended_root);
200 | memcpy(&pstruct->str[diroff_1], prepended_root, pstruct->filenameoff);
201 | if (pstruct->filenameoff) {
202 | dirsep = &pstruct->str[pstruct->filenameoff + diroff_1 - 1];
203 | if (dirsep[0] != '/' && dirsep[0] != '\\') {
204 | dirsep[1] = '/';
205 | ++pstruct->filenameoff;
206 | }
207 | }
208 | }
209 | }
210 | slash_convert(&pstruct->str[diroff_1], pstruct->filenameoff);
211 | pstruct->diroffpreroot = diroff_1;
212 | diroff = pstruct->filenameoff + diroff_1;
213 | pstruct->dirlen = diroff;
214 | pstruct->namelen = p - pathname;
215 | if (p != pathname) {
216 | if (fileext) {
217 | pstruct->extlen = p - fileext;
218 | pstruct->namelen -= p - fileext;
219 | }
220 | if ( pstruct->extlen + pstruct->namelen + pstruct->dirlen > PATH_MAX) {
221 | return LOTERR_FILENAME_TOO_LONG;
222 | }
223 | memcpy(&pstruct->str[diroff], pathname, pstruct->namelen);
224 | }
225 | offsetofext = pstruct->namelen + diroff;
226 | if (pstruct->extlen) {
227 | memcpy(&pstruct->str[offsetofext], fileext, pstruct->extlen);
228 | } else if (extension) {
229 | pstruct->extlen = strlen(extension) + 1;
230 |
231 | if (pstruct->extlen + pstruct->namelen + pstruct->dirlen > PATH_MAX) {
232 | return LOTERR_FILENAME_TOO_LONG;
233 | }
234 |
235 | pstruct->str[offsetofext] = '.';
236 | extptr = &pstruct->str[offsetofext + 1];
237 | strcpy(extptr, extension);
238 | if (checklen)
239 | fallback_and_coerce_case(extptr);
240 | }
241 |
242 | pstruct->offext = offsetofext;
243 |
244 | // Extension cannot exceed 3 chars (+.) in DOS mode.
245 | if (file_mode != FILE_MODE_UNIX && checklen) {
246 | if (pstruct->extlen > 4)
247 | extlen = 4;
248 | else
249 | extlen = pstruct->extlen;
250 | pathend = extlen + pstruct->offext;
251 | } else {
252 | pathend = pstruct->extlen + pstruct->offext;
253 | }
254 | pstruct->str[pathend] = 0;
255 | if (expandpath)
256 | result = file_path_expand(pstruct);
257 | if (!result && checklen)
258 | file_name_case_check(pstruct->str);
259 | if (pstruct->extlen && !pstruct->namelen) {
260 | if (pstruct->filenameoff || pstruct->diroff || pstruct->extlen != 1)
261 | return LOTERR_WORKSHEET_UNNAMED;
262 | pstruct->namelen = 1;
263 | pstruct->extlen = 0;
264 | pstruct->offext = 1;
265 | }
266 | return result;
267 | }
268 |
269 |
--------------------------------------------------------------------------------
/forceplt.s:
--------------------------------------------------------------------------------
1 | .text
2 | .type __require_ref,@function
3 | __require_ref:
4 | call a64l@plt
5 | call abort@plt
6 | call abs@plt
7 | call access@plt
8 | call acct@plt
9 | call alarm@plt
10 | call asctime@plt
11 | call atof@plt
12 | call atoi@plt
13 | call atol@plt
14 | call bsearch@plt
15 | call calloc@plt
16 | call chdir@plt
17 | call chmod@plt
18 | call chroot@plt
19 | call clearerr@plt
20 | call clock@plt
21 | call closedir@plt
22 | call copysign@plt
23 | call creat@plt
24 | call ctermid@plt
25 | call ctime@plt
26 | call cuserid@plt
27 | call daylight@plt
28 | call drand48@plt
29 | call dup2@plt
30 | call dup@plt
31 | call eaccess@plt
32 | call ecvt@plt
33 | call endgrent@plt
34 | call endpwent@plt
35 | call endutent@plt
36 | call erand48@plt
37 | call execle@plt
38 | call execl@plt
39 | call execlp@plt
40 | call execve@plt
41 | call execv@plt
42 | call execvp@plt
43 | call _exit@plt
44 | call exit@plt
45 | call fcvt@plt
46 | call free@plt
47 | call frexp@plt
48 | call fstatfs@plt
49 | call fstat@plt
50 | call ftok@plt
51 | call ftw@plt
52 | call gcvt@plt
53 | call getchar@plt
54 | call getcwd@plt
55 | call getegid@plt
56 | call getenv@plt
57 | call geteuid@plt
58 | call getuid@plt
59 | call getgid@plt
60 | call getgrent@plt
61 | call getgrgid@plt
62 | call getgrnam@plt
63 | call getlogin@plt
64 | call getopt@plt
65 | call getpass@plt
66 | call getpgrp@plt
67 | call getpid@plt
68 | call getppid@plt
69 | call getpwent@plt
70 | call getpwnam@plt
71 | call getpwuid@plt
72 | call getutent@plt
73 | call getutid@plt
74 | call getutline@plt
75 | call getw@plt
76 | call gmtime@plt
77 | call gsignal@plt
78 | call hcreate@plt
79 | call hdestroy@plt
80 | call hsearch@plt
81 | call isatty@plt
82 | call jrand48@plt
83 | call kill@plt
84 | call l64a@plt
85 | call labs@plt
86 | call lcong48@plt
87 | call ldexp@plt
88 | call lfind@plt
89 | call link@plt
90 | call localtime@plt
91 | call lockf@plt
92 | call lrand48@plt
93 | call lsearch@plt
94 | call lseek@plt
95 | call mallinfo@plt
96 | call malloc@plt
97 | call mallopt@plt
98 | call _mcount@plt
99 | call memccpy@plt
100 | call memchr@plt
101 | call memcmp@plt
102 | call memcpy@plt
103 | call memmove@plt
104 | call memset@plt
105 | call mkdir@plt
106 | call mknod@plt
107 | call mlock@plt
108 | call modf@plt
109 | call mount@plt
110 | call mrand48@plt
111 | call msgget@plt
112 | call msgrcv@plt
113 | call msgsnd@plt
114 | call nice@plt
115 | call nrand48@plt
116 | call opendir@plt
117 | call open@plt
118 | call pause@plt
119 | call perror@plt
120 | call poll@plt
121 | call printf@plt
122 | call profil@plt
123 | call ptrace@plt
124 | call putchar@plt
125 | call putenv@plt
126 | call putpwent@plt
127 | call puts@plt
128 | call pututline@plt
129 | call putw@plt
130 | call qsort@plt
131 | call rand@plt
132 | call readdir@plt
133 | call read@plt
134 | call realloc@plt
135 | call rename@plt
136 | call rewind@plt
137 | call rmdir@plt
138 | call sbrk@plt
139 | call scanf@plt
140 | call seed48@plt
141 | call seekdir@plt
142 | call semget@plt
143 | call semop@plt
144 | call setbuf@plt
145 | call setgid@plt
146 | call setgrent@plt
147 | call setpgrp@plt
148 | call setpwent@plt
149 | call setuid@plt
150 | call setutent@plt
151 | call setvbuf@plt
152 | call shmat@plt
153 | call shmdt@plt
154 | call shmget@plt
155 | call sighold@plt
156 | call sigignore@plt
157 | call signal@plt
158 | call sigpause@plt
159 | call sigrelse@plt
160 | call sigset@plt
161 | call sleep@plt
162 | call sprintf@plt
163 | call srand48@plt
164 | call srand@plt
165 | call sscanf@plt
166 | call ssignal@plt
167 | call statfs@plt
168 | call stat@plt
169 | call strcat@plt
170 | call strchr@plt
171 | call strcmp@plt
172 | call strcpy@plt
173 | call strcspn@plt
174 | call strdup@plt
175 | call strlen@plt
176 | call strncat@plt
177 | call strncmp@plt
178 | call strncpy@plt
179 | call strpbrk@plt
180 | call strrchr@plt
181 | call strspn@plt
182 | call strtod@plt
183 | call strtok@plt
184 | call strtol@plt
185 | call swab@plt
186 | call sync@plt
187 | call syscall@plt
188 | call system@plt
189 | call tdelete@plt
190 | call telldir@plt
191 | call tfind@plt
192 | call time@plt
193 | call times@plt
194 | call timezone@plt
195 | call tolower@plt
196 | call toupper@plt
197 | call tsearch@plt
198 | call ttyname@plt
199 | call ttyslot@plt
200 | call twalk@plt
201 | call tzname@plt
202 | call tzset@plt
203 | call ulimit@plt
204 | call umask@plt
205 | call umount@plt
206 | call uname@plt
207 | call ungetc@plt
208 | call unlink@plt
209 | call utime@plt
210 | call utmpname@plt
211 | call vfprintf@plt
212 | call vprintf@plt
213 | call vsprintf@plt
214 | call wait@plt
215 | call write@plt
216 | call close@plt
217 | call tgetent@plt
218 | call tgetstr@plt
219 | call tgetflag@plt
220 | call tgetnum@plt
221 | call tgoto@plt
222 | call tputs@plt
223 | call tmpfile@plt
224 | call chown@plt
225 | call msgctl@plt
226 | call semctl@plt
227 | call shmctl@plt
228 | call fstat64@plt
229 | call stat64@plt
230 | call atexit@plt
231 | call sin@plt
232 | call cos@plt
233 | call tan@plt
234 | call atan2@plt
235 | call asin@plt
236 | call acos@plt
237 | call remainder@plt
238 | call sqrt@plt
239 | call log@plt
240 | call log10@plt
241 | call floor@plt
242 | call ceil@plt
243 | call fmod@plt
244 | call fabs@plt
245 |
--------------------------------------------------------------------------------
/globalize.lst:
--------------------------------------------------------------------------------
1 | # vim: set ft=conf:
2 | #
3 | # Note: blank lines are not permitted.
4 | #
5 | # These are symbols that are not visible, but should be so that we
6 | # can call them from our code.
7 | #
8 | # It is also useful to sometimes list symbols here you want gdb to be able to
9 | # see when debugging.
10 | #
11 | lexit
12 | read_setup_file
13 | get_number
14 | read_startup_ri_file
15 | get_info
16 | get_dirname
17 | scr_status_to_errcode
18 | getseq
19 | syserrclr
20 | getshort
21 | getlong
22 | keyreader
23 | set_raw_mode
24 | input_task
25 | waiting_for_chunk
26 | get_kfun
27 | repaint
28 | filerange_lock_unlock
29 | file_lock
30 | get_column_labels
31 | RastHandle
32 | V3_disp_grph_compute_view
33 | V3_disp_grph_process
34 | V3_disp_grph_set_cur_view
35 | x_disp_graph
36 | x_disp_text
37 | x_disp_grph_compute_view
38 | x_disp_grph_process
39 | x_disp_grph_set_cur_view
40 | x_disp_grph_txt_fit
41 | x_disp_grph_txt_size
42 | x_disp_info
43 | x_disp_open
44 | x_disp_close
45 | x_disp_close_retain_termcap
46 | x_disp_grph_load_font
47 | tty_disp_open
48 | core_funcs
49 | dliopen
50 | dliclose
51 | opcodes
52 | MapX
53 | MapY
54 | banner_printed
55 | at_date
56 | encode_date
57 | check_three_numbers
58 | get_integer
59 | need_to_close
60 | clear_screen_buffer
61 | opline
62 | get_screen_size
63 | slash_convert
64 | fname_real_len
65 | file_path_expand
66 | check_lengths
67 | vpos
68 | inprint
69 | display_region
70 | origx
71 | origy
72 | origz
73 | region
74 | setup_column_widths
75 | setup_invalid_columns
76 | display_scan_row
77 | region_top
78 | row_set_pos
79 | row_valid
80 | cell_col_invalid
81 | display_date
82 | display_filename
83 | filename_length
84 | mac_nobreak
85 | mac_str
86 | drop_one_push_stack_string
87 | drop_one_push_err
88 | get_allow_autoexec
89 |
--------------------------------------------------------------------------------
/graphics.h:
--------------------------------------------------------------------------------
1 | #ifndef __GRAPHICS_H
2 | #define __GRAPHICS_H
3 |
4 | // These are the rasterizer opcodes.
5 | enum {
6 | ROP_JMP,
7 | ROP_NOP,
8 | ROP_SETLINECOLOR,
9 | ROP_SETLINEPATTERN,
10 | ROP_SETLINEWIDTH,
11 | ROP_SETFILLCOLOR,
12 | ROP_SETFILLPATTERN,
13 | ROP_UNDEFINED,
14 | ROP_SETTEXTSIZE,
15 | ROP_SETTEXTALIGN,
16 | ROP_SETTEXTANGLE,
17 | ROP_SETTEXTSTYLE,
18 | ROP_SETMARKERSIZE,
19 | ROP_SETMARKERSYMBOL,
20 | ROP_CLEARRECTANGLE,
21 | ROP_DRAWSINGLELINE,
22 | ROP_DRAWRECTANGLEEDGES,
23 | ROP_DRAWRECTANGLEINTERIOR,
24 | ROP_DRAWRECTANGLE,
25 | ROP_DRAWSLICEEDGES,
26 | ROP_DRAWSLICEINTERIOR,
27 | ROP_DRAWSLICE,
28 | ROP_DRAWARC,
29 | ROP_DRAWPOLYLINE,
30 | ROP_DRAWPOLYGONINTERIOR,
31 | ROP_DRAWPOLYGON,
32 | ROP_DRAWTEXT,
33 | ROP_DRAWMARKER,
34 | };
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/gzip.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is only necessary if you have a *very* old gzip.
4 | #
5 | GZIP_DL="https://ftp.gnu.org/gnu/gzip"
6 | GZIP_XZ="gzip-1.12.tar.xz"
7 | GZIP_DIR="$(basename $GZIP_XZ .tar.xz)"
8 | GZIP_URL="$GZIP_DL/$GZIP_XZ"
9 | ORIG_DIR="$(pwd)"
10 |
11 | # Dont continue on error.
12 | set -e
13 |
14 | # Help text.
15 | if [ "$1" = '-h' ]; then
16 | echo >&2 "Usage: $0 [clean]"
17 | echo >&2
18 | echo >&2 'Specify "clean" to remove gzip, otherwise it will be downloaded and built.'
19 | exit 1
20 | fi
21 |
22 | # Optional cleanup if requested.
23 | if [ "$1" = 'clean' ]; then
24 | rm -fv gzip gunzip zcat "$GZIP_XZ"
25 | rm -rfv "$GZIP_DIR"
26 | exit
27 | fi
28 |
29 | # Download gzip.
30 | if [ ! -f "$GZIP_XZ" ]; then
31 | wget "$GZIP_URL"
32 | fi
33 |
34 | # Extract gzip.
35 | if [ ! -d "$GZIP_DIR" ]; then
36 | tar xf "$GZIP_XZ"
37 | fi
38 |
39 | # Compile gzip.
40 | if [ ! -x "$GZIP_DIR/gzip" ]; then
41 | cd "$GZIP_DIR"
42 | ./configure
43 | make -j$(nproc)
44 | fi
45 |
46 | # Copy compiled binaries to working directory.
47 | copy() {
48 | test ! -x "$2" && cp -v "$1" "$2"
49 | }
50 |
51 | cd "$ORIG_DIR"
52 | copy "$GZIP_DIR/gzip" gzip
53 | copy "$GZIP_DIR/gunzip" gunzip
54 | copy "$GZIP_DIR/zcat" zcat
55 |
--------------------------------------------------------------------------------
/import.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 |
11 | #include "lottypes.h"
12 | #include "lotdefs.h"
13 | #include "lotfuncs.h"
14 | #include "loterrs.h"
15 | #include "atfuncs.h"
16 |
17 | // This is a reimplementation of the /File Import code
18 | // with the line length limits removed.
19 | uint16_t fm_file_import(char *filename, int16_t textmode)
20 | {
21 | uint16_t result;
22 | int16_t rdcol;
23 | int16_t mbcs;
24 | struct CELLCOORD dstcell;
25 | struct CELLCOORD rdcell;
26 | int16_t len;
27 | struct CELLCOORD nextcell;
28 | int16_t sbcs;
29 | char *base;
30 | int retcode;
31 | int readret;
32 | uint16_t *table;
33 | char *outptr;
34 | char *label;
35 | uint16_t readcnt;
36 | struct SYSHANDLE *fd;
37 | struct CELLCOORD cp;
38 | static char outbuf[4096];
39 | static char inbuf[4096];
40 | struct PATHNAME *pathname;
41 |
42 | pathname = alloca(sizeof *pathname + PATH_MAX);
43 |
44 | memset(pathname, 0, sizeof *pathname + PATH_MAX);
45 |
46 | fd = INVALID_HANDLE_VALUE;
47 | cp = get_cellpointer();
48 |
49 | if (file_mode != FILE_MODE_UNIX) {
50 | base = access_resource(0xC42u);
51 | result = file_name_compose(filename, pathname, base, 1);
52 | } else {
53 | result = file_name_compose(filename, pathname, 0, 1);
54 | }
55 |
56 | if (result != 0)
57 | return result;
58 |
59 | if (print_file_in_use(pathname))
60 | return LOTERR_FILE_IN_USE;
61 |
62 | if ((retcode = file_access_read(pathname, &fd, 0)))
63 | return get_resource_handle(retcode);
64 |
65 | vmr[3] = (void *) filename;
66 |
67 | table = get_fname_content_xlt_tbl(filename, 1);
68 |
69 | // This doesn't seem possible, but the original checks.
70 | if (cp.row > MAX_ROW)
71 | goto finished;
72 |
73 | while (true) {
74 | readret = file_read_line(&fd->fd, sizeof inbuf, inbuf, &readcnt);
75 |
76 | if (readret && readcnt == 0)
77 | break;
78 |
79 | if (readcnt) {
80 | if (convert_from_table(table, inbuf, outbuf, sizeof outbuf) == 0) {
81 | result = 9105;
82 | goto finished;
83 | }
84 |
85 | // This is the /File Import Text option
86 | if (textmode) {
87 | // Check we can modify the cell first.
88 | if ((result = cell_immutable(cp)))
89 | goto finished;
90 |
91 | // Everything read becomes a label.
92 | if ((result = make_label_cell(cp, outbuf, '\'')))
93 | goto finished;
94 | } else {
95 | // Reset column after every line.
96 | rdcol = cp.col;
97 | outptr = outbuf;
98 | while (true) {
99 |
100 | // Check for end of string.
101 | if ((sbcs = peek_next_sbcs(outptr)) == '\0')
102 | break;
103 |
104 | // Check if this is quoted.
105 | if (sbcs == '"') {
106 | // This is a label, skip past opening quote.
107 | skip_next_mbcs(&outptr);
108 |
109 | // This is the start of a label.
110 | label = outptr;
111 |
112 | // Find the closing quote.
113 | if (find_first_sbcs(&outptr, '"')) {
114 | mbcs = skip_next_mbcs(&outptr);
115 | // Set the quote to end of string.
116 | // The original does this, but why not just *outptr = '\0'?
117 | outptr[-mbcs] = 0;
118 | } else {
119 | // No closing quote, just use the end.
120 | get_string_end(&outptr);
121 | }
122 |
123 | if ((result = import_outof_bounds(rdcol, cp.row)))
124 | goto finished;
125 |
126 | dstcell = xyz2coord(rdcol, cp.row, cp.sheet);
127 |
128 | if ((result = cell_immutable(dstcell)))
129 | goto finished;
130 |
131 | rdcell = xyz2coord(rdcol++, cp.row, cp.sheet);
132 | // Make cell a label.
133 | if ((result = make_label_cell(rdcell, label, '\'')))
134 | goto finished;
135 | } else {
136 | // This is a number.
137 | len = strlen(outptr);
138 | readcnt = parse_number(outptr, len);
139 | if ( readcnt ) {
140 | result = import_outof_bounds(rdcol, cp.row);
141 | if ( result || ((result = cell_immutable(xyz2coord(rdcol, cp.row, cp.sheet))) != 0)) {
142 | drop_one();
143 | goto finished;
144 | }
145 | nextcell = xyz2coord(rdcol++, cp.row, cp.sheet);
146 |
147 | if ((result = make_number_cell(nextcell)))
148 | goto finished;
149 |
150 | outptr += readcnt;
151 | } else {
152 | skip_next_mbcs(&outptr);
153 | }
154 | }
155 | }
156 | }
157 | }
158 | if (readret == 1) {
159 | result = 0;
160 | goto finished;
161 | }
162 |
163 | // This line is complete, try the next row.
164 | if (++cp.row > MAX_ROW)
165 | goto finished;
166 | }
167 |
168 | // If we reach here, everything worked.
169 | result = 0;
170 |
171 | if (readret != 1)
172 | result = 8980;
173 |
174 | finished:
175 | sheet_modified(cp.sheet);
176 | return file_finished_shell(&fd, result);
177 | }
178 |
--------------------------------------------------------------------------------
/keymap/Makefile:
--------------------------------------------------------------------------------
1 | LDLIBS = -lncurses -ltinfo
2 | CFLAGS = -ggdb3 -O0
3 | CPPFLAGS = -I.. -W -Wall -Wno-missing-field-initializers -Wno-sign-compare -Wno-unused-parameter -Wno-pointer-sign
4 | LDFLAGS = $(CFLAGS)
5 |
6 | .PHONY: clean
7 |
8 | all: keymap
9 |
10 | clean:
11 | $(RM) *.o keymap
12 |
--------------------------------------------------------------------------------
/keymap/keymap.bt:
--------------------------------------------------------------------------------
1 | // Lotus 1-2-3 for UNIX SystemV Keymap File
2 | //
3 | // Tavis Ormandy
4 | //
5 | // vim:set ft=c:
6 |
7 | struct KEYNODE {
8 | uint32 nextkeyhi ;
9 | uint32 nextkeylo ;
10 | };
11 |
12 | struct KEYDATA {
13 | uint32 next ;
14 | uint32 timeout ;
15 | };
16 |
17 | typedef struct {
18 | uint16 keyval ;
19 | uint16 kfun ;
20 | if (keyval != 0x100) {
21 | struct KEYNODE node;
22 | } else {
23 | struct KEYDATA data;
24 | }
25 | uint32 next ;
26 | } KEYINFO ;
27 |
28 | struct {
29 | uint16 Magic;
30 | uint16 Version;
31 | uint32 Zero;
32 | uint32 KeyTreeSize;
33 | uint32 KeyDataSize;
34 | uint32 Root ;
35 | } HEADER;
36 |
37 | // Calculate how many nodes there are in the tree.
38 | local int KeyTreeNodes = HEADER.KeyTreeSize / sizeof(KEYINFO);
39 |
40 | // The serialized nodes.
41 | KEYINFO KeyTree[KeyTreeNodes] ;
42 |
43 | // Number of Key names/sequences.
44 | uint32 NumKeys;
45 |
46 | uint16 FuncCodes[KeyTreeNodes];
47 |
48 | struct {
49 | string Name;
50 | } KeyNames[NumKeys] ;
51 |
52 | struct {
53 | string Seq;
54 | } EscSequence[NumKeys] ;
55 |
56 | uint32 AdjustOffset(uint32 offset)
57 | {
58 | return offset ? startof(KeyTree) + offset - sizeof(HEADER) : offset;
59 | }
60 | string FileOffset(uint32 root)
61 | {
62 | return Str("%xh", AdjustOffset(root));
63 | }
64 |
65 | string KeyName(KEYINFO &key)
66 | {
67 | string name;
68 |
69 | switch (key.keyval) {
70 | case 0: return "\\0";
71 | case 8: return "\\b";
72 | case 9: return "\\t";
73 | case 12: return "\\f";
74 | case 13: return "\\r";
75 | case 27: return "\\e";
76 | case 32: return "SPC";
77 | case '[':
78 | case ']':
79 | case ';':
80 | case '~':
81 | SPrintf(name, "%c", key.keyval);
82 | return name;
83 | }
84 |
85 | if (IsCharAlphaNum(key.keyval)) {
86 | SPrintf(name, "%c", key.keyval);
87 | } else if (key.keyval < 0x100) {
88 | SPrintf(name, "\\x%#02x", key.keyval);
89 | } else if (key.keyval == 0x100) {
90 | return "ENDSEQ";
91 | } else {
92 | SPrintf(name, "%#02x", key.keyval);
93 | }
94 | return name;
95 | }
96 |
--------------------------------------------------------------------------------
/keymap/keymap.h:
--------------------------------------------------------------------------------
1 | #ifndef __KEYMAP_H
2 | #define __KEYMAP_H
3 |
4 | #pragma pack(1)
5 |
6 | typedef struct KEYINFO {
7 | uint16_t keyval;
8 | uint16_t kfun;
9 | union {
10 | struct KEYNODE {
11 | uint32_t nextkey_hi;
12 | uint32_t nextkey_lo;
13 | } node;
14 | struct KEYDATA {
15 | uint32_t next;
16 | uint32_t timeout;
17 | } data;
18 | };
19 | uint32_t next;
20 | } KEYINFO, *PKEYINFO;
21 |
22 | typedef struct KEYMAP {
23 | uint16_t magic;
24 | uint16_t version;
25 | uint32_t zero;
26 | uint32_t keytreesize;
27 | uint32_t keydatasize;
28 | uint32_t root;
29 | } KEYMAP, *PKEYMAP;
30 |
31 | #define MAX_ALTCAPS 8
32 |
33 | typedef struct KEYDEF {
34 | const char *name;
35 | const char *cap;
36 | uint16_t kfun;
37 | const char *kseq;
38 | const char *altcaps[MAX_ALTCAPS];
39 | } KEYDEF, *PKEYDEF;
40 |
41 | // The offsets in the KEYINFO tree are encoded in an unusual way, these
42 | // inline functions abstract away the ugly casting required to use them
43 | // when traversing the tree.
44 | //
45 | // Given the base of a keytree and an offset, give me a new node pointer.
46 | static inline PKEYINFO keyptr(const PKEYINFO base, uint32_t offset)
47 | {
48 | if (offset == 0)
49 | return NULL;
50 |
51 | return (PKEYINFO)((uintptr_t)(base) + offset - sizeof(KEYMAP));
52 | }
53 | // Given the base of a keytree and an entry, return the array index.
54 | static inline int keyidx(const PKEYINFO base, const PKEYINFO state)
55 | {
56 | return ((uintptr_t)(state) - (uintptr_t)(base)) / sizeof (KEYINFO);
57 | }
58 | // Given the base of a keytree and an entry, return an offset suitable for
59 | // storing as node ptr.
60 | static inline int keyoff(const PKEYINFO base, const PKEYINFO state)
61 | {
62 | return sizeof(KEYMAP) + (uintptr_t)(state) - (uintptr_t)(base);
63 | }
64 |
65 | #define KEY_TIMEOUT 0x100
66 |
67 | #endif
68 |
--------------------------------------------------------------------------------
/keymap/lmbcs.h:
--------------------------------------------------------------------------------
1 | #ifndef __LMBCS_H
2 | #define __LMBCS_H
3 |
4 | // This is a table of LMBCS codes and their UTF-8 equivalent sequence.
5 | // See Appendix 2 of the 1-2-3 R3.1 Reference.
6 | //
7 | // The intention here is that we can encode UTF-8 input to the internal
8 | // lmbcs sequence directly from the keymap. As a user enters a UTF-8
9 | // character, we can check if there is a LMBCS encoding and use that.
10 | //
11 | // This only works for characters that have a LMBCS encoding, but it's a start.
12 | // See issue #73.
13 | //
14 | // Eventually, 123 should use UTF-8 instead of LMBCS.
15 | //
16 | // Known problems:
17 | // - LMBCS has a diacritic symbols, should they be comibining or spacing?
18 | // - There does not seem to be a Krone symbol in unicode.
19 | // - LMBCS has the character "l bullet", what is that?
20 | // - LMBCS has box drawing characters, but doesn't specify light/heavy/etc.
21 | static struct {
22 | uint16_t lmbcs;
23 | const char *str;
24 | } lmbcs_input_translate[] = {
25 | { 128, "Ç" },
26 | { 129, "ü" },
27 | { 130, "é" },
28 | { 131, "â" },
29 | { 132, "ä" },
30 | { 133, "à" },
31 | { 134, "å" },
32 | { 135, "ç" },
33 | { 136, "ê" },
34 | { 137, "ë" },
35 | { 138, "è" },
36 | { 139, "ï" },
37 | { 140, "î" },
38 | { 141, "ì" },
39 | { 142, "Ä" },
40 | { 143, "Å" },
41 | { 144, "É" },
42 | { 145, "æ" },
43 | { 146, "Æ" },
44 | { 147, "ô" },
45 | { 148, "ö" },
46 | { 149, "ò" },
47 | { 150, "ü" },
48 | { 151, "ù" },
49 | { 152, "ÿ" },
50 | { 153, "Ö" },
51 | { 154, "Ü" },
52 | { 155, "ø" },
53 | { 156, "£" },
54 | { 157, "Ø" },
55 | { 158, "×" },
56 | { 159, "ƒ" },
57 | { 160, "á" },
58 | { 161, "í" },
59 | { 162, "ó" },
60 | { 163, "ú" },
61 | { 164, "ñ" },
62 | { 165, "Ñ" },
63 | { 166, "ª" },
64 | { 167, "º" },
65 | { 168, "¿" },
66 | { 169, "®" },
67 | { 170, "¬" },
68 | { 171, "½" },
69 | { 172, "¼" },
70 | { 173, "¡" },
71 | { 174, "«" },
72 | { 175, "»" },
73 | { 176, "░" },
74 | { 177, "▒" },
75 | { 178, "▓" },
76 | { 179, "│" },
77 | { 180, "┤" },
78 | { 181, "Á" },
79 | { 182, "Ä" },
80 | { 183, "À" },
81 | { 184, "©" },
82 | { 185, "╣" },
83 | { 186, "║" },
84 | { 187, "╗" },
85 | { 188, "╝" },
86 | { 189, "¢" },
87 | { 190, "¥" },
88 | { 191, "┐" },
89 | { 192, "└" },
90 | { 193, "┴" },
91 | { 194, "┬" },
92 | { 195, "├" },
93 | { 196, "─" },
94 | { 197, "┼" },
95 | { 198, "ã" },
96 | { 199, "Ã" },
97 | { 200, "╚" },
98 | { 201, "╔" },
99 | { 202, "╩" },
100 | { 203, "╦" },
101 | { 204, "╠" },
102 | { 205, "═" },
103 | { 206, "╬" },
104 | { 207, "¤" },
105 | { 207, "€" },
106 | { 208, "ð" },
107 | { 209, "Ð" },
108 | { 210, "Ê" },
109 | { 211, "Ë" },
110 | { 212, "È" },
111 | { 213, "ı" },
112 | { 214, "Í" },
113 | { 215, "Î" },
114 | { 216, "Ï" },
115 | { 217, "┘" },
116 | { 218, "┌" },
117 | { 219, "█" },
118 | { 220, "▄" },
119 | { 221, "╎" },
120 | { 222, "Ì" },
121 | { 223, "▀" },
122 | { 224, "Ó" },
123 | { 225, "ß" },
124 | { 226, "Ô" },
125 | { 227, "Ò" },
126 | { 228, "õ" },
127 | { 229, "Õ" },
128 | { 230, "μ" },
129 | { 231, "þ" },
130 | { 232, "Þ" },
131 | { 233, "Ú" },
132 | { 234, "Û" },
133 | { 235, "Ù" },
134 | { 236, "ý" },
135 | { 237, "Ý" },
136 | { 238, "‾" },
137 | { 239, "´" },
138 | { 240, "‐" },
139 | { 241, "±" },
140 | { 242, "‗" },
141 | { 243, "¾" },
142 | { 244, "¶" },
143 | { 245, "§" },
144 | { 246, "÷" },
145 | { 247, "¸" },
146 | { 248, "°" },
147 | { 249, "¨" },
148 | { 250, "·" },
149 | { 251, "¹" },
150 | { 252, "³" },
151 | { 253, "²" },
152 | { 254, "▪" },
153 | { 257, "☺" },
154 | { 258, "☻" },
155 | { 259, "♥" },
156 | { 260, "♦" },
157 | { 261, "♣" },
158 | { 262, "♠" },
159 | { 263, "•" },
160 | { 264, "◘" },
161 | { 265, "○" },
162 | { 266, "◙" },
163 | { 267, "♂" },
164 | { 268, "♀" },
165 | { 269, "♪" },
166 | { 270, "♫" },
167 | { 271, "☼" },
168 | { 272, "►" },
169 | { 273, "◄" },
170 | { 274, "↕" },
171 | { 275, "‼" },
172 | { 276, "¶" },
173 | { 277, "§" },
174 | { 278, "▬" },
175 | { 279, "↨" },
176 | { 280, "↑" },
177 | { 281, "↓" },
178 | { 282, "→" },
179 | { 283, "←" },
180 | { 284, "∟" },
181 | { 285, "↔" },
182 | { 286, "▲" },
183 | { 287, "▼" },
184 | { 288, "¨" },
185 | { 289, "˜" },
186 | { 290, "˚" },
187 | { 291, "ˆ" },
188 | { 292, "ˋ" },
189 | { 293, "´" },
190 | { 294, "ˮ" },
191 | { 295, "‛" },
192 | { 296, "…" },
193 | { 297, "–" },
194 | { 298, "—" },
195 | { 302, "⟨" },
196 | { 303, "⟩" },
197 | { 304, "¨" },
198 | { 305, "˜" },
199 | { 306, "˚" },
200 | { 307, "ˆ" },
201 | { 308, "ˋ" },
202 | { 309, "ˊ" },
203 | { 310, "„" },
204 | { 311, "‚" },
205 | { 312, "”" },
206 | { 313, "‗" },
207 | { 320, "Œ" },
208 | { 321, "œ" },
209 | { 322, "Ÿ" },
210 | { 326, "╞" },
211 | { 327, "╟" },
212 | { 328, "▌" },
213 | { 329, "▐" },
214 | { 336, "╧" },
215 | { 337, "╤" },
216 | { 338, "╥" },
217 | { 339, "╙" },
218 | { 340, "╘" },
219 | { 341, "╒" },
220 | { 342, "╓" },
221 | { 343, "╫" },
222 | { 344, "╪" },
223 | { 345, "╡" },
224 | { 346, "╢" },
225 | { 347, "╖" },
226 | { 348, "╕" },
227 | { 349, "╜" },
228 | { 350, "╛" },
229 | { 351, "╧" },
230 | { 352, "ij" },
231 | { 353, "IJ" },
232 | { 354, "fi" },
233 | { 355, "fl" },
234 | { 356, "ʼn" },
235 | { 368, "†" },
236 | { 369, "‡" },
237 | { 374, "™" },
238 | { 375, "ℓ" },
239 | { 381, "⌐" },
240 | { 382, "₺" },
241 | { 383, "₧" },
242 | { 0 },
243 | };
244 |
245 | #endif
246 |
--------------------------------------------------------------------------------
/kfun.h:
--------------------------------------------------------------------------------
1 | #ifndef __KFUN_H
2 | #define __KFUN_H
3 |
4 | #pragma pack(1)
5 |
6 | enum {
7 | KFUN_HOME = 0x1801,
8 | KFUN_FIRST_CELL = 0x1802,
9 | KFUN_END = 0x1803,
10 | KFUN_UP = 0x1804,
11 | KFUN_DOWN = 0x1805,
12 | KFUN_LEFT = 0x1806,
13 | KFUN_RIGHT = 0x1807,
14 | KFUN_PG_UP = 0x1808,
15 | KFUN_PG_DN = 0x1809,
16 | KFUN_BIG_LEFT = 0x180a,
17 | KFUN_BIG_RIGHT = 0x180b,
18 | KFUN_TAB = 0x180c,
19 | KFUN_WINDOW = 0x180d,
20 | KFUN_NEXT_SHEET = 0x180e,
21 | KFUN_PREV_SHEET = 0x180f,
22 | KFUN_BREAK = 0x1810,
23 | KFUN_ESC = 0x1811,
24 | KFUN_RETURN = 0x1812,
25 | KFUN_INS = 0x1813,
26 | KFUN_DEL = 0x1814,
27 | KFUN_BACKSPACE = 0x1815,
28 | KFUN_HELP = 0x1816,
29 | KFUN_ABS = 0x1817,
30 | KFUN_GOTO = 0x1818,
31 | KFUN_CALC = 0x1819,
32 | KFUN_EDIT = 0x181a,
33 | KFUN_NAME = 0x181b,
34 | KFUN_ZOOM = 0x181c,
35 | KFUN_GRAPH = 0x181d,
36 | KFUN_TABLE = 0x181e,
37 | KFUN_QUERY = 0x181f,
38 | KFUN_UNDO = 0x1820,
39 | KFUN_COMPOSE = 0x1821,
40 | KFUN_FILE = 0x1822,
41 | KFUN_HAL = 0x1823,
42 | KFUN_ADDIN = 0x1824,
43 | KFUN_APP1 = 0x1825,
44 | KFUN_APP2 = 0x1826,
45 | KFUN_APP3 = 0x1827,
46 | KFUN_MAC_A = 0x1828,
47 | KFUN_MAC_B = 0x1829,
48 | KFUN_MAC_C = 0x182a,
49 | KFUN_MAC_D = 0x182b,
50 | KFUN_MAC_E = 0x182c,
51 | KFUN_MAC_F = 0x182d,
52 | KFUN_MAC_G = 0x182e,
53 | KFUN_MAC_H = 0x182f,
54 | KFUN_MAC_I = 0x1830,
55 | KFUN_MAC_J = 0x1831,
56 | KFUN_MAC_K = 0x1832,
57 | KFUN_MAC_L = 0x1833,
58 | KFUN_MAC_M = 0x1834,
59 | KFUN_MAC_N = 0x1835,
60 | KFUN_MAC_O = 0x1836,
61 | KFUN_MAC_P = 0x1837,
62 | KFUN_MAC_Q = 0x1838,
63 | KFUN_MAC_R = 0x1839,
64 | KFUN_MAC_S = 0x183a,
65 | KFUN_MAC_T = 0x183b,
66 | KFUN_MAC_U = 0x183c,
67 | KFUN_MAC_V = 0x183D,
68 | KFUN_MAC_W = 0x183e,
69 | KFUN_MAC_X = 0x183f,
70 | KFUN_MAC_Y = 0x1840,
71 | KFUN_MAC_Z = 0x1841,
72 | KFUN_RUN = 0x1842,
73 | KFUN_STEP = 0x1843,
74 | KFUN_RECORD = 0x1844,
75 | KFUN_FLUSH = 0x1845,
76 | KFUN_BELL = 0x1846,
77 | KFUN_SCROLL_LOCK = 0x184e,
78 | KFUN_REFRESH = 0x1864,
79 | };
80 |
81 | static inline const char * kfun_name(uint16_t kfun)
82 | {
83 | switch (kfun) {
84 | case KFUN_HOME: return "{HOME}";
85 | case KFUN_FIRST_CELL: return "{FIRST CELL}";
86 | case KFUN_END: return "{END}";
87 | case KFUN_UP: return "{UP}";
88 | case KFUN_DOWN: return "{DOWN}";
89 | case KFUN_LEFT: return "{LEFT}";
90 | case KFUN_RIGHT: return "{RIGHT}";
91 | case KFUN_PG_UP: return "{PG UP}";
92 | case KFUN_PG_DN: return "{PG DN}";
93 | case KFUN_BIG_LEFT: return "{BIG LEFT}";
94 | case KFUN_BIG_RIGHT: return "{BIG RIGHT}";
95 | case KFUN_TAB: return "{TAB}";
96 | case KFUN_WINDOW: return "{WINDOW}";
97 | case KFUN_NEXT_SHEET: return "{NEXT SHEET}";
98 | case KFUN_PREV_SHEET: return "{PREV SHEET}";
99 | case KFUN_BREAK: return "{BREAK}";
100 | case KFUN_ESC: return "{ESC}";
101 | case KFUN_RETURN: return "{RETURN}";
102 | case KFUN_INS: return "{INS}";
103 | case KFUN_DEL: return "{DEL}";
104 | case KFUN_BACKSPACE: return "{BACKSPACE}";
105 | case KFUN_HELP: return "{HELP}";
106 | case KFUN_ABS: return "{ABS}";
107 | case KFUN_GOTO: return "{GOTO}";
108 | case KFUN_CALC: return "{CALC}";
109 | case KFUN_EDIT: return "{EDIT}";
110 | case KFUN_NAME: return "{NAME}";
111 | case KFUN_ZOOM: return "{ZOOM}";
112 | case KFUN_GRAPH: return "{GRAPH}";
113 | case KFUN_TABLE: return "{TABLE}";
114 | case KFUN_QUERY: return "{QUERY}";
115 | case KFUN_UNDO: return "{UNDO}";
116 | case KFUN_COMPOSE: return "{COMPOSE}";
117 | case KFUN_FILE: return "{FILE}";
118 | case KFUN_HAL: return "{HAL}";
119 | case KFUN_ADDIN: return "{ADDIN}";
120 | case KFUN_APP1: return "{APP1}";
121 | case KFUN_APP2: return "{APP2}";
122 | case KFUN_APP3: return "{APP3}";
123 | case KFUN_MAC_A: return "{MAC_A}";
124 | case KFUN_MAC_B: return "{MAC_B}";
125 | case KFUN_MAC_C: return "{MAC_C}";
126 | case KFUN_MAC_D: return "{MAC_D}";
127 | case KFUN_MAC_E: return "{MAC_E}";
128 | case KFUN_MAC_F: return "{MAC_F}";
129 | case KFUN_MAC_G: return "{MAC_G}";
130 | case KFUN_MAC_H: return "{MAC_H}";
131 | case KFUN_MAC_I: return "{MAC_I}";
132 | case KFUN_MAC_J: return "{MAC_J}";
133 | case KFUN_MAC_K: return "{MAC_K}";
134 | case KFUN_MAC_L: return "{MAC_L}";
135 | case KFUN_MAC_M: return "{MAC_M}";
136 | case KFUN_MAC_N: return "{MAC_N}";
137 | case KFUN_MAC_O: return "{MAC_O}";
138 | case KFUN_MAC_P: return "{MAC_P}";
139 | case KFUN_MAC_Q: return "{MAC_Q}";
140 | case KFUN_MAC_R: return "{MAC_R}";
141 | case KFUN_MAC_S: return "{MAC_S}";
142 | case KFUN_MAC_T: return "{MAC_T}";
143 | case KFUN_MAC_U: return "{MAC_U}";
144 | case KFUN_MAC_V: return "{MAC_V}";
145 | case KFUN_MAC_W: return "{MAC_W}";
146 | case KFUN_MAC_X: return "{MAC_X}";
147 | case KFUN_MAC_Y: return "{MAC_Y}";
148 | case KFUN_MAC_Z: return "{MAC_Z}";
149 | case KFUN_RUN: return "{RUN}";
150 | case KFUN_STEP: return "{STEP}";
151 | case KFUN_RECORD: return "{RECORD}";
152 | case KFUN_FLUSH: return "{FLUSH}";
153 | case KFUN_BELL: return "{BELL}";
154 | case KFUN_SCROLL_LOCK: return "{SCROLL LOCK}";
155 | case KFUN_REFRESH: return "{REFRESH}";
156 |
157 | }
158 |
159 | return NULL;
160 | }
161 |
162 | #endif
163 |
--------------------------------------------------------------------------------
/l123set.cf:
--------------------------------------------------------------------------------
1 | 1001 Lotus 1-2-3 Version "10"
2 | 1002 Base Directory "/{LOTUSROOT}"
3 | 1003 Printer Interface "lpr -o dest={dest} {file}"
4 | 1004 Graphics Driver "dumb" "ega egas25cc.vbd"
5 | 1007 Help Language "USA English" "USA-English"
6 | 1011 Display Char. Set "US ASCII" "l_ascii.bun"
7 | 1005 Country Driver "USA-English Numbers First" "l13cUSF3.cbd"
8 | 1006 Resource Language "USA English" "USA-English"
9 | 1012 File Character Set "US ASCII" "l_ascii.bun"
10 | 1008 File Name Mode "UNIX file name mode" "0"
11 | 1009 Keymaps Directory "/{LOTUSROOT}/keymaps"
12 |
--------------------------------------------------------------------------------
/localize.lst:
--------------------------------------------------------------------------------
1 | # vim: set ft=conf:
2 | #
3 | # Note: blank lines are not permitted.
4 | #
5 | # These are symbols that 123.o exports that should not be visible to other
6 | # code, such as statically linked libc functions.
7 | #
8 | fopen
9 | fread
10 | fflush
11 | fgets
12 | fclose
13 | fseek
14 | fgetc
15 | close_range
16 | fputc
17 | fgetgrent
18 | fgetpwent
19 | popen
20 | pclose
21 | fdopen
22 | fprintf
23 | fscanf
24 | freopen
25 | fwrite
26 | sys_errlist
27 | sys_nerr
28 | gtty
29 | stty
30 | initscr
31 | refresh
32 | getch
33 | endwin
34 | wmove
35 | waddch
36 | wclear
37 | wrefresh
38 | LINES
39 | COLS
40 | getmsg
41 | putmsg
42 | stime
43 | opterr
44 | optind
45 | optopt
46 | optarg
47 | getopt
48 | tmpnam
49 | tempnam
50 | move
51 | clrtoeol
52 | clrtobot
53 | clear
54 | addstr
55 | addnstr
56 | beep
57 | reset_shell_mode
58 | addch
59 | reset_prog_mode
60 | def_shell_mode
61 | tparm
62 | setupterm
63 | ttytype
64 | cur_term
65 | longjmp
66 | setjmp
67 | file_name_split
68 | ready_to_read
69 | find_row
70 | reset_screen_dimensions
71 | init_invalidation
72 | find_invalid_column
73 |
--------------------------------------------------------------------------------
/lotdefs.h:
--------------------------------------------------------------------------------
1 | #ifndef __LOTDEFS_H
2 | #define __LOTDEFS_H
3 |
4 | #include
5 |
6 | #define MAX_ROW 8191
7 | #define MAX_ROWS (MAX_ROW + 1)
8 | #define MAX_COL 255
9 | #define MAX_COLS (MAX_COL + 1)
10 | #define NUM_WIN 26
11 |
12 | enum {
13 | FILE_MODE_UNIX,
14 | FILE_MODE_DOSUPPER,
15 | FILE_MODE_DOSLOWER,
16 | };
17 |
18 | enum {
19 | VMR_0,
20 | VMR_1,
21 | VMR_2,
22 | VMR_3,
23 | };
24 |
25 | #define LpiVmr(type, v) ((type)(vmr[v]))
26 |
27 | extern uint8_t *vmr[];
28 | extern struct SCREENPOS vpos;
29 | extern struct SCREENPOS currpos;
30 | extern struct SCREENPOS real_pos;
31 | extern uint16_t inprint;
32 | extern uint8_t ref_cur_attr;
33 | extern struct DISPLAYWINDOW win[NUM_WIN];
34 | extern int8_t cell_col_invalid[MAX_COLS];
35 |
36 | extern struct DISPLAYWINDOW *displayed_window;
37 | extern struct RECT *region;
38 | extern struct RECT gview_srect;
39 | extern uint16_t origx;
40 | extern uint16_t origy;
41 | extern uint16_t origz;
42 | extern uint16_t region_top;
43 | extern uint16_t row_valid;
44 |
45 | extern int get_column_labels(uint16_t, uint16_t, char *, uint16_t);
46 |
47 | extern void (*x_disp_txt_set_pos)(uint16_t col, uint16_t line);
48 | extern int (*x_disp_txt_write)(uint16_t byteslen, char *lmbcsptr, int attrs);
49 |
50 | extern int V3_disp_grph_compute_view(void *);
51 | extern int V3_disp_grph_process(void *);
52 | extern int V3_disp_grph_set_cur_view(void *);
53 | extern int init_unix_display_code();
54 | extern void set_actual_grph_type();
55 | extern int disp_txt_init(char *bdlpath);
56 |
57 | extern char *char_set_bundle;
58 | extern void *Find_changes;
59 | extern void (*Flush)(void);
60 | extern void (*Check_cursor)(void);
61 | extern int (*Initsc)(void);
62 | extern int (*Clrandhome)(void);
63 | extern int (*Clrtoend)(void);
64 | extern int (*Clrtobot)(void);
65 | extern int (*Home)(void);
66 | extern int (*Atset)(char);
67 | extern void (*opcodes[])(void);
68 | extern uint16_t display_turned_off;
69 | extern void gen_disp_txt_clear();
70 | extern int gen_disp_txt_copy(uint16_t width, uint16_t height, uint16_t dstx, uint16_t dsty);
71 | extern void gen_disp_txt_curs_off();
72 | extern void gen_disp_txt_curs_on();
73 | extern void gen_disp_txt_curs_type();
74 | extern void gen_disp_txt_fg_clear(uint16_t cols, uint16_t lines);
75 | extern int gen_disp_txt_fit(int16_t strarglen,
76 | char *strarg,
77 | int16_t ncols,
78 | int16_t *bytesneeded);
79 | extern void gen_disp_txt_lock();
80 | extern void gen_disp_txt_set_bg(uint16_t cols, uint16_t lines, int attrs);
81 | extern void gen_disp_txt_set_pos(uint16_t col, uint16_t line);
82 | extern void gen_disp_txt_set_pos_hpu();
83 | extern void gen_disp_txt_size();
84 | extern void gen_disp_txt_sync();
85 | extern void gen_disp_txt_unlock();
86 | extern int gen_disp_txt_write(uint16_t, char *, int);
87 | extern void gen_disp_txt_zone(int16_t byteslen, char *lmbcsptr, int attrs, int16_t startpad, int16_t endpad);
88 | extern void stdio_flush();
89 | extern void tty_disp_close();
90 | extern void tty_disp_close_retain_termcap();
91 | extern void tty_disp_post_system();
92 | extern void tty_disp_pre_system();
93 | extern void *x_disp_close;
94 | extern void *x_disp_close_retain_termcap;
95 | extern void *x_disp_graph;
96 | extern void *x_disp_grph_compute_view;
97 | extern void *x_disp_grph_process;
98 | extern void *x_disp_grph_set_cur_view;
99 | extern void *x_disp_grph_txt_fit;
100 | extern void *x_disp_grph_txt_size;
101 | extern void *x_disp_grph_load_font;
102 | extern void *x_disp_info;
103 | extern void *x_disp_open;
104 | extern void *x_disp_post_system;
105 | extern void *x_disp_pre_system;
106 | extern void *x_disp_text;
107 | extern void *x_disp_txt_clear;
108 | extern int (*x_disp_txt_copy)(uint16_t width, uint16_t height, uint16_t dstx, uint16_t dsty);
109 | extern void (*x_disp_txt_curs_off)();
110 | extern void (*x_disp_txt_curs_on)();
111 | extern void (*x_disp_txt_curs_type)();
112 | extern void (*x_disp_txt_fg_clear)(uint16_t cols, uint16_t lines);
113 | extern int (*x_disp_txt_fit)(int16_t strarglen,
114 | char *strarg,
115 | int16_t ncols,
116 | int16_t *bytesneeded);
117 | extern void *x_disp_txt_lock;
118 | extern void (*x_disp_txt_set_bg)(uint16_t cols, uint16_t lines, int attrs);
119 | extern void *x_disp_txt_set_pos_hpu;
120 | extern void *x_disp_txt_size;
121 | extern void *x_disp_txt_sync;
122 | extern void *x_disp_txt_unlock;
123 | extern void (*x_disp_txt_zone)(int16_t byteslen, char *lmbcsptr, int attrs, int16_t startpad, int16_t endpad);
124 | extern void *dliopen;
125 | extern void *dliclose;
126 | extern int (*Replace_cursor)(void);
127 | extern void set_error_string(char *errstr, int16_t, uint8_t hlpmsgid);
128 | extern int mac_nobreak(void);
129 |
130 | extern int MapX(uint16_t);
131 | extern int MapY(uint16_t);
132 |
133 | extern uint16_t banner_printed;
134 |
135 | extern int undo_on_cmd();
136 | extern int undo_off_cmd();
137 | extern int reset_undo(int);
138 | extern void full_redisplay();
139 | extern int erase_screen();
140 | extern int invalidate_screen();
141 | extern int16_t need_to_close;
142 | extern int16_t screen_width_hpus;
143 | extern int16_t row_label_hpus;
144 | extern int16_t separate_graph_window;
145 | extern int16_t graph_in_window();
146 | extern void redraw();
147 | extern void repaint();
148 | extern struct {
149 | uint16_t hpu_per_col;
150 | uint16_t _pad;
151 | void *disp_txt_size;
152 | void *disp_txt_fit;
153 | } display_metrics;
154 |
155 | extern struct PSCREEN pscreen;
156 | extern struct PSCREEN dscreen;
157 | // Note: these are structures.
158 | extern uint8_t bg_equiv_map[64];
159 | extern uint8_t fg_equiv_map[16];
160 |
161 | extern int16_t gph_display_flag;
162 | extern int16_t cmdhandle;
163 |
164 | extern struct LINEFUNCS *lfvec;
165 | extern char *opline;
166 | extern uint32_t scr_init_state;
167 | extern int get_screen_size();
168 | extern void *lts_malloc(size_t size);
169 | extern void *alloc_mptr(char tag, uint16_t size, int vmr);
170 | extern int clear_screen_buffer(struct PSCREEN *screenbuf);
171 | extern char *tc_setup_line_funcs();
172 |
173 | extern struct LOTUSFUNCS *core_funcs;
174 | extern int RastHandle;
175 | extern int16_t file_mode;
176 | extern uint16_t num_text_cols;
177 | extern uint16_t num_text_rows;
178 | extern uint16_t hpu_per_col;
179 | extern uint8_t flags[8];
180 | extern int gbl_clock_status;
181 | extern uint16_t clock_invalid;
182 | extern int display_filename(void);
183 | extern uint16_t filename_length;
184 | extern int display_date(void);
185 | extern int erase_background(uint16_t x,
186 | uint16_t y,
187 | uint16_t width,
188 | uint16_t height,
189 | int);
190 | extern int input_key(void);
191 | extern char *mac_str;
192 | extern struct CELLCOORD mac_cell;
193 | extern struct CELLCOORD mac_lastgoodcell;
194 | extern struct MACXRTNS mac_xrtns;
195 | extern int dspcref(uint8_t, struct CELLCOORD, int, char **, int16_t);
196 |
197 | extern void slash_convert(char *path, uint16_t len);
198 | extern uint16_t fname_real_len(const char *start, const char *end);
199 | extern int file_path_expand(struct PATHNAME *src);
200 | extern int check_lengths(char *);
201 | extern uint16_t *get_fname_content_xlt_tbl(char *, uint16_t);
202 | extern int file_name_clear(struct PATHNAME *name);
203 | extern int16_t file_name_reduce(char *dst, char *pathname);
204 | extern void file_name_case_check(char *pathname);
205 | extern void fallback_and_coerce_case(char *path);
206 | extern int16_t char_size(char);
207 | extern int peek_next_mbcs(char *);
208 | extern uint16_t resource_substr_match(uint16_t resid, char **matchpart);
209 | extern void macro_buff_run(struct MACXRTNS *callbacks);
210 | extern int in_rdy_mode();
211 | extern void kfqueue_submit_kfun(uint16_t kfun);
212 | extern int macro_key_run(char key);
213 | extern struct CELLCOORD xyz2coord(uint8_t col, uint16_t row, uint16_t sheet);
214 | extern struct CELLCOORD get_cellpointer();
215 | extern bool coord_in_range(struct CELLCOORD cell, struct CELLCOORD rngstart, struct CELLCOORD rngend);
216 | extern char * access_resource(uint16_t resid);
217 | extern int16_t file_name_compose(char *filename, struct PATHNAME *path, char *root, uint16_t useroot);
218 | int16_t print_file_in_use(struct PATHNAME *path);
219 | extern int file_access_read(struct PATHNAME *pathname, struct SYSHANDLE **outfd, int16_t share);
220 | extern int get_resource_handle(int16_t);
221 | extern int file_read_line(int *fd, uint16_t bufsiz, char *buf, uint16_t *numread);
222 | extern int16_t convert_from_table(uint16_t *table, char *inbuf, char *outbuf, int16_t outbufsiz);
223 | extern int cell_immutable(struct CELLCOORD cell);
224 | extern int make_label_cell(struct CELLCOORD cell, char *label, uint8_t labeltype);
225 | extern int16_t peek_next_sbcs(char *);
226 | extern uint16_t find_first_sbcs(char **haystack, uint16_t needle);
227 | extern int skip_next_mbcs(char **strptr);
228 | extern int get_string_end(char **str);
229 | extern int import_outof_bounds(uint16_t maxcol, uint16_t maxrow);
230 | extern int16_t parse_number(char *num, int16_t len);
231 | extern int16_t make_number_cell(struct CELLCOORD cell);
232 | extern int file_finished_shell(struct SYSHANDLE **fd, uint16_t result);
233 | extern int sheet_modified(int16_t sheetnum);
234 | extern int erase_window_cellhighlight(struct DISPLAYWINDOW *dp);
235 | extern int set_dspcache(uint16_t size);
236 | extern int16_t init_showme(int16_t termrows);
237 | extern void map_win_screen_info(struct DISPLAYWINDOW *dp);
238 | extern void map_row_invalid(struct DISPLAYWINDOW *dp);
239 | extern void map_col_invalid(struct DISPLAYWINDOW *dp);
240 | extern void set_dirty(unsigned int line, unsigned int col, int len, int, int16_t dirty);
241 | extern uint16_t check_hidden_columns(uint16_t, int16_t);
242 | extern void setup_column_widths(uint16_t, int16_t);
243 | extern int16_t get_col_title_width();
244 | extern void reset_dspcache(uint16_t basecol, uint16_t baserow, uint16_t endrow);
245 | extern void row_set_pos();
246 | extern int16_t get_row_label(int16_t row, char *buf);
247 | extern void display_scan_row(struct CELLCOORD start, int16_t numcols);
248 | extern int win_column_width(uint16_t, int16_t);
249 | extern void tty_disp_info(struct DISPLAYINFO *dpyinfo);
250 | extern void memdup(void *, uint32_t, uint32_t);
251 | extern int16_t get_allow_autoexec();
252 | #endif
253 |
--------------------------------------------------------------------------------
/loterrs.h:
--------------------------------------------------------------------------------
1 | #ifndef __LOTERRS_H
2 | #define __LOTERRS_H
3 |
4 | enum {
5 | LOTERR_FILENAME_TOO_LONG = 9083,
6 | LOTERR_FILENAME_INVALID = 9248,
7 | LOTERR_WORKSHEET_UNNAMED = 9130,
8 | LOTERR_FILE_IN_USE = 9043,
9 | LOTERR_CELL_IMMUTABLE = 9034,
10 | LOTERR_MEMORY_FULL = 8962,
11 | };
12 |
13 | #endif
14 |
--------------------------------------------------------------------------------
/lotfuncs.h:
--------------------------------------------------------------------------------
1 | #ifndef __LOTCALLS_H
2 | #define __LOTCALLS_H
3 |
4 | #pragma pack(push, 1)
5 |
6 | struct LOTUSFUNCS
7 | {
8 | int (*drv_max_segsize)(void);
9 | void *(*alloc_mptr)(int vmr, int size, int tag);
10 | void (*free_mptr)(void *ptr, int size);
11 | void *(*alloc_fixed_block)(int size);
12 | int (*free_fixed_block)(void *ptr, int, int size);
13 | void *(*drv_map_mptr)(void *m, int v);
14 | void *(*drv_get_vmr)(int v);
15 | void *drv_unmap_vmr;
16 | void *drv_yield_test;
17 | void *file_name_parse;
18 | void *file_exist;
19 | void *file_find;
20 | void *file_access_read;
21 | void *file_get_fileinfo;
22 | void *file_get_filepointer;
23 | void *file_lseek;
24 | void *file_read;
25 | void *file_access_finished;
26 | void *file_access_write;
27 | void *file_write;
28 | int (*open_rasterizer)(struct DEVDATA *);
29 | void *rast_init_device;
30 | void (*set_strip)(int, int);
31 | int (*raster)(int, void *ptr);
32 | void (*close_rasterizer)(int hdl);
33 | int (*rast_compute_view)(int, void *ptr);
34 | int (*rast_txt_size)(int, int, void *ptr1, void *ptr2);
35 | int (*rast_txt_fit)(int, int, void *a, int, void *b, void *c);
36 | void *p_link_init;
37 | void *p_link_transmit;
38 | void *p_link_term;
39 | void *p_link_start_job;
40 | void *p_link_end_job;
41 | void *p_signal_user;
42 | void *p_print_link_alert;
43 | void *p_print_link_message;
44 | void *country_reduce_string;
45 | void *country_convert_string;
46 | void *dyload;
47 | void *dyunload;
48 | void *drv_wait_nticks;
49 | unsigned (*set_disp_buf)(unsigned *selector, int base, int limit);
50 | void *circle_position;
51 | void *drv_display_reset;
52 | void *open_system_file;
53 | void *drv_largest_avail;
54 | void *quit_now;
55 | void *is_encoded;
56 | void *command;
57 | void *x_get_date;
58 | void *x_get_time;
59 | void *x_sysbeep;
60 | void *convert_from_table;
61 | void *reduce_from_table;
62 | void *country_text_cnv;
63 | void (*drop_disp_buf)(int seg);
64 | void *x_cntry_tbl_release2;
65 | void *char_size;
66 | };
67 |
68 | #pragma pack(pop)
69 | #endif
70 |
--------------------------------------------------------------------------------
/lottypes.h:
--------------------------------------------------------------------------------
1 | #ifndef __LOTTYPES_H
2 | #define __LOTTYPES_H
3 |
4 | #define INVALID_HANDLE_VALUE ((struct SYSHANDLE *)-1)
5 | #define MAX_ROW 8191
6 |
7 | #pragma pack(push, 1)
8 |
9 | struct DISPLAYINFO
10 | {
11 | uint16_t num_text_cols;
12 | uint16_t num_text_rows;
13 | uint16_t graphics;
14 | uint16_t full_screen_graph;
15 | uint16_t hpu_per_col;
16 | uint16_t graph_cols;
17 | uint16_t graph_rows;
18 | uint16_t graph_col_res;
19 | uint16_t graph_row_res;
20 | uint16_t view_set_size;
21 | uint16_t iscolor;
22 | uint16_t sep_graph_win;
23 | };
24 |
25 | struct BDLHDR
26 | {
27 | uint16_t bdllen;
28 | uint16_t bdlver;
29 | uint16_t driver_id;
30 | uint16_t bundle_id;
31 | };
32 |
33 | struct BDLRECHDR
34 | {
35 | uint16_t bdltype;
36 | uint16_t reclen;
37 | };
38 |
39 | struct DEVPRIM
40 | {
41 | void (*scan_linx)(int, int, int, int);
42 | void (*fill_rect)(int, int, int, int, int);
43 | void (*thin_diag_line)(int, int, int, int, int);
44 | void (*thin_vert_line)(int, int, int, int);
45 | void (*shade_rect)(int, int, int, int, void *, int);
46 | void (*fill_scan_list)();
47 | };
48 |
49 | typedef struct DEVPRIM DEVPRIM;
50 |
51 | struct POINT
52 | {
53 | uint16_t pty;
54 | uint16_t ptx;
55 | };
56 |
57 | typedef struct POINT POINT;
58 |
59 | struct PATT
60 | {
61 | POINT pattsize;
62 | POINT patthotSpot;
63 | char *pattptr;
64 | POINT pattSecOffset;
65 | };
66 |
67 | typedef struct PATT PATT;
68 |
69 | struct DEVDATA
70 | {
71 | uint16_t ShowMeFlag;
72 | uint16_t RasterHeight;
73 | uint16_t AspectX;
74 | uint16_t AspectY;
75 | uint16_t RHmusPerTHmu;
76 | uint16_t RHmuPerGHmus;
77 | uint16_t RVmusPerTVmu;
78 | uint16_t RVmuPerGVmus;
79 | PATT FillPatts[12];
80 | unsigned char ColorMap[16];
81 | uint16_t SrcPatD[8];
82 | uint16_t SrcPatS[8];
83 | uint32_t FIndexCnt;
84 | void *FontIndex;
85 | uint32_t FNamesCnt;
86 | void *FontNames;
87 | DEVPRIM DevFuncs;
88 | };
89 |
90 | struct FONTINFO
91 | {
92 | char name[9];
93 | uint8_t _padding;
94 | uint16_t angle;
95 | uint16_t em_pix_hgt;
96 | uint16_t em_pix_wid;
97 | };
98 |
99 | struct GRAPH;
100 |
101 | struct LINE
102 | {
103 | uint8_t dirty;
104 | uint8_t _padding[3];
105 | uint32_t maxy;
106 | uint8_t *lineattr;
107 | char *linebuf;
108 | };
109 |
110 | struct PSCREEN
111 | {
112 | uint8_t dirty;
113 | uint8_t _padding[3];
114 | uint32_t nlines;
115 | struct LINE *linedata;
116 | };
117 |
118 | struct PATHNAME {
119 | uint16_t start;
120 | uint16_t diroff;
121 | uint16_t diroffpreroot;
122 | uint16_t filenameoff;
123 | uint16_t dirlen;
124 | uint16_t namelen;
125 | uint16_t offext;
126 | uint16_t extlen;
127 | char str[0];
128 | };
129 |
130 | struct MACXRTNS {
131 | const char * (*get_mac_text)(int16_t n);
132 | uint32_t mac_stop;
133 | void (*get_mac_name)(char **str);
134 | uint32_t mac_restart;
135 | };
136 |
137 | struct CELLCOORD {
138 | uint16_t row;
139 | uint8_t sheet;
140 | uint8_t col;
141 | };
142 |
143 | struct LINEFUNCS {
144 | int (*lfprint)(char *str, unsigned len);
145 | int (*lfmove)(int ypos, int xpos);
146 | };
147 |
148 | struct SCREENPOS {
149 | uint32_t line;
150 | uint32_t col;
151 | };
152 |
153 | struct SYSHANDLE {
154 | int fd;
155 | uint16_t flags;
156 | uint16_t _pad;
157 | };
158 |
159 | typedef struct SYSHANDLE SYSHANDLE;
160 |
161 | struct RECT {
162 | uint16_t topleftrow;
163 | uint16_t topleftcol;
164 | uint16_t height;
165 | uint16_t width;
166 | };
167 |
168 | struct DISPLAYWINDOW {
169 | uint16_t *row_invalid;
170 | uint16_t *col_invalid;
171 | struct RECT win;
172 | struct RECT regionb;
173 | uint16_t sheetnum;
174 | uint16_t field_1A;
175 | uint16_t field_1C;
176 | uint16_t field_1E;
177 | uint16_t field_20;
178 | uint16_t field_22;
179 | uint16_t xpos;
180 | uint16_t ypos;
181 | uint16_t real_height;
182 | uint16_t columns;
183 | uint8_t field_2C;
184 | uint8_t field_2D;
185 | struct RECT regionc;
186 | struct RECT regiona;
187 | struct RECT regiond;
188 | uint16_t field_46;
189 | };
190 |
191 | struct RESHDR {
192 | uint32_t magic;
193 | uint16_t version;
194 | uint16_t groups;
195 | uint32_t data[0];
196 | };
197 |
198 | typedef struct RESHDR RESHDR;
199 |
200 | #pragma pack(pop)
201 | #endif
202 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "lottypes.h"
14 | #include "lotdefs.h"
15 | #include "lotfuncs.h"
16 | #include "atfuncs.h"
17 |
18 | extern int __unix_main(int argc, char **argv, char **envp);
19 | extern int setchrclass(const char *class);
20 |
21 | static void canonicalize_auto_worksheet(int *argc, char **argv, const char *wkspath)
22 | {
23 | static char autofile[PATH_MAX];
24 |
25 | if (realpath(wkspath, autofile) == NULL) {
26 | warn("Failed to canonicalize worksheet path.");
27 | return;
28 | }
29 |
30 | if (access(autofile, F_OK) != 0) {
31 | warn("The worksheet specified does not exist.");
32 | return;
33 | }
34 |
35 | // Okay, replace it with a canonical path!
36 | argv[(*argc)++] = "-w";
37 | argv[(*argc)++] = autofile;
38 | }
39 |
40 | #define MAX_MACRO 64
41 |
42 | // This is used to evaluate a macro on the commandline.
43 | static int macro_cell_num = -1;
44 | static int macro_cell_cnt;
45 | static int macro_run_signal;
46 | static const char *macro_cell_text[MAX_MACRO];
47 |
48 | static const char *playback_macro_text(int16_t n)
49 | {
50 | // n is set when 123 wants to advance to the next cell, so
51 | // we just remove the contents. Note that some macro commands
52 | // "advance" the macro cell automatically, like {OPEN}, {WRITE},
53 | // {IF}.
54 | macro_cell_num += n;
55 |
56 | if (macro_cell_num < MAX_MACRO) {
57 | return macro_cell_text[macro_cell_num];
58 | }
59 | return NULL;
60 | }
61 |
62 | // This is called when 123 is ready to receive input, we can check
63 | // if we have any jobs we want to do.
64 | int ready_to_read(int fd)
65 | {
66 | static struct MACXRTNS macro = {
67 | .get_mac_text = playback_macro_text,
68 | };
69 |
70 | // Only check for macro jobs in READY mode.
71 | if (!in_rdy_mode())
72 | return 0;
73 |
74 | // If we have a macro to evaluate, submit it here.
75 | if (macro_cell_cnt && macro_cell_num == -1) {
76 | // Start at the first cell, this allows multiple cells to be
77 | // used on the commandline, -e a -e b -e c, and so on.
78 | macro_cell_num = 0;
79 | // Submit it.
80 | macro_buff_run(¯o);
81 | }
82 |
83 | // If a signal handler macro has been triggered, run it here.
84 | if (macro_run_signal) {
85 | // Mark this signal processed.
86 | macro_run_signal = 0;
87 |
88 | // Trigger a {REFRESH} to leave the idle loop.
89 | kfqueue_submit_kfun(6244);
90 |
91 | // Invoke the macro \1.
92 | macro_key_run(24);
93 | }
94 |
95 | return 0;
96 | }
97 |
98 | // This signal handler triggers the use of macro \1.
99 | static void macro_signal(int n)
100 | {
101 | macro_run_signal++;
102 | }
103 |
104 | // This is an atexit() routine that is called after 1-2-3 prints
105 | // it's own help, so we can append any flags we support.
106 | static void print_help()
107 | {
108 | static int printed;
109 |
110 | // This can happen if multiple invalid options are passed.
111 | if (printed++)
112 | return;
113 |
114 | printf(" -b to enable banner\n");
115 | printf(" -u to disable undo support\n");
116 | printf(" -e macro to evaluate a macro\n");
117 | }
118 |
119 | static const char *optstring = ":f:c:k:np:w:hbue:";
120 |
121 | int main(int argc, char **argv, char **envp)
122 | {
123 | char **lotargv;
124 | char dumpfile[64];
125 | int lotargc;
126 | int opt;
127 |
128 | // The location of terminfo definitions.
129 | setenv("TERMINFO", "/usr/share/terminfo/", 0);
130 |
131 | // This controls how long lotus waits to see if an escape character is part
132 | // of a sequence or on it's own. Increase it if escape characters are not
133 | // being recognized properly, perhaps you're using 123 over a very slow
134 | // connection. If you set it too high, lotus might seem to pause when
135 | // pressing the Esc key.
136 | setenv("LOTUS_ESCAPE_TIMEOUT", "1", 0);
137 |
138 | // This changes how some timeouts work, is this still necessary?
139 | setenv("LOTUS_OS_ENV", "systemv", 0);
140 |
141 | // If you send lotus a SIGUSR1 (e.g. kill -USR1 $(pidof 123)), it will save
142 | // a copy of the screen to the specified file. You can use this for automation
143 | // or grabbing some figures over ssh from a session you left running.
144 | snprintf(dumpfile, sizeof dumpfile, "123screen.%d.txt", getpid());
145 | setenv("LOTUS_SCREEN_DUMP", dumpfile, 0);
146 |
147 | setchrclass("ascii");
148 |
149 | // Setup a signal handler for SIGUSR2
150 | signal(SIGUSR2, macro_signal);
151 |
152 | // Disable the banner by default, it can be re-enabled via -b.
153 | banner_printed = true;
154 |
155 | // Enable undo by default, you can disable it via -u.
156 | reset_undo(1);
157 |
158 | // No need to close the printer driver, it is currently a noop.
159 | need_to_close = false;
160 |
161 | // Configure our @functions here.
162 | init_at_funcs();
163 |
164 | // We always need at least two entries, for argv[0] and a terminator.
165 | lotargc = 2;
166 |
167 | // We need to do a first pass through the options to see how many there
168 | // are.
169 | while ((opt = getopt(argc, argv, optstring)) != -1)
170 | lotargc++;
171 |
172 | // If there was a non-option parameter, we will inject a synthetic
173 | // parameter.
174 | if (argv[optind] != NULL)
175 | lotargc += 2;
176 |
177 | // Allocate the argument vector we're going to pass through to lotus.
178 | lotargv = alloca(lotargc * sizeof(*argv));
179 |
180 | // Now reset and copy the options over.
181 | lotargc = 0;
182 |
183 | // Reset optind to restart getopt();
184 | optind = 1;
185 |
186 | // The first argument is the same.
187 | lotargv[lotargc++] = argv[0];
188 |
189 | // This time we copy options over.
190 | while ((opt = getopt(argc, argv, optstring)) != -1) {
191 | // Here 'continue' means don't pass this option to Lotus and 'break'
192 | // means pass it through verbatim.
193 | switch (opt) {
194 | case 'b': banner_printed = false;
195 | continue;
196 | case 'u': undo_off_cmd();
197 | continue;
198 | case 'e': if (macro_cell_cnt < MAX_MACRO) {
199 | macro_cell_text[macro_cell_cnt++] = optarg;
200 | }
201 | continue;
202 | case '?':
203 | case 'h': atexit(print_help);
204 | break;
205 | }
206 |
207 | // If we reach here, we want to pass this option through to
208 | // Lotus verbatim.
209 | lotargv[lotargc] = alloca(3);
210 | lotargv[lotargc][0] = '-';
211 | lotargv[lotargc][1] = opt;
212 | lotargv[lotargc][2] = 0;
213 | lotargc++;
214 |
215 | // There may also be an argument.
216 | if (optarg)
217 | lotargv[lotargc++] = optarg;
218 |
219 | }
220 |
221 | // Lotus does not accept any non-options arguments. If you do provide
222 | // one, we will try to translate it into a worksheet name.
223 | if (argv[optind] != NULL) {
224 | canonicalize_auto_worksheet(&lotargc, lotargv, argv[optind]);
225 | }
226 |
227 | // Always add a NULL terminator
228 | lotargv[lotargc] = NULL;
229 |
230 | return __unix_main(lotargc, lotargv, envp);
231 | }
232 |
--------------------------------------------------------------------------------
/patch.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "lottypes.h"
8 | #include "lotdefs.h"
9 | #include "lotfuncs.h"
10 |
11 | int lic_init(void)
12 | {
13 | return 2;
14 | }
15 |
16 | // Printing does not currently work, but this can be fixed in future.
17 | int open_printer_drivers(void)
18 | {
19 | return 1;
20 | }
21 | int close_printer_drivers(void)
22 | {
23 | return 0;
24 | }
25 | int load_printer_drivers(void)
26 | {
27 | return 0;
28 | }
29 | int read_print_config_dir(void)
30 | {
31 | return 0;
32 | }
33 |
34 | // The original function uses a fixed size stack buffer which crashes if
35 | // COLUMNS > 256. This rewrite uses a dynamic buffer.
36 | int display_column_labels()
37 | {
38 | uint8_t *dspinfo = vmr[3];
39 | uint16_t column_labels = 0;
40 | char *buf;
41 |
42 | // Is this right?
43 | buf = alloca(COLS);
44 |
45 | // I have no idea what this structure is.
46 | if (dspinfo[11]) {
47 | column_labels = get_column_labels(dspinfo[10],
48 | dspinfo[11],
49 | buf,
50 | displayed_window->regiona.width);
51 | }
52 |
53 | get_column_labels(dspinfo[9],
54 | displayed_window->field_1A,
55 | &buf[column_labels],
56 | displayed_window->regionb.width);
57 | x_disp_txt_set_pos(displayed_window->ypos,
58 | displayed_window->xpos - 1);
59 | return x_disp_txt_write(displayed_window->columns, buf, 0);
60 | }
61 |
62 | // This routine normally installs the original termios state but I would
63 | // rather handle that with ncurses, so we do nothing.
64 | void kbd_term()
65 | {
66 | return;
67 | }
68 |
69 | // Lotus uses SysV "raw" mode with a bunch of ioctls, but rather than trying
70 | // to translate termios structures, we can translate the intent by using
71 | // ncurses instead.
72 | int set_raw_mode()
73 | {
74 | return 0;
75 | }
76 |
77 | int unset_raw_mode()
78 | {
79 | return 0;
80 | }
81 |
82 | uint32_t fmt_cpy(uint32_t *dst, uint32_t *src, uint16_t src_len)
83 | {
84 | static const uint32_t dst_max = 256;
85 | uint32_t *dptr, *dst_end = dst + dst_max;
86 |
87 | for (dptr = dst; src_len >= 4 && dptr < dst_end; dptr++) {
88 | *dptr = *src++;
89 | src_len -= 4;
90 | if (*dptr > 0x80000000) {
91 | *dptr &= 0x7fffffff;
92 | if (src_len != 0) {
93 | uint8_t *sptr = (uint8_t *)src;
94 | uint8_t reps = *sptr;
95 | if (dptr + reps + 1 >= dst_end) {
96 | // prevent overflow
97 | break;
98 | }
99 | memdup(dptr, 4, reps*4 + 4);
100 | src_len -= 1;
101 | dptr += reps;
102 | src = (uint32_t *)(sptr+1);
103 | }
104 | }
105 | }
106 | return dptr - dst;
107 | }
108 |
--------------------------------------------------------------------------------
/redefine.lst:
--------------------------------------------------------------------------------
1 | # vim: set ft=conf:
2 | #
3 | # Note: blank lines are not permitted.
4 | #
5 | # These symbols should be renamed because they clash with other symbols, or we
6 | # want to install wrappers.
7 | #
8 | # Magic symbols that we want to provide.
9 | main __unix_main
10 | errno __unix_errno
11 | # Incompatible libc calls.
12 | stat __unix_stat
13 | fstat __unix_fstat
14 | open __unix_open
15 | uname __unix_uname
16 | times __unix_times
17 | sysi86 __unix_sysi86
18 | ioctl __unix_ioctl
19 | fcntl __unix_fcntl
20 | read __unix_read
21 | access __unix_access
22 | readdir __unix_readdir
23 | signal __unix_signal
24 | # 1-2-3 often uses memcpy with overlapping ranges. This was a bug even on UNIX,
25 | # but worked due to implementation quirks. This will cause a minor performance
26 | # penalty, but avoid these hard to track down bugs, see issue #45.
27 | memcpy memmove
28 | # Difficult symbol names.
29 | banner_printed.0 banner_printed
30 | inprint.0 inprint
31 | # 1-2-3 provides its own math functions. However, modern libm outperforms them.
32 | # Let's map them to libm counterparts.
33 | ksin sin
34 | kcos cos
35 | ktan tan
36 | katan2 atan2
37 | kasin asin
38 | kacos acos
39 | # drem is obsolete.
40 | drem remainder
41 |
--------------------------------------------------------------------------------
/res/Makefile:
--------------------------------------------------------------------------------
1 | LDLIBS =
2 | CFLAGS = -ggdb3 -O0
3 | CPPFLAGS = -I.. -W -Wall -Wno-missing-field-initializers -Wno-sign-compare -Wno-unused-parameter -Wno-pointer-sign
4 | LDFLAGS = $(CFLAGS)
5 |
6 | .PHONY: clean
7 |
8 | all: resgen resources
9 |
10 | resources: l123txt3.ri
11 |
12 | %.ri: %.txt | resgen
13 | ./resgen < $< > $@
14 |
15 | clean:
16 | $(RM) *.o *.ri hlp resgen
17 |
--------------------------------------------------------------------------------
/res/README.md:
--------------------------------------------------------------------------------
1 | # Lotus Resources
2 |
3 | This is a work in progress attempt to allow 123 to be localized. 1-2-3 stores
4 | all of the strings, help and so on in resource files. These are binary files
5 | and we don't have the originals.
6 |
7 | However, we can figure out the format and compile our own. This is a first
8 | attempt at getting that working.
9 |
10 | ## Usage
11 |
12 | The `resgen` command can take a simple text format, and generate binary
13 | resource files.
14 |
15 | ```
16 | $ ./resgen < l123txt3.txt > l123txt3.ri
17 | ```
18 |
19 | Assuming that worked, you can put it in `/usr/share/lotus/123.v10/ri/USA-English/l123txt3.ri`.
20 |
21 | Try starting 1-2-3, and the string you changed in `l123txt3.txt` should be updated.
22 |
23 | ## Notes
24 |
25 | This doesn't work on the help resources yet, which uses huffman encoding.
26 |
27 | Eventually, it would be nice to import the updated help files from 123r4.
28 |
--------------------------------------------------------------------------------
/res/hlp.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #define id2dat(x) (((x) >> 8) & 0xff)
8 | #define id2sek(x) ((x) & 0xff)
9 |
10 | struct RESHDR {
11 | uint32_t magic;
12 | uint16_t version;
13 | uint16_t hdrwords;
14 | uint16_t data[0];
15 | };
16 |
17 | // Note: this is not useful, should be doing read_image()
18 | int find_resource_in_file(FILE *handle,
19 | struct RESHDR *hdr,
20 | uint16_t resid,
21 | void *buf,
22 | uint16_t bufsiz)
23 | {
24 | uint16_t dataid = id2dat(resid);
25 | uint16_t seekid = id2sek(resid) * sizeof(uint16_t);
26 | off_t objoffset;
27 | int result;
28 |
29 | fseek(handle, sizeof(*hdr) + hdr->hdrwords * 2, SEEK_SET);
30 |
31 | for (objoffset = seekid; --dataid != 0xFFFF; objoffset += hdr->data[dataid])
32 | ;
33 |
34 | if (fseek(handle, objoffset, SEEK_CUR) != 0)
35 | return -1;
36 |
37 | if (fread(&dataid, sizeof(dataid), 1, handle) != 1)
38 | return -1;
39 |
40 | if (fseek(handle, dataid - seekid - sizeof(dataid), SEEK_CUR) != 0)
41 | return -1;
42 |
43 | return fread(buf, 1, bufsiz, handle);
44 | }
45 |
46 | int main(int argc, char **argv)
47 | {
48 | struct RESHDR *hdr;
49 | FILE *resfile;
50 | uint16_t **data;
51 | uint32_t magic;
52 |
53 | if ((resfile = fopen(argv[1], "r")) == NULL) {
54 | err(EXIT_FAILURE, "Failed to open file");
55 | }
56 |
57 | hdr = malloc(sizeof *hdr);
58 |
59 | if (fread(hdr, sizeof *hdr, 1, resfile) != 1) {
60 | err(EXIT_FAILURE, "Failed to read header");
61 | }
62 |
63 | // This is read_file_hdr()
64 | if (hdr->magic != 0x1ad9c) {
65 | err(EXIT_FAILURE, "Magic number %#x not correct", hdr->magic);
66 | }
67 |
68 | if (hdr->version != 1) {
69 | err(EXIT_FAILURE, "Version %u not correct", hdr->version);
70 | }
71 |
72 | hdr = realloc(hdr, sizeof(*hdr) + hdr->hdrwords * 2);
73 | data = calloc(sizeof(void *), hdr->hdrwords);
74 |
75 | fread(hdr->data, 2, hdr->hdrwords, resfile);
76 |
77 | for (int i = 0; i < hdr->hdrwords; i++) {
78 | data[i] = calloc(1, hdr->data[i]);
79 |
80 | fprintf(stdout, "=group %u, %u bytes\n", i, hdr->data[i]);
81 |
82 | fread(data[i], 1, hdr->data[i], resfile);
83 |
84 | for (int id = 0;; id++) {
85 | char *msg = (char *)(data[i]) + data[i][id];
86 | char *end = (char *)(data[i]) + hdr->data[i];
87 |
88 | if (data[i][id] == 0) {
89 | fprintf(stdout, "; %u \n", id);
90 | } else {
91 | fprintf(stdout, "%u \"%s\"\n", id, msg);
92 | if (msg + strlen(msg) + 1 >= end) {
93 | fprintf(stdout, "\n");
94 | break;
95 | }
96 | }
97 | }
98 | }
99 |
100 | if (fread(&magic, sizeof(magic), 1, resfile) != 1) {
101 | fprintf(stderr, "no huffman\n");
102 | } else {
103 | fprintf(stderr, "magic is %#x\n", magic);
104 | }
105 |
106 | fclose(resfile);
107 | return 0;
108 | }
109 |
--------------------------------------------------------------------------------
/res/resgen.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "lottypes.h"
8 |
9 | // ; Comment
10 | // =456 ; group
11 | // 123 "This is a message"
12 | //
13 | //
14 | //
15 | // file format:
16 | //
17 | // magic
18 | // version
19 | // numgroups
20 | // numgroups * uint16_t => each one is a size in bytes a group
21 | // groups {
22 | // first word = 0
23 | // uint16_t offsets of string
24 | // }
25 |
26 | static uint16_t find_max_resource(char **msgtable)
27 | {
28 | uint16_t max = 0;
29 |
30 | for (int i = 0; i <= UINT8_MAX; i++) {
31 | max = msgtable[i] ? i : max;
32 | }
33 |
34 | return max;
35 | }
36 |
37 | int main(int argc, char **argv)
38 | {
39 | char *line = NULL;
40 | size_t n = 0;
41 | uint16_t grpsz;
42 | struct RESHDR hdr = {
43 | .magic = 0x1ad9c,
44 | .version = 1,
45 | .groups = 0,
46 | };
47 |
48 | // Resource IDs are 2 8bit selectors, so cannot exceed 2^8
49 | char **msgtab[256] = {0};
50 |
51 | while (getline(&line, &n, stdin) != -1) {
52 | char *msg;
53 | uint16_t id;
54 |
55 | // Skip blank lines or comments.
56 | if (*line == '\0' || *line == ';' || *line == '\n')
57 | continue;
58 |
59 | if (*line == '=') {
60 | hdr.groups++;
61 |
62 | msgtab[hdr.groups] = calloc(sizeof(char *), 256);
63 |
64 | // The rest of the line is an ignored comment or name.
65 | // fprintf(stderr, "Group %d, %s", hdr.groups, line + 1);
66 | continue;
67 | }
68 |
69 | // Check that there is a message.
70 | char *suffix = strrchr(line, '"');
71 | char *prefix = strchr(line, '"');
72 |
73 | // Try to parse the first token as a number.
74 | id = strtoul(line, &msg, 0);
75 |
76 | if (!suffix || !prefix || suffix == prefix || msg == line) {
77 | err(EXIT_FAILURE, "failed to parse line `%s`", line);
78 | }
79 |
80 | if (id > UINT8_MAX) {
81 | err(EXIT_FAILURE, "id %u was out of range", id);
82 | }
83 |
84 | // Clear the suffix and prefix.
85 | *suffix = 0; msg = prefix + 1;
86 |
87 | //fprintf(stderr, "id = %d, found msg: %s\n", id, msg);
88 |
89 | // Now add this to our table.
90 | msgtab[hdr.groups][id] = strdup(msg);
91 | }
92 |
93 | fwrite(&hdr, sizeof(hdr), 1, stdout);
94 |
95 | // Now we need to write the total size of each group.
96 | for (int group = 1; group <= hdr.groups; group++) {
97 | uint16_t maxid = find_max_resource(msgtab[group]);
98 |
99 | //fprintf(stderr, "There are %d messages in group %d\n", maxid, group);
100 |
101 | // We will need at least a word for each entry.
102 | grpsz = (maxid + 1) * sizeof(uint16_t);
103 |
104 | // And now the strings themselves.
105 | for (int res = 0; res <= maxid; res++) {
106 | // If it does exist, add the size.
107 | if (msgtab[group][res]) {
108 |
109 | grpsz += strlen(msgtab[group][res]);
110 |
111 | // Add space for the terminating nul. "Empty"
112 | // selectors just point to the nul at the start, so
113 | // don't take any more room and still return a nul string.
114 | grpsz++;
115 | }
116 | }
117 |
118 | // Write out the size.
119 | fwrite(&grpsz, sizeof(grpsz), 1, stdout);
120 | }
121 |
122 | // Now we need to write the strings themselves.
123 | for (int group = 1; group <= hdr.groups; group++) {
124 | uint16_t maxid = find_max_resource(msgtab[group]);
125 |
126 | // We will need at least a word for each entry.
127 | grpsz = (maxid + 1) * sizeof(uint16_t);
128 |
129 | // And now the string sizes.
130 | for (int res = 0; res <= maxid; res++) {
131 | // If it does exist, add the size.
132 | if (msgtab[group][res]) {
133 | fwrite(&grpsz, sizeof(grpsz), 1, stdout);
134 | grpsz += strlen(msgtab[group][res]);
135 |
136 | // Add space for the terminating nul. "Empty"
137 | // selectors just point to the nul at the start, so
138 | // don't take any more room and still return a nul string.
139 | grpsz++;
140 | } else {
141 | uint16_t z = 0;
142 | fwrite(&z, sizeof(z), 1, stdout);
143 | }
144 | }
145 |
146 | // And now the strings.
147 | for (int res = 0; res <= maxid; res++) {
148 | if (msgtab[group][res]) {
149 | fwrite(msgtab[group][res], strlen(msgtab[group][res]), 1, stdout);
150 |
151 | // And a terminating nul
152 | fputc('\0', stdout);
153 | }
154 | // No longer need the strings.
155 | free(msgtab[group][res]);
156 | }
157 |
158 | // Finished
159 | free(msgtab[group]);
160 | }
161 |
162 | fprintf(stderr, "Generated %d resource tables.\n", hdr.groups);
163 |
164 | free(line);
165 | return 0;
166 | }
167 |
--------------------------------------------------------------------------------
/src/display.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "lottypes.h"
11 | #include "lotdefs.h"
12 | #include "lotfuncs.h"
13 | #include "loterrs.h"
14 | #include "kfun.h"
15 |
16 | static inline bool is_cell_valid(struct CELLCOORD cell)
17 | {
18 | return !(cell.row == 0xFFFF
19 | && cell.col == 0xFF
20 | && cell.sheet == 0xFF);
21 | }
22 |
23 | static inline bool compare_cell(struct CELLCOORD a, struct CELLCOORD b)
24 | {
25 | return a.row == b.row
26 | && a.col == b.col
27 | && a.sheet == b.sheet;
28 | }
29 |
30 | int mr_step_wait()
31 | {
32 | char stpstatus[64];
33 | uint32_t key = input_key();
34 | uint16_t wkey = key;
35 | char *p;
36 |
37 | // Setup a status indicator in p.
38 | p = stpstatus;
39 |
40 | // Figure out the current location of the macro.
41 | if (is_cell_valid(mac_cell)) {
42 | dspcref(0xff, mac_cell, 0, &p, 1);
43 | } else if (mac_xrtns.get_mac_name) {
44 | mac_xrtns.get_mac_name(&p);
45 | } else if (is_cell_valid(mac_lastgoodcell)) {
46 | dspcref(0xff, mac_lastgoodcell, 0, &p, 1);
47 | }
48 |
49 | // Terminate the string.
50 | *p++ = 0;
51 |
52 | // If possible, give a snippet of the macro.
53 | if (cmdhandle) {
54 | char *cmdname = access_resource(cmdhandle);
55 | strcat(stpstatus, ": {");
56 | strcat(stpstatus, cmdname);
57 | strcat(stpstatus, "}");
58 |
59 | if (mac_str) {
60 | strcat(stpstatus, " (");
61 | strncat(stpstatus, mac_str, 16);
62 |
63 | // If the string is very long, truncate it.
64 | if (strlen(mac_str) > 16)
65 | strcat(stpstatus, "...");
66 |
67 | strcat(stpstatus, ")");
68 | }
69 | }
70 |
71 | // Display it in the indicator panel.
72 | if (strlen(stpstatus)) {
73 | int16_t szneeded;
74 | x_disp_txt_fit(strlen(stpstatus),
75 | stpstatus,
76 | 32,
77 | &szneeded);
78 |
79 | stpstatus[szneeded] = '\0';
80 | x_disp_txt_set_pos(0, num_text_rows - 1);
81 | x_disp_txt_set_bg(32 * hpu_per_col, 1, 0);
82 | x_disp_txt_zone(strlen(stpstatus), stpstatus, 0, 0, 32 * hpu_per_col);
83 | }
84 |
85 | // Check if a key has been pressed.
86 | if (key & 0xFFFF0000)
87 | return true;
88 |
89 | if (wkey == 0)
90 | return false;
91 |
92 | // Check for Ctrl+C.
93 | if (wkey != KFUN_BREAK)
94 | return true;
95 |
96 | if (!mac_nobreak())
97 | return true;
98 |
99 | return false;
100 | }
101 |
--------------------------------------------------------------------------------
/src/showme.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "lottypes.h"
11 | #include "lotdefs.h"
12 | #include "lotfuncs.h"
13 | #include "loterrs.h"
14 |
15 | #define COLIDX(n) ((n) >> 4)
16 | #define MAX_SHOWME 2048
17 | #pragma pack(1)
18 |
19 | // This is 1bit for each column of a row, A-IV.
20 | typedef struct SHOWMEROW {
21 | int16_t colflags[16];
22 | } SHOWMEROW;
23 |
24 | static struct SHOWME {
25 | int16_t count;
26 | SHOWMEROW rowdata;
27 | int16_t rowindex[MAX_SHOWME];
28 | int16_t sheetindex[MAX_SHOWME];
29 | SHOWMEROW rows[MAX_SHOWME];
30 | } showme_data;
31 |
32 | struct SHOWMECELL {
33 | uint16_t col;
34 | uint16_t sheet;
35 | uint16_t row;
36 | };
37 |
38 | // Utility functions for setting/clearing/getting a column flag.
39 | static inline uint16_t get_column_bit(SHOWMEROW *row, uint16_t column)
40 | {
41 | return !! (row->colflags[column >> 4] & (1 << (column & 0xf)));
42 | }
43 |
44 | static inline void clr_column_bit(SHOWMEROW *row, uint16_t column)
45 | {
46 | row->colflags[column >> 4] &= ~(1 << (column & 0xf));
47 | }
48 |
49 | static inline void set_column_bit(SHOWMEROW *row, uint16_t column)
50 | {
51 | row->colflags[column >> 4] |= (1 << (column & 0xf));
52 | }
53 |
54 | SHOWMEROW * find_row(int16_t sheet, int16_t row)
55 | {
56 | for (int idx = 0; idx < showme_data.count; idx++) {
57 | if (row != showme_data.rowindex[idx])
58 | continue;
59 | if (sheet != showme_data.sheetindex[idx])
60 | continue;
61 | // Match
62 | return &showme_data.rows[idx];
63 | }
64 |
65 | // Not found.
66 | return &showme_data.rowdata;
67 | }
68 |
69 | int16_t init_showme(int16_t termrows)
70 | {
71 | if (termrows > MAX_SHOWME)
72 | return LOTERR_MEMORY_FULL;
73 | return 0;
74 | }
75 |
76 | void reset_showme()
77 | {
78 | showme_data.count = 0;
79 | }
80 |
81 | void clear_showme(int16_t sheet, int16_t row, int16_t numrows)
82 | {
83 | do {
84 | if (find_row(sheet, row) == &showme_data.rowdata) {
85 | showme_data.rowindex[showme_data.count] = row;
86 | showme_data.sheetindex[showme_data.count] = sheet;
87 | memset(&showme_data.rows[showme_data.count], 0, sizeof(SHOWMEROW));
88 | ++showme_data.count;
89 | }
90 | ++row;
91 | --numrows;
92 | } while (numrows);
93 | }
94 |
95 | int scan_showme_rows(struct SHOWMECELL *cell)
96 | {
97 | do {
98 | if (showme_data.count <= cell->col)
99 | return 0;
100 | cell->sheet = showme_data.sheetindex[cell->col];
101 | cell->row = showme_data.rowindex[cell->col++];
102 | } while (cell->row > MAX_ROW);
103 | return 1;
104 | }
105 |
106 | void adjust_showme_cols(int16_t startsheet,
107 | int16_t endsheet,
108 | int16_t begincol,
109 | int16_t endcol)
110 | {
111 | int idx;
112 | int16_t sheet;
113 | int16_t startc;
114 | int16_t maxcol;
115 | int16_t c;
116 | int16_t currcol;
117 | int16_t startcol;
118 | int16_t j;
119 | SHOWMEROW *row;
120 | int16_t i;
121 |
122 | // This is used for insert/delete column
123 | for (idx = 0, i = showme_data.count; i; --i, idx++) {
124 | sheet = showme_data.sheetindex[idx];
125 |
126 | // Check if in sheet range.
127 | if (sheet <= endsheet && sheet >= startsheet) {
128 | // Get a row pointer to easily clear/set bits.
129 | row = &showme_data.rows[idx];
130 |
131 | if (endcol >= 0) {
132 | maxcol = MAX_COL;
133 | startcol = MAX_COLS - endcol - begincol;
134 | if (MAX_COLS - endcol != begincol) {
135 | do {
136 | clr_column_bit(row, maxcol);
137 | set_column_bit(row, maxcol - endcol);
138 | --maxcol;
139 | } while (--startcol);
140 | }
141 | for ( j = endcol; j; --maxcol) {
142 | set_column_bit(row, maxcol);
143 | --j;
144 | }
145 | } else {
146 | startc = begincol;
147 | c = endcol + MAX_COLS - begincol;
148 | if ( endcol + MAX_COLS != begincol ) {
149 | do {
150 | clr_column_bit(row, startc);
151 | set_column_bit(row, startc - endcol);
152 | ++startc;
153 | } while (--c);
154 | }
155 | currcol = endcol;
156 | do {
157 | set_column_bit(row, startc);
158 | ++startc;
159 | } while (++currcol);
160 | }
161 | }
162 | }
163 | }
164 |
165 | void invalidate_region(struct CELLCOORD rngstart, struct CELLCOORD rngend)
166 | {
167 | uint16_t startcol;
168 | uint16_t endcol;
169 | struct CELLCOORD coord;
170 | SHOWMEROW *row;
171 | uint16_t numcols;
172 | uint16_t endcolmask;
173 | uint16_t startcolmask;
174 |
175 | // This routine will set the showme bits for all of the columns between
176 | // start and end for all the rows between them. colflags is an array of 256
177 | // bits, and say we want to invalidate all the columns between a and b:
178 | //
179 | // row a---------------------------------b
180 | // 0 0000000000000000 0000000000000000 0000000000000000
181 | // 1 0000000000000000 0000000000000000 0000000000000000
182 | // 2 0000000000000000 0000000000000000 0000000000000000
183 | //
184 | // The middle word is easy, just to 0xffff. The start and end
185 | // word need a mask.
186 |
187 | startcol = COLIDX(rngstart.col);
188 | endcol = COLIDX(rngend.col);
189 |
190 | // So these are the masks for the word columns a and b are in.
191 | // a-------- [...] -------b
192 | // 0000000000000000 0000000000000000
193 | // mask 1111111000000000 0000000011111111
194 | startcolmask = 0xFFFF << (rngstart.col & 0xF);
195 | endcolmask = 0xFFFE << (rngend.col & 0xF);
196 | endcolmask = ~endcolmask;
197 |
198 | // If the column starts and ends in the same word, then we need
199 | // to or these together.
200 | // a------b
201 | // 0000000000000000
202 | // maska 1111000000000000
203 | // maskb 0000000000001111
204 | // result 1111000000001111
205 | if (endcol == startcol) {
206 | // Why is this and and not or? is this a bug?
207 | endcolmask &= startcolmask;
208 | startcolmask = endcolmask;
209 | }
210 |
211 | // Calculate how many whole columns are between start and end.
212 | // These can be just set to 0xffff.
213 | if (startcol >= endcol) {
214 | // Just use the masks.
215 | numcols = 0;
216 | } else {
217 | numcols = endcol - startcol - 1;
218 | }
219 |
220 | for (int16_t i = 0; i < showme_data.count; i++) {
221 | coord = xyz2coord(rngstart.col,
222 | showme_data.rowindex[i],
223 | showme_data.sheetindex[i]);
224 |
225 | // Now check if we need to set this row.
226 | if (coord_in_range(coord, rngstart, rngend)) {
227 | row = &showme_data.rows[i];
228 | row->colflags[COLIDX(rngstart.col)] |= startcolmask;
229 | row->colflags[COLIDX(rngend.col)] |= endcolmask;
230 | // Set all the columns between.
231 | if (numcols) {
232 | memset(&row->colflags[COLIDX(rngstart.col) + 1], 0xFF, 2 * numcols);
233 | }
234 | }
235 | }
236 | }
237 |
238 | // This is used when inserting/removing rows.
239 | void adjust_showme_rows(int16_t startsheet,
240 | int16_t endsheet,
241 | int16_t startrow,
242 | int16_t endrow)
243 | {
244 | for (int16_t i = 0; i < showme_data.count; i++) {
245 | int16_t sheet = showme_data.sheetindex[i];
246 |
247 | if (startrow <= showme_data.rowindex[i]
248 | && endsheet >= sheet
249 | && sheet >= startsheet) {
250 | int16_t nextrow = showme_data.rowindex[i] + endrow;
251 |
252 | // Adhjust row
253 | showme_data.rowindex[i] = nextrow;
254 |
255 | // This is true when we're deleting a row, so make the index invalid.
256 | if (startrow > nextrow) {
257 | showme_data.rowindex[i] = -1;
258 | }
259 | }
260 | }
261 | }
262 |
263 | void set_showme(struct CELLCOORD cell)
264 | {
265 | SHOWMEROW *row = find_row(cell.sheet, cell.row);
266 | set_column_bit(row, cell.col);
267 | }
268 |
269 | SHOWMEROW *get_showme_row(uint16_t sheet, uint16_t row)
270 | {
271 | return find_row(sheet, row);
272 | }
273 |
274 | int16_t get_showme(struct CELLCOORD cell)
275 | {
276 | SHOWMEROW *row = find_row(cell.sheet, cell.row);
277 | return get_column_bit(row, cell.col);
278 | }
279 |
280 | // This searches for the next set column after the specified column.
281 | int scan_showme(SHOWMEROW *row, int16_t column)
282 | {
283 | for (int i = column; i < MAX_COLS; i++) {
284 | if (get_column_bit(row, i)) {
285 | return i;
286 | }
287 | }
288 | return -1;
289 | }
290 |
291 | // This searches for the column before the specified column.
292 | //
293 | // I think the 123 implementation is buggy, it won't cross a mod16 boundary. I
294 | // see no reason that would be deliberate. Perhaps nobody noticed because
295 | // nobody could see 16 columns simultaneously? This implementation removes that
296 | // limitation.
297 | int scan_showme_leftwards(SHOWMEROW *row, int16_t column)
298 | {
299 | for (int i = column; i >= 0; i--) {
300 | if (get_column_bit(row, i)) {
301 | return i;
302 | }
303 | }
304 | return -1;
305 | }
306 |
--------------------------------------------------------------------------------
/symbols.lst:
--------------------------------------------------------------------------------
1 | --add-symbol fmt_cpy=.text:0x6e530,function,local
2 |
--------------------------------------------------------------------------------
/test/macutil.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This file contains helper functions for building test macros.
4 |
5 | # printrange file range
6 | function printrange()
7 | {
8 | printf -- " -e '/pf{CE}%s~r{CE}%s~gq'" "${1}" "${2}"
9 | }
10 |
11 | function quit()
12 | {
13 | printf -- " -e '/qyy'"
14 | }
15 |
16 | # escape [count]
17 | function escape()
18 | {
19 | printf -- " -e '{ESC %s}'" "${1:-1}"
20 | }
21 |
22 | function enter()
23 | {
24 | printf -- " -e '~'"
25 | }
26 |
27 | # goto [cell]
28 | function goto()
29 | {
30 | printf -- " -e '{GOTO}{CE}%s~'" "${1:-A1}"
31 | }
32 |
33 | # sendkeys string
34 | function sendkeys()
35 | {
36 | printf -- " -e '%s'" "${@}"
37 | }
38 |
39 | # putstring cell string
40 | function putstring()
41 | {
42 | printf -- " -e '{PUT %s,0,0,%s}'" "${1}" "${2}"
43 | }
44 |
45 | # saveas filename
46 | function saveas()
47 | {
48 | printf -- " -e '/fs{CE}%s~'" "${1}"
49 | }
50 |
51 | # retrieve filename
52 | function retrieve()
53 | {
54 | printf -- " -e '/fr{CE}%s~'" "${1}"
55 | }
56 |
57 | # system shellcommand
58 | function system()
59 | {
60 | printf -- " -e '{SYSTEM %c%s%c}'" '"' "${*}" '"'
61 | }
62 |
63 | # macsleep seconds
64 | function macsleep()
65 | {
66 | printf -- " -e '{WAIT @NOW+@TIME(0,0,%u)}'" "${1:-1}"
67 | }
68 |
69 | function screendump()
70 | {
71 | printf -- " -e '{WINDOWSOFF}{SYSTEM +%s & @CHAR(36) & %s}{WINDOWSON}'" '"kill -USR1 "' '"{PPID}"'
72 | }
73 |
74 | # writerange file range
75 | function writerange()
76 | {
77 | local o w c
78 | printf -v o -- '{OPEN "%s","w"}' "${1}"
79 | printf -v w -- '{WRITE %s}' "${2}"
80 | printf -v c -- '{CLOSE}'
81 | printf -- " -e '%s'" "${o}" "${w}" "${c}"
82 | }
83 |
84 | # This turns off the clock -- useful to make screendumps deterministic.
85 | function noclock()
86 | {
87 | printf -- " -e '/wgdocnq'"
88 | }
89 |
90 | # import filename
91 | function import()
92 | {
93 | printf -- " -e '/fin{CE}%s~'" "${1}"
94 | }
95 |
--------------------------------------------------------------------------------
/test/smpfiles.txt:
--------------------------------------------------------------------------------
1 | cc4dca69df9aeec01167b5a7b64631a4 ACCTG.WK3.txt
2 | 81b3f33bc223f42c6d7105e397c85477 CONSOL.WK3.txt
3 | 43f8445b6674e7bf9c6078ba8dd3e50b DATA.WK3.txt
4 | 0504e257c6b5fff0d904c731ed9437bf DBT13S.WK3.txt
5 | 0504e257c6b5fff0d904c731ed9437bf DBT14S.WK3.txt
6 | 25f0104058d853e661cf888ef6881920 INC10S.WK3.txt
7 | b6cf6f2c4372cf5cdc57f0e9231c23ec INC11S.WK3.txt
8 | fe7fb9aee9546cc78e7d786036e24ed9 INC12S.WK3.txt
9 | fe7fb9aee9546cc78e7d786036e24ed9 INC16S.WK3.txt
10 | 4102a1dbc51ff120b53c39c770755091 INC2S.WK3.txt
11 | 8104f8f4397eadf530be809ab2bcb90b INC4S.WK3.txt
12 | a2c68b9a992ad36ee7923cecb51680d9 INC5S.WK3.txt
13 | d7f78856264048541f0f01ff6a439c14 INC6S.WK3.txt
14 | 25f0104058d853e661cf888ef6881920 INC7S.WK3.txt
15 | 25f0104058d853e661cf888ef6881920 INC8S.WK3.txt
16 | 25f0104058d853e661cf888ef6881920 INC9S.WK3.txt
17 | fe7fb9aee9546cc78e7d786036e24ed9 MAC17S.WK3.txt
18 | 892060fc7f749eb06de82be2462b8700 MFG.WK3.txt
19 | ebed629c45245eb532928a276fb7afff SALES.WK3.txt
20 | 943218b8d80e4f39c1524f820a3af138 SAMPMACS.WK3.txt
21 | dccdfaf0b653feb81fc2e5495c7ae5cd SHOES.WK3.txt
22 | 792cd5502c326b43613ba1cfdb2802c2 SMPSCHED.WK3.txt
23 | d767417f51299920dc1d590442225e57 SUM1988S.WK3.txt
24 | 5d47b89c29d5fdebffb65289190621a6 SUMMARY.WK3.txt
25 | 6b0afeaa2f2045e40dbafadbb18b759f TABLES.WK3.txt
26 | 971ca2703aad525803387c2e56446de9 consale.wk1.txt
27 | 81e6fde585f3f35ed2e4e487dbece54f consale.wk3.txt
28 | 0447a242a39192a2b27d606519bdd24f consaler.wk1.txt
29 | 323154b767859cf00ff74bc286adbc98 income.wk3.txt
30 | e337d0b7d51c3bce80595776454eabf5 income2.wk3.txt
31 | 02c543be5ff83af11ff9c5b044102fa1 income3.wk3.txt
32 | 38591478f9e0f18f4cbc67aee95ae23f income4.wk3.txt
33 | 323154b767859cf00ff74bc286adbc98 incomer.wk3.txt
34 | 343a8d2b811e7f156c4f09ddd534c4d5 menu2.wk3.txt
35 | 343a8d2b811e7f156c4f09ddd534c4d5 menu3.wk3.txt
36 | 343a8d2b811e7f156c4f09ddd534c4d5 menu4.wk3.txt
37 |
--------------------------------------------------------------------------------
/test/testcases/bug103.wk3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taviso/123elf/9f0df595115ad67348a94dd6a2d741a9012ea095/test/testcases/bug103.wk3
--------------------------------------------------------------------------------
/test/testutil.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # This script contains helper functions for verifying 1-2-3 functionality.
4 | #
5 |
6 | function runmacro()
7 | {
8 | if ! eval ../123 "${@}"; then
9 | printf "error: 123 did not complete successfully\n" 1>&2
10 | exit 1
11 | fi
12 | }
13 |
14 | # verifyexist file [false]
15 | function verifyexist()
16 | {
17 | if ! test -e "${1}"; then
18 | if test ${2:-true} == "true"; then
19 | printf "error: the file %s did not exist\n" "${1}" 1>&2
20 | exit 1
21 | fi
22 | else
23 | if test ${2:-true} == "false"; then
24 | printf "error: the file %s did exist\n" "${1}" 1>&2
25 | exit 1
26 | fi
27 | fi
28 | }
29 | # verifycontents file contents
30 | function verifycontents()
31 | {
32 | local temp=$(mktemp -u)
33 | local orig="${1}"
34 |
35 | verifyexist "${orig}"
36 | shift
37 | printf -- "${*}" >> "${temp}"
38 |
39 | verifymatch "${orig}" "${temp}"
40 |
41 | # Clean up.
42 | rm -f "${temp}"
43 | }
44 |
45 | function verifymatch()
46 | {
47 | verifyexist "${1}"
48 | verifyexist "${2}"
49 |
50 | if ! cmp "${1}" "${2}"; then
51 | printf "error: the contents of %s and %s did not match\n" "${1}" "${2}" 1>&2
52 | diff -ruN "${1}" "${2}"
53 | exit 1
54 | fi
55 | }
56 |
57 | # verifysum filename checksum size
58 | function verifysum()
59 | {
60 | local sum
61 |
62 | verifyexist "${1}"
63 | if ! sum=$(cksum < "${1}"); then
64 | printf "error: failed to get checksum of file\n" 1>&2
65 | exit 1
66 | fi
67 | if ! test "${sum}" == "${2} ${3}"; then
68 | printf "error: checksum of %s didn't match\n" "${1}" 1>&2
69 | exit 1
70 | fi
71 | }
72 |
73 | function starttest()
74 | {
75 | printf "Testing %s..." "${1}"
76 |
77 | if test ${#} -gt 1; then
78 | if ! type "${2}" &> /dev/null; then
79 | printf "skipped (no %s)\n" "${2}"
80 | return 1
81 | fi
82 | fi
83 |
84 | return 0
85 | }
86 |
87 | function endtest()
88 | {
89 | printf "ok\n"
90 | # Cleanup any temporary files specified
91 | rm -f -- "${@}"
92 | }
93 |
--------------------------------------------------------------------------------
/ttydraw/COPYING:
--------------------------------------------------------------------------------
1 |
2 | The files in this directory are derived from libcaca, which is published under
3 | the license below.
4 |
5 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6 | Version 2, December 2004
7 |
8 | Copyright (C) 2004 Sam Hocevar
9 |
10 | Everyone is permitted to copy and distribute verbatim or modified
11 | copies of this license document, and changing it is allowed as long
12 | as the name is changed.
13 |
14 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
15 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
16 |
17 | 0. You just DO WHAT THE FUCK YOU WANT TO.
18 |
19 |
--------------------------------------------------------------------------------
/ttydraw/Makefile:
--------------------------------------------------------------------------------
1 | CPPFLAGS=-I.
2 | CFLAGS=-m32 $(OPTFLAGS) -fno-stack-protector -fvisibility=hidden
3 | LDFLAGS=$(CFLAGS)
4 | LDLIBS=-lncurses -ltinfo
5 |
6 | .PHONY: clean
7 |
8 | all: ttydraw.a drawtest
9 |
10 | ttydraw.a: attr.o box.o canvas.o charset.o conic.o frame.o line.o string.o transfrm.o triangle.o
11 | $(AR) r $@ $^
12 |
13 | drawtest: drawtest.o ttydraw.a
14 |
15 |
16 | clean:
17 | rm -f *.a *.o drawtest
18 |
--------------------------------------------------------------------------------
/ttydraw/attr.c:
--------------------------------------------------------------------------------
1 | /*
2 | * libcaca Colour ASCII-Art library
3 | * Copyright © 2002—2018 Sam Hocevar
4 | * All Rights Reserved
5 | *
6 | * This library is free software. It comes without any warranty, to
7 | * the extent permitted by applicable law. You can redistribute it
8 | * and/or modify it under the terms of the Do What the Fuck You Want
9 | * to Public License, Version 2, as published by Sam Hocevar. See
10 | * http://www.wtfpl.net/ for more details.
11 | */
12 |
13 | /*
14 | * This file contains functions for attribute management and colourspace
15 | * conversions.
16 | */
17 |
18 | #include
19 | #include "config.h"
20 |
21 | #include "ttydraw.h"
22 | #include "ttyint.h"
23 |
24 | int caca_set_attr(caca_canvas_t *cv, uint32_t attr)
25 | {
26 | cv->curattr = attr;
27 | return 0;
28 | }
29 |
30 | uint32_t caca_get_attr(caca_canvas_t const *cv)
31 | {
32 | return cv->curattr;
33 | }
34 |
35 | uint8_t caca_attr_from_bg_fg(uint8_t fg, uint8_t bg)
36 | {
37 | bg &= 7;
38 | bg <<= 3;
39 | fg &= 7;
40 | return fg | bg;
41 | }
42 |
43 | int caca_set_attr_bg_fg(caca_canvas_t *cv, uint8_t fg, uint8_t bg)
44 | {
45 | return caca_set_attr(cv, caca_attr_from_bg_fg(fg, bg));
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/ttydraw/box.c:
--------------------------------------------------------------------------------
1 | /*
2 | * libcaca Colour ASCII-Art library
3 | * Copyright © 2002—2018 Sam Hocevar
4 | * All Rights Reserved
5 | *
6 | * This library is free software. It comes without any warranty, to
7 | * the extent permitted by applicable law. You can redistribute it
8 | * and/or modify it under the terms of the Do What the Fuck You Want
9 | * to Public License, Version 2, as published by Sam Hocevar. See
10 | * http://www.wtfpl.net/ for more details.
11 | */
12 |
13 | /*
14 | * This file contains box drawing functions, both filled and outline.
15 | */
16 |
17 | #include "config.h"
18 |
19 | #if !defined(__KERNEL__)
20 | # include
21 | #endif
22 |
23 | #include "ttydraw.h"
24 | #include "ttyint.h"
25 |
26 | static int draw_box(caca_canvas_t *cv, int x, int y, int w, int h,
27 | uint32_t const *chars);
28 |
29 | /** \brief Draw a box on the canvas using the given character.
30 | *
31 | * This function never fails.
32 | *
33 | * \param cv The handle to the libcaca canvas.
34 | * \param x X coordinate of the upper-left corner of the box.
35 | * \param y Y coordinate of the upper-left corner of the box.
36 | * \param w Width of the box.
37 | * \param h Height of the box.
38 | * \param ch UTF-32 character to be used to draw the box.
39 | * \return This function always returns 0.
40 | */
41 | int caca_draw_box(caca_canvas_t *cv, int x, int y, int w, int h, uint32_t ch)
42 | {
43 | int x2 = x + w - 1;
44 | int y2 = y + h - 1;
45 |
46 | caca_draw_line(cv, x, y, x, y2, ch);
47 | caca_draw_line(cv, x, y2, x2, y2, ch);
48 | caca_draw_line(cv, x2, y2, x2, y, ch);
49 | caca_draw_line(cv, x2, y, x, y, ch);
50 |
51 | return 0;
52 | }
53 |
54 | /** \brief Draw a thin box on the canvas.
55 | *
56 | * This function never fails.
57 | *
58 | * \param cv The handle to the libcaca canvas.
59 | * \param x X coordinate of the upper-left corner of the box.
60 | * \param y Y coordinate of the upper-left corner of the box.
61 | * \param w Width of the box.
62 | * \param h Height of the box.
63 | * \return This function always returns 0.
64 | */
65 | int caca_draw_thin_box(caca_canvas_t *cv, int x, int y, int w, int h)
66 | {
67 | static uint32_t const ascii_chars[] =
68 | {
69 | '-', '|', ',', '`', '.', '\''
70 | };
71 |
72 | return draw_box(cv, x, y, w, h, ascii_chars);
73 | }
74 |
75 | /** \brief Draw a box on the canvas using CP437 characters.
76 | *
77 | * This function never fails.
78 | *
79 | * \param cv The handle to the libcaca canvas.
80 | * \param x X coordinate of the upper-left corner of the box.
81 | * \param y Y coordinate of the upper-left corner of the box.
82 | * \param w Width of the box.
83 | * \param h Height of the box.
84 | * \return This function always returns 0.
85 | */
86 | int caca_draw_cp437_box(caca_canvas_t *cv, int x, int y, int w, int h)
87 | {
88 | static uint32_t const cp437_chars[] =
89 | {
90 | /* ─ │ ┌ └ ┐ ┘ */
91 | 0x2500, 0x2502, 0x250c, 0x2514, 0x2510, 0x2518
92 | };
93 |
94 | return draw_box(cv, x, y, w, h, cp437_chars);
95 | }
96 |
97 | /** \brief Fill a box on the canvas using the given character.
98 | *
99 | * This function never fails.
100 | *
101 | * \param cv The handle to the libcaca canvas.
102 | * \param x X coordinate of the upper-left corner of the box.
103 | * \param y Y coordinate of the upper-left corner of the box.
104 | * \param w Width of the box.
105 | * \param h Height of the box.
106 | * \param ch UTF-32 character to be used to draw the box.
107 | * \return This function always returns 0.
108 | */
109 | int caca_fill_box(caca_canvas_t *cv, int x, int y, int w, int h,
110 | uint32_t ch)
111 | {
112 | int i, j, xmax, ymax;
113 |
114 | int x2 = x + w - 1;
115 | int y2 = y + h - 1;
116 |
117 | if(x > x2)
118 | {
119 | int tmp = x;
120 | x = x2; x2 = tmp;
121 | }
122 |
123 | if(y > y2)
124 | {
125 | int tmp = y;
126 | y = y2; y2 = tmp;
127 | }
128 |
129 | xmax = cv->width - 1;
130 | ymax = cv->height - 1;
131 |
132 | if(x2 < 0 || y2 < 0 || x > xmax || y > ymax)
133 | return 0;
134 |
135 | if(x < 0) x = 0;
136 | if(y < 0) y = 0;
137 | if(x2 > xmax) x2 = xmax;
138 | if(y2 > ymax) y2 = ymax;
139 |
140 | #if 0
141 | /* FIXME: this fails with fullwidth character blits. Also, the dirty
142 | * rectangle handling may miss fullwidth cells. */
143 | /* Optimise dirty rectangle handling, part 1 */
144 | cv->dirty_disabled++;
145 | #endif
146 |
147 | for(j = y; j <= y2; j++)
148 | for(i = x; i <= x2; i++)
149 | caca_put_char(cv, i, j, ch);
150 |
151 | #if 0
152 | /* Optimise dirty rectangle handling, part 2 */
153 | cv->dirty_disabled--;
154 | if(!cv->dirty_disabled)
155 | caca_add_dirty_rect(cv, x, y, x2 - x + 1, y2 - y + 1);
156 | #endif
157 |
158 | return 0;
159 | }
160 |
161 | /*
162 | * XXX: The following functions are local.
163 | */
164 |
165 | static int draw_box(caca_canvas_t *cv, int x, int y, int w, int h,
166 | uint32_t const *chars)
167 | {
168 | int i, j, xmax, ymax;
169 |
170 | int x2 = x + w - 1;
171 | int y2 = y + h - 1;
172 |
173 | if(x > x2)
174 | {
175 | int tmp = x;
176 | x = x2; x2 = tmp;
177 | }
178 |
179 | if(y > y2)
180 | {
181 | int tmp = y;
182 | y = y2; y2 = tmp;
183 | }
184 |
185 | xmax = cv->width - 1;
186 | ymax = cv->height - 1;
187 |
188 | if(x2 < 0 || y2 < 0 || x > xmax || y > ymax)
189 | return 0;
190 |
191 | /* Draw edges */
192 | if(y >= 0)
193 | for(i = x < 0 ? 1 : x + 1; i < x2 && i < xmax; i++)
194 | caca_put_char(cv, i, y, chars[0]);
195 |
196 | if(y2 <= ymax)
197 | for(i = x < 0 ? 1 : x + 1; i < x2 && i < xmax; i++)
198 | caca_put_char(cv, i, y2, chars[0]);
199 |
200 | if(x >= 0)
201 | for(j = y < 0 ? 1 : y + 1; j < y2 && j < ymax; j++)
202 | caca_put_char(cv, x, j, chars[1]);
203 |
204 | if(x2 <= xmax)
205 | for(j = y < 0 ? 1 : y + 1; j < y2 && j < ymax; j++)
206 | caca_put_char(cv, x2, j, chars[1]);
207 |
208 | /* Draw corners */
209 | caca_put_char(cv, x, y, chars[2]);
210 | caca_put_char(cv, x, y2, chars[3]);
211 | caca_put_char(cv, x2, y, chars[4]);
212 | caca_put_char(cv, x2, y2, chars[5]);
213 |
214 | return 0;
215 | }
216 |
217 |
--------------------------------------------------------------------------------
/ttydraw/cacastub.h:
--------------------------------------------------------------------------------
1 | /*
2 | * libcaca Colour ASCII-Art library
3 | * Copyright (c) 2006-2012 Sam Hocevar
4 | * All Rights Reserved
5 | *
6 | * This library is free software. It comes without any warranty, to
7 | * the extent permitted by applicable law. You can redistribute it
8 | * and/or modify it under the terms of the Do What the Fuck You Want
9 | * to Public License, Version 2, as published by Sam Hocevar. See
10 | * http://www.wtfpl.net/ for more details.
11 | */
12 |
13 | /*
14 | * This file contains replacements for commonly found object types and
15 | * function prototypes that are sometimes missing.
16 | */
17 |
18 | #ifndef __CACA_STUBS_H__
19 | #define __CACA_STUBS_H__
20 |
21 | /* errno handling */
22 | #if defined HAVE_ERRNO_H && !defined __KERNEL__
23 | # include
24 | static inline void seterrno(int e) { errno = e; }
25 | static inline int geterrno(void) { return errno; }
26 | #else
27 | # define seterrno(x) do { (void)(x); } while(0)
28 | # define geterrno(x) 0
29 | #endif
30 |
31 | /* hton16() and hton32() */
32 | #if defined HAVE_HTONS && !defined __KERNEL__
33 | # if defined HAVE_ARPA_INET_H
34 | # include
35 | # elif defined HAVE_NETINET_IN_H
36 | # include
37 | # endif
38 | # define hton16 htons
39 | # define hton32 htonl
40 | #else
41 | # if defined __KERNEL__
42 | /* Nothing to do */
43 | # elif defined HAVE_ENDIAN_H
44 | # include
45 | # endif
46 | static inline uint16_t hton16(uint16_t x)
47 | {
48 | /* This is compile-time optimised with at least -O1 or -Os */
49 | #if defined HAVE_ENDIAN_H
50 | if(__BYTE_ORDER == __BIG_ENDIAN)
51 | #else
52 | uint32_t const dummy = 0x12345678;
53 | if(*(uint8_t const *)&dummy == 0x12)
54 | #endif
55 | return x;
56 | else
57 | return (x >> 8) | (x << 8);
58 | }
59 |
60 | static inline uint32_t hton32(uint32_t x)
61 | {
62 | /* This is compile-time optimised with at least -O1 or -Os */
63 | #if defined HAVE_ENDIAN_H
64 | if(__BYTE_ORDER == __BIG_ENDIAN)
65 | #else
66 | uint32_t const dummy = 0x12345678;
67 | if(*(uint8_t const *)&dummy == 0x12)
68 | #endif
69 | return x;
70 | else
71 | return (x >> 24) | ((x >> 8) & 0x0000ff00)
72 | | ((x << 8) & 0x00ff0000) | (x << 24);
73 | }
74 | #endif
75 |
76 | #endif /* __CACA_STUBS_H__ */
77 |
78 |
--------------------------------------------------------------------------------
/ttydraw/canvas.c:
--------------------------------------------------------------------------------
1 | //
2 | // This file is derived from libcaca, but modified to add simple drawing
3 | // primitives to a CGA framebuffer for Lotus 1-2-3.
4 | //
5 | // The original copyright notice is below.
6 |
7 | /*
8 | * libcaca Colour ASCII-Art library
9 | * Copyright © 2002—2018 Sam Hocevar
10 | * All Rights Reserved
11 | *
12 | * This library is free software. It comes without any warranty, to
13 | * the extent permitted by applicable law. You can redistribute it
14 | * and/or modify it under the terms of the Do What the Fuck You Want
15 | * to Public License, Version 2, as published by Sam Hocevar. See
16 | * http://www.wtfpl.net/ for more details.
17 | */
18 |
19 | #include "config.h"
20 | #include
21 | #include
22 |
23 | #include "ttydraw.h"
24 | #include "ttyint.h"
25 |
26 | caca_canvas_t * caca_create_canvas(int width, int height)
27 | {
28 | static caca_canvas_t canvas;
29 | static struct caca_frame frame;
30 | caca_canvas_t * cv = &canvas;
31 |
32 | if (cv->refcount != 0) {
33 | // It is not currently possible to change the width/height.
34 | if (cv->frames[0].width != width || cv->frames[0].height != height)
35 | return NULL;
36 | // Just increment the refcount.
37 | cv->refcount++;
38 | return cv;
39 | }
40 |
41 | if (width < 0 || height < 0) {
42 | seterrno(EINVAL);
43 | return NULL;
44 | }
45 |
46 | cv->refcount++;
47 | cv->autoinc = 0;
48 | cv->resize_callback = NULL;
49 | cv->resize_data = NULL;
50 |
51 | cv->frame = 0;
52 | cv->framecount = 1;
53 | cv->frames = &frame;
54 | cv->frames[0].width = width;
55 | cv->frames[0].height = height;
56 | cv->frames[0].x = cv->frames[0].y = 0;
57 | cv->frames[0].handlex = cv->frames[0].handley = 0;
58 | cv->frames[0].curattr = 0;
59 | cv->frames[0].name = "frame#00000000";
60 |
61 | _caca_load_frame_info(cv);
62 |
63 | cv->ndirty = 0;
64 | cv->dirty_disabled = 1;
65 | cv->ff = NULL;
66 | return cv;
67 | }
68 |
69 | int caca_get_canvas_width(caca_canvas_t const *cv)
70 | {
71 | return cv->width;
72 | }
73 |
74 | int caca_get_canvas_height(caca_canvas_t const *cv)
75 | {
76 | return cv->height;
77 | }
78 |
79 | int caca_free_canvas(caca_canvas_t *cv)
80 | {
81 | if (cv) {
82 | cv->refcount--;
83 | }
84 | return 0;
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/ttydraw/codec.h:
--------------------------------------------------------------------------------
1 | /*
2 | * libcaca Colour ASCII-Art library
3 | * Copyright (c) 2002-2014 Sam Hocevar
4 | * 2006 Jean-Yves Lamoureux
5 | * All Rights Reserved
6 | *
7 | * This library is free software. It comes without any warranty, to
8 | * the extent permitted by applicable law. You can redistribute it
9 | * and/or modify it under the terms of the Do What the Fuck You Want
10 | * to Public License, Version 2, as published by Sam Hocevar. See
11 | * http://www.wtfpl.net/ for more details.
12 | */
13 |
14 | ssize_t _import_text(caca_canvas_t *, void const *, size_t);
15 | ssize_t _import_ansi(caca_canvas_t *, void const *, size_t, int);
16 | ssize_t _import_bin(caca_canvas_t *, void const *, size_t);
17 |
18 | void *_export_ansi(caca_canvas_t const *, size_t *);
19 | void *_export_plain(caca_canvas_t const *, size_t *);
20 | void *_export_utf8(caca_canvas_t const *, size_t *, int);
21 | void *_export_irc(caca_canvas_t const *, size_t *);
22 |
23 |
--------------------------------------------------------------------------------
/ttydraw/config.h:
--------------------------------------------------------------------------------
1 | #define EOVERFLOW E2BIG
2 | #define ENOSYS EINVAL
3 |
4 | #define HAVE_VSNPRINTF 1
5 | #define HAVE_ERRNO_H 1
6 | #define HAVE_HTONS 1
7 | #define HAVE_ARPA_INET_H 1
8 |
9 | /* Enable ELF visibility */
10 | #define CACA_ENABLE_VISIBILITY 1
11 |
--------------------------------------------------------------------------------
/ttydraw/conic.c:
--------------------------------------------------------------------------------
1 | /*
2 | * libcaca Colour ASCII-Art library
3 | * Copyright © 2002—2018 Sam Hocevar
4 | * All Rights Reserved
5 | *
6 | * This library is free software. It comes without any warranty, to
7 | * the extent permitted by applicable law. You can redistribute it
8 | * and/or modify it under the terms of the Do What the Fuck You Want
9 | * to Public License, Version 2, as published by Sam Hocevar. See
10 | * http://www.wtfpl.net/ for more details.
11 | */
12 |
13 | /*
14 | * This file contains ellipse and circle drawing functions, both filled
15 | * and outline.
16 | */
17 |
18 | #include "config.h"
19 |
20 | #if !defined(__KERNEL__)
21 | # include
22 | #endif
23 |
24 | #include "ttydraw.h"
25 | #include "ttyint.h"
26 |
27 | static void ellipsepoints(caca_canvas_t *, int, int, int, int, uint32_t, int);
28 |
29 | /** \brief Draw a circle on the canvas using the given character.
30 | *
31 | * This function never fails.
32 | *
33 | * \param cv The handle to the libcaca canvas.
34 | * \param x Center X coordinate.
35 | * \param y Center Y coordinate.
36 | * \param r Circle radius.
37 | * \param ch UTF-32 character to be used to draw the circle outline.
38 | * \return This function always returns 0.
39 | */
40 | int caca_draw_circle(caca_canvas_t *cv, int x, int y, int r, uint32_t ch)
41 | {
42 | int test, dx, dy;
43 |
44 | /* Optimized Bresenham. Kick ass. */
45 | for(test = 0, dx = 0, dy = r ; dx <= dy ; dx++)
46 | {
47 | ellipsepoints(cv, x, y, dx, dy, ch, 1);
48 | ellipsepoints(cv, x, y, dy, dx, ch, 1);
49 |
50 | test += test > 0 ? dx - dy-- : dx;
51 | }
52 |
53 | return 0;
54 | }
55 |
56 | /** \brief Fill an ellipse on the canvas using the given character.
57 | *
58 | * This function never fails.
59 | *
60 | * \param cv The handle to the libcaca canvas.
61 | * \param xo Center X coordinate.
62 | * \param yo Center Y coordinate.
63 | * \param a Ellipse X radius.
64 | * \param b Ellipse Y radius.
65 | * \param ch UTF-32 character to be used to fill the ellipse.
66 | * \return This function always returns 0.
67 | */
68 | int caca_fill_ellipse(caca_canvas_t *cv, int xo, int yo, int a, int b,
69 | uint32_t ch)
70 | {
71 | int d2;
72 | int x = 0;
73 | int y = b;
74 | int d1 = b*b - (a*a*b) + (a*a/4);
75 |
76 | while(a*a*y - a*a/2 > b*b*(x+1))
77 | {
78 | if(d1 < 0)
79 | {
80 | d1 += b*b*(2*x+1); /* XXX: "Computer Graphics" has + 3 here. */
81 | }
82 | else
83 | {
84 | d1 += b*b*(2*x*1) + a*a*(-2*y+2);
85 | caca_draw_line(cv, xo - x, yo - y, xo + x, yo - y, ch);
86 | caca_draw_line(cv, xo - x, yo + y, xo + x, yo + y, ch);
87 | y--;
88 | }
89 | x++;
90 | }
91 |
92 | caca_draw_line(cv, xo - x, yo - y, xo + x, yo - y, ch);
93 | caca_draw_line(cv, xo - x, yo + y, xo + x, yo + y, ch);
94 |
95 | d2 = b*b*(x+0.5)*(x+0.5) + a*a*(y-1)*(y-1) - a*a*b*b;
96 | while(y > 0)
97 | {
98 | if(d2 < 0)
99 | {
100 | d2 += b*b*(2*x+2) + a*a*(-2*y+3);
101 | x++;
102 | }
103 | else
104 | {
105 | d2 += a*a*(-2*y+3);
106 | }
107 |
108 | y--;
109 | caca_draw_line(cv, xo - x, yo - y, xo + x, yo - y, ch);
110 | caca_draw_line(cv, xo - x, yo + y, xo + x, yo + y, ch);
111 | }
112 |
113 | return 0;
114 | }
115 |
116 | /** \brief Draw an ellipse on the canvas using the given character.
117 | *
118 | * This function never fails.
119 | *
120 | * \param cv The handle to the libcaca canvas.
121 | * \param xo Center X coordinate.
122 | * \param yo Center Y coordinate.
123 | * \param a Ellipse X radius.
124 | * \param b Ellipse Y radius.
125 | * \param ch UTF-32 character to be used to draw the ellipse outline.
126 | * \return This function always returns 0.
127 | */
128 | int caca_draw_ellipse(caca_canvas_t *cv, int xo, int yo, int a, int b,
129 | uint32_t ch)
130 | {
131 | int d2;
132 | int x = 0;
133 | int y = b;
134 | int d1 = b*b - (a*a*b) + (a*a/4);
135 |
136 | ellipsepoints(cv, xo, yo, x, y, ch, 0);
137 |
138 | while(a*a*y - a*a/2 > b*b*(x+1))
139 | {
140 | if(d1 < 0)
141 | {
142 | d1 += b*b*(2*x+1); /* XXX: "Computer Graphics" has + 3 here. */
143 | }
144 | else
145 | {
146 | d1 += b*b*(2*x*1) + a*a*(-2*y+2);
147 | y--;
148 | }
149 | x++;
150 | ellipsepoints(cv, xo, yo, x, y, ch, 0);
151 | }
152 |
153 | d2 = b*b*(x+0.5)*(x+0.5) + a*a*(y-1)*(y-1) - a*a*b*b;
154 | while(y > 0)
155 | {
156 | if(d2 < 0)
157 | {
158 | d2 += b*b*(2*x+2) + a*a*(-2*y+3);
159 | x++;
160 | }
161 | else
162 | {
163 | d2 += a*a*(-2*y+3);
164 | }
165 |
166 | y--;
167 | ellipsepoints(cv, xo, yo, x, y, ch, 0);
168 | }
169 |
170 | return 0;
171 | }
172 |
173 | /** \brief Draw a thin ellipse on the canvas.
174 | *
175 | * This function never fails.
176 | *
177 | * \param cv The handle to the libcaca canvas.
178 | * \param xo Center X coordinate.
179 | * \param yo Center Y coordinate.
180 | * \param a Ellipse X radius.
181 | * \param b Ellipse Y radius.
182 | * \return This function always returns 0.
183 | */
184 | int caca_draw_thin_ellipse(caca_canvas_t *cv, int xo, int yo, int a, int b)
185 | {
186 | /* FIXME: this is not correct */
187 | int d2;
188 | int x = 0;
189 | int y = b;
190 | int d1 = b*b - (a*a*b) + (a*a/4);
191 |
192 | ellipsepoints(cv, xo, yo, x, y, '-', 1);
193 |
194 | while(a*a*y - a*a/2 > b*b*(x+1))
195 | {
196 | if(d1 < 0)
197 | {
198 | d1 += b*b*(2*x+1); /* XXX: "Computer Graphics" has + 3 here. */
199 | ellipsepoints(cv, xo, yo, x + 1, y, '0', 1);
200 | }
201 | else
202 | {
203 | d1 += b*b*(2*x*1) + a*a*(-2*y+2);
204 | y--;
205 | ellipsepoints(cv, xo, yo, x + 1, y, '1', 1);
206 | }
207 | x++;
208 |
209 |
210 | }
211 |
212 | d2 = b*b*(x+0.5)*(x+0.5) + a*a*(y-1)*(y-1) - a*a*b*b;
213 | while(y > 0)
214 | {
215 | if(d2 < 0)
216 | {
217 | d2 += b*b*(2*x+2) + a*a*(-2*y+3);
218 | x++;
219 | ellipsepoints(cv, xo, yo, x , y - 1, '2', 1);
220 | }
221 | else
222 | {
223 | d2 += a*a*(-2*y+3);
224 | ellipsepoints(cv, xo, yo, x , y - 1, '3', 1);
225 | }
226 |
227 | y--;
228 |
229 |
230 | }
231 |
232 | return 0;
233 | }
234 |
235 | static void ellipsepoints(caca_canvas_t *cv, int xo, int yo, int x, int y,
236 | uint32_t ch, int thin)
237 | {
238 | uint8_t b = 0;
239 |
240 | if(xo + x >= 0 && xo + x < (int)cv->width)
241 | b |= 0x1;
242 | if(xo - x >= 0 && xo - x < (int)cv->width)
243 | b |= 0x2;
244 | if(yo + y >= 0 && yo + y < (int)cv->height)
245 | b |= 0x4;
246 | if(yo - y >= 0 && yo - y < (int)cv->height)
247 | b |= 0x8;
248 |
249 | if((b & (0x1|0x4)) == (0x1|0x4)) {
250 | uint32_t c = ch;
251 |
252 | if(thin) {
253 | switch(c) {
254 | case '0':
255 | c = '-';
256 | break;
257 | case '1':
258 | c = ',';
259 | break;
260 | case '2':
261 | c = '/';
262 | break;
263 | case '3':
264 | c = '|';
265 | break;
266 | }
267 |
268 | }
269 | caca_put_char(cv, xo + x, yo + y, c);
270 | }
271 | if((b & (0x2|0x4)) == (0x2|0x4)) {
272 | uint32_t c = ch;
273 |
274 | if(thin) {
275 | switch(c) {
276 | case '0':
277 | c = '-';
278 | break;
279 | case '1':
280 | c = '.';
281 | break;
282 | case '2':
283 | c = '\\';
284 | break;
285 | case '3':
286 | c = '|';
287 | break;
288 | }
289 |
290 | }
291 | caca_put_char(cv, xo - x, yo + y, c);
292 | }
293 |
294 |
295 | if((b & (0x1|0x8)) == (0x1|0x8)) {
296 | uint32_t c = ch;
297 |
298 | if(thin) {
299 | switch(c) {
300 | case '0':
301 | c = '-';
302 | break;
303 | case '1':
304 | c = '`';
305 | break;
306 | case '2':
307 | c = '\\';
308 | break;
309 | case '3':
310 | c = '|';
311 | break;
312 | }
313 |
314 | }
315 | caca_put_char(cv, xo + x, yo - y, c);
316 | }
317 |
318 | if((b & (0x2|0x8)) == (0x2|0x8)) {
319 | uint32_t c = ch;
320 |
321 | if(thin) {
322 | switch(c) {
323 | case '0':
324 | c = '-';
325 | break;
326 | case '1':
327 | c = '\'';
328 | break;
329 | case '2':
330 | c = '/';
331 | break;
332 | case '3':
333 | c = '|';
334 | break;
335 | }
336 |
337 | }
338 | caca_put_char(cv, xo - x, yo - y, c);
339 | }
340 | }
341 |
342 |
--------------------------------------------------------------------------------
/ttydraw/drawtest.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include "config.h"
7 |
8 | #include "ttydraw.h"
9 |
10 | int main(int argc, char *argv[])
11 | {
12 | caca_canvas_t *cv;
13 | initscr();
14 | cv = caca_create_canvas(100, 30);
15 | caca_draw_thin_line(cv, 10, 10, 80, 25);
16 | caca_draw_cp437_box(cv, 20, 5, 20, 10);
17 | caca_fill_ellipse(cv, 80, 8, 5, 5, '#');
18 | getch();
19 | endwin();
20 | return 0;
21 | }
22 |
--------------------------------------------------------------------------------
/ttydraw/frame.c:
--------------------------------------------------------------------------------
1 | #include "config.h"
2 | #include "ttydraw.h"
3 | #include "ttyint.h"
4 |
5 | int caca_get_frame_count(caca_canvas_t const *cv)
6 | {
7 | return cv->framecount;
8 | }
9 |
10 | int caca_set_frame(caca_canvas_t *cv, int id)
11 | {
12 | if(id < 0 || id >= cv->framecount)
13 | {
14 | seterrno(EINVAL);
15 | return -1;
16 | }
17 |
18 | if(id != cv->frame)
19 | return -1;
20 |
21 | return 0;
22 | }
23 |
24 | char const *caca_get_frame_name(caca_canvas_t const *cv)
25 | {
26 | return cv->frames[cv->frame].name;
27 | }
28 |
29 | int caca_set_frame_name(caca_canvas_t *cv, char const *name)
30 | {
31 | return -1;
32 | }
33 |
34 | int caca_create_frame(caca_canvas_t *cv, int id)
35 | {
36 | return -1;
37 | }
38 |
39 | int caca_free_frame(caca_canvas_t *cv, int id)
40 | {
41 | int f;
42 |
43 | if(id < 0 || id >= cv->framecount)
44 | {
45 | seterrno(EINVAL);
46 | return -1;
47 | }
48 |
49 | if(cv->framecount == 1)
50 | {
51 | seterrno(EINVAL);
52 | return -1;
53 | }
54 |
55 | return 0;
56 | }
57 |
58 | void _caca_save_frame_info(caca_canvas_t *cv)
59 | {
60 | cv->frames[cv->frame].width = cv->width;
61 | cv->frames[cv->frame].height = cv->height;
62 | cv->frames[cv->frame].curattr = cv->curattr;
63 | }
64 |
65 | void _caca_load_frame_info(caca_canvas_t *cv)
66 | {
67 | cv->width = cv->frames[cv->frame].width;
68 | cv->height = cv->frames[cv->frame].height;
69 | cv->curattr = cv->frames[cv->frame].curattr;
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/ttydraw/line.c:
--------------------------------------------------------------------------------
1 | /*
2 | * libcaca Colour ASCII-Art library
3 | * Copyright © 2002—2018 Sam Hocevar
4 | * All Rights Reserved
5 | *
6 | * This library is free software. It comes without any warranty, to
7 | * the extent permitted by applicable law. You can redistribute it
8 | * and/or modify it under the terms of the Do What the Fuck You Want
9 | * to Public License, Version 2, as published by Sam Hocevar. See
10 | * http://www.wtfpl.net/ for more details.
11 | */
12 |
13 | /*
14 | * This file contains line and polyline drawing functions, with both thin
15 | * and thick styles.
16 | */
17 |
18 | #include "config.h"
19 |
20 | #if !defined(__KERNEL__)
21 | # include
22 | #endif
23 |
24 | #include "ttydraw.h"
25 | #include "ttyint.h"
26 |
27 | #if !defined(_DOXYGEN_SKIP_ME)
28 | struct line
29 | {
30 | int x1, y1;
31 | int x2, y2;
32 | uint32_t ch;
33 | void (*draw) (caca_canvas_t *, struct line*);
34 | };
35 | #endif
36 |
37 | static void clip_line(caca_canvas_t*, struct line*);
38 | static uint8_t clip_bits(caca_canvas_t*, int, int);
39 | static void draw_solid_line(caca_canvas_t*, struct line*);
40 | static void draw_thin_line(caca_canvas_t*, struct line*);
41 |
42 | /** \brief Draw a line on the canvas using the given character.
43 | *
44 | * This function never fails.
45 | *
46 | * \param cv The handle to the libcaca canvas.
47 | * \param x1 X coordinate of the first point.
48 | * \param y1 Y coordinate of the first point.
49 | * \param x2 X coordinate of the second point.
50 | * \param y2 Y coordinate of the second point.
51 | * \param ch UTF-32 character to be used to draw the line.
52 | * \return This function always returns 0.
53 | */
54 | int caca_draw_line(caca_canvas_t *cv, int x1, int y1, int x2, int y2,
55 | uint32_t ch)
56 | {
57 | struct line s;
58 | s.x1 = x1;
59 | s.y1 = y1;
60 | s.x2 = x2;
61 | s.y2 = y2;
62 | s.ch = ch;
63 | s.draw = draw_solid_line;
64 | clip_line(cv, &s);
65 |
66 | return 0;
67 | }
68 |
69 | /** \brief Draw a polyline.
70 | *
71 | * Draw a polyline on the canvas using the given character and coordinate
72 | * arrays. The first and last points are not connected, hence in order to
73 | * draw a polygon you need to specify the starting point at the end of the
74 | * list as well.
75 | *
76 | * This function never fails.
77 | *
78 | * \param cv The handle to the libcaca canvas.
79 | * \param x Array of X coordinates. Must have \p n + 1 elements.
80 | * \param y Array of Y coordinates. Must have \p n + 1 elements.
81 | * \param n Number of lines to draw.
82 | * \param ch UTF-32 character to be used to draw the lines.
83 | * \return This function always returns 0.
84 | */
85 | int caca_draw_polyline(caca_canvas_t *cv, int const x[], int const y[],
86 | int n, uint32_t ch)
87 | {
88 | int i;
89 | struct line s;
90 | s.ch = ch;
91 | s.draw = draw_solid_line;
92 |
93 | for(i = 0; i < n; i++)
94 | {
95 | s.x1 = x[i];
96 | s.y1 = y[i];
97 | s.x2 = x[i+1];
98 | s.y2 = y[i+1];
99 | clip_line(cv, &s);
100 | }
101 |
102 | return 0;
103 | }
104 |
105 | /** \brief Draw a thin line on the canvas, using ASCII art.
106 | *
107 | * This function never fails.
108 | *
109 | * \param cv The handle to the libcaca canvas.
110 | * \param x1 X coordinate of the first point.
111 | * \param y1 Y coordinate of the first point.
112 | * \param x2 X coordinate of the second point.
113 | * \param y2 Y coordinate of the second point.
114 | * \return This function always returns 0.
115 | */
116 | int caca_draw_thin_line(caca_canvas_t *cv, int x1, int y1, int x2, int y2)
117 | {
118 | struct line s;
119 | s.x1 = x1;
120 | s.y1 = y1;
121 | s.x2 = x2;
122 | s.y2 = y2;
123 | s.draw = draw_thin_line;
124 | clip_line(cv, &s);
125 |
126 | return 0;
127 | }
128 |
129 | /** \brief Draw an ASCII art thin polyline.
130 | *
131 | * Draw a thin polyline on the canvas using the given coordinate arrays and
132 | * with ASCII art. The first and last points are not connected, so in order
133 | * to draw a polygon you need to specify the starting point at the end of
134 | * the list as well.
135 | *
136 | * This function never fails.
137 | *
138 | * \param cv The handle to the libcaca canvas.
139 | * \param x Array of X coordinates. Must have \p n + 1 elements.
140 | * \param y Array of Y coordinates. Must have \p n + 1 elements.
141 | * \param n Number of lines to draw.
142 | * \return This function always returns 0.
143 | */
144 | int caca_draw_thin_polyline(caca_canvas_t *cv, int const x[], int const y[],
145 | int n)
146 | {
147 | int i;
148 | struct line s;
149 | s.draw = draw_thin_line;
150 |
151 | for(i = 0; i < n; i++)
152 | {
153 | s.x1 = x[i];
154 | s.y1 = y[i];
155 | s.x2 = x[i+1];
156 | s.y2 = y[i+1];
157 | clip_line(cv, &s);
158 | }
159 |
160 | return 0;
161 | }
162 |
163 | /*
164 | * XXX: The following functions are local.
165 | */
166 |
167 | /* Generic Cohen-Sutherland line clipping function. */
168 | static void clip_line(caca_canvas_t *cv, struct line* s)
169 | {
170 | uint8_t bits1, bits2;
171 |
172 | bits1 = clip_bits(cv, s->x1, s->y1);
173 | bits2 = clip_bits(cv, s->x2, s->y2);
174 |
175 | if(bits1 & bits2)
176 | return;
177 |
178 | if(bits1 == 0)
179 | {
180 | if(bits2 == 0)
181 | s->draw(cv, s);
182 | else
183 | {
184 | int tmp;
185 | tmp = s->x1; s->x1 = s->x2; s->x2 = tmp;
186 | tmp = s->y1; s->y1 = s->y2; s->y2 = tmp;
187 | clip_line(cv, s);
188 | }
189 |
190 | return;
191 | }
192 |
193 | if(bits1 & (1<<0))
194 | {
195 | s->y1 = s->y2 - (s->x2 - 0) * (s->y2 - s->y1) / (s->x2 - s->x1);
196 | s->x1 = 0;
197 | }
198 | else if(bits1 & (1<<1))
199 | {
200 | int xmax = cv->width - 1;
201 | s->y1 = s->y2 - (s->x2 - xmax) * (s->y2 - s->y1) / (s->x2 - s->x1);
202 | s->x1 = xmax;
203 | }
204 | else if(bits1 & (1<<2))
205 | {
206 | s->x1 = s->x2 - (s->y2 - 0) * (s->x2 - s->x1) / (s->y2 - s->y1);
207 | s->y1 = 0;
208 | }
209 | else if(bits1 & (1<<3))
210 | {
211 | int ymax = cv->height - 1;
212 | s->x1 = s->x2 - (s->y2 - ymax) * (s->x2 - s->x1) / (s->y2 - s->y1);
213 | s->y1 = ymax;
214 | }
215 |
216 | clip_line(cv, s);
217 | }
218 |
219 | /* Helper function for clip_line(). */
220 | static uint8_t clip_bits(caca_canvas_t *cv, int x, int y)
221 | {
222 | uint8_t b = 0;
223 |
224 | if(x < 0)
225 | b |= (1<<0);
226 | else if(x >= (int)cv->width)
227 | b |= (1<<1);
228 |
229 | if(y < 0)
230 | b |= (1<<2);
231 | else if(y >= (int)cv->height)
232 | b |= (1<<3);
233 |
234 | return b;
235 | }
236 |
237 | /* Solid line drawing function, using Bresenham's mid-point line
238 | * scan-conversion algorithm. */
239 | static void draw_solid_line(caca_canvas_t *cv, struct line* s)
240 | {
241 | int x1, y1, x2, y2;
242 | int dx, dy;
243 | int xinc, yinc;
244 |
245 | x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
246 |
247 | dx = abs(x2 - x1);
248 | dy = abs(y2 - y1);
249 |
250 | xinc = (x1 > x2) ? -1 : 1;
251 | yinc = (y1 > y2) ? -1 : 1;
252 |
253 | if(dx >= dy)
254 | {
255 | int dpr = dy << 1;
256 | int dpru = dpr - (dx << 1);
257 | int delta = dpr - dx;
258 |
259 | for(; dx>=0; dx--)
260 | {
261 | caca_put_char(cv, x1, y1, s->ch);
262 | if(delta > 0)
263 | {
264 | x1 += xinc;
265 | y1 += yinc;
266 | delta += dpru;
267 | }
268 | else
269 | {
270 | x1 += xinc;
271 | delta += dpr;
272 | }
273 | }
274 | }
275 | else
276 | {
277 | int dpr = dx << 1;
278 | int dpru = dpr - (dy << 1);
279 | int delta = dpr - dy;
280 |
281 | for(; dy >= 0; dy--)
282 | {
283 | caca_put_char(cv, x1, y1, s->ch);
284 | if(delta > 0)
285 | {
286 | x1 += xinc;
287 | y1 += yinc;
288 | delta += dpru;
289 | }
290 | else
291 | {
292 | y1 += yinc;
293 | delta += dpr;
294 | }
295 | }
296 | }
297 | }
298 |
299 | /* Thin line drawing function, using Bresenham's mid-point line
300 | * scan-conversion algorithm and ASCII art graphics. */
301 | static void draw_thin_line(caca_canvas_t *cv, struct line* s)
302 | {
303 | uint32_t charmapx[2], charmapy[2];
304 | int x1, y1, x2, y2;
305 | int dx, dy;
306 | int yinc;
307 |
308 | if(s->x2 >= s->x1)
309 | {
310 | charmapx[0] = (s->y1 > s->y2) ? ',' : '`';
311 | charmapx[1] = (s->y1 > s->y2) ? '\'' : '.';
312 | x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
313 | }
314 | else
315 | {
316 | charmapx[0] = (s->y1 > s->y2) ? '`' : '.';
317 | charmapx[1] = (s->y1 > s->y2) ? ',' : '\'';
318 | x2 = s->x1; y2 = s->y1; x1 = s->x2; y1 = s->y2;
319 | }
320 |
321 | dx = abs(x2 - x1);
322 | dy = abs(y2 - y1);
323 |
324 | if(y1 > y2)
325 | {
326 | charmapy[0] = ',';
327 | charmapy[1] = '\'';
328 | yinc = -1;
329 | }
330 | else
331 | {
332 | yinc = 1;
333 | charmapy[0] = '`';
334 | charmapy[1] = '.';
335 | }
336 |
337 | if(dx >= dy)
338 | {
339 | int dpr = dy << 1;
340 | int dpru = dpr - (dx << 1);
341 | int delta = dpr - dx;
342 | int prev = 0;
343 |
344 | for(; dx>=0; dx--)
345 | {
346 | if(delta > 0)
347 | {
348 | caca_put_char(cv, x1, y1, charmapy[1]);
349 | x1++;
350 | y1 += yinc;
351 | delta += dpru;
352 | prev = 1;
353 | }
354 | else
355 | {
356 | if(prev)
357 | caca_put_char(cv, x1, y1, charmapy[0]);
358 | else
359 | caca_put_char(cv, x1, y1, '-');
360 | x1++;
361 | delta += dpr;
362 | prev = 0;
363 | }
364 | }
365 | }
366 | else
367 | {
368 | int dpr = dx << 1;
369 | int dpru = dpr - (dy << 1);
370 | int delta = dpr - dy;
371 |
372 | for(; dy >= 0; dy--)
373 | {
374 | if(delta > 0)
375 | {
376 | caca_put_char(cv, x1, y1, charmapx[0]);
377 | caca_put_char(cv, x1 + 1, y1, charmapx[1]);
378 | x1++;
379 | y1 += yinc;
380 | delta += dpru;
381 | }
382 | else
383 | {
384 | caca_put_char(cv, x1, y1, '|');
385 | y1 += yinc;
386 | delta += dpr;
387 | }
388 | }
389 | }
390 | }
391 |
392 |
--------------------------------------------------------------------------------
/ttydraw/string.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "config.h"
8 |
9 | #include "ttydraw.h"
10 | #include "ttyint.h"
11 |
12 | int caca_put_char(caca_canvas_t *cv, int x, int y, uint32_t ch)
13 | {
14 | mvaddch(y, x, ch | COLOR_PAIR(cv->curattr));
15 | return 1;
16 | }
17 |
18 | int caca_put_str(caca_canvas_t *cv, int x, int y, char const *s)
19 | {
20 | int len;
21 | if (y < 0 || y >= (int)cv->height || x >= (int)cv->width) {
22 | return strlen(s);
23 | }
24 |
25 | for (len = 0; *s; len++) {
26 | caca_put_char(cv, x + len, y, *s++);
27 | }
28 |
29 | return len;
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/ttydraw/triangle.c:
--------------------------------------------------------------------------------
1 | /*
2 | * libcaca Colour ASCII-Art library
3 | * Copyright © 2002—2018 Sam Hocevar
4 | * All Rights Reserved
5 | *
6 | * This library is free software. It comes without any warranty, to
7 | * the extent permitted by applicable law. You can redistribute it
8 | * and/or modify it under the terms of the Do What the Fuck You Want
9 | * to Public License, Version 2, as published by Sam Hocevar. See
10 | * http://www.wtfpl.net/ for more details.
11 | */
12 |
13 | /*
14 | * This file contains triangle drawing functions, both filled and outline.
15 | */
16 |
17 | #include "config.h"
18 |
19 | #if !defined(__KERNEL__)
20 | # include
21 | #endif
22 |
23 | #include "ttydraw.h"
24 | #include "ttyint.h"
25 |
26 | /** \brief Draw a triangle on the canvas using the given character.
27 | *
28 | * This function never fails.
29 | *
30 | * \param cv The handle to the libcaca canvas.
31 | * \param x1 X coordinate of the first point.
32 | * \param y1 Y coordinate of the first point.
33 | * \param x2 X coordinate of the second point.
34 | * \param y2 Y coordinate of the second point.
35 | * \param x3 X coordinate of the third point.
36 | * \param y3 Y coordinate of the third point.
37 | * \param ch UTF-32 character to be used to draw the triangle outline.
38 | * \return This function always returns 0.
39 | */
40 | int caca_draw_triangle(caca_canvas_t * cv, int x1, int y1, int x2, int y2,
41 | int x3, int y3, uint32_t ch)
42 | {
43 | caca_draw_line(cv, x1, y1, x2, y2, ch);
44 | caca_draw_line(cv, x2, y2, x3, y3, ch);
45 | caca_draw_line(cv, x3, y3, x1, y1, ch);
46 |
47 | return 0;
48 | }
49 |
50 | /** \brief Draw a thin triangle on the canvas.
51 | *
52 | * This function never fails.
53 | *
54 | * \param cv The handle to the libcaca canvas.
55 | * \param x1 X coordinate of the first point.
56 | * \param y1 Y coordinate of the first point.
57 | * \param x2 X coordinate of the second point.
58 | * \param y2 Y coordinate of the second point.
59 | * \param x3 X coordinate of the third point.
60 | * \param y3 Y coordinate of the third point.
61 | * \return This function always returns 0.
62 | */
63 | int caca_draw_thin_triangle(caca_canvas_t * cv, int x1, int y1,
64 | int x2, int y2, int x3, int y3)
65 | {
66 | caca_draw_thin_line(cv, x1, y1, x2, y2);
67 | caca_draw_thin_line(cv, x2, y2, x3, y3);
68 | caca_draw_thin_line(cv, x3, y3, x1, y1);
69 |
70 | return 0;
71 | }
72 |
73 | /** \brief Fill a triangle on the canvas using the given character.
74 | *
75 | * This function never fails.
76 | *
77 | * \param cv The handle to the libcaca canvas.
78 | * \param x1 X coordinate of the first point.
79 | * \param y1 Y coordinate of the first point.
80 | * \param x2 X coordinate of the second point.
81 | * \param y2 Y coordinate of the second point.
82 | * \param x3 X coordinate of the third point.
83 | * \param y3 Y coordinate of the third point.
84 | * \param ch UTF-32 character to be used to fill the triangle.
85 | * \return This function always returns 0.
86 | */
87 | int caca_fill_triangle(caca_canvas_t * cv, int x1, int y1, int x2, int y2,
88 | int x3, int y3, uint32_t ch)
89 | {
90 | int x, y, xmin, xmax, ymin, ymax;
91 | int xx1, xx2, xa, xb, sl21, sl31, sl32;
92 |
93 | /* Bubble-sort y1 <= y2 <= y3 */
94 | if (y1 > y2)
95 | return caca_fill_triangle(cv, x2, y2, x1, y1, x3, y3, ch);
96 |
97 | if (y2 > y3)
98 | return caca_fill_triangle(cv, x1, y1, x3, y3, x2, y2, ch);
99 |
100 | /* Compute slopes and promote precision */
101 | sl21 = (y2 == y1) ? 0 : (x2 - x1) * 0x10000 / (y2 - y1);
102 | sl31 = (y3 == y1) ? 0 : (x3 - x1) * 0x10000 / (y3 - y1);
103 | sl32 = (y3 == y2) ? 0 : (x3 - x2) * 0x10000 / (y3 - y2);
104 |
105 | x1 *= 0x10000;
106 | x2 *= 0x10000;
107 | x3 *= 0x10000;
108 |
109 | ymin = y1 < 0 ? 0 : y1;
110 | ymax = y3 + 1 < cv->height ? y3 + 1 : cv->height;
111 |
112 | if (ymin < y2)
113 | {
114 | xa = x1 + sl21 * (ymin - y1);
115 | xb = x1 + sl31 * (ymin - y1);
116 | }
117 | else if (ymin == y2)
118 | {
119 | xa = x2;
120 | xb = (y1 == y3) ? x3 : x1 + sl31 * (ymin - y1);
121 | }
122 | else /* (ymin > y2) */
123 | {
124 | xa = x3 + sl32 * (ymin - y3);
125 | xb = x3 + sl31 * (ymin - y3);
126 | }
127 |
128 | /* Rasterize our triangle */
129 | for (y = ymin; y < ymax; y++)
130 | {
131 | /* Rescale xa and xb, recentering the division */
132 | if (xa < xb)
133 | {
134 | xx1 = (xa + 0x800) / 0x10000;
135 | xx2 = (xb + 0x801) / 0x10000;
136 | }
137 | else
138 | {
139 | xx1 = (xb + 0x800) / 0x10000;
140 | xx2 = (xa + 0x801) / 0x10000;
141 | }
142 |
143 | xmin = xx1 < 0 ? 0 : xx1;
144 | xmax = xx2 + 1 < cv->width ? xx2 + 1 : cv->width;
145 |
146 | for (x = xmin; x < xmax; x++)
147 | caca_put_char(cv, x, y, ch);
148 |
149 | xa += y < y2 ? sl21 : sl32;
150 | xb += sl31;
151 | }
152 |
153 | return 0;
154 | }
155 |
156 |
--------------------------------------------------------------------------------
/ttydraw/ttyint.h:
--------------------------------------------------------------------------------
1 | /*
2 | * libcaca Colour ASCII-Art library
3 | * Copyright (c) 2002-2012 Sam Hocevar
4 | * All Rights Reserved
5 | *
6 | * This library is free software. It comes without any warranty, to
7 | * the extent permitted by applicable law. You can redistribute it
8 | * and/or modify it under the terms of the Do What the Fuck You Want
9 | * to Public License, Version 2, as published by Sam Hocevar. See
10 | * http://www.wtfpl.net/ for more details.
11 | */
12 |
13 | #ifndef __CACA_INTERNALS_H__
14 | #define __CACA_INTERNALS_H__
15 |
16 | #include
17 |
18 | #include "cacastub.h"
19 |
20 | typedef struct caca_timer caca_timer_t;
21 | typedef struct caca_privevent caca_privevent_t;
22 |
23 | #if !defined(_DOXYGEN_SKIP_ME)
24 | # define STAT_VALUES 32
25 | # define EVENTBUF_LEN 10
26 | # define MAX_DIRTY_COUNT 8
27 | #endif
28 |
29 | struct caca_frame
30 | {
31 | /* Frame size */
32 | int width, height;
33 |
34 | /* Painting context */
35 | int x, y;
36 | int handlex, handley;
37 | uint32_t curattr;
38 |
39 | /* Frame name */
40 | char *name;
41 | };
42 |
43 | struct caca_canvas
44 | {
45 | /* XXX: look at caca_set_canvas_boundaries() before adding anything
46 | * to this structure. The function is quite hacky. */
47 |
48 | /* Frame information */
49 | int frame, framecount;
50 | struct caca_frame *frames;
51 |
52 | /* Canvas management */
53 | int refcount;
54 | int autoinc;
55 | int (*resize_callback)(void *);
56 | void *resize_data;
57 |
58 | /* Dirty rectangles */
59 | int ndirty, dirty_disabled;
60 | struct
61 | {
62 | int xmin, ymin, xmax, ymax;
63 | }
64 | dirty[MAX_DIRTY_COUNT + 1];
65 |
66 | /* Shortcut to the active frame information */
67 | int width, height;
68 | uint32_t curattr;
69 |
70 | /* FIGfont management */
71 | caca_charfont_t *ff;
72 | };
73 |
74 | /* Graphics driver */
75 | enum caca_driver
76 | {
77 | CACA_DRIVER_NULL = 0,
78 | CACA_DRIVER_RAW = 1,
79 | #if defined(USE_COCOA)
80 | CACA_DRIVER_COCOA = 2,
81 | #endif
82 | #if defined(USE_CONIO)
83 | CACA_DRIVER_CONIO = 3,
84 | #endif
85 | #if defined(USE_GL)
86 | CACA_DRIVER_GL = 4,
87 | #endif
88 | #if defined(USE_NCURSES)
89 | CACA_DRIVER_NCURSES = 5,
90 | #endif
91 | #if defined(USE_SLANG)
92 | CACA_DRIVER_SLANG = 6,
93 | #endif
94 | #if defined(USE_VGA)
95 | CACA_DRIVER_VGA = 7,
96 | #endif
97 | #if defined(USE_WIN32)
98 | CACA_DRIVER_WIN32 = 8,
99 | #endif
100 | #if defined(USE_X11)
101 | CACA_DRIVER_X11 = 9,
102 | #endif
103 | };
104 |
105 | /* Available external drivers */
106 | #if defined(USE_COCOA)
107 | int cocoa_install(caca_display_t *);
108 | #endif
109 | #if defined(USE_CONIO)
110 | int conio_install(caca_display_t *);
111 | #endif
112 | #if defined(USE_GL)
113 | int gl_install(caca_display_t *);
114 | #endif
115 | #if defined(USE_NCURSES)
116 | int ncurses_install(caca_display_t *);
117 | #endif
118 | int null_install(caca_display_t *);
119 | int raw_install(caca_display_t *);
120 | #if defined(USE_SLANG)
121 | int slang_install(caca_display_t *);
122 | #endif
123 | #if defined(USE_VGA)
124 | int vga_install(caca_display_t *);
125 | #endif
126 | #if defined(USE_WIN32)
127 | int win32_install(caca_display_t *);
128 | #endif
129 | #if defined(USE_X11)
130 | int x11_install(caca_display_t *);
131 | #endif
132 |
133 | /* Timer structure */
134 | struct caca_timer
135 | {
136 | int last_sec, last_usec;
137 | };
138 |
139 | /* Statistic structure for profiling */
140 | struct caca_stat
141 | {
142 | int itable[STAT_VALUES];
143 | int32_t imean;
144 | char *name;
145 | };
146 |
147 | /* Private event structure */
148 | struct caca_privevent
149 | {
150 | enum caca_event_type type;
151 |
152 | union
153 | {
154 | struct { int x, y, button; } mouse;
155 | struct { int w, h; } resize;
156 | struct { int ch; uint32_t utf32; char utf8[8]; } key;
157 | } data;
158 | };
159 |
160 | /* Internal caca display context */
161 | struct caca_display
162 | {
163 | /* A link to our caca canvas */
164 | caca_canvas_t *cv;
165 | int autorelease;
166 |
167 | #if defined(USE_PLUGINS)
168 | void *plugin;
169 | #endif
170 |
171 | /* Device-specific functions */
172 | struct drv
173 | {
174 | char const * driver;
175 | enum caca_driver id;
176 | struct driver_private *p;
177 |
178 | int (* init_graphics) (caca_display_t *);
179 | int (* end_graphics) (caca_display_t *);
180 | int (* set_display_title) (caca_display_t *, char const *);
181 | int (* get_display_width) (caca_display_t const *);
182 | int (* get_display_height) (caca_display_t const *);
183 | void (* display) (caca_display_t *);
184 | void (* handle_resize) (caca_display_t *);
185 | int (* get_event) (caca_display_t *, caca_privevent_t *);
186 | void (* set_mouse) (caca_display_t *, int);
187 | void (* set_cursor) (caca_display_t *, int);
188 | } drv;
189 |
190 | /* Mouse position */
191 | struct mouse
192 | {
193 | int x, y;
194 | } mouse;
195 |
196 | /* Window resize handling */
197 | struct resize
198 | {
199 | int resized; /* A resize event was requested */
200 | int allow; /* The display driver allows resizing */
201 | int w, h; /* Requested width and height */
202 | } resize;
203 |
204 | /* Framerate handling */
205 | int delay, rendertime;
206 | caca_timer_t timer;
207 | #if defined PROF
208 | struct caca_stat display_stat, wait_stat;
209 | struct caca_stat ev_sys_stat, ev_wait_stat;
210 | #endif
211 | int lastticks;
212 |
213 | struct events
214 | {
215 | #if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL)
216 | caca_privevent_t buf[EVENTBUF_LEN];
217 | int queue;
218 | #endif
219 | #if defined(USE_SLANG) || defined(USE_NCURSES)
220 | caca_timer_t key_timer;
221 | int last_key_ticks;
222 | int autorepeat_ticks;
223 | caca_privevent_t last_key_event;
224 | #endif
225 | uint8_t not_empty_struct;
226 | } events;
227 | };
228 |
229 | /* Dirty rectangle functions */
230 | extern void _caca_clip_dirty_rect_list(caca_canvas_t *);
231 |
232 | /* Colour functions */
233 | extern uint32_t _caca_attr_to_rgb24fg(uint32_t);
234 | extern uint32_t _caca_attr_to_rgb24bg(uint32_t);
235 |
236 | /* Frames functions */
237 | extern void _caca_save_frame_info(caca_canvas_t *);
238 | extern void _caca_load_frame_info(caca_canvas_t *);
239 |
240 | /* Internal timer functions */
241 | extern void _caca_sleep(int);
242 | extern int _caca_getticks(caca_timer_t *);
243 |
244 | /* Internal event functions */
245 | extern void _caca_handle_resize(caca_display_t *);
246 | #if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL)
247 | extern void _push_event(caca_display_t *, caca_privevent_t *);
248 | extern int _pop_event(caca_display_t *, caca_privevent_t *);
249 | #endif
250 |
251 | /* Internal window functions */
252 | extern void _caca_set_term_title(char const *);
253 |
254 | /* Profiling functions */
255 | #if defined PROF
256 | extern void _caca_dump_stats(void);
257 | extern void _caca_init_stat(struct caca_stat *, char const *, ...);
258 | extern void _caca_fini_stat(struct caca_stat *);
259 | #endif
260 |
261 | #endif /* __CACA_INTERNALS_H__ */
262 |
--------------------------------------------------------------------------------
/undefine.lst:
--------------------------------------------------------------------------------
1 | _start
2 | __unix_ioctl
3 | __unix_fcntl
4 | __unix_stat
5 | __unix_fstat
6 | __unix_open
7 | __unix_uname
8 | __unix_times
9 | __unix_read
10 | __unix_sysi86
11 | __unix_access
12 | __unix_readdir
13 | __unix_signal
14 | a64l
15 | abort
16 | abs
17 | access
18 | acct
19 | alarm
20 | asctime
21 | atof
22 | atoi
23 | atol
24 | bsearch
25 | calloc
26 | chdir
27 | chmod
28 | chroot
29 | clearerr
30 | clock
31 | closedir
32 | copysign
33 | creat
34 | ctermid
35 | ctime
36 | cuserid
37 | daylight
38 | drand48
39 | dup2
40 | dup
41 | eaccess
42 | ecvt
43 | endgrent
44 | endpwent
45 | endutent
46 | environ
47 | erand48
48 | execle
49 | execl
50 | execlp
51 | execve
52 | execv
53 | execvp
54 | _exit
55 | exit
56 | fcvt
57 | free
58 | frexp
59 | fstatfs
60 | fstat
61 | ftok
62 | ftw
63 | gcvt
64 | getchar
65 | getcwd
66 | getegid
67 | getenv
68 | geteuid
69 | getgid
70 | getgrent
71 | getgrgid
72 | getgrnam
73 | getlogin
74 | getpass
75 | getpgrp
76 | getpid
77 | getppid
78 | getpwent
79 | getpwnam
80 | getpwuid
81 | getuid
82 | getutent
83 | getutid
84 | getutline
85 | getw
86 | gmtime
87 | gsignal
88 | hcreate
89 | hdestroy
90 | hsearch
91 | isatty
92 | jrand48
93 | kill
94 | l64a
95 | labs
96 | lcong48
97 | ldexp
98 | lfind
99 | link
100 | localtime
101 | lockf
102 | lrand48
103 | lsearch
104 | lseek
105 | mallinfo
106 | malloc
107 | mallopt
108 | _mcount
109 | memccpy
110 | memchr
111 | memcmp
112 | memcpy
113 | memmove
114 | memset
115 | mkdir
116 | mknod
117 | mlock
118 | modf
119 | mount
120 | mrand48
121 | msgget
122 | msgrcv
123 | msgsnd
124 | nice
125 | nrand48
126 | opendir
127 | open
128 | pause
129 | perror
130 | poll
131 | printf
132 | profil
133 | ptrace
134 | putchar
135 | putenv
136 | putpwent
137 | puts
138 | pututline
139 | putw
140 | qsort
141 | rand
142 | readdir
143 | read
144 | realloc
145 | rename
146 | rewind
147 | rmdir
148 | sbrk
149 | scanf
150 | seed48
151 | seekdir
152 | semget
153 | semop
154 | setbuf
155 | setgid
156 | setgrent
157 | setpgrp
158 | setpwent
159 | setuid
160 | setutent
161 | setvbuf
162 | shmat
163 | shmdt
164 | shmget
165 | sighold
166 | sigignore
167 | signal
168 | sigpause
169 | sigrelse
170 | sigset
171 | sleep
172 | sprintf
173 | srand48
174 | srand
175 | sscanf
176 | ssignal
177 | statfs
178 | stat
179 | strcat
180 | strchr
181 | strcmp
182 | strcpy
183 | strcspn
184 | strdup
185 | strlen
186 | strncat
187 | strncmp
188 | strncpy
189 | strpbrk
190 | strrchr
191 | strspn
192 | strtod
193 | strtok
194 | strtol
195 | swab
196 | sync
197 | syscall
198 | system
199 | tdelete
200 | telldir
201 | tfind
202 | time
203 | times
204 | timezone
205 | tolower
206 | toupper
207 | tsearch
208 | ttyname
209 | ttyslot
210 | twalk
211 | tzname
212 | tzset
213 | ulimit
214 | umask
215 | umount
216 | uname
217 | ungetc
218 | unlink
219 | utime
220 | utmpname
221 | vfprintf
222 | vprintf
223 | vsprintf
224 | wait
225 | write
226 | close
227 | tgetent
228 | tgetstr
229 | tgetflag
230 | tgetnum
231 | tgoto
232 | tputs
233 | tmpfile
234 | chown
235 | msgctl
236 | semctl
237 | shmctl
238 | fstat64
239 | stat64
240 | lic_init
241 | open_printer_drivers
242 | load_printer_drivers
243 | close_printer_drivers
244 | read_print_config_dir
245 | display_column_labels
246 | init_unix_display_code
247 | at_date
248 | sin
249 | cos
250 | tan
251 | tan2
252 | asin
253 | acos
254 | remainder
255 | sqrt
256 | log
257 | log10
258 | floor
259 | ceil
260 | fmod
261 | fabs
262 | kbd_term
263 | set_raw_mode
264 | ustat
265 | setup_screen_mem
266 | file_name_split
267 | ready_to_read
268 | init_showme
269 | clear_showme
270 | get_showme_row
271 | get_showme
272 | set_showme
273 | invalidate_region
274 | adjust_showme_rows
275 | adjust_showme_cols
276 | scan_showme_rows
277 | reset_showme
278 | tty_find_changes
279 | fm_file_import
280 | scan_showme_leftwards
281 | scan_showme
282 | move_block
283 | init_invalidation
284 | move_rectangles
285 | move_rows_or_cols
286 | invalidate_rows
287 | reset_screen_dimensions
288 | square_copy
289 | display_region
290 | display_cells
291 | setup_invalid_columns
292 | invalidate_cell_cols
293 | find_invalid_column
294 | tty_disp_info
295 | fmt_cpy
296 | mr_step_wait
297 |
--------------------------------------------------------------------------------
/unixterm.h:
--------------------------------------------------------------------------------
1 | #ifndef __UNIXTERM_H
2 | #define __UNIXTERM_H
3 |
4 | #include
5 |
6 | #pragma pack(push, 1)
7 | struct unixtermios {
8 | uint16_t c_iflag;
9 | uint16_t c_oflag;
10 | uint16_t c_cflag;
11 | uint16_t c_lflag;
12 | char c_line;
13 | uint8_t c_cc[8];
14 | };
15 | #pragma pack(pop)
16 |
17 | #define UNIX_VMIN 4
18 | #define UNIX_VTIME 5
19 | #define UNIX_VSWITCH 7
20 | #define UNIX_VMAGIC UNIX_VSWITCH
21 | #define VMAGIC VSWITCH
22 |
23 | // I've only figured out the parameters used by cfmakeraw()
24 | #define UNIX_IGNBRK 0x0001
25 | #define UNIX_BRKINT 0x0002
26 | #define UNIX_PARMRK 0x0008
27 | #define UNIX_ISTRIP 0x0020
28 | #define UNIX_INLCR 0x0040
29 | #define UNIX_IGNCR 0x0080
30 | #define UNIX_ICRNL 0x0100
31 | #define UNIX_IXON 0x0400
32 |
33 | #define UNIX_OPOST 0x0001
34 |
35 | #define UNIX_ECHO 0x0008
36 | #define UNIX_ECHONL 0x0040
37 | #define UNIX_ICANON 0x0002
38 | #define UNIX_ISIG 0x0001
39 | #define UNIX_IEXTEN 0x8000
40 |
41 | #define UNIX_CSIZE 0x0030
42 | #define UNIX_PARENB 0x0100
43 |
44 | #define UNIX_CS8 0x0030
45 |
46 | // These are used by set_raw_mode()
47 | #define UNIX_TABDLY 0x1800
48 |
49 | #endif
50 |
--------------------------------------------------------------------------------
/unixtypes.h:
--------------------------------------------------------------------------------
1 | #ifndef __UNIXTYPES_H
2 | #define __UNIXTYPES_H
3 |
4 | #pragma pack(push, 1)
5 |
6 | struct unixflock {
7 | uint16_t l_type;
8 | uint16_t l_whence;
9 | uint32_t l_start;
10 | uint32_t l_len;
11 | uint16_t l_pid;
12 | };
13 |
14 | struct unixstat {
15 | uint16_t st_dev;
16 | uint16_t st_ino;
17 | uint16_t st_mode;
18 | uint16_t st_nlink;
19 | uint16_t st_uid;
20 | uint16_t st_gid;
21 | uint16_t st_rdev;
22 | uint16_t pad;
23 | uint32_t st_size;
24 | uint32_t st_uatime;
25 | uint32_t st_umtime;
26 | uint32_t st_uctime;
27 | };
28 |
29 | #define UNIX_S_IFBLK 0x6000
30 | #define UNIX_S_IFREG 0x8000
31 | #define UNIX_S_IFLNK 0xA000
32 | #define UNIX_S_IFDIR 0x4000
33 | #define UNIX_S_IFCHR 0x2000
34 | #define UNIX_S_IFIFO 0x1000
35 |
36 | struct unixdirent {
37 | uint16_t d_ino;
38 | uint32_t d_off;
39 | uint16_t d_reclen;
40 | uint16_t d_type;
41 | char d_name[256];
42 | };
43 |
44 | struct unixutsname {
45 | char sysname[9];
46 | char nodename[9];
47 | char release[9];
48 | char version[9];
49 | char machine[9];
50 | };
51 |
52 | #pragma pack(pop)
53 | #endif
54 |
--------------------------------------------------------------------------------
/wrappers.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #include "unixterm.h"
18 | #include "unixtypes.h"
19 | #include "filemap.h"
20 |
21 | // The Lotus view of errno.
22 | extern int __unix_errno;
23 |
24 | #define SI86FPHW 40
25 | #define FP_387 3
26 |
27 | static struct termios original;
28 |
29 | void __attribute__((constructor)) init_terminal_settings()
30 | {
31 | // Make a backup of the terminal state to restore to later.
32 | if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &original) != 0) {
33 | err(EXIT_FAILURE, "Failed to query terminal attributes.");
34 | }
35 | }
36 |
37 | void __attribute__((destructor)) fini_terminal_settings()
38 | {
39 | // Restore any terminal craziness that 123 left behind.
40 | if (isatty(STDIN_FILENO) && tcsetattr(STDIN_FILENO, TCSANOW, &original) != 0) {
41 | warn("Failed to restore terminal attributes, sorry!");
42 | }
43 | }
44 |
45 | int __unix_ioctl(int fd, unsigned long request, struct unixtermios *argp)
46 | {
47 | int action;
48 | struct termios tio;
49 |
50 | if (argp == NULL) {
51 | return -1;
52 | }
53 |
54 | // Assume changes should be immediate by default.
55 | action = TCSANOW;
56 |
57 | // Translating termios is really difficult, but 1-2-3 only wants to use a
58 | // few features. It wasnts to enable and disable "raw" mode, and change
59 | // VTIME and VMIN.
60 | switch (request) {
61 | case TCSETSW:
62 | // Choose the right action.
63 | action = TCSADRAIN;
64 |
65 | // fallthrough
66 | case TCSETS:
67 | // Fetch current attributes.
68 | if (tcgetattr(fd, &tio) != 0) {
69 | err(EXIT_FAILURE, "Failed to translate ioctl() to tcgetattr()");
70 | }
71 |
72 | // Translate timeouts.
73 | tio.c_cc[VTIME] = argp->c_cc[UNIX_VTIME];
74 | tio.c_cc[VMIN] = argp->c_cc[UNIX_VMIN];
75 |
76 | // Install new attributes.
77 | if (tcsetattr(fd, action, &tio) != 0) {
78 | err(EXIT_FAILURE, "Failed to translate ioctl() to tcsetattr()");
79 | }
80 |
81 | return 0;
82 | case TCGETS:
83 | // Fetch real attributes.
84 | if (tcgetattr(fd, &tio) != 0) {
85 | err(EXIT_FAILURE, "Failed to translate ioctl() to tcgetattr()");
86 | }
87 |
88 | // Initialize output in case 123 didn't.
89 | memset(argp, 0, sizeof *argp);
90 |
91 | // Translate VTIME and VMIN.
92 | argp->c_cc[UNIX_VTIME] = tio.c_cc[VTIME];
93 | argp->c_cc[UNIX_VMIN] = tio.c_cc[VMIN];
94 |
95 | return 0;
96 | case 0x7602: // Unknown?
97 | case 0x7603: // Unknown?
98 | case 0x4B01: // Unknown?
99 | case 0x4544: // Unknown, 'DE'?
100 | break;
101 | default:
102 | warnx("ioctl: unknown request %#lx", request);
103 | }
104 | __unix_errno = errno;
105 | return -1;
106 | }
107 |
108 | int __unix_fcntl(int fd, int cmd, void *arg)
109 | {
110 | static int unix_cmd_table[32] = {
111 | [0] = F_DUPFD,
112 | [1] = F_GETFD,
113 | [2] = F_SETFD,
114 | [3] = F_GETFL,
115 | [4] = F_SETFL,
116 | [5] = F_GETLK,
117 | [6] = F_SETLK,
118 | [7] = F_SETLKW,
119 | };
120 | static int unix_lck_table[] = {
121 | [1] = F_RDLCK,
122 | [2] = F_WRLCK,
123 | [3] = F_UNLCK,
124 | };
125 | static int linux_lck_table[] = {
126 | [F_RDLCK] = 1,
127 | [F_WRLCK] = 2,
128 | [F_UNLCK] = 3,
129 | };
130 |
131 | // Translate command from UNIX to Linux.
132 | cmd = unix_cmd_table[cmd];
133 |
134 | switch (cmd) {
135 | case F_GETFL: {
136 | int linuxflags = fcntl(fd, cmd);
137 | int unixflags = 0;
138 |
139 | // Pass through errno.
140 | __unix_errno = errno;
141 |
142 | if (linuxflags == -1)
143 | return -1;
144 |
145 | // I think these are all the flags 123 uses.
146 | if (linuxflags & O_WRONLY)
147 | unixflags |= 1;
148 | if (linuxflags & O_RDWR)
149 | unixflags |= 2;
150 | if (linuxflags & O_NONBLOCK)
151 | unixflags |= 4;
152 | if (linuxflags & O_APPEND)
153 | unixflags |= 8;
154 | if (linuxflags & O_CREAT)
155 | unixflags |= 0x100;
156 |
157 | return unixflags;
158 | }
159 | case F_SETFL: {
160 | int unixflags = cmd;
161 | int linuxflags = 0;
162 |
163 | // I think these are the only flags you can change.
164 | if (unixflags & 4)
165 | linuxflags |= O_NONBLOCK;
166 | if (unixflags & 8)
167 | linuxflags |= O_APPEND;
168 |
169 | if (fcntl(fd, cmd, &linuxflags) == 0) {
170 | return 0;
171 | }
172 |
173 | __unix_errno = errno;
174 |
175 | return -1;
176 | }
177 | case F_SETLK: {
178 | struct unixflock *ufl = arg;
179 | struct flock lfl = {0};
180 |
181 | // Translate the lock structure over.
182 | lfl.l_type = unix_lck_table[ufl->l_type];
183 | lfl.l_start = ufl->l_start;
184 | lfl.l_len = ufl->l_len;
185 | lfl.l_whence = ufl->l_whence;
186 |
187 | if (fcntl(fd, cmd, &lfl) == 0) {
188 | return 0;
189 | }
190 | __unix_errno = errno;
191 | return -1;
192 | }
193 | case F_GETLK: {
194 | struct unixflock *ufl = arg;
195 | struct flock lfl = {0};
196 |
197 | // Check if there is a lock.
198 | if (fcntl(fd, cmd, &lfl) == -1) {
199 | __unix_errno = errno;
200 | return -1;
201 | }
202 |
203 | // Translate the lock over.
204 | ufl->l_start = lfl.l_start;
205 | ufl->l_len = lfl.l_len;
206 | ufl->l_whence = lfl.l_whence;
207 | ufl->l_type = linux_lck_table[lfl.l_type];
208 | ufl->l_pid = lfl.l_pid;
209 | return 0;
210 | }
211 | case F_DUPFD: {
212 | if (fcntl(fd, cmd, (int) arg) == -1) {
213 | __unix_errno = errno;
214 | return -1;
215 | }
216 | return 0;
217 | }
218 | default:
219 | err(EXIT_FAILURE, "fcntl: unknown cmd %u requested.", cmd);
220 | }
221 | return -1;
222 | }
223 |
224 | static int translate_linux_stat(const struct stat *linuxstat, struct unixstat *unixstat)
225 | {
226 | memset(unixstat, 0, sizeof *unixstat);
227 | unixstat->st_dev = linuxstat->st_dev;
228 | unixstat->st_ino = linuxstat->st_ino;
229 | unixstat->st_nlink = linuxstat->st_nlink;
230 | unixstat->st_mode = linuxstat->st_mode & 0x1FF;
231 | unixstat->st_uid = linuxstat->st_uid;
232 | unixstat->st_gid = linuxstat->st_gid;
233 | unixstat->st_rdev = linuxstat->st_rdev;
234 | unixstat->st_size = linuxstat->st_size;
235 | unixstat->st_uatime = linuxstat->st_atim.tv_sec;
236 | unixstat->st_umtime = linuxstat->st_mtim.tv_sec;
237 | unixstat->st_uctime = linuxstat->st_ctim.tv_sec;
238 |
239 | // The MSB of st_dev is queried by file_is_local()
240 | unixstat->st_dev &= ~0x8000;
241 |
242 | switch (linuxstat->st_mode & S_IFMT) {
243 | case S_IFREG: unixstat->st_mode |= UNIX_S_IFREG; break;
244 | case S_IFDIR: unixstat->st_mode |= UNIX_S_IFDIR; break;
245 | case S_IFLNK: unixstat->st_mode |= UNIX_S_IFLNK; break;
246 | case S_IFBLK: unixstat->st_mode |= UNIX_S_IFBLK; break;
247 | case S_IFCHR: unixstat->st_mode |= UNIX_S_IFCHR; break;
248 | case S_IFIFO: unixstat->st_mode |= UNIX_S_IFIFO; break;
249 | default:
250 | warnx("Failed to translate filetype %#x.", linuxstat->st_mode);
251 | }
252 |
253 | return 0;
254 | }
255 |
256 | int __unix_stat(const char *pathname, struct unixstat *statbuf)
257 | {
258 | struct stat buf;
259 |
260 | // This routine can change filenames to make them more suitable for Linux.
261 | pathname = map_unix_pathname(pathname);
262 |
263 | if (stat(pathname, &buf) != 0) {
264 | __unix_errno = errno;
265 | return -1;
266 | }
267 |
268 | // Reset errno
269 | __unix_errno = 0;
270 |
271 | return translate_linux_stat(&buf, statbuf);
272 | }
273 |
274 | int __unix_fstat(int fd, struct unixstat *statbuf)
275 | {
276 | struct stat buf;
277 |
278 | if (fstat(fd, &buf) != 0) {
279 | __unix_errno = errno;
280 | return -1;
281 | }
282 |
283 | return translate_linux_stat(&buf, statbuf);
284 | }
285 |
286 | int __unix_open(const char *pathname, int flags, mode_t mode)
287 | {
288 | // This routine can change filenames to make them more suitable for Linux.
289 | pathname = map_unix_pathname(pathname);
290 |
291 | switch (flags) {
292 | case 0x000: return open(pathname, O_RDONLY);
293 | case 0x001: return open(pathname, O_WRONLY);
294 | case 0x002: return open(pathname, O_RDWR);
295 | case 0x102: return open(pathname, O_CREAT | O_RDWR, mode);
296 | case 0x101: return open(pathname, O_CREAT | O_WRONLY, mode);
297 | case 0x109: return open(pathname, O_CREAT | O_WRONLY | O_APPEND, mode);
298 | case 0x302: return open(pathname, O_CREAT | O_TRUNC | O_RDWR, mode);
299 | default:
300 | errx(EXIT_FAILURE, "open() was called with unrecognized flags %#x", flags);
301 | }
302 | return -1;
303 | }
304 |
305 | int __unix_uname(struct unixutsname *unixname)
306 | {
307 | struct utsname name;
308 |
309 | if (uname(&name) != 0) {
310 | return -1;
311 | }
312 |
313 | memset(unixname, 0, sizeof(*unixname));
314 |
315 | // This is used in @INFO("osversion")
316 | strncpy(unixname->release, "Linux", 8);
317 | strncpy(unixname->version, name.release, 8);
318 |
319 | return 0;
320 | }
321 |
322 | int __unix_times(void *buffer)
323 | {
324 | struct tms buf;
325 | // Note: 123 only cares about the return code.
326 | return times(&buf);
327 | }
328 |
329 | int __unix_read(int fd, void *buf, size_t count)
330 | {
331 | int result;
332 |
333 | result = read(fd, buf, count);
334 |
335 | if (result == -1) {
336 | // Lotus requests non-blocking io, but then doesn't
337 | // handle EAGAIN.
338 | if (errno == EAGAIN) {
339 | return 0;
340 | }
341 | __unix_errno = errno;
342 | }
343 |
344 | return result;
345 | }
346 |
347 | int __unix_sysi86(int cmd, uint32_t *result)
348 | {
349 | // This is used to check for x87 support, nothing else is supported.
350 | if (cmd != SI86FPHW)
351 | return -1;
352 |
353 | *result = FP_387;
354 | return 0;
355 | }
356 |
357 | int __unix_access(const char *pathname, int mode)
358 | {
359 | // The mode definitions is compatible with Linux, but we might want to
360 | // adjust pathnames.
361 | if (access(map_unix_pathname(pathname), mode) != 0) {
362 | __unix_errno = errno;
363 | return -1;
364 | }
365 |
366 | return 0;
367 | }
368 |
369 | struct unixdirent * __unix_readdir(DIR *dirp)
370 | {
371 |
372 | struct dirent *lent;
373 | static struct unixdirent uent;
374 |
375 | // Reset errno so that end of stream and error can be distinguished.
376 | errno = 0;
377 |
378 | // We reuse the same static dirent, so reset it here.
379 | memset(&uent, 0, sizeof uent);
380 |
381 | // Fetch the real entry, and translate it to the UNIX format.
382 | if ((lent = readdir(dirp))) {
383 | uent.d_ino = lent->d_ino;
384 | uent.d_off = lent->d_off;
385 | uent.d_reclen = lent->d_reclen;
386 | uent.d_type = lent->d_type;
387 | strncpy(uent.d_name, lent->d_name, sizeof uent.d_name);
388 | return &uent;
389 | }
390 |
391 | // Error, or end of stream, pass through errno.
392 | __unix_errno = errno;
393 | return NULL;
394 | }
395 |
396 | sighandler_t __unix_signal(int signum, sighandler_t handler)
397 | {
398 | static int unix_sig_table[] = {
399 | [7] = -1, // SIGEMT
400 | [10] = SIGBUS,
401 | [12] = SIGSYS,
402 | [16] = SIGUSR1,
403 | [17] = -1, // SIGUSR2, Reserved
404 | [18] = SIGCHLD,
405 | [19] = SIGPWR,
406 | [20] = SIGWINCH,
407 | [21] = SIGURG,
408 | [22] = SIGPOLL,
409 | [23] = SIGSTOP,
410 | [24] = SIGTSTP,
411 | [25] = SIGCONT,
412 | [26] = SIGTTIN,
413 | [27] = SIGTTOU,
414 | [28] = SIGVTALRM,
415 | [29] = SIGPROF,
416 | [30] = SIGXCPU,
417 | [31] = SIGXFSZ,
418 | [32] = -1, // SIGWAITING
419 | [33] = -1, // SIGLWP
420 | [34] = -1, // SIGAIO
421 | };
422 |
423 | // Translate the signal number, 0 means no translation necessary.
424 | if (unix_sig_table[signum] != 0) {
425 | signum = unix_sig_table[signum];
426 | }
427 |
428 | return signal(signum, handler);
429 | }
430 |
--------------------------------------------------------------------------------