├── .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 | ![Lotus 1-2-3 for Linux](https://lock.cmpxchg8b.com/img/123linux.png) 7 | 8 | ## Screenshots 9 | 10 | Here are some screenshots of Lotus 1-2-3 on Linux. 11 | 12 | | ![liveupdate_small](https://user-images.githubusercontent.com/123814/177441429-ee4ec586-46f5-45bf-96a0-0715215d2a0d.png) | ![help_small](https://user-images.githubusercontent.com/123814/177441458-435be771-2934-4199-9115-5b81311d89d2.png) | ![stock_small](https://user-images.githubusercontent.com/123814/177441483-f52fc2d3-9c3b-4c74-b805-99922c7c8b16.png) | ![perspective](https://user-images.githubusercontent.com/123814/173251674-cb29357d-8686-4dde-83bc-17c441957512.png) 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 | --------------------------------------------------------------------------------