Repository: taviso/lotusdrv Branch: master Commit: fb6ebcafc44e Files: 58 Total size: 298.7 KB Directory structure: gitextract_179s49fo/ ├── .gitignore ├── Makefile ├── README.md ├── addin/ │ ├── Makefile │ ├── lpl.cnf │ └── modern.pl ├── attr.c ├── attr.h ├── bundle.c ├── bundle.h ├── cgadraw/ │ ├── COPYING │ ├── Makefile │ ├── README.md │ ├── attr.c │ ├── box.c │ ├── caca.h │ ├── cacaint.h │ ├── cacastub.h │ ├── cacatyp.h │ ├── canvas.c │ ├── cgadraw.c │ ├── charset.c │ ├── codec.h │ ├── config.h │ ├── conic.c │ ├── drawtest.c │ ├── frame.c │ ├── line.c │ ├── snprintf.c │ ├── snprintf.h │ ├── string.c │ ├── transfrm.c │ └── triangle.c ├── config.h ├── debug/ │ ├── CGAF25CC.VBD │ ├── CGAF25CC.cmarkers │ ├── CGAF25CC.names │ ├── Makefile │ ├── exports.inc │ ├── l13vcgad.asm │ ├── logcalls.c │ └── logcbs.c ├── debug.c ├── debug.h ├── draw.c ├── draw.h ├── idatypes.h ├── l13vdemu.asm ├── l13vdemu.def ├── lmbcs.c ├── lmbcs.h ├── lotcalls.h ├── lotdemu.c ├── lottypes.h ├── raster.c ├── raster.h ├── snprintf.c └── snprintf.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.obj .*.sw? *.pdb *.tlb *.res *.exe *.manifest *.exp *.ilk *.lib *.udb *.msi *.wixobj *.wixpdb *.dll *.o *.core *.map *.dld *.zip *.id0 *.id1 *.idb *.nam *.til compile_commands.json build-*/ a.out autom4te.cache/ config.log .clangd/ ================================================ FILE: Makefile ================================================ DFLAGS=-quiet -dumb ASFLAGS=/m3 /q /z /w0 #CPPFLAGS=/Icgadraw /I. /DNDEBUG CPPFLAGS=/Icgadraw /I. WARNLEVEL=4 CFLAGS=/AL /nologo /NDAAA /Od /G3 /Gs /Gc /Zi /FPi87 /Zp1 /Zl /Gf /f- /W$(WARNLEVEL) LDFLAGS=/map:full /b /nologo /onerror:noexe /nod /noe LDLIBS=LLIBC7 EFLAGS=/verbose /nologo LFLAGS=-q .PHONY: clean distclean cgadraw.lib l13vcgaf.obj l13vcgad.obj logcalls.obj logcbs.obj %.plc: %.pl @rm -f $@ dosemu $(DFLAGS) -E "IN G:/ LPL $(LFLAGS) $<" %.plh: %.plc @rm -f $@ dosemu $(DFLAGS) -E "IN G:/ DESCRIBE $<" # MS-DOS doesn't allow long commandlines, so params are written to a "response" # file. %.obj: %.c @rm -f $@ printf "$(CPPFLAGS) $(CFLAGS)\r\n" > $(@:.obj=.cl) dosemu $(DFLAGS) -E "IN G:/ CL /c @$(@:.obj=.cl) /Fo$@ $<" @rm -f $(@:.obj=.cl) %.obj: %.asm @rm -f $@ dosemu $(DFLAGS) -E "IN G:/ TASM32 /c $(ASFLAGS) $<" %.exe: %.obj @rm -f $@ printf "$^,$@,$(@:.exe=),$(LDLIBS),nul\r\n" > $(@:.exe=.lnk) dosemu $(DFLAGS) -E "IN G:/ LINK $(LDFLAGS) @$(@:.exe=.lnk)" @rm -f $(@:.exe=.lnk) %.dld: %.exe mv $< $@ # dosemu $(DFLAGS) -E "IN G:/ EXEHDR $(EFLAGS) $@" all: l13vdemu.dld cgadraw.lib: make -C cgadraw $@ @cp cgadraw/$@ $@ l13vcgaf.obj l13vcgad.obj logcalls.obj logcbs.obj: make -C debug $@ @cp debug/$@ $@ # Third party code, reduce warning level snprintf.obj: WARNLEVEL=1 l13vdemu.exe: LDLIBS+=CGADRAW l13vdemu.exe: l13vdemu.obj lotdemu.obj snprintf.obj debug.obj bundle.obj \ lmbcs.obj attr.obj draw.obj raster.obj | cgadraw.lib # Original driver. l13vcgaf.exe: l13vcgaf.obj # Original driver patched to add debugging calls. l13vcgad.exe: l13vcgad.obj logcalls.obj logcbs.obj snprintf.obj debug.obj \ l13vcgaf.obj release: CPPFLAGS+=/DRELEASE release: clean l13vdemu.dld zip l13vdemu.zip l13vdemu.dld README.md install: l13vdemu.dld cp $< ~/.dosemu/drive_c/123R4D/L13VCGAF.DLD clean: rm -f *.exe *.obj *.dld *.map *.pdb *.lnk *.cl *.lib *.plc l13vdemu.zip distclean: clean make -C cgadraw clean make -C debug clean make -C addin clean ================================================ FILE: README.md ================================================ # Lotus 1-2-3 R4D Display Driver for DOSEMU2 This is a work-in-progress display driver for Lotus 1-2-3 R4D to enable support for arbitrary text resolutions in [DOSEMU2](https://github.com/dosemu2/dosemu2). That means you can run 123 in a maximized xterm, and see more columns than you could possibly need! ![screenshot](screenshot.png) > Note: This driver is intended for DOSEMU2 in *term* mode, i.e. running in a terminal. This driver basically works, but I haven't fully implemented all the API, so if you do something unusual it might trap. If you are excited to help hacking on this, or writing modern add-ins for 1-2-3, I'd love to hear about it! Future ideas: - A FILE driver that adds native support for XLS, or ODT? - ~~libcaca support so that text mode graphs work?~~ working on it! - More modern @functions? - Javascript/Python/lua bindings? # Graphs I have an incomplete ascii-art graphics implementation. That means drawing simple graphs will work even in a terminal! Don't expect high resolution graphics, but simple line, bar, and even pie charts really do work! Here is a screenshot of a sine wave line graph, it will improve in future. ![sinewave](lotus-sine-wave.gif) I have some development notes available [here](https://lock.cmpxchg8b.com/lotus123.html). # Building > If you just want a binary to try, check out the Releases section. Note that this code isn't really ready for non-developer use yet, but you're welcome to try it out and tell me about any visual glitches or crashes! # 123 FAQ **Q. Which display driver should I be using for terminal mode?** A. Select the CGA driver during install, then install this driver :-) **Q. If I use the /Worksheet/Status command, 123 does not see all the EMS/XMS memory I have configured in DOSEMU?** A. Try adding `SET 123MEMSIZE=134217728` (that's 128M, use an appropriate number for your configuration) to `fdppauto.bat`. There is also `123SWAPPATH`, `123VIRTSIZE` and `123SWAPSIZE` if you want to tweak it. **Q. If I try to use 123 in two xterms simultaneously, I get an error like "The stand-alone license is not currently available".** A. Add something like this to your `fdppauto.bat` ```bat REM CLEAN UP LICENSE FOR 1-2-3 DEL C:\123R4D\LICENSE.000 > NUL COPY NUL C:\123R4D\LICENSE.000 > NUL ``` **Q. What DOSEMU settings do you use for 123?** A. Here is my [dosemurc](https://lock.cmpxchg8b.com/files/dosemurc) **Q. How can I send 123 worksheets to someone else?** A. LibreOffice can open and convert WK3 files, if necessary. Most formulas and features will be preserved. **Q. Can I fetch external data into 123, like stock prices with a macro?** A. I use a macro like this to fetch stock prices (simplified): ``` {SYSTEM "UNIX stocks.sh GOOGL > %TEMP%\STOCKS.TXT"} {OPEN "C:\TMP\STOCKS.TXT", "r"} {READLN C1} {CLOSE} ``` The `UNIX` command is a `DOSEMU` feature, it runs a command on the host. The 123 online help explains how to loop over ranges, etc. I have an example sheet that demonstrates how to do this available [here](https://lock.cmpxchg8b.com/files/findemo.wk3), you can use it as a template. It automatically populates a table of stock prices when you press `Alt-R`. Use `Ctrl-PgUp` and `Ctrl-PgDn` to switch sheets and browse around. You will need a script in your `$PATH` called `stocks.sh` like this, and don't forget to add the full path to your `$_unix_exec` in `.dosemurc`. ```bash #!/bin/bash declare api="https://query1.finance.yahoo.com/v7/finance/quote" declare filter=".quoteResponse.result[].regularMarketPrice" curl -s "${api}?symbols=${1:-^GSPC}" | jq -r "${filter}" | cat ``` **Q. What do I need to know to get started?** A. If you don't have a manual, there's one available online [here](https://archive.org/details/lotus-1-2-3-release-3.1-reference/Lotus%201-2-3%20Release%203.1%20-%20Tutorial). 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. | Key | Description | | --- | ------------| | / | Open the 123 menu. | F1 | Open online help. | F2 | Edit an existing cell contents, just type to overwrite cell. | F3 | Show names, press F3 while editing to see a list of functions or ranges. | F4 | Enter point mode - it's like visual mode in Vim - to select ranges. | F5 | Goto Address. | Ctrl PgUp/PgDn | Move between open tabs/sheets (use /Worksheet/Insert/Sheet to add a tab). | F9 | Recalculate, if you press it while entering a formula, the text will be replaced with it's value. If you want to be able to save your documents to your home directory, you can add something like `LREDIR D: \\linux\fs\home\foo\Documents` to your `fdppauto.bat`. **Q. Why does selecting text with the mouse not work?** DOSEMU emulates a mouse (even in terminal mode!) so when you try to select text DOSEMU is reporting mouse events to DOS. How to stop that happening depends on your terminal. In `XTerm` you can Shift-RightClick and disable "Allow Mouse Ops", change the `allowMouseOps` resource to make it permanent. In most terminals you can hold down `Shift` while selecting. **Q. How do I undo my last action?** The default keybinding is `Alt-F4`, but that can be hard to enter on modern systems. If you can't press `Alt-F4` easily, there are two options. 1. DOSEMU has support for simulating modifier keys, you can type: Ctrl-6 Shift-A F4 Ctrl-6 Shift-A. 2. That's a lot of keys though, another workaround is to add [SCANCODE](http://bretjohnson.us/index.htm) to your `%PATH%`, and then bind a macro to something like `{SYSTEM "SCANCODE M 2,W 1,0 \"READY\",ALT-F4"}`. You can then bind a macro to `Alt-Z` by naming a range `\z`. ================================================ FILE: addin/Makefile ================================================ addins: modern.plc include ../Makefile ================================================ FILE: addin/lpl.cnf ================================================ -U C:\123DADK\INCLUDE -U C:\123R4D\ ================================================ FILE: addin/modern.pl ================================================ -- vim: set ft=rexx: -- -- This is an attempt to implement some modern @FUNCTIONS in 1-2-3 DOS. -- -- Tavis Ormandy , April 2022 -- module modern is use Register use Range use Cell use AtStr use Utility Register("Match", At_Function) -- MATCH(lookup_value, lookup_array, [match_type]) procedure Match (integer value, Range table, integer mtype) returns (integer result) signals (Err, NA) purpose ("Searches for a specified item in a range of cells") is integer count = 0 result = 0 -- This only works on one-dimensional tables. if table.Columns <> 1 & table.Rows <> 1 then signal Err end if if table.Sheets <> 1 then signal Err end if begin for integer i = 1 to table.Sheets loop for integer j = 1 to table.Columns loop for integer k = 1 to table.Rows loop count = count + 1 case mtype of 1: -- MATCH finds the largest value that is less than or equal to lookup_value if table[i, j, k] <= value then result = count end if of 0: -- MATCH finds the first value that is exactly equal to lookup_value if table[i, j, k] = value then result = count return end if of -1: -- MATCH finds the smallest value that is greater than or equal to lookup_value if table[i, j, k] >= value then result = count end if end case end loop end loop end loop -- If MATCH is unsuccessful in finding a match, it returns the #N/A error value. if result = 0 then signal NA end if end except on others (string sig) do Print("Signal ", sig, " raised.") end except end procedure Match ================================================ FILE: attr.c ================================================ #include #include #include #include #include #include #include #include #include "config.h" #include "debug.h" #include "idatypes.h" #include "lottypes.h" #include "lotcalls.h" #include "bundle.h" #include "lmbcs.h" #include "attr.h" // These map 1-2-3's character attribute flags onto CGA character attributes. // i.e. // // NOTE: I tend to write 123 attributes in octal, and CGA attributes in hex. // // BG FG // 123 CGA Decoded *RGB*RGB BG FG (* is bright or blinking) // 000 0x07 0b00000111 -> 0b00000111 WHITE / BLACK // 001 0x30 0b00110000 -> 0b00110000 CYAN / WHITE // 002 0x17 0b00010111 -> 0b00010111 BLUE / BLACK // 003 0xC0 0b11000000 -> 0b11000000 *RED / WHITE // 030 0x06 0b00000110 -> 0b00000110 BLACK / YELLOW // ...and so on. // // 123 has 6 bits of attributes, 3 FG and 3 BG bits // // 111 111 // // CGA has two additional bits, for bright/blinking. // const unsigned char lot_char_attr_table[0100] = { 0x07, 0x30, 0x17, 0xC0, 0x70, 0x30, 0x10, 0x40, 0x01, 0x32, 0x13, 0xC1, 0x71, 0x31, 0x11, 0x41, 0x02, 0x31, 0x12, 0xC2, 0x72, 0x32, 0x12, 0x42, 0x06, 0x36, 0x16, 0xC6, 0x73, 0x33, 0x13, 0x43, 0x04, 0x34, 0x14, 0xC7, 0x74, 0x34, 0x14, 0x44, 0x09, 0x3A, 0x1B, 0xC9, 0x75, 0x35, 0x15, 0x45, 0x0C, 0x3C, 0x1C, 0xCF, 0x76, 0x36, 0x16, 0x46, 0x0E, 0x3E, 0x1E, 0xCE, 0x77, 0x37, 0x17, 0x47, }; ================================================ FILE: attr.h ================================================ #ifndef __ATTR_H #define __ATTR_H // The octal usage here is intentional, doesn't it make the numbers look nice? extern const unsigned char lot_char_attr_table[0100]; // Masks to change only bg/fg. #define ATTR_FG 070 #define ATTR_BG 007 #define ATTR_ALL 077 #define ATTR_NONE 000 // Sometimes lotus will pass a BG attribute only. #define MKFG(c) (unsigned char)((((c) & 007) << 3)) // Just for symmetry, not sure it's needed. #define MKBG(c) (unsigned char)((((c) & 007))) // If you need to specify them both. #define MKFGBG(f,b) (unsigned char)(((((b) & 007) << 3)) | ((((f) & 007)))) // Translate a 123 attribute into a CGA attribute. #define LATTR(c) (lot_char_attr_table[(c) & ATTR_ALL]) #endif ================================================ FILE: bundle.c ================================================ #include #include #include #include #include #include #include #include "config.h" #include "debug.h" #include "lottypes.h" #include "idatypes.h" #include "lotcalls.h" #include "bundle.h" int ParseConfig(struct BDLHDR far *bdlptr) { trace("ParseVbdConfig"); //callbacks->MapMemVmr(bdlptr, 0); //callbacks->LoadVmr(0); traceint(bdlptr->bdllen); traceint(bdlptr->bdlver); traceint(bdlptr->driver_id); traceint(bdlptr->bundle_id); tracebuf(bdlptr, bdlptr->bdllen); return 0; } ================================================ FILE: bundle.h ================================================ #ifndef __BUNDLE_H #define __BUNDLE_H int ParseConfig(struct BDLHDR far *bdlptr); #endif ================================================ FILE: cgadraw/COPYING ================================================ The files in this directory are derived from libcaca, which is published under the license below. DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004 Copyright (C) 2004 Sam Hocevar Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. ================================================ FILE: cgadraw/Makefile ================================================ DFLAGS=-quiet -dumb WARNLEVEL=0 CPPFLAGS=/I. CFLAGS=/AL /nologo /NDAAB /Od /G3 /Gs /Gc /Zi /FPi87 /Zp1 /Zl /Gf /f- /W$(WARNLEVEL) /WX LDFLAGS= LDLIBS=CGADRAW .PHONY: clean # MS-DOS doesn't allow long commandlines, so params are written to a "response" # file. %.obj: %.c @rm -f $@ printf "$(CPPFLAGS) $(CFLAGS)\r\n" > $(@:.obj=.cl) dosemu $(DFLAGS) -E "IN G:/ CL /c @$(@:.obj=.cl) /Fo$@ $<" @rm -f $(@:.obj=.cl) %.lib: %.obj @rm -f $@ printf "$(LFLAGS) $(addprefix +,$^),nul\r\n" > $(@:.lib=.cl) dosemu $(DFLAGS) -E "IN G:/ LIB $@ @$(@:.lib=.cl)" @rm -f $(@:.lib=.cl) %.exe: %.obj @rm -f $@ printf "$(CFLAGS) $(LDFLAGS) $^ $(addprefix /link ,$(LDLIBS))\r\n" > $(@:.exe=.cl) dosemu $(DFLAGS) -E "IN G:/ CL /Fe$@ @$(@:.exe=.cl)" @rm -f $(@:.exe=.cl) all: cgadraw.lib drawtest.obj: CFLAGS:=$(filter-out /Zl,$(CFLAGS)) drawtest.exe: snprintf.obj | cgadraw.lib cgadraw.lib: attr.obj box.obj canvas.obj charset.obj conic.obj frame.obj \ line.obj string.obj transfrm.obj triangle.obj clean: rm -f *.exe *.lib *.obj *.map *.pdb *.cl ================================================ FILE: cgadraw/README.md ================================================ # cgadraw This is a stripped down version of libcaca 0.99 used for adding drawing primitives to the CGA framebuffer. You might ask why not just use full libcaca, but it requires around `8 * width * height` bytes for the canvas, plus another `width * height` to get the data out of a canvas, and some management overhead. If you want to add frames or any kind of transformation, double that again at least. That is a totally reasonable amount normally, but in a tiny display driver it's really pushing the limits of what I can scratch together. ================================================ FILE: cgadraw/attr.c ================================================ /* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /* * This file contains functions for attribute management and colourspace * conversions. */ #include "config.h" #include "caca.h" #include "cacaint.h" static uint8_t nearest_ansi(uint16_t); /* RGB colours for the ANSI palette. There is no real standard, so we * use the same values as gnome-terminal. The 7th colour (brown) is a bit * special: 0xfa50 instead of 0xfaa0. */ static const uint16_t ansitab16[16] = { 0xf000, 0xf00a, 0xf0a0, 0xf0aa, 0xfa00, 0xfa0a, 0xfa50, 0xfaaa, 0xf555, 0xf55f, 0xf5f5, 0xf5ff, 0xff55, 0xff5f, 0xfff5, 0xffff, }; /* Same table, except on 14 bits (3-4-4-3) */ static const uint16_t ansitab14[16] = { 0x3800, 0x3805, 0x3850, 0x3855, 0x3d00, 0x3d05, 0x3d28, 0x3d55, 0x3aaa, 0x3aaf, 0x3afa, 0x3aff, 0x3faa, 0x3faf, 0x3ffa, 0x3fff, }; /** \brief Get the text attribute at the given coordinates. * * Get the internal \e libcaca attribute value of the character at the * given coordinates. The attribute value has 32 significant bits, * organised as follows from MSB to LSB: * - 3 bits for the background alpha * - 4 bits for the background red component * - 4 bits for the background green component * - 3 bits for the background blue component * - 3 bits for the foreground alpha * - 4 bits for the foreground red component * - 4 bits for the foreground green component * - 3 bits for the foreground blue component * - 4 bits for the bold, italics, underline and blink flags * * If the coordinates are outside the canvas boundaries, the current * attribute is returned. * * This function never fails. * * \param cv A handle to the libcaca canvas. * \param x X coordinate. * \param y Y coordinate. * \return The requested attribute. */ uint32_t caca_get_attr(caca_canvas_t const *cv, int x, int y) { if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height) return cv->curattr; return cv->c[x + y * cv->width].attr; // XXX FIXME } /** \brief Set the default character attribute. * * Set the default character attribute for drawing. Attributes define * foreground and background colour, transparency, bold, italics and * underline styles, as well as blink. String functions such as * caca_printf() and graphical primitive functions such as caca_draw_line() * will use this attribute. * * The value of \e attr is either: * - a 32-bit integer as returned by caca_get_attr(), in which case it * also contains colour information, * - a combination (bitwise OR) of style values (\e CACA_UNDERLINE, * \e CACA_BLINK, \e CACA_BOLD and \e CACA_ITALICS), in which case * setting the attribute does not modify the current colour information. * * To retrieve the current attribute value, use caca_get_attr(-1,-1). * * This function never fails. * * \param cv A handle to the libcaca canvas. * \param attr The requested attribute value. * \return This function always returns 0. */ int caca_set_attr(caca_canvas_t *cv, uint32_t attr) { if(attr < 0x00000010) attr = (cv->curattr & 0xfffffff0) | attr; cv->curattr = attr; return 0; } /** \brief Unset flags in the default character attribute. * * Unset flags in the default character attribute for drawing. Attributes * define foreground and background colour, transparency, bold, italics and * underline styles, as well as blink. String functions such as * caca_printf() and graphical primitive functions such as caca_draw_line() * will use this attribute. * * The value of \e attr is a combination (bitwise OR) of style values * (\e CACA_UNDERLINE, \e CACA_BLINK, \e CACA_BOLD and \e CACA_ITALICS). * Unsetting these attributes does not modify the current colour information. * * To retrieve the current attribute value, use caca_get_attr(-1,-1). * * This function never fails. * * \param cv A handle to the libcaca canvas. * \param attr The requested attribute values to unset. * \return This function always returns 0. */ int caca_unset_attr(caca_canvas_t *cv, uint32_t attr) { cv->curattr &= ~(attr & 0x0000000f); return 0; } /** \brief Toggle flags in the default character attribute. * * Toggle flags in the default character attribute for drawing. Attributes * define foreground and background colour, transparency, bold, italics and * underline styles, as well as blink. String functions such as * caca_printf() and graphical primitive functions such as caca_draw_line() * will use this attribute. * * The value of \e attr is a combination (bitwise OR) of style values * (\e CACA_UNDERLINE, \e CACA_BLINK, \e CACA_BOLD and \e CACA_ITALICS). * Toggling these attributes does not modify the current colour information. * * To retrieve the current attribute value, use caca_get_attr(-1,-1). * * This function never fails. * * \param cv A handle to the libcaca canvas. * \param attr The requested attribute values to toggle. * \return This function always returns 0. */ int caca_toggle_attr(caca_canvas_t *cv, uint32_t attr) { cv->curattr ^= attr & 0x0000000f; return 0; } /** \brief Set the character attribute at the given coordinates. * * Set the character attribute, without changing the character's value. If * the character at the given coordinates is a fullwidth character, both * cells' attributes are replaced. * * The value of \e attr is either: * - a 32-bit integer as returned by caca_get_attr(), in which case it * also contains colour information, * - a combination (bitwise OR) of style values (\e CACA_UNDERLINE, * \e CACA_BLINK, \e CACA_BOLD and \e CACA_ITALICS), in which case * setting the attribute does not modify the current colour information. * * This function never fails. * * \param cv A handle to the libcaca canvas. * \param x X coordinate. * \param y Y coordinate. * \param attr The requested attribute value. * \return This function always returns 0. */ int caca_put_attr(caca_canvas_t *cv, int x, int y, uint32_t attr) { if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height) return 0; cv->c[x + y * cv->width].attr = caca_attr_to_ansi(attr); return 0; } /** \brief Set the default colour pair for text (ANSI version). * * Set the default ANSI colour pair for text drawing. String functions such * as caca_printf() and graphical primitive functions such as caca_draw_line() * will use these attributes. * * Color values are those defined in caca.h, such as CACA_RED * or CACA_TRANSPARENT. * * If an error occurs, -1 is returned and \b errno is set accordingly: * - \c EINVAL At least one of the colour values is invalid. * * \param cv A handle to the libcaca canvas. * \param fg The requested ANSI foreground colour. * \param bg The requested ANSI background colour. * \return 0 in case of success, -1 if an error occurred. */ int caca_set_color_ansi(caca_canvas_t *cv, uint8_t fg, uint8_t bg) { uint32_t attr; if(fg > 0x20 || bg > 0x20) { seterrno(EINVAL); return -1; } attr = ((uint32_t)(bg | 0x40) << 18) | ((uint32_t)(fg | 0x40) << 4); cv->curattr = (cv->curattr & 0x0000000f) | attr; return 0; } /** \brief Set the default colour pair for text (truecolor version). * * Set the default ARGB colour pair for text drawing. String functions such * as caca_printf() and graphical primitive functions such as caca_draw_line() * will use these attributes. * * Colors are 16-bit ARGB values, each component being coded on 4 bits. For * instance, 0xf088 is solid dark cyan (A=15 R=0 G=8 B=8), and 0x8fff is * white with 50% alpha (A=8 R=15 G=15 B=15). * * This function never fails. * * \param cv A handle to the libcaca canvas. * \param fg The requested ARGB foreground colour. * \param bg The requested ARGB background colour. * \return This function always returns 0. */ int caca_set_color_argb(caca_canvas_t *cv, uint16_t fg, uint16_t bg) { uint32_t attr; if(fg < 0x100) fg += 0x100; if(bg < 0x100) bg += 0x100; fg = ((fg >> 1) & 0x7ff) | ((fg >> 13) << 11); bg = ((bg >> 1) & 0x7ff) | ((bg >> 13) << 11); attr = ((uint32_t)bg << 18) | ((uint32_t)fg << 4); cv->curattr = (cv->curattr & 0x0000000f) | attr; return 0; } /** \brief Get DOS ANSI information from attribute. * * Get the ANSI colour pair for a given attribute. The returned value is * an 8-bit value whose higher 4 bits are the background colour and lower * 4 bits are the foreground colour. * * If the attribute has ARGB colours, the nearest colour is used. Special * attributes such as \e CACA_DEFAULT and \e CACA_TRANSPARENT are not * handled and are both replaced with \e CACA_LIGHTGRAY for the foreground * colour and \e CACA_BLACK for the background colour. * * This function never fails. If the attribute value is outside the expected * 32-bit range, higher order bits are simply ignored. * * \param attr The requested attribute value. * \return The corresponding DOS ANSI value. */ uint8_t caca_attr_to_ansi(uint32_t attr) { uint8_t fg = nearest_ansi((attr >> 4) & 0x3fff); uint8_t bg = nearest_ansi(attr >> 18); return (fg < 0x10 ? fg : CACA_LIGHTGRAY) | ((bg < 0x10 ? bg : CACA_BLACK) << 4); } /** \brief Get ANSI foreground information from attribute. * * Get the ANSI foreground colour value for a given attribute. The returned * value is either one of the \e CACA_RED, \e CACA_BLACK etc. predefined * colours, or the special value \e CACA_DEFAULT meaning the media's * default foreground value, or the special value \e CACA_TRANSPARENT. * * If the attribute has ARGB colours, the nearest colour is returned. * * This function never fails. If the attribute value is outside the expected * 32-bit range, higher order bits are simply ignored. * * \param attr The requested attribute value. * \return The corresponding ANSI foreground value. */ uint8_t caca_attr_to_ansi_fg(uint32_t attr) { return nearest_ansi((attr >> 4) & 0x3fff); } /** \brief Get ANSI background information from attribute. * * Get the ANSI background colour value for a given attribute. The returned * value is either one of the \e CACA_RED, \e CACA_BLACK etc. predefined * colours, or the special value \e CACA_DEFAULT meaning the media's * default background value, or the special value \e CACA_TRANSPARENT. * * If the attribute has ARGB colours, the nearest colour is returned. * * This function never fails. If the attribute value is outside the expected * 32-bit range, higher order bits are simply ignored. * * \param attr The requested attribute value. * \return The corresponding ANSI background value. */ uint8_t caca_attr_to_ansi_bg(uint32_t attr) { return nearest_ansi(attr >> 18); } /** \brief Get 12-bit RGB foreground information from attribute. * * Get the 12-bit foreground colour value for a given attribute. The returned * value is a native-endian encoded integer with each red, green and blue * values encoded on 8 bits in the following order: * - 8-11 most significant bits: red * - 4-7 most significant bits: green * - least significant bits: blue * * This function never fails. If the attribute value is outside the expected * 32-bit range, higher order bits are simply ignored. * * \param attr The requested attribute value. * \return The corresponding 12-bit RGB foreground value. */ uint16_t caca_attr_to_rgb12_fg(uint32_t attr) { uint16_t fg = (attr >> 4) & 0x3fff; if(fg < (0x10 | 0x40)) return ansitab16[fg ^ 0x40] & 0x0fff; if(fg == (CACA_DEFAULT | 0x40)) return ansitab16[CACA_LIGHTGRAY] & 0x0fff; if(fg == (CACA_TRANSPARENT | 0x40)) return ansitab16[CACA_LIGHTGRAY] & 0x0fff; return (fg << 1) & 0x0fff; } /** \brief Get 12-bit RGB background information from attribute. * * Get the 12-bit background colour value for a given attribute. The returned * value is a native-endian encoded integer with each red, green and blue * values encoded on 8 bits in the following order: * - 8-11 most significant bits: red * - 4-7 most significant bits: green * - least significant bits: blue * * This function never fails. If the attribute value is outside the expected * 32-bit range, higher order bits are simply ignored. * * \param attr The requested attribute value. * \return The corresponding 12-bit RGB background value. */ uint16_t caca_attr_to_rgb12_bg(uint32_t attr) { uint16_t bg = attr >> 18; if(bg < (0x10 | 0x40)) return ansitab16[bg ^ 0x40] & 0x0fff; if(bg == (CACA_DEFAULT | 0x40)) return ansitab16[CACA_BLACK] & 0x0fff; if(bg == (CACA_TRANSPARENT | 0x40)) return ansitab16[CACA_BLACK] & 0x0fff; return (bg << 1) & 0x0fff; } /** \brief Get 64-bit ARGB information from attribute. * * Get the 64-bit colour and alpha values for a given attribute. The values * are written as 8-bit integers in the \e argb array in the following order: * - \e argb[0]: background alpha value * - \e argb[1]: background red value * - \e argb[2]: background green value * - \e argb[3]: background blue value * - \e argb[4]: foreground alpha value * - \e argb[5]: foreground red value * - \e argb[6]: foreground green value * - \e argb[7]: foreground blue value * * This function never fails. If the attribute value is outside the expected * 32-bit range, higher order bits are simply ignored. * * \param attr The requested attribute value. * \param argb An array of 8-bit integers. */ void caca_attr_to_argb64(uint32_t attr, uint8_t argb[8]) { uint16_t fg = (attr >> 4) & 0x3fff; uint16_t bg = attr >> 18; if(bg < (0x10 | 0x40)) bg = ansitab16[bg ^ 0x40]; else if(bg == (CACA_DEFAULT | 0x40)) bg = ansitab16[CACA_BLACK]; else if(bg == (CACA_TRANSPARENT | 0x40)) bg = 0x0fff; else bg = ((bg << 2) & 0xf000) | ((bg << 1) & 0x0fff); argb[0] = bg >> 12; argb[1] = (bg >> 8) & 0xf; argb[2] = (bg >> 4) & 0xf; argb[3] = bg & 0xf; if(fg < (0x10 | 0x40)) fg = ansitab16[fg ^ 0x40]; else if(fg == (CACA_DEFAULT | 0x40)) fg = ansitab16[CACA_LIGHTGRAY]; else if(fg == (CACA_TRANSPARENT | 0x40)) fg = 0x0fff; else fg = ((fg << 2) & 0xf000) | ((fg << 1) & 0x0fff); argb[4] = fg >> 12; argb[5] = (fg >> 8) & 0xf; argb[6] = (fg >> 4) & 0xf; argb[7] = fg & 0xf; } /* * XXX: the following functions are local */ static uint8_t nearest_ansi(uint16_t argb14) { unsigned int i, best, dist; if(argb14 < (0x10 | 0x40)) return argb14 ^ 0x40; if(argb14 == (CACA_DEFAULT | 0x40) || argb14 == (CACA_TRANSPARENT | 0x40)) return argb14 ^ 0x40; if(argb14 < 0x0fff) /* too transparent */ return CACA_TRANSPARENT; best = CACA_DEFAULT; dist = 0x3fff; for(i = 0; i < 16; i++) { unsigned int d = 0; int a, b; a = (ansitab14[i] >> 7) & 0xf; b = (argb14 >> 7) & 0xf; d += (a - b) * (a - b); a = (ansitab14[i] >> 3) & 0xf; b = (argb14 >> 3) & 0xf; d += (a - b) * (a - b); a = (ansitab14[i] << 1) & 0xf; b = (argb14 << 1) & 0xf; d += (a - b) * (a - b); if(d < dist) { dist = d; best = i; } } return best; } #define RGB12TO24(i) \ (((uint32_t)((i & 0xf00) >> 8) * 0x110000) \ | ((uint32_t)((i & 0x0f0) >> 4) * 0x001100) \ | ((uint32_t)(i & 0x00f) * 0x000011)) uint32_t _caca_attr_to_rgb24fg(uint32_t attr) { return RGB12TO24(caca_attr_to_rgb12_fg(attr)); } uint32_t _caca_attr_to_rgb24bg(uint32_t attr) { return RGB12TO24(caca_attr_to_rgb12_bg(attr)); } ================================================ FILE: cgadraw/box.c ================================================ /* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /* * This file contains box drawing functions, both filled and outline. */ #include "config.h" #if !defined(__KERNEL__) # include #endif #include "caca.h" #include "cacaint.h" static int draw_box(caca_canvas_t *cv, int x, int y, int w, int h, uint32_t const *chars); /** \brief Draw a box on the canvas using the given character. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x X coordinate of the upper-left corner of the box. * \param y Y coordinate of the upper-left corner of the box. * \param w Width of the box. * \param h Height of the box. * \param ch UTF-32 character to be used to draw the box. * \return This function always returns 0. */ int caca_draw_box(caca_canvas_t *cv, int x, int y, int w, int h, uint32_t ch) { int x2 = x + w - 1; int y2 = y + h - 1; caca_draw_line(cv, x, y, x, y2, ch); caca_draw_line(cv, x, y2, x2, y2, ch); caca_draw_line(cv, x2, y2, x2, y, ch); caca_draw_line(cv, x2, y, x, y, ch); return 0; } /** \brief Draw a thin box on the canvas. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x X coordinate of the upper-left corner of the box. * \param y Y coordinate of the upper-left corner of the box. * \param w Width of the box. * \param h Height of the box. * \return This function always returns 0. */ int caca_draw_thin_box(caca_canvas_t *cv, int x, int y, int w, int h) { static uint32_t const ascii_chars[] = { '-', '|', ',', '`', '.', '\'' }; return draw_box(cv, x, y, w, h, ascii_chars); } /** \brief Draw a box on the canvas using CP437 characters. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x X coordinate of the upper-left corner of the box. * \param y Y coordinate of the upper-left corner of the box. * \param w Width of the box. * \param h Height of the box. * \return This function always returns 0. */ int caca_draw_cp437_box(caca_canvas_t *cv, int x, int y, int w, int h) { static uint32_t const cp437_chars[] = { /* ─ │ ┌ └ ┐ ┘ */ 0x2500, 0x2502, 0x250c, 0x2514, 0x2510, 0x2518 }; return draw_box(cv, x, y, w, h, cp437_chars); } /** \brief Fill a box on the canvas using the given character. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x X coordinate of the upper-left corner of the box. * \param y Y coordinate of the upper-left corner of the box. * \param w Width of the box. * \param h Height of the box. * \param ch UTF-32 character to be used to draw the box. * \return This function always returns 0. */ int caca_fill_box(caca_canvas_t *cv, int x, int y, int w, int h, uint32_t ch) { int i, j, xmax, ymax; int x2 = x + w - 1; int y2 = y + h - 1; if(x > x2) { int tmp = x; x = x2; x2 = tmp; } if(y > y2) { int tmp = y; y = y2; y2 = tmp; } xmax = cv->width - 1; ymax = cv->height - 1; if(x2 < 0 || y2 < 0 || x > xmax || y > ymax) return 0; if(x < 0) x = 0; if(y < 0) y = 0; if(x2 > xmax) x2 = xmax; if(y2 > ymax) y2 = ymax; #if 0 /* FIXME: this fails with fullwidth character blits. Also, the dirty * rectangle handling may miss fullwidth cells. */ /* Optimise dirty rectangle handling, part 1 */ cv->dirty_disabled++; #endif for(j = y; j <= y2; j++) for(i = x; i <= x2; i++) caca_put_char(cv, i, j, ch); #if 0 /* Optimise dirty rectangle handling, part 2 */ cv->dirty_disabled--; if(!cv->dirty_disabled) caca_add_dirty_rect(cv, x, y, x2 - x + 1, y2 - y + 1); #endif return 0; } /* * XXX: The following functions are local. */ static int draw_box(caca_canvas_t *cv, int x, int y, int w, int h, uint32_t const *chars) { int i, j, xmax, ymax; int x2 = x + w - 1; int y2 = y + h - 1; if(x > x2) { int tmp = x; x = x2; x2 = tmp; } if(y > y2) { int tmp = y; y = y2; y2 = tmp; } xmax = cv->width - 1; ymax = cv->height - 1; if(x2 < 0 || y2 < 0 || x > xmax || y > ymax) return 0; /* Draw edges */ if(y >= 0) for(i = x < 0 ? 1 : x + 1; i < x2 && i < xmax; i++) caca_put_char(cv, i, y, chars[0]); if(y2 <= ymax) for(i = x < 0 ? 1 : x + 1; i < x2 && i < xmax; i++) caca_put_char(cv, i, y2, chars[0]); if(x >= 0) for(j = y < 0 ? 1 : y + 1; j < y2 && j < ymax; j++) caca_put_char(cv, x, j, chars[1]); if(x2 <= xmax) for(j = y < 0 ? 1 : y + 1; j < y2 && j < ymax; j++) caca_put_char(cv, x2, j, chars[1]); /* Draw corners */ caca_put_char(cv, x, y, chars[2]); caca_put_char(cv, x, y2, chars[3]); caca_put_char(cv, x2, y, chars[4]); caca_put_char(cv, x2, y2, chars[5]); return 0; } ================================================ FILE: cgadraw/caca.h ================================================ /* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /** \file caca.h * \author Sam Hocevar * \brief The \e libcaca public header. * * This header contains the public types and functions that applications * using \e libcaca may use. */ #ifndef __CACA_H__ #define __CACA_H__ #include #include #undef __extern #if defined _DOXYGEN_SKIP_ME #elif defined _WIN32 && defined __LIBCACA__ && defined DLL_EXPORT # define __extern extern __declspec(dllexport) #elif defined _WIN32 && !defined __LIBCACA__ && !defined CACA_STATIC # define __extern extern __declspec(dllimport) #elif defined CACA_ENABLE_VISIBILITY # define __extern extern __attribute__((visibility("default"))) #else # define __extern extern #endif /** libcaca API version */ #define CACA_API_VERSION_1 #ifdef __cplusplus extern "C" { #endif /** \e libcaca canvas */ typedef struct caca_canvas caca_canvas_t; /** dither structure */ typedef struct caca_dither caca_dither_t; /** character font structure */ typedef struct caca_charfont caca_charfont_t; /** bitmap font structure */ typedef struct caca_font caca_font_t; /** file handle structure */ typedef struct caca_file caca_file_t; /** \e libcaca display context */ typedef struct caca_display caca_display_t; /** \e libcaca event structure */ typedef struct caca_event caca_event_t; /** \defgroup caca_attr libcaca attribute definitions * * Colours and styles that can be used with caca_set_attr(). * * @{ */ /** \e libcaca colour keyword */ enum caca_color { CACA_BLACK = 0x00, /**< The colour index for black. */ CACA_BLUE = 0x01, /**< The colour index for blue. */ CACA_GREEN = 0x02, /**< The colour index for green. */ CACA_CYAN = 0x03, /**< The colour index for cyan. */ CACA_RED = 0x04, /**< The colour index for red. */ CACA_MAGENTA = 0x05, /**< The colour index for magenta. */ CACA_BROWN = 0x06, /**< The colour index for brown. */ CACA_LIGHTGRAY = 0x07, /**< The colour index for light gray. */ CACA_DARKGRAY = 0x08, /**< The colour index for dark gray. */ CACA_LIGHTBLUE = 0x09, /**< The colour index for blue. */ CACA_LIGHTGREEN = 0x0a, /**< The colour index for light green. */ CACA_LIGHTCYAN = 0x0b, /**< The colour index for light cyan. */ CACA_LIGHTRED = 0x0c, /**< The colour index for light red. */ CACA_LIGHTMAGENTA = 0x0d, /**< The colour index for light magenta. */ CACA_YELLOW = 0x0e, /**< The colour index for yellow. */ CACA_WHITE = 0x0f, /**< The colour index for white. */ CACA_DEFAULT = 0x10, /**< The output driver's default colour. */ CACA_TRANSPARENT = 0x20, /**< The transparent colour. */ }; /** \e libcaca style keyword */ enum caca_style { CACA_BOLD = 0x01, /**< The style mask for bold. */ CACA_ITALICS = 0x02, /**< The style mask for italics. */ CACA_UNDERLINE = 0x04, /**< The style mask for underline. */ CACA_BLINK = 0x08, /**< The style mask for blink. */ }; /* @} */ /** \brief User event type enumeration. * * This enum serves two purposes: * - Build listening masks for caca_get_event(). * - Define the type of a \e caca_event_t. */ enum caca_event_type { CACA_EVENT_NONE = 0x0000, /**< No event. */ CACA_EVENT_KEY_PRESS = 0x0001, /**< A key was pressed. */ CACA_EVENT_KEY_RELEASE = 0x0002, /**< A key was released. */ CACA_EVENT_MOUSE_PRESS = 0x0004, /**< A mouse button was pressed. */ CACA_EVENT_MOUSE_RELEASE = 0x0008, /**< A mouse button was released. */ CACA_EVENT_MOUSE_MOTION = 0x0010, /**< The mouse was moved. */ CACA_EVENT_RESIZE = 0x0020, /**< The window was resized. */ CACA_EVENT_QUIT = 0x0040, /**< The user requested to quit. */ }; #define CACA_EVENT_ANY 0xffff /**< Bitmask for any event. */ /** \brief Handling of user events. * * This structure is filled by caca_get_event() when an event is received. * It is an opaque structure that should only be accessed through * caca_event_get_type() and similar functions. The struct members may no * longer be directly accessible in future versions. */ struct caca_event { enum caca_event_type type; /**< The event type. */ union { struct { int x, y, button; } mouse; struct { int w, h; } resize; struct { int ch; uint32_t utf32; char utf8[8]; } key; } data; /**< The event information data */ #if !defined(_DOXYGEN_SKIP_ME) uint8_t padding[16]; #endif }; /** \brief Option parsing. * * This structure contains commandline parsing information for systems * where getopt_long() is unavailable. */ struct caca_option { char const *name; int has_arg; int *flag; int val; }; /** \brief Special key values. * * Special key values returned by caca_get_event() for which there is no * printable ASCII equivalent. */ enum caca_key { CACA_KEY_UNKNOWN = 0x00, /**< Unknown key. */ /* The following keys have ASCII equivalents */ CACA_KEY_CTRL_A = 0x01, /**< The Ctrl-A key. */ CACA_KEY_CTRL_B = 0x02, /**< The Ctrl-B key. */ CACA_KEY_CTRL_C = 0x03, /**< The Ctrl-C key. */ CACA_KEY_CTRL_D = 0x04, /**< The Ctrl-D key. */ CACA_KEY_CTRL_E = 0x05, /**< The Ctrl-E key. */ CACA_KEY_CTRL_F = 0x06, /**< The Ctrl-F key. */ CACA_KEY_CTRL_G = 0x07, /**< The Ctrl-G key. */ CACA_KEY_BACKSPACE = 0x08, /**< The backspace key. */ CACA_KEY_TAB = 0x09, /**< The tabulation key. */ CACA_KEY_CTRL_J = 0x0a, /**< The Ctrl-J key. */ CACA_KEY_CTRL_K = 0x0b, /**< The Ctrl-K key. */ CACA_KEY_CTRL_L = 0x0c, /**< The Ctrl-L key. */ CACA_KEY_RETURN = 0x0d, /**< The return key. */ CACA_KEY_CTRL_N = 0x0e, /**< The Ctrl-N key. */ CACA_KEY_CTRL_O = 0x0f, /**< The Ctrl-O key. */ CACA_KEY_CTRL_P = 0x10, /**< The Ctrl-P key. */ CACA_KEY_CTRL_Q = 0x11, /**< The Ctrl-Q key. */ CACA_KEY_CTRL_R = 0x12, /**< The Ctrl-R key. */ CACA_KEY_PAUSE = 0x13, /**< The pause key. */ CACA_KEY_CTRL_T = 0x14, /**< The Ctrl-T key. */ CACA_KEY_CTRL_U = 0x15, /**< The Ctrl-U key. */ CACA_KEY_CTRL_V = 0x16, /**< The Ctrl-V key. */ CACA_KEY_CTRL_W = 0x17, /**< The Ctrl-W key. */ CACA_KEY_CTRL_X = 0x18, /**< The Ctrl-X key. */ CACA_KEY_CTRL_Y = 0x19, /**< The Ctrl-Y key. */ CACA_KEY_CTRL_Z = 0x1a, /**< The Ctrl-Z key. */ CACA_KEY_ESCAPE = 0x1b, /**< The escape key. */ CACA_KEY_DELETE = 0x7f, /**< The delete key. */ /* The following keys do not have ASCII equivalents but have been * chosen to match the SDL equivalents */ CACA_KEY_UP = 0x111, /**< The up arrow key. */ CACA_KEY_DOWN = 0x112, /**< The down arrow key. */ CACA_KEY_LEFT = 0x113, /**< The left arrow key. */ CACA_KEY_RIGHT = 0x114, /**< The right arrow key. */ CACA_KEY_INSERT = 0x115, /**< The insert key. */ CACA_KEY_HOME = 0x116, /**< The home key. */ CACA_KEY_END = 0x117, /**< The end key. */ CACA_KEY_PAGEUP = 0x118, /**< The page up key. */ CACA_KEY_PAGEDOWN = 0x119, /**< The page down key. */ CACA_KEY_F1 = 0x11a, /**< The F1 key. */ CACA_KEY_F2 = 0x11b, /**< The F2 key. */ CACA_KEY_F3 = 0x11c, /**< The F3 key. */ CACA_KEY_F4 = 0x11d, /**< The F4 key. */ CACA_KEY_F5 = 0x11e, /**< The F5 key. */ CACA_KEY_F6 = 0x11f, /**< The F6 key. */ CACA_KEY_F7 = 0x120, /**< The F7 key. */ CACA_KEY_F8 = 0x121, /**< The F8 key. */ CACA_KEY_F9 = 0x122, /**< The F9 key. */ CACA_KEY_F10 = 0x123, /**< The F10 key. */ CACA_KEY_F11 = 0x124, /**< The F11 key. */ CACA_KEY_F12 = 0x125, /**< The F12 key. */ CACA_KEY_F13 = 0x126, /**< The F13 key. */ CACA_KEY_F14 = 0x127, /**< The F14 key. */ CACA_KEY_F15 = 0x128 /**< The F15 key. */ }; /** \defgroup libcaca libcaca basic functions * * These functions provide the basic \e libcaca routines for library * initialisation, system information retrieval and configuration. * * @{ */ __extern caca_canvas_t * far caca_create_canvas(int, int, int, int); __extern int caca_manage_canvas(caca_canvas_t *, int (*)(void *), void *); __extern int caca_unmanage_canvas(caca_canvas_t *, int (*)(void *), void *); __extern int caca_set_canvas_size(caca_canvas_t *, int, int); __extern int caca_get_canvas_width(caca_canvas_t const *); __extern int caca_get_canvas_height(caca_canvas_t const *); __extern uint32_t const * far caca_get_canvas_chars(caca_canvas_t const *); __extern uint32_t const * far caca_get_canvas_attrs(caca_canvas_t const *); __extern int caca_free_canvas(caca_canvas_t *); __extern int caca_rand(int, int); __extern char const * far caca_get_version(void); /* @} */ /** \defgroup caca_canvas libcaca canvas drawing * * These functions provide low-level character printing routines and * higher level graphics functions. * * @{ */ #define CACA_MAGIC_FULLWIDTH 0x000ffffe /**< Used to indicate that the previous character was a fullwidth glyph. */ __extern int caca_gotoxy(caca_canvas_t *, int, int); __extern int caca_wherex(caca_canvas_t const *); __extern int caca_wherey(caca_canvas_t const *); __extern int caca_put_char(caca_canvas_t *, int, int, uint32_t); __extern uint32_t caca_get_char(caca_canvas_t const *, int, int); __extern int caca_put_str(caca_canvas_t *, int, int, char const *); __extern int caca_printf(caca_canvas_t *, int, int, char const *, ...); __extern int caca_vprintf(caca_canvas_t *, int, int, char const *, va_list); __extern int caca_clear_canvas(caca_canvas_t *); __extern int caca_set_canvas_handle(caca_canvas_t *, int, int); __extern int caca_get_canvas_handle_x(caca_canvas_t const *); __extern int caca_get_canvas_handle_y(caca_canvas_t const *); __extern int caca_blit(caca_canvas_t *, int, int, caca_canvas_t const *, caca_canvas_t const *); __extern int caca_set_canvas_boundaries(caca_canvas_t *, int, int, int, int); /* @} */ /** \defgroup caca_dirty libcaca dirty rectangle manipulation * * These functions manipulate dirty rectangles for optimised blitting. * @{ */ __extern int caca_disable_dirty_rect(caca_canvas_t *); __extern int caca_enable_dirty_rect(caca_canvas_t *); __extern int caca_get_dirty_rect_count(caca_canvas_t *); __extern int caca_get_dirty_rect(caca_canvas_t *, int, int *, int *, int *, int *); __extern int caca_add_dirty_rect(caca_canvas_t *, int, int, int, int); __extern int caca_remove_dirty_rect(caca_canvas_t *, int, int, int, int); __extern int caca_clear_dirty_rect_list(caca_canvas_t *); /* @} */ /** \defgroup caca_transform libcaca canvas transformation * * These functions perform horizontal and vertical canvas flipping. * * @{ */ __extern int caca_invert(caca_canvas_t *); __extern int caca_flip(caca_canvas_t *); __extern int caca_flop(caca_canvas_t *); __extern int caca_rotate_180(caca_canvas_t *); __extern int caca_rotate_left(caca_canvas_t *); __extern int caca_rotate_right(caca_canvas_t *); __extern int caca_stretch_left(caca_canvas_t *); __extern int caca_stretch_right(caca_canvas_t *); /* @} */ /** \defgroup caca_attributes libcaca attribute conversions * * These functions perform conversions between attribute values. * * @{ */ __extern uint32_t caca_get_attr(caca_canvas_t const *, int, int); __extern int caca_set_attr(caca_canvas_t *, uint32_t); __extern int caca_unset_attr(caca_canvas_t *, uint32_t); __extern int caca_toggle_attr(caca_canvas_t *, uint32_t); __extern int caca_put_attr(caca_canvas_t *, int, int, uint32_t); __extern int caca_set_color_ansi(caca_canvas_t *, uint8_t, uint8_t); __extern int caca_set_color_argb(caca_canvas_t *, uint16_t, uint16_t); __extern uint8_t caca_attr_to_ansi(uint32_t); __extern uint8_t caca_attr_to_ansi_fg(uint32_t); __extern uint8_t caca_attr_to_ansi_bg(uint32_t); __extern uint16_t caca_attr_to_rgb12_fg(uint32_t); __extern uint16_t caca_attr_to_rgb12_bg(uint32_t); __extern void caca_attr_to_argb64(uint32_t, uint8_t[8]); /* @} */ /** \defgroup caca_charset libcaca character set conversions * * These functions perform conversions between usual character sets. * * @{ */ __extern uint32_t caca_utf8_to_utf32(char const *, size_t *); __extern size_t caca_utf32_to_utf8(char *, uint32_t); __extern uint8_t caca_utf32_to_cp437(uint32_t); __extern uint32_t caca_cp437_to_utf32(uint8_t); __extern char caca_utf32_to_ascii(uint32_t); __extern int caca_utf32_is_fullwidth(uint32_t); /* @} */ /** \defgroup caca_primitives libcaca primitives drawing * * These functions provide routines for primitive drawing, such as lines, * boxes, triangles and ellipses. * * @{ */ __extern int caca_draw_line(caca_canvas_t *, int, int, int, int, uint32_t); __extern int caca_draw_polyline(caca_canvas_t *, int const x[], int const y[], int, uint32_t); __extern int caca_draw_thin_line(caca_canvas_t *, int, int, int, int); __extern int caca_draw_thin_polyline(caca_canvas_t *, int const x[], int const y[], int); __extern int caca_draw_circle(caca_canvas_t *, int, int, int, uint32_t); __extern int caca_draw_ellipse(caca_canvas_t *, int, int, int, int, uint32_t); __extern int caca_draw_thin_ellipse(caca_canvas_t *, int, int, int, int); __extern int caca_fill_ellipse(caca_canvas_t *, int, int, int, int, uint32_t); __extern int caca_draw_box(caca_canvas_t *, int, int, int, int, uint32_t); __extern int caca_draw_thin_box(caca_canvas_t *, int, int, int, int); __extern int caca_draw_cp437_box(caca_canvas_t *, int, int, int, int); __extern int caca_fill_box(caca_canvas_t *, int, int, int, int, uint32_t); __extern int caca_draw_triangle(caca_canvas_t *, int, int, int, int, int, int, uint32_t); __extern int caca_draw_thin_triangle(caca_canvas_t *, int, int, int, int, int, int); __extern int caca_fill_triangle(caca_canvas_t *, int, int, int, int, int, int, uint32_t); __extern int caca_fill_triangle_textured(caca_canvas_t *cv, int coords[6], caca_canvas_t *tex, float uv[6]); /* @} */ /** \defgroup caca_frame libcaca canvas frame handling * * These functions provide high level routines for canvas frame insertion, * removal, copying etc. * * @{ */ __extern int caca_get_frame_count(caca_canvas_t const *); __extern int caca_set_frame(caca_canvas_t *, int); __extern char const *caca_get_frame_name(caca_canvas_t const *); __extern int caca_set_frame_name(caca_canvas_t *, char const *); __extern int caca_create_frame(caca_canvas_t *, int); __extern int caca_free_frame(caca_canvas_t *, int); /* @} */ /** \defgroup caca_dither libcaca bitmap dithering * * These functions provide high level routines for dither allocation and * rendering. * * @{ */ __extern caca_dither_t *caca_create_dither(int, int, int, int, uint32_t, uint32_t, uint32_t, uint32_t); __extern int caca_set_dither_palette(caca_dither_t *, uint32_t r[], uint32_t g[], uint32_t b[], uint32_t a[]); __extern int caca_set_dither_brightness(caca_dither_t *, float); __extern float caca_get_dither_brightness(caca_dither_t const *); __extern int caca_set_dither_gamma(caca_dither_t *, float); __extern float caca_get_dither_gamma(caca_dither_t const *); __extern int caca_set_dither_contrast(caca_dither_t *, float); __extern float caca_get_dither_contrast(caca_dither_t const *); __extern int caca_set_dither_antialias(caca_dither_t *, char const *); __extern char const * const * caca_get_dither_antialias_list(caca_dither_t const *); __extern char const * caca_get_dither_antialias(caca_dither_t const *); __extern int caca_set_dither_color(caca_dither_t *, char const *); __extern char const * const * caca_get_dither_color_list(caca_dither_t const *); __extern char const * caca_get_dither_color(caca_dither_t const *); __extern int caca_set_dither_charset(caca_dither_t *, char const *); __extern char const * const * caca_get_dither_charset_list(caca_dither_t const *); __extern char const * caca_get_dither_charset(caca_dither_t const *); __extern int caca_set_dither_algorithm(caca_dither_t *, char const *); __extern char const * const * caca_get_dither_algorithm_list(caca_dither_t const *); __extern char const * caca_get_dither_algorithm(caca_dither_t const *); __extern int caca_dither_bitmap(caca_canvas_t *, int, int, int, int, caca_dither_t const *, void const *); __extern int caca_free_dither(caca_dither_t *); /* @} */ /** \defgroup caca_charfont libcaca character font handling * * These functions provide character font handling routines. * * @{ */ __extern caca_charfont_t *caca_load_charfont(void const *, size_t); __extern int caca_free_charfont(caca_charfont_t *); /* @} */ /** \defgroup caca_font libcaca bitmap font handling * * These functions provide bitmap font handling routines and high quality * canvas to bitmap rendering. * * @{ */ __extern caca_font_t *caca_load_font(void const *, size_t); __extern char const * const * caca_get_font_list(void); __extern int caca_get_font_width(caca_font_t const *); __extern int caca_get_font_height(caca_font_t const *); __extern uint32_t const *caca_get_font_blocks(caca_font_t const *); __extern int caca_render_canvas(caca_canvas_t const *, caca_font_t const *, void *, int, int, int); __extern int caca_free_font(caca_font_t *); /* @} */ /** \defgroup caca_figfont libcaca FIGfont handling * * These functions provide FIGlet and TOIlet font handling routines. * * @{ */ __extern int caca_canvas_set_figfont(caca_canvas_t *, char const *); __extern int caca_set_figfont_smush(caca_canvas_t *, char const *); __extern int caca_set_figfont_width(caca_canvas_t *, int); __extern int caca_put_figchar(caca_canvas_t *, uint32_t); __extern int caca_flush_figlet(caca_canvas_t *); /* @} */ /** \defgroup caca_file libcaca file IO * * These functions allow to read and write files in a platform-independent * way. * @{ */ __extern caca_file_t *caca_file_open(char const *, const char *); __extern int caca_file_close(caca_file_t *); __extern uint32_t caca_file_tell(caca_file_t *); __extern size_t caca_file_read(caca_file_t *, void *, size_t); __extern size_t caca_file_write(caca_file_t *, const void *, size_t); __extern char * caca_file_gets(caca_file_t *, char *, int); __extern int caca_file_eof(caca_file_t *); /* @} */ /** \defgroup caca_importexport libcaca importers/exporters from/to various * formats * * These functions import various file formats into a new canvas, or export * the current canvas to various text formats. * * @{ */ __extern ssize_t caca_import_canvas_from_memory(caca_canvas_t *, void const *, size_t, char const *); __extern ssize_t caca_import_canvas_from_file(caca_canvas_t *, char const *, char const *); __extern ssize_t caca_import_area_from_memory(caca_canvas_t *, int, int, void const *, size_t, char const *); __extern ssize_t caca_import_area_from_file(caca_canvas_t *, int, int, char const *, char const *); __extern char const * const * caca_get_import_list(void); __extern void *caca_export_canvas_to_memory(caca_canvas_t const *, char const *, size_t *); __extern void *caca_export_area_to_memory(caca_canvas_t const *, int, int, int, int, char const *, size_t *); __extern char const * const * caca_get_export_list(void); /* @} */ /** \defgroup caca_display libcaca display functions * * These functions provide the basic \e libcaca routines for display * initialisation, system information retrieval and configuration. * * @{ */ __extern caca_display_t * caca_create_display(caca_canvas_t *); __extern caca_display_t * caca_create_display_with_driver(caca_canvas_t *, char const *); __extern char const * const * caca_get_display_driver_list(void); __extern char const * caca_get_display_driver(caca_display_t *); __extern int caca_set_display_driver(caca_display_t *, char const *); __extern int caca_free_display(caca_display_t *); __extern caca_canvas_t * caca_get_canvas(caca_display_t *); __extern int caca_refresh_display(caca_display_t *); __extern int caca_set_display_time(caca_display_t *, int); __extern int caca_get_display_time(caca_display_t const *); __extern int caca_get_display_width(caca_display_t const *); __extern int caca_get_display_height(caca_display_t const *); __extern int caca_set_display_title(caca_display_t *, char const *); __extern int caca_set_mouse(caca_display_t *, int); __extern int caca_set_cursor(caca_display_t *, int); /* @} */ /** \defgroup caca_event libcaca event handling * * These functions handle user events such as keyboard input and mouse * clicks. * * @{ */ __extern int caca_get_event(caca_display_t *, int, caca_event_t *, int); __extern int caca_get_mouse_x(caca_display_t const *); __extern int caca_get_mouse_y(caca_display_t const *); __extern enum caca_event_type caca_get_event_type(caca_event_t const *); __extern int caca_get_event_key_ch(caca_event_t const *); __extern uint32_t caca_get_event_key_utf32(caca_event_t const *); __extern int caca_get_event_key_utf8(caca_event_t const *, char *); __extern int caca_get_event_mouse_button(caca_event_t const *); __extern int caca_get_event_mouse_x(caca_event_t const *); __extern int caca_get_event_mouse_y(caca_event_t const *); __extern int caca_get_event_resize_width(caca_event_t const *); __extern int caca_get_event_resize_height(caca_event_t const *); /* @} */ /** \defgroup caca_process libcaca process management * * These functions help with various process handling tasks such as * option parsing, DLL injection. * * @{ */ __extern int caca_optind; __extern char *caca_optarg; __extern int caca_getopt(int, char * const[], char const *, struct caca_option const *, int *); /* @} */ /** \brief DOS colours * * This enum lists the colour values for the DOS conio.h compatibility * layer. */ enum CACA_CONIO_COLORS { CACA_CONIO_BLINK = 128, CACA_CONIO_BLACK = 0, CACA_CONIO_BLUE = 1, CACA_CONIO_GREEN = 2, CACA_CONIO_CYAN = 3, CACA_CONIO_RED = 4, CACA_CONIO_MAGENTA = 5, CACA_CONIO_BROWN = 6, CACA_CONIO_LIGHTGRAY = 7, CACA_CONIO_DARKGRAY = 8, CACA_CONIO_LIGHTBLUE = 9, CACA_CONIO_LIGHTGREEN = 10, CACA_CONIO_LIGHTCYAN = 11, CACA_CONIO_LIGHTRED = 12, CACA_CONIO_LIGHTMAGENTA = 13, CACA_CONIO_YELLOW = 14, CACA_CONIO_WHITE = 15, }; /** \brief DOS cursor modes * * This enum lists the cursor mode values for the DOS conio.h compatibility * layer. */ enum CACA_CONIO_CURSOR { CACA_CONIO__NOCURSOR = 0, CACA_CONIO__SOLIDCURSOR = 1, CACA_CONIO__NORMALCURSOR = 2, }; /** \brief DOS video modes * * This enum lists the video mode values for the DOS conio.h compatibility * layer. */ enum CACA_CONIO_MODE { CACA_CONIO_LASTMODE = -1, CACA_CONIO_BW40 = 0, CACA_CONIO_C40 = 1, CACA_CONIO_BW80 = 2, CACA_CONIO_C80 = 3, CACA_CONIO_MONO = 7, CACA_CONIO_C4350 = 64, }; /** \brief DOS text area information * * This structure stores text area information for the DOS conio.h * compatibility layer. */ struct caca_conio_text_info { unsigned char winleft; /**< left window coordinate */ unsigned char wintop; /**< top window coordinate */ unsigned char winright; /**< right window coordinate */ unsigned char winbottom; /**< bottom window coordinate */ unsigned char attribute; /**< text attribute */ unsigned char normattr; /**< normal attribute */ unsigned char currmode; /**< current video mode: BW40, BW80, C40, C80, or C4350 */ unsigned char screenheight; /**< text screen's height */ unsigned char screenwidth; /**< text screen's width */ unsigned char curx; /**< x-coordinate in current window */ unsigned char cury; /**< y-coordinate in current window */ }; /** \brief DOS direct video control */ __extern int caca_conio_directvideo; /** \brief DOS scrolling control */ __extern int caca_conio__wscroll; /** \defgroup conio libcaca DOS conio.h compatibility layer * * These functions implement DOS-like functions for high-level text * operations. * * @{ */ __extern char * caca_conio_cgets(char *str); __extern void caca_conio_clreol(void); __extern void caca_conio_clrscr(void); __extern int caca_conio_cprintf(const char *format, ...); __extern int caca_conio_cputs(const char *str); __extern int caca_conio_cscanf(char *format, ...); __extern void caca_conio_delay(unsigned int); __extern void caca_conio_delline(void); __extern int caca_conio_getch(void); __extern int caca_conio_getche(void); __extern char * caca_conio_getpass(const char *prompt); __extern int caca_conio_gettext(int left, int top, int right, int bottom, void *destin); __extern void caca_conio_gettextinfo(struct caca_conio_text_info *r); __extern void caca_conio_gotoxy(int x, int y); __extern void caca_conio_highvideo(void); __extern void caca_conio_insline(void); __extern int caca_conio_kbhit(void); __extern void caca_conio_lowvideo(void); __extern int caca_conio_movetext(int left, int top, int right, int bottom, int destleft, int desttop); __extern void caca_conio_normvideo(void); __extern void caca_conio_nosound(void); __extern int caca_conio_printf(const char *format, ...); __extern int caca_conio_putch(int ch); __extern int caca_conio_puttext(int left, int top, int right, int bottom, void *destin); __extern void caca_conio__setcursortype(int cur_t); __extern void caca_conio_sleep(unsigned int); __extern void caca_conio_sound(unsigned int); __extern void caca_conio_textattr(int newattr); __extern void caca_conio_textbackground(int newcolor); __extern void caca_conio_textcolor(int newcolor); __extern void caca_conio_textmode(int newmode); __extern int caca_conio_ungetch(int ch); __extern int caca_conio_wherex(void); __extern int caca_conio_wherey(void); __extern void caca_conio_window(int left, int top, int right, int bottom); /* @} */ #if !defined(_DOXYGEN_SKIP_ME) /* Legacy stuff from beta versions, will probably disappear in 1.0 */ # if defined __GNUC__ && __GNUC__ >= 3 # define CACA_DEPRECATED __attribute__ ((__deprecated__)) # else # define CACA_DEPRECATED # endif # if defined __GNUC__ && __GNUC__ > 3 && !defined __APPLE__ # define CACA_ALIAS(x) __attribute__ ((weak, alias(#x))) # else # define CACA_ALIAS(x) # endif # if defined __GNUC__ && __GNUC__ > 3 # define CACA_WEAK __attribute__ ((weak)) # else # define CACA_WEAK # endif /* Aliases from old libcaca functions */ #if !defined _WIN32 || !defined __GNUC__ __extern ssize_t caca_import_memory(caca_canvas_t *, void const *, size_t, char const *) CACA_DEPRECATED; __extern ssize_t caca_import_file(caca_canvas_t *, char const *, char const *) CACA_DEPRECATED; __extern void *caca_export_memory(caca_canvas_t const *, char const *, size_t *) CACA_DEPRECATED; #endif # if !defined __LIBCACA__ # define caca_get_cursor_x caca_wherex # define caca_get_cursor_y caca_wherey # endif #endif #ifdef __cplusplus } #endif #undef __extern #endif /* __CACA_H__ */ ================================================ FILE: cgadraw/cacaint.h ================================================ /* * libcaca Colour ASCII-Art library * Copyright (c) 2002-2012 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ #ifndef __CACA_INTERNALS_H__ #define __CACA_INTERNALS_H__ #include #include "cacastub.h" typedef struct caca_timer caca_timer_t; typedef struct caca_privevent caca_privevent_t; #if !defined(_DOXYGEN_SKIP_ME) # define STAT_VALUES 32 # define EVENTBUF_LEN 10 # define MAX_DIRTY_COUNT 8 #endif struct cgachar { uint8_t c; uint8_t attr; }; struct caca_frame { /* Frame size */ int width, height; /* Cell information */ //uint32_t *chars; //uint32_t *attrs; struct cgachar far *c; /* Painting context */ int x, y; int handlex, handley; uint32_t curattr; /* Frame name */ char *name; }; struct caca_canvas { /* XXX: look at caca_set_canvas_boundaries() before adding anything * to this structure. The function is quite hacky. */ /* Frame information */ int frame, framecount; struct caca_frame *frames; /* Canvas management */ int refcount; int autoinc; int (*resize_callback)(void *); void *resize_data; /* Dirty rectangles */ int ndirty, dirty_disabled; struct { int xmin, ymin, xmax, ymax; } dirty[MAX_DIRTY_COUNT + 1]; /* Shortcut to the active frame information */ int width, height; //uint32_t *chars; //uint32_t *attrs; struct cgachar far *c; uint32_t curattr; /* FIGfont management */ caca_charfont_t *ff; }; /* Graphics driver */ enum caca_driver { CACA_DRIVER_NULL = 0, CACA_DRIVER_RAW = 1, #if defined(USE_COCOA) CACA_DRIVER_COCOA = 2, #endif #if defined(USE_CONIO) CACA_DRIVER_CONIO = 3, #endif #if defined(USE_GL) CACA_DRIVER_GL = 4, #endif #if defined(USE_NCURSES) CACA_DRIVER_NCURSES = 5, #endif #if defined(USE_SLANG) CACA_DRIVER_SLANG = 6, #endif #if defined(USE_VGA) CACA_DRIVER_VGA = 7, #endif #if defined(USE_WIN32) CACA_DRIVER_WIN32 = 8, #endif #if defined(USE_X11) CACA_DRIVER_X11 = 9, #endif }; /* Available external drivers */ #if defined(USE_COCOA) int cocoa_install(caca_display_t *); #endif #if defined(USE_CONIO) int conio_install(caca_display_t *); #endif #if defined(USE_GL) int gl_install(caca_display_t *); #endif #if defined(USE_NCURSES) int ncurses_install(caca_display_t *); #endif int null_install(caca_display_t *); int raw_install(caca_display_t *); #if defined(USE_SLANG) int slang_install(caca_display_t *); #endif #if defined(USE_VGA) int vga_install(caca_display_t *); #endif #if defined(USE_WIN32) int win32_install(caca_display_t *); #endif #if defined(USE_X11) int x11_install(caca_display_t *); #endif /* Timer structure */ struct caca_timer { int last_sec, last_usec; }; /* Statistic structure for profiling */ struct caca_stat { int itable[STAT_VALUES]; int32_t imean; char *name; }; /* Private event structure */ struct caca_privevent { enum caca_event_type type; union { struct { int x, y, button; } mouse; struct { int w, h; } resize; struct { int ch; uint32_t utf32; char utf8[8]; } key; } data; }; /* Internal caca display context */ struct caca_display { /* A link to our caca canvas */ caca_canvas_t *cv; int autorelease; #if defined(USE_PLUGINS) void *plugin; #endif /* Device-specific functions */ struct drv { char const * driver; enum caca_driver id; struct driver_private *p; int (* init_graphics) (caca_display_t *); int (* end_graphics) (caca_display_t *); int (* set_display_title) (caca_display_t *, char const *); int (* get_display_width) (caca_display_t const *); int (* get_display_height) (caca_display_t const *); void (* display) (caca_display_t *); void (* handle_resize) (caca_display_t *); int (* get_event) (caca_display_t *, caca_privevent_t *); void (* set_mouse) (caca_display_t *, int); void (* set_cursor) (caca_display_t *, int); } drv; /* Mouse position */ struct mouse { int x, y; } mouse; /* Window resize handling */ struct resize { int resized; /* A resize event was requested */ int allow; /* The display driver allows resizing */ int w, h; /* Requested width and height */ } resize; /* Framerate handling */ int delay, rendertime; caca_timer_t timer; #if defined PROF struct caca_stat display_stat, wait_stat; struct caca_stat ev_sys_stat, ev_wait_stat; #endif int lastticks; struct events { #if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL) caca_privevent_t buf[EVENTBUF_LEN]; int queue; #endif #if defined(USE_SLANG) || defined(USE_NCURSES) caca_timer_t key_timer; int last_key_ticks; int autorepeat_ticks; caca_privevent_t last_key_event; #endif uint8_t not_empty_struct; } events; }; /* Dirty rectangle functions */ extern void _caca_clip_dirty_rect_list(caca_canvas_t *); /* Colour functions */ extern uint32_t _caca_attr_to_rgb24fg(uint32_t); extern uint32_t _caca_attr_to_rgb24bg(uint32_t); /* Frames functions */ extern void _caca_save_frame_info(caca_canvas_t *); extern void _caca_load_frame_info(caca_canvas_t *); /* Internal timer functions */ extern void _caca_sleep(int); extern int _caca_getticks(caca_timer_t *); /* Internal event functions */ extern void _caca_handle_resize(caca_display_t *); #if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL) extern void _push_event(caca_display_t *, caca_privevent_t *); extern int _pop_event(caca_display_t *, caca_privevent_t *); #endif /* Internal window functions */ extern void _caca_set_term_title(char const *); /* Profiling functions */ #if defined PROF extern void _caca_dump_stats(void); extern void _caca_init_stat(struct caca_stat *, char const *, ...); extern void _caca_fini_stat(struct caca_stat *); #endif #endif /* __CACA_INTERNALS_H__ */ ================================================ FILE: cgadraw/cacastub.h ================================================ /* * libcaca Colour ASCII-Art library * Copyright (c) 2006-2012 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /* * This file contains replacements for commonly found object types and * function prototypes that are sometimes missing. */ #ifndef __CACA_STUBS_H__ #define __CACA_STUBS_H__ /* errno handling */ #if defined HAVE_ERRNO_H && !defined __KERNEL__ # include static inline void seterrno(int e) { errno = e; } static inline int geterrno(void) { return errno; } #else # define seterrno(x) do { (void)(x); } while(0) # define geterrno(x) 0 #endif /* hton16() and hton32() */ #if defined HAVE_HTONS && !defined __KERNEL__ # if defined HAVE_ARPA_INET_H # include # elif defined HAVE_NETINET_IN_H # include # endif # define hton16 htons # define hton32 htonl #else # if defined __KERNEL__ /* Nothing to do */ # elif defined HAVE_ENDIAN_H # include # endif static inline uint16_t hton16(uint16_t x) { /* This is compile-time optimised with at least -O1 or -Os */ #if defined HAVE_ENDIAN_H if(__BYTE_ORDER == __BIG_ENDIAN) #else uint32_t const dummy = 0x12345678; if(*(uint8_t const *)&dummy == 0x12) #endif return x; else return (x >> 8) | (x << 8); } static inline uint32_t hton32(uint32_t x) { /* This is compile-time optimised with at least -O1 or -Os */ #if defined HAVE_ENDIAN_H if(__BYTE_ORDER == __BIG_ENDIAN) #else uint32_t const dummy = 0x12345678; if(*(uint8_t const *)&dummy == 0x12) #endif return x; else return (x >> 24) | ((x >> 8) & 0x0000ff00) | ((x << 8) & 0x00ff0000) | (x << 24); } #endif #endif /* __CACA_STUBS_H__ */ ================================================ FILE: cgadraw/cacatyp.h ================================================ /* * libcaca Colour ASCII-Art library * Copyright (c) 2008-2012 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /* * This file contains definitions for required C99 integer types. */ #ifndef __CACA_TYPES_H__ #define __CACA_TYPES_H__ typedef signed char _caca_int8_t; typedef signed short _caca_int16_t; typedef signed long int _caca_int32_t; # undef int8_t # define int8_t _caca_int8_t # undef int16_t # define int16_t _caca_int16_t # undef int32_t # define int32_t _caca_int32_t typedef unsigned char _caca_uint8_t; typedef unsigned short _caca_uint16_t; typedef unsigned long int _caca_uint32_t; # undef uint8_t # define uint8_t _caca_uint8_t # undef uint16_t # define uint16_t _caca_uint16_t # undef uint32_t # define uint32_t _caca_uint32_t typedef long int _caca_intptr_t; typedef unsigned long int _caca_uintptr_t; # undef intptr_t # define intptr_t _caca_intptr_t # undef uintptr_t # define uintptr_t _caca_uintptr_t typedef int _caca_ssize_t; typedef unsigned int _caca_size_t; # undef ssize_t # define ssize_t _caca_ssize_t # undef size_t # define size_t _caca_size_t #endif __CACA_TYPES_H__ ================================================ FILE: cgadraw/canvas.c ================================================ // // This file is derived from libcaca, but modified to add simple drawing // primitives to a CGA framebuffer for Lotus 1-2-3. // // The original copyright notice is below. /* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ #include "config.h" #include #include #include #include "caca.h" #include "cacaint.h" caca_canvas_t * far caca_create_canvas(int width, int height, int sel, int off) { static caca_canvas_t canvas; static struct caca_frame frame; caca_canvas_t * far cv; if (canvas.refcount != 0) return NULL; if (width < 0 || height < 0) { seterrno(EINVAL); return NULL; } cv = &canvas; cv->refcount++; cv->autoinc = 0; cv->resize_callback = NULL; cv->resize_data = NULL; cv->frame = 0; cv->framecount = 1; cv->frames = &frame; cv->frames[0].width = width; cv->frames[0].height = height; cv->frames[0].c = MK_FP(sel, off); cv->frames[0].x = cv->frames[0].y = 0; cv->frames[0].handlex = cv->frames[0].handley = 0; cv->frames[0].curattr = 0; cv->frames[0].name = "frame#00000000"; _caca_load_frame_info(cv); caca_set_color_ansi(cv, CACA_DEFAULT, CACA_TRANSPARENT); cv->ndirty = 0; cv->dirty_disabled = 1; cv->ff = NULL; return cv; } int caca_manage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p) { return -1; } int caca_unmanage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p) { return -1; } int caca_set_canvas_size(caca_canvas_t *cv, int width, int height) { return -1; } int caca_get_canvas_width(caca_canvas_t const *cv) { return cv->width; } int caca_get_canvas_height(caca_canvas_t const *cv) { return cv->height; } uint32_t const * caca_get_canvas_chars(caca_canvas_t const *cv) { return NULL; } uint32_t const * caca_get_canvas_attrs(caca_canvas_t const *cv) { return NULL; } int caca_free_canvas(caca_canvas_t *cv) { cv->refcount--; return 0; } int caca_rand(int min, int max) { return min; } int caca_resize(caca_canvas_t *cv, int width, int height) { return -1; } ================================================ FILE: cgadraw/cgadraw.c ================================================ #include "config.h" #include "caca.h" #include "cacaint.h" char const * caca_get_version(void) { return "cgadraw"; } ================================================ FILE: cgadraw/charset.c ================================================ /* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * 2007 Ben Wiley Sittler * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /* * This file contains functions for converting characters between * various character sets. */ #include "config.h" #if !defined(__KERNEL__) # include #endif #include "caca.h" #include "cacaint.h" /* * UTF-8 handling */ static uint8_t const trailing[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; static uint32_t const offsets[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; /* * CP437 handling */ static uint32_t const cp437_lookup1[] = { /* 0x01 - 0x0f: ☺ ☻ ♥ ♦ ♣ ♠ • ◘ ○ ◙ ♂ ♀ ♪ ♫ ☼ */ 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c, /* 0x10 - 0x1f: ► ◄ ↕ ‼ ¶ § ▬ ↨ ↑ ↓ → ← ∟ ↔ ▲ ▼ */ 0x25ba, 0x25c4, 0x2195, 0x203c, 0xb6, 0xa7, 0x25ac, 0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc }; static uint32_t const cp437_lookup2[] = { /* 0x7f: ⌂ */ 0x2302, /* 0x80 - 0x8f: Ç ü é â ä à å ç ê ë è ï î ì Ä Å */ 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, /* 0x90 - 0x9f: É æ Æ ô ö ò û ù ÿ Ö Ü ¢ £ ¥ ₧ ƒ */ 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, 0xff, 0xd6, 0xdc, 0xa2, 0xa3, 0xa5, 0x20a7, 0x192, /* 0xa0 - 0xaf: á í ó ú ñ Ñ ª º ¿ ⌐ ¬ ½ ¼ ¡ « » */ 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, 0xbf, 0x2310, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, /* 0xb0 - 0xbf: ░ ▒ ▓ │ ┤ ╡ ╢ ╖ ╕ ╣ ║ ╗ ╝ ╜ ╛ ┐ */ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, /* 0xc0 - 0xcf: └ ┴ ┬ ├ ─ ┼ ╞ ╟ ╚ ╔ ╩ ╦ ╠ ═ ╬ ╧ */ 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, /* 0xd0 - 0xdf: ╨ ╤ ╥ ╙ ╘ ╒ ╓ ╫ ╪ ┘ ┌ █ ▄ ▌ ▐ ▀ */ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, /* 0xe0 - 0xef: α ß Γ π Σ σ µ τ Φ Θ Ω δ ∞ φ ε ∩ */ 0x3b1, 0xdf, 0x393, 0x3c0, 0x3a3, 0x3c3, 0xb5, 0x3c4, 0x3a6, 0x398, 0x3a9, 0x3b4, 0x221e, 0x3c6, 0x3b5, 0x2229, /* 0xf0 - 0xff: ≡ ± ≥ ≤ ⌠ ⌡ ÷ ≈ ° ∙ · √ ⁿ ² ■ */ 0x2261, 0xb1, 0x2265, 0x2264, 0x2320, 0x2321, 0xf7, 0x2248, 0xb0, 0x2219, 0xb7, 0x221a, 0x207f, 0xb2, 0x25a0, 0xa0 }; /** \brief Convert a UTF-8 character to UTF-32. * * Convert a UTF-8 character read from a string and return its value in * the UTF-32 character set. If the second argument is not null, the total * number of read bytes is written in it. * * If a null byte was reached before the expected end of the UTF-8 sequence, * this function returns zero and the number of read bytes is set to zero. * * This function never fails, but its behaviour with illegal UTF-8 sequences * is undefined. * * \param s A string containing the UTF-8 character. * \param bytes A pointer to a size_t to store the number of bytes in the * character, or NULL. * \return The corresponding UTF-32 character, or zero if the character * is incomplete. */ uint32_t caca_utf8_to_utf32(char const *s, size_t *bytes) { int todo = trailing[(int)(unsigned char)*s]; int i = 0; uint32_t ret = 0; for(;;) { if(!*s) { if(bytes) *bytes = 0; return 0; } ret += ((uint32_t)(unsigned char)*s++) << (6 * (todo - i)); if(todo == i++) { if(bytes) *bytes = i; return ret - offsets[todo]; } } } /** \brief Convert a UTF-32 character to UTF-8. * * Convert a UTF-32 character read from a string and write its value in * the UTF-8 character set into the given buffer. * * This function never fails, but its behaviour with illegal UTF-32 characters * is undefined. * * \param buf A pointer to a character buffer where the UTF-8 sequence will * be written. * \param ch The UTF-32 character. * \return The number of bytes written. */ size_t caca_utf32_to_utf8(char *buf, uint32_t ch) { static const uint8_t mark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; char *parser = buf; size_t bytes; if(ch < 0x80) { *parser++ = ch; return 1; } bytes = (ch < 0x800) ? 2 : (ch < 0x10000) ? 3 : 4; parser += bytes; switch(bytes) { case 4: *--parser = (ch | 0x80) & 0xbf; ch >>= 6; case 3: *--parser = (ch | 0x80) & 0xbf; ch >>= 6; case 2: *--parser = (ch | 0x80) & 0xbf; ch >>= 6; } *--parser = ch | mark[bytes]; return bytes; } /** \brief Convert a UTF-32 character to CP437. * * Convert a UTF-32 character read from a string and return its value in * the CP437 character set, or "?" if the character has no equivalent. * * This function never fails. * * \param ch The UTF-32 character. * \return The corresponding CP437 character, or "?" if not representable. */ uint8_t caca_utf32_to_cp437(uint32_t ch) { unsigned int i; if(ch < 0x00000020) return '?'; if(ch < 0x00000080) return ch; for(i = 0; i < sizeof(cp437_lookup1) / sizeof(*cp437_lookup1); i++) if(cp437_lookup1[i] == ch) return 0x01 + i; for(i = 0; i < sizeof(cp437_lookup2) / sizeof(*cp437_lookup2); i++) if(cp437_lookup2[i] == ch) return 0x7f + i; return '?'; } /** \brief Convert a CP437 character to UTF-32. * * Convert a CP437 character read from a string and return its value in * the UTF-32 character set, or zero if the character is a CP437 control * character. * * This function never fails. * * \param ch The CP437 character. * \return The corresponding UTF-32 character, or zero if not representable. */ uint32_t caca_cp437_to_utf32(uint8_t ch) { if(ch > 0x7f) return cp437_lookup2[ch - 0x7f]; if(ch >= 0x20) return (uint32_t)ch; if(ch > 0) return cp437_lookup1[ch - 0x01]; return 0x00000000; } /** \brief Convert a UTF-32 character to ASCII. * * Convert a UTF-32 character into an ASCII character. When no equivalent * exists, a graphically close equivalent is sought. * * This function never fails, but its behaviour with illegal UTF-32 characters * is undefined. * * \param ch The UTF-32 character. * \return The corresponding ASCII character, or a graphically close * equivalent if found, or "?" if not representable. */ char caca_utf32_to_ascii(uint32_t ch) { /* Standard ASCII */ if(ch < 0x80) return ch; /* Fullwidth Forms */ if(ch > 0x0000ff00 && ch < 0x0000ff5f) return ' ' + (ch - 0x0000ff00); switch (ch) { case 0x000000a0: /*   (nbsp) */ case 0x00003000: /*   (ideographic space) */ return ' '; case 0x000000a3: /* £ */ return 'f'; case 0x000000b0: /* ° */ return '\''; case 0x000000b1: /* ± */ return '#'; case 0x000000b7: /* · */ case 0x00002219: /* ∙ */ case 0x000030fb: /* ・ */ return '.'; case 0x000003c0: /* π */ return '*'; case 0x00002018: /* ‘ */ case 0x00002019: /* ’ */ return '\''; case 0x0000201c: /* “ */ case 0x0000201d: /* ” */ return '"'; case 0x00002190: /* ← */ return '<'; case 0x00002191: /* ↑ */ return '^'; case 0x00002192: /* → */ return '>'; case 0x00002193: /* ↓ */ return 'v'; case 0x00002260: /* ≠ */ return '!'; case 0x00002261: /* ≡ */ return '='; case 0x00002264: /* ≤ */ return '<'; case 0x00002265: /* ≥ */ return '>'; case 0x000023ba: /* ⎺ */ case 0x000023bb: /* ⎻ */ case 0x000023bc: /* ⎼ */ case 0x000023bd: /* ⎽ */ case 0x00002500: /* ─ */ case 0x00002550: /* ═ */ return '-'; case 0x00002502: /* │ */ case 0x00002551: /* ║ */ return '|'; case 0x0000250c: /* ┌ */ case 0x00002552: /* ╒ */ case 0x00002553: /* ╓ */ case 0x00002554: /* ╔ */ case 0x00002514: /* └ */ case 0x00002558: /* ╘ */ case 0x00002559: /* ╙ */ case 0x0000255a: /* ╚ */ case 0x0000251c: /* ├ */ case 0x0000255e: /* ╞ */ case 0x0000255f: /* ╟ */ case 0x00002560: /* ╠ */ case 0x0000252c: /* ┬ */ case 0x00002564: /* ╤ */ case 0x00002565: /* ╥ */ case 0x00002566: /* ╦ */ case 0x00002534: /* ┴ */ case 0x00002567: /* ╧ */ case 0x00002568: /* ╨ */ case 0x00002569: /* ╩ */ case 0x0000253c: /* ┼ */ case 0x0000256a: /* ╪ */ case 0x0000256b: /* ╫ */ case 0x0000256c: /* ╬ */ return '+'; case 0x00002510: /* ┐ */ case 0x00002555: /* ╕ */ case 0x00002556: /* ╖ */ case 0x00002557: /* ╗ */ case 0x00002518: /* ┘ */ case 0x0000255b: /* ╛ */ case 0x0000255c: /* ╜ */ case 0x0000255d: /* ╝ */ case 0x00002524: /* ┤ */ case 0x00002561: /* ╡ */ case 0x00002562: /* ╢ */ case 0x00002563: /* ╣ */ return '+'; case 0x00002591: /* ░ */ case 0x00002592: /* ▒ */ case 0x00002593: /* ▓ */ case 0x00002588: /* █ */ case 0x0000258c: /* ▌ */ case 0x00002590: /* ▐ */ case 0x000025a0: /* ■ */ case 0x000025ac: /* ▬ */ case 0x000025ae: /* ▮ */ return '#'; case 0x00002580: /* ▀ */ return '"'; case 0x00002584: /* ▄ */ return ','; case 0x000025c6: /* ◆ */ case 0x00002666: /* ♦ */ return '+'; case 0x00002022: /* • */ case 0x000025cb: /* ○ */ case 0x000025cf: /* ● */ case 0x00002603: /* ☃ */ case 0x0000263c: /* ☼ */ return 'o'; case 0x0000301c: /* 〜 */ return '~'; } return '?'; } /** \brief Tell whether a UTF-32 character is fullwidth. * * Check whether the given UTF-32 character should be printed at twice * the normal width (fullwidth characters). If the character is unknown * or if its status cannot be decided, it is treated as a standard-width * character. * * This function never fails. * * \param ch The UTF-32 character. * \return 1 if the character is fullwidth, 0 otherwise. */ int caca_utf32_is_fullwidth(uint32_t ch) { if(ch < 0x2e80) /* Standard stuff */ return 0; if(ch < 0xa700) /* Japanese, Korean, CJK, Yi... */ return 1; if(ch < 0xac00) /* Modified Tone Letters, Syloti Nagri */ return 0; if(ch < 0xd800) /* Hangul Syllables */ return 1; if(ch < 0xf900) /* Misc crap */ return 0; if(ch < 0xfb00) /* More CJK */ return 1; if(ch < 0xfe20) /* Misc crap */ return 0; if(ch < 0xfe70) /* More CJK */ return 1; if(ch < 0xff00) /* Misc crap */ return 0; if(ch < 0xff61) /* Fullwidth forms */ return 1; if(ch < 0xffe0) /* Halfwidth forms */ return 0; if(ch < 0xffe8) /* More fullwidth forms */ return 1; if(ch < 0x20000) /* Misc crap */ return 0; if(ch < 0xe0000) /* More CJK */ return 1; return 0; } ================================================ FILE: cgadraw/codec.h ================================================ /* * libcaca Colour ASCII-Art library * Copyright (c) 2002-2014 Sam Hocevar * 2006 Jean-Yves Lamoureux * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ ssize_t _import_text(caca_canvas_t *, void const *, size_t); ssize_t _import_ansi(caca_canvas_t *, void const *, size_t, int); ssize_t _import_bin(caca_canvas_t *, void const *, size_t); void *_export_ansi(caca_canvas_t const *, size_t *); void *_export_plain(caca_canvas_t const *, size_t *); void *_export_utf8(caca_canvas_t const *, size_t *, int); void *_export_irc(caca_canvas_t const *, size_t *); ================================================ FILE: cgadraw/config.h ================================================ #define inline #define EOVERFLOW E2BIG #define ENOSYS EINVAL #define HAVE_VSNPRINTF 1 #define __KERNEL__ #include #define abs(x) (((x) < 0) ? (x) * -1 : (x)) #define snprintf portable_snprintf #define vsnprintf portable_vsnprintf ================================================ FILE: cgadraw/conic.c ================================================ /* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /* * This file contains ellipse and circle drawing functions, both filled * and outline. */ #include "config.h" #if !defined(__KERNEL__) # include #endif #include "caca.h" #include "cacaint.h" static void ellipsepoints(caca_canvas_t *, int, int, int, int, uint32_t, int); /** \brief Draw a circle on the canvas using the given character. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x Center X coordinate. * \param y Center Y coordinate. * \param r Circle radius. * \param ch UTF-32 character to be used to draw the circle outline. * \return This function always returns 0. */ int caca_draw_circle(caca_canvas_t *cv, int x, int y, int r, uint32_t ch) { int test, dx, dy; /* Optimized Bresenham. Kick ass. */ for(test = 0, dx = 0, dy = r ; dx <= dy ; dx++) { ellipsepoints(cv, x, y, dx, dy, ch, 1); ellipsepoints(cv, x, y, dy, dx, ch, 1); test += test > 0 ? dx - dy-- : dx; } return 0; } /** \brief Fill an ellipse on the canvas using the given character. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param xo Center X coordinate. * \param yo Center Y coordinate. * \param a Ellipse X radius. * \param b Ellipse Y radius. * \param ch UTF-32 character to be used to fill the ellipse. * \return This function always returns 0. */ int caca_fill_ellipse(caca_canvas_t *cv, int xo, int yo, int a, int b, uint32_t ch) { int d2; int x = 0; int y = b; int d1 = b*b - (a*a*b) + (a*a/4); while(a*a*y - a*a/2 > b*b*(x+1)) { if(d1 < 0) { d1 += b*b*(2*x+1); /* XXX: "Computer Graphics" has + 3 here. */ } else { d1 += b*b*(2*x*1) + a*a*(-2*y+2); caca_draw_line(cv, xo - x, yo - y, xo + x, yo - y, ch); caca_draw_line(cv, xo - x, yo + y, xo + x, yo + y, ch); y--; } x++; } caca_draw_line(cv, xo - x, yo - y, xo + x, yo - y, ch); caca_draw_line(cv, xo - x, yo + y, xo + x, yo + y, ch); d2 = b*b*(x+0.5)*(x+0.5) + a*a*(y-1)*(y-1) - a*a*b*b; while(y > 0) { if(d2 < 0) { d2 += b*b*(2*x+2) + a*a*(-2*y+3); x++; } else { d2 += a*a*(-2*y+3); } y--; caca_draw_line(cv, xo - x, yo - y, xo + x, yo - y, ch); caca_draw_line(cv, xo - x, yo + y, xo + x, yo + y, ch); } return 0; } /** \brief Draw an ellipse on the canvas using the given character. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param xo Center X coordinate. * \param yo Center Y coordinate. * \param a Ellipse X radius. * \param b Ellipse Y radius. * \param ch UTF-32 character to be used to draw the ellipse outline. * \return This function always returns 0. */ int caca_draw_ellipse(caca_canvas_t *cv, int xo, int yo, int a, int b, uint32_t ch) { int d2; int x = 0; int y = b; int d1 = b*b - (a*a*b) + (a*a/4); ellipsepoints(cv, xo, yo, x, y, ch, 0); while(a*a*y - a*a/2 > b*b*(x+1)) { if(d1 < 0) { d1 += b*b*(2*x+1); /* XXX: "Computer Graphics" has + 3 here. */ } else { d1 += b*b*(2*x*1) + a*a*(-2*y+2); y--; } x++; ellipsepoints(cv, xo, yo, x, y, ch, 0); } d2 = b*b*(x+0.5)*(x+0.5) + a*a*(y-1)*(y-1) - a*a*b*b; while(y > 0) { if(d2 < 0) { d2 += b*b*(2*x+2) + a*a*(-2*y+3); x++; } else { d2 += a*a*(-2*y+3); } y--; ellipsepoints(cv, xo, yo, x, y, ch, 0); } return 0; } /** \brief Draw a thin ellipse on the canvas. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param xo Center X coordinate. * \param yo Center Y coordinate. * \param a Ellipse X radius. * \param b Ellipse Y radius. * \return This function always returns 0. */ int caca_draw_thin_ellipse(caca_canvas_t *cv, int xo, int yo, int a, int b) { /* FIXME: this is not correct */ int d2; int x = 0; int y = b; int d1 = b*b - (a*a*b) + (a*a/4); ellipsepoints(cv, xo, yo, x, y, '-', 1); while(a*a*y - a*a/2 > b*b*(x+1)) { if(d1 < 0) { d1 += b*b*(2*x+1); /* XXX: "Computer Graphics" has + 3 here. */ ellipsepoints(cv, xo, yo, x + 1, y, '0', 1); } else { d1 += b*b*(2*x*1) + a*a*(-2*y+2); y--; ellipsepoints(cv, xo, yo, x + 1, y, '1', 1); } x++; } d2 = b*b*(x+0.5)*(x+0.5) + a*a*(y-1)*(y-1) - a*a*b*b; while(y > 0) { if(d2 < 0) { d2 += b*b*(2*x+2) + a*a*(-2*y+3); x++; ellipsepoints(cv, xo, yo, x , y - 1, '2', 1); } else { d2 += a*a*(-2*y+3); ellipsepoints(cv, xo, yo, x , y - 1, '3', 1); } y--; } return 0; } static void ellipsepoints(caca_canvas_t *cv, int xo, int yo, int x, int y, uint32_t ch, int thin) { uint8_t b = 0; if(xo + x >= 0 && xo + x < (int)cv->width) b |= 0x1; if(xo - x >= 0 && xo - x < (int)cv->width) b |= 0x2; if(yo + y >= 0 && yo + y < (int)cv->height) b |= 0x4; if(yo - y >= 0 && yo - y < (int)cv->height) b |= 0x8; if((b & (0x1|0x4)) == (0x1|0x4)) { uint32_t c = ch; if(thin) { switch(c) { case '0': c = '-'; break; case '1': c = ','; break; case '2': c = '/'; break; case '3': c = '|'; break; } } caca_put_char(cv, xo + x, yo + y, c); } if((b & (0x2|0x4)) == (0x2|0x4)) { uint32_t c = ch; if(thin) { switch(c) { case '0': c = '-'; break; case '1': c = '.'; break; case '2': c = '\\'; break; case '3': c = '|'; break; } } caca_put_char(cv, xo - x, yo + y, c); } if((b & (0x1|0x8)) == (0x1|0x8)) { uint32_t c = ch; if(thin) { switch(c) { case '0': c = '-'; break; case '1': c = '`'; break; case '2': c = '\\'; break; case '3': c = '|'; break; } } caca_put_char(cv, xo + x, yo - y, c); } if((b & (0x2|0x8)) == (0x2|0x8)) { uint32_t c = ch; if(thin) { switch(c) { case '0': c = '-'; break; case '1': c = '\''; break; case '2': c = '/'; break; case '3': c = '|'; break; } } caca_put_char(cv, xo - x, yo - y, c); } } ================================================ FILE: cgadraw/drawtest.c ================================================ #include #include #include #include #include #include #include "config.h" #include "caca.h" int __cdecl main(int argc, char *argv[]) { caca_canvas_t *cv; cv = caca_create_canvas(100, 30, 0xB800, 0); caca_draw_thin_line(cv, 10, 10, 80, 25); caca_draw_cp437_box(cv, 20, 5, 20, 10); caca_fill_ellipse(cv, 80, 8, 5, 5, '#'); return 0; } ================================================ FILE: cgadraw/frame.c ================================================ #include "config.h" #include "caca.h" #include "cacaint.h" int caca_get_frame_count(caca_canvas_t const *cv) { return cv->framecount; } int caca_set_frame(caca_canvas_t *cv, int id) { if(id < 0 || id >= cv->framecount) { seterrno(EINVAL); return -1; } if(id != cv->frame) return -1; return 0; } char const *caca_get_frame_name(caca_canvas_t const *cv) { return cv->frames[cv->frame].name; } int caca_set_frame_name(caca_canvas_t *cv, char const *name) { return -1; } int caca_create_frame(caca_canvas_t *cv, int id) { return -1; } int caca_free_frame(caca_canvas_t *cv, int id) { int f; if(id < 0 || id >= cv->framecount) { seterrno(EINVAL); return -1; } if(cv->framecount == 1) { seterrno(EINVAL); return -1; } return 0; } void _caca_save_frame_info(caca_canvas_t *cv) { cv->frames[cv->frame].width = cv->width; cv->frames[cv->frame].height = cv->height; cv->frames[cv->frame].curattr = cv->curattr; } void _caca_load_frame_info(caca_canvas_t *cv) { cv->width = cv->frames[cv->frame].width; cv->height = cv->frames[cv->frame].height; cv->c = cv->frames[cv->frame].c; cv->curattr = cv->frames[cv->frame].curattr; } ================================================ FILE: cgadraw/line.c ================================================ /* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /* * This file contains line and polyline drawing functions, with both thin * and thick styles. */ #include "config.h" #include #if !defined(__KERNEL__) # include #endif #include "caca.h" #include "cacaint.h" #if !defined(_DOXYGEN_SKIP_ME) struct line { int x1, y1; int x2, y2; uint32_t ch; void (far *draw) (caca_canvas_t *, struct line*); }; #endif static void clip_line(caca_canvas_t*, struct line*); static uint8_t clip_bits(caca_canvas_t*, int, int); static void draw_solid_line(caca_canvas_t*, struct line*); static void draw_thin_line(caca_canvas_t*, struct line*); /** \brief Draw a line on the canvas using the given character. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x1 X coordinate of the first point. * \param y1 Y coordinate of the first point. * \param x2 X coordinate of the second point. * \param y2 Y coordinate of the second point. * \param ch UTF-32 character to be used to draw the line. * \return This function always returns 0. */ int caca_draw_line(caca_canvas_t *cv, int x1, int y1, int x2, int y2, uint32_t ch) { struct line s; s.x1 = x1; s.y1 = y1; s.x2 = x2; s.y2 = y2; s.ch = ch; s.draw = draw_solid_line; clip_line(cv, &s); return 0; } /** \brief Draw a polyline. * * Draw a polyline on the canvas using the given character and coordinate * arrays. The first and last points are not connected, hence in order to * draw a polygon you need to specify the starting point at the end of the * list as well. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x Array of X coordinates. Must have \p n + 1 elements. * \param y Array of Y coordinates. Must have \p n + 1 elements. * \param n Number of lines to draw. * \param ch UTF-32 character to be used to draw the lines. * \return This function always returns 0. */ int caca_draw_polyline(caca_canvas_t *cv, int const x[], int const y[], int n, uint32_t ch) { int i; struct line s; s.ch = ch; s.draw = draw_solid_line; for(i = 0; i < n; i++) { s.x1 = x[i]; s.y1 = y[i]; s.x2 = x[i+1]; s.y2 = y[i+1]; clip_line(cv, &s); } return 0; } /** \brief Draw a thin line on the canvas, using ASCII art. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x1 X coordinate of the first point. * \param y1 Y coordinate of the first point. * \param x2 X coordinate of the second point. * \param y2 Y coordinate of the second point. * \return This function always returns 0. */ int caca_draw_thin_line(caca_canvas_t *cv, int x1, int y1, int x2, int y2) { struct line s; s.x1 = x1; s.y1 = y1; s.x2 = x2; s.y2 = y2; s.draw = draw_thin_line; clip_line(cv, &s); return 0; } /** \brief Draw an ASCII art thin polyline. * * Draw a thin polyline on the canvas using the given coordinate arrays and * with ASCII art. The first and last points are not connected, so in order * to draw a polygon you need to specify the starting point at the end of * the list as well. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x Array of X coordinates. Must have \p n + 1 elements. * \param y Array of Y coordinates. Must have \p n + 1 elements. * \param n Number of lines to draw. * \return This function always returns 0. */ int caca_draw_thin_polyline(caca_canvas_t *cv, int const x[], int const y[], int n) { int i; struct line s; s.draw = draw_thin_line; for(i = 0; i < n; i++) { s.x1 = x[i]; s.y1 = y[i]; s.x2 = x[i+1]; s.y2 = y[i+1]; clip_line(cv, &s); } return 0; } /* * XXX: The following functions are local. */ /* Generic Cohen-Sutherland line clipping function. */ static void clip_line(caca_canvas_t *cv, struct line* s) { uint8_t bits1, bits2; bits1 = clip_bits(cv, s->x1, s->y1); bits2 = clip_bits(cv, s->x2, s->y2); if(bits1 & bits2) return; if(bits1 == 0) { if(bits2 == 0) s->draw(cv, s); else { int tmp; tmp = s->x1; s->x1 = s->x2; s->x2 = tmp; tmp = s->y1; s->y1 = s->y2; s->y2 = tmp; clip_line(cv, s); } return; } if(bits1 & (1<<0)) { s->y1 = s->y2 - (s->x2 - 0) * (s->y2 - s->y1) / (s->x2 - s->x1); s->x1 = 0; } else if(bits1 & (1<<1)) { int xmax = cv->width - 1; s->y1 = s->y2 - (s->x2 - xmax) * (s->y2 - s->y1) / (s->x2 - s->x1); s->x1 = xmax; } else if(bits1 & (1<<2)) { s->x1 = s->x2 - (s->y2 - 0) * (s->x2 - s->x1) / (s->y2 - s->y1); s->y1 = 0; } else if(bits1 & (1<<3)) { int ymax = cv->height - 1; s->x1 = s->x2 - (s->y2 - ymax) * (s->x2 - s->x1) / (s->y2 - s->y1); s->y1 = ymax; } clip_line(cv, s); } /* Helper function for clip_line(). */ static uint8_t clip_bits(caca_canvas_t *cv, int x, int y) { uint8_t b = 0; if(x < 0) b |= (1<<0); else if(x >= (int)cv->width) b |= (1<<1); if(y < 0) b |= (1<<2); else if(y >= (int)cv->height) b |= (1<<3); return b; } /* Solid line drawing function, using Bresenham's mid-point line * scan-conversion algorithm. */ static void draw_solid_line(caca_canvas_t *cv, struct line* s) { int x1, y1, x2, y2; int dx, dy; int xinc, yinc; x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2; dx = abs(x2 - x1); dy = abs(y2 - y1); xinc = (x1 > x2) ? -1 : 1; yinc = (y1 > y2) ? -1 : 1; if(dx >= dy) { int dpr = dy << 1; int dpru = dpr - (dx << 1); int delta = dpr - dx; for(; dx>=0; dx--) { caca_put_char(cv, x1, y1, s->ch); if(delta > 0) { x1 += xinc; y1 += yinc; delta += dpru; } else { x1 += xinc; delta += dpr; } } } else { int dpr = dx << 1; int dpru = dpr - (dy << 1); int delta = dpr - dy; for(; dy >= 0; dy--) { caca_put_char(cv, x1, y1, s->ch); if(delta > 0) { x1 += xinc; y1 += yinc; delta += dpru; } else { y1 += yinc; delta += dpr; } } } } /* Thin line drawing function, using Bresenham's mid-point line * scan-conversion algorithm and ASCII art graphics. */ static void draw_thin_line(caca_canvas_t *cv, struct line* s) { uint32_t charmapx[2], charmapy[2]; int x1, y1, x2, y2; int dx, dy; int yinc; if(s->x2 >= s->x1) { charmapx[0] = (s->y1 > s->y2) ? ',' : '`'; charmapx[1] = (s->y1 > s->y2) ? '\'' : '.'; x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2; } else { charmapx[0] = (s->y1 > s->y2) ? '`' : '.'; charmapx[1] = (s->y1 > s->y2) ? ',' : '\''; x2 = s->x1; y2 = s->y1; x1 = s->x2; y1 = s->y2; } dx = abs(x2 - x1); dy = abs(y2 - y1); if(y1 > y2) { charmapy[0] = ','; charmapy[1] = '\''; yinc = -1; } else { yinc = 1; charmapy[0] = '`'; charmapy[1] = '.'; } if(dx >= dy) { int dpr = dy << 1; int dpru = dpr - (dx << 1); int delta = dpr - dx; int prev = 0; for(; dx>=0; dx--) { if(delta > 0) { caca_put_char(cv, x1, y1, charmapy[1]); x1++; y1 += yinc; delta += dpru; prev = 1; } else { if(prev) caca_put_char(cv, x1, y1, charmapy[0]); else caca_put_char(cv, x1, y1, '-'); x1++; delta += dpr; prev = 0; } } } else { int dpr = dx << 1; int dpru = dpr - (dy << 1); int delta = dpr - dy; for(; dy >= 0; dy--) { if(delta > 0) { caca_put_char(cv, x1, y1, charmapx[0]); caca_put_char(cv, x1 + 1, y1, charmapx[1]); x1++; y1 += yinc; delta += dpru; } else { caca_put_char(cv, x1, y1, '|'); y1 += yinc; delta += dpr; } } } } ================================================ FILE: cgadraw/snprintf.c ================================================ /* * snprintf.c - a portable implementation of snprintf * * AUTHOR * Mark Martinec , April 1999. * * Copyright 1999, Mark Martinec. All rights reserved. * * TERMS AND CONDITIONS * This program is free software; you can redistribute it and/or modify * it under the terms of the "Frontier Artistic License" which comes * with this Kit. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Frontier Artistic License for more details. * * You should have received a copy of the Frontier Artistic License * with this Kit in the file named LICENSE.txt . * If not, I'll be glad to provide one. * * FEATURES * - careful adherence to specs regarding flags, field width and precision; * - good performance for large string handling (large format, large * argument or large paddings). Performance is similar to system's sprintf * and in several cases significantly better (make sure you compile with * optimizations turned on, tell the compiler the code is strict ANSI * if necessary to give it more freedom for optimizations); * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); * - written in standard ISO/ANSI C - requires an ANSI C compiler. * * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES * * This snprintf only supports the following conversion specifiers: * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) * with flags: '-', '+', ' ', '0' and '#'. * An asterisk is supported for field width as well as precision. * * Length modifiers 'h' (short int), 'l' (long int), * and 'll' (long long int) are supported. * NOTE: * If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the * length modifier 'll' is recognized but treated the same as 'l', * which may cause argument value truncation! Defining * SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also * handles length modifier 'll'. long long int is a language extension * which may not be portable. * * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) * with length modifiers (none or h, l, ll) is left to the system routine * sprintf, but all handling of flags, field width and precision as well as * c and s conversions is done very carefully by this portable routine. * If a string precision (truncation) is specified (e.g. %.8s) it is * guaranteed the string beyond the specified precision will not be referenced. * * Length modifiers h, l and ll are ignored for c and s conversions (data * types wint_t and wchar_t are not supported). * * The following common synonyms for conversion characters are supported: * - i is a synonym for d * - D is a synonym for ld, explicit length modifiers are ignored * - U is a synonym for lu, explicit length modifiers are ignored * - O is a synonym for lo, explicit length modifiers are ignored * The D, O and U conversion characters are nonstandard, they are supported * for backward compatibility only, and should not be used for new code. * * The following is specifically NOT supported: * - flag ' (thousands' grouping character) is recognized but ignored * - numeric conversion specifiers: f, e, E, g, G and synonym F, * as well as the new a and A conversion specifiers * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) * - wide character/string conversions: lc, ls, and nonstandard * synonyms C and S * - writeback of converted string length: conversion character n * - the n$ specification for direct reference to n-th argument * - locales * * It is permitted for str_m to be zero, and it is permitted to specify NULL * pointer for resulting string argument if str_m is zero (as per ISO C99). * * The return value is the number of characters which would be generated * for the given input, excluding the trailing null. If this value * is greater or equal to str_m, not all characters from the result * have been stored in str, output bytes beyond the (str_m-1) -th character * are discarded. If str_m is greater than zero it is guaranteed * the resulting string will be null-terminated. * * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, * but is different from some older and vendor implementations, * and is also different from XPG, XSH5, SUSv2 specifications. * For historical discussion on changes in the semantics and standards * of snprintf see printf(3) man page in the Linux programmers manual. * * Routines asprintf and vasprintf return a pointer (in the ptr argument) * to a buffer sufficiently large to hold the resulting string. This pointer * should be passed to free(3) to release the allocated storage when it is * no longer needed. If sufficient space cannot be allocated, these functions * will return -1 and set ptr to be a NULL pointer. These two routines are a * GNU C library extensions (glibc). * * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 * characters into the allocated output string, the last character in the * allocated buffer then gets the terminating null. If the formatted string * length (the return value) is greater than or equal to the str_m argument, * the resulting string was truncated and some of the formatted characters * were discarded. These routines present a handy way to limit the amount * of allocated memory to some sane value. * * AVAILABILITY * http://www.ijs.si/software/snprintf/ * * REVISION HISTORY * 1999-04 V0.9 Mark Martinec * - initial version, some modifications after comparing printf * man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10, * and checking how Perl handles sprintf (differently!); * 1999-04-09 V1.0 Mark Martinec * - added main test program, fixed remaining inconsistencies, * added optional (long long int) support; * 1999-04-12 V1.1 Mark Martinec * - support the 'p' conversion (pointer to void); * - if a string precision is specified * make sure the string beyond the specified precision * will not be referenced (e.g. by strlen); * 1999-04-13 V1.2 Mark Martinec * - support synonyms %D=%ld, %U=%lu, %O=%lo; * - speed up the case of long format string with few conversions; * 1999-06-30 V1.3 Mark Martinec * - fixed runaway loop (eventually crashing when str_l wraps * beyond 2^31) while copying format string without * conversion specifiers to a buffer that is too short * (thanks to Edwin Young for * spotting the problem); * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) * to snprintf.h * 2000-02-14 V2.0 (never released) Mark Martinec * - relaxed license terms: The Artistic License now applies. * You may still apply the GNU GENERAL PUBLIC LICENSE * as was distributed with previous versions, if you prefer; * - changed REVISION HISTORY dates to use ISO 8601 date format; * - added vsnprintf (patch also independently proposed by * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01) * 2000-06-27 V2.1 Mark Martinec * - removed POSIX check for str_m<1; value 0 for str_m is * allowed by ISO C99 (and GNU C library 2.1) - (pointed out * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie). * Besides relaxed license this change in standards adherence * is the main reason to bump up the major version number; * - added nonstandard routines asnprintf, vasnprintf, asprintf, * vasprintf that dynamically allocate storage for the * resulting string; these routines are not compiled by default, * see comments where NEED_V?ASN?PRINTF macros are defined; * - autoconf contributed by Caolan McNamara * 2000-10-06 V2.2 Mark Martinec * - BUG FIX: the %c conversion used a temporary variable * that was no longer in scope when referenced, * possibly causing incorrect resulting character; * - BUG FIX: make precision and minimal field width unsigned * to handle huge values (2^31 <= n < 2^32) correctly; * also be more careful in the use of signed/unsigned/size_t * internal variables - probably more careful than many * vendor implementations, but there may still be a case * where huge values of str_m, precision or minimal field * could cause incorrect behaviour; * - use separate variables for signed/unsigned arguments, * and for short/int, long, and long long argument lengths * to avoid possible incompatibilities on certain * computer architectures. Also use separate variable * arg_sign to hold sign of a numeric argument, * to make code more transparent; * - some fiddling with zero padding and "0x" to make it * Linux compatible; * - systematically use macros fast_memcpy and fast_memset * instead of case-by-case hand optimization; determine some * breakeven string lengths for different architectures; * - terminology change: 'format' -> 'conversion specifier', * 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")', * 'alternative form' -> 'alternate form', * 'data type modifier' -> 'length modifier'; * - several comments rephrased and new ones added; * - make compiler not complain about 'credits' defined but * not used; */ /* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf. * * If HAVE_SNPRINTF is defined this module will not produce code for * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well, * causing this portable version of snprintf to be called portable_snprintf * (and portable_vsnprintf). */ #define HAVE_SNPRINTF /* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and * vsnprintf but you would prefer to use the portable routine(s) instead. * In this case the portable routine is declared as portable_snprintf * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf') * is defined to expand to 'portable_v?snprintf' - see file snprintf.h . * Defining this macro is only useful if HAVE_SNPRINTF is also defined, * but does does no harm if defined nevertheless. */ #define PREFER_PORTABLE_SNPRINTF /* Define SNPRINTF_LONGLONG_SUPPORT if you want to support * data type (long long int) and length modifier 'll' (e.g. %lld). * If undefined, 'll' is recognized but treated as a single 'l'. * * If the system's sprintf does not handle 'll' * the SNPRINTF_LONGLONG_SUPPORT must not be defined! * * This is off by default as (long long int) is a language extension. */ /* #define SNPRINTF_LONGLONG_SUPPORT */ /* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf. * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly, * otherwise both snprintf and vsnprintf routines will be defined * and snprintf will be a simple wrapper around vsnprintf, at the expense * of an extra procedure call. */ /* #define NEED_SNPRINTF_ONLY */ /* Define NEED_V?ASN?PRINTF macros if you need library extension * routines asprintf, vasprintf, asnprintf, vasnprintf respectively, * and your system library does not provide them. They are all small * wrapper routines around portable_vsnprintf. Defining any of the four * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY * and turns on PREFER_PORTABLE_SNPRINTF. * * Watch for name conflicts with the system library if these routines * are already present there. * * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as * specified by C99, to be able to traverse the same list of arguments twice. * I don't know of any other standard and portable way of achieving the same. * With some versions of gcc you may use __va_copy(). You might even get away * with "ap2 = ap", in this case you must not call va_end(ap2) ! * #define va_copy(ap2,ap) ap2 = ap */ /* #define NEED_ASPRINTF */ /* #define NEED_ASNPRINTF */ /* #define NEED_VASPRINTF */ /* #define NEED_VASNPRINTF */ /* Define the following macros if desired: * SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE, * HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE, * DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE, * PERL_COMPATIBLE, PERL_BUG_COMPATIBLE, * * - For portable applications it is best not to rely on peculiarities * of a given implementation so it may be best not to define any * of the macros that select compatibility and to avoid features * that vary among the systems. * * - Selecting compatibility with more than one operating system * is not strictly forbidden but is not recommended. * * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE . * * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is * documented in a sprintf man page on a given operating system * and actually adhered to by the system's sprintf (but not on * most other operating systems). It may also refer to and enable * a behaviour that is declared 'undefined' or 'implementation specific' * in the man page but a given implementation behaves predictably * in a certain way. * * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf * that contradicts the sprintf man page on the same operating system. * * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE * conditionals take into account all idiosyncrasies of a particular * implementation, there may be other incompatibilities. */ /* ============================================= */ /* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */ /* ============================================= */ #define PORTABLE_SNPRINTF_VERSION_MAJOR 2 #define PORTABLE_SNPRINTF_VERSION_MINOR 2 #if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) # if defined(NEED_SNPRINTF_ONLY) # undef NEED_SNPRINTF_ONLY # endif # if !defined(PREFER_PORTABLE_SNPRINTF) # define PREFER_PORTABLE_SNPRINTF # endif #endif #if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE) #define SOLARIS_COMPATIBLE #endif #if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE) #define HPUX_COMPATIBLE #endif #if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE) #define DIGITAL_UNIX_COMPATIBLE #endif #if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE) #define PERL_COMPATIBLE #endif #if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE) #define LINUX_COMPATIBLE #endif #include #include #include #include #include #include #include #ifdef isdigit #undef isdigit #endif #define isdigit(c) ((c) >= '0' && (c) <= '9') /* For copying strings longer or equal to 'breakeven_point' * it is more efficient to call memcpy() than to do it inline. * The value depends mostly on the processor architecture, * but also on the compiler and its optimization capabilities. * The value is not critical, some small value greater than zero * will be just fine if you don't care to squeeze every drop * of performance out of the code. * * Small values favor memcpy, large values favor inline code. */ #if defined(__alpha__) || defined(__alpha) # define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ #endif #if defined(__i386__) || defined(__i386) # define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ #endif #if defined(__hppa) # define breakeven_point 10 /* HP-PA - gcc */ #endif #if defined(__sparc__) || defined(__sparc) # define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ #endif /* some other values of possible interest: */ /* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ /* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ #ifndef breakeven_point # define breakeven_point 6 /* some reasonable one-size-fits-all value */ #endif #define fast_memcpy(d,s,n) \ { register size_t nn = (size_t)(n); \ if (nn >= breakeven_point) memcpy((d), (s), nn); \ else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ register char *dd; register const char *ss; \ for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } #define fast_memset(d,c,n) \ { register size_t nn = (size_t)(n); \ if (nn >= breakeven_point) memset((d), (int)(c), nn); \ else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ register char *dd; register const int cc=(int)(c); \ for (dd=(d); nn>0; nn--) *dd++ = cc; } } /* prototypes */ #if defined(NEED_ASPRINTF) int asprintf (char **ptr, const char *fmt, /*args*/ ...); #endif #if defined(NEED_VASPRINTF) int vasprintf (char **ptr, const char *fmt, va_list ap); #endif #if defined(NEED_ASNPRINTF) int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); #endif #if defined(NEED_VASNPRINTF) int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap); #endif #if defined(HAVE_SNPRINTF) /* declare our portable snprintf routine under name portable_snprintf */ /* declare our portable vsnprintf routine under name portable_vsnprintf */ #else /* declare our portable routines under names snprintf and vsnprintf */ #define portable_snprintf snprintf #if !defined(NEED_SNPRINTF_ONLY) #define portable_vsnprintf vsnprintf #endif #endif #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); #if !defined(NEED_SNPRINTF_ONLY) int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); #endif #endif /* declarations */ #if 0 static char credits[] = "\n\ @(#)snprintf.c, v2.2: Mark Martinec, \n\ @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; #endif #if defined(NEED_ASPRINTF) int asprintf(char **ptr, const char *fmt, /*args*/ ...) { va_list ap; size_t str_m; int str_l; *ptr = NULL; va_start(ap, fmt); /* measure the required size */ str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); va_end(ap); assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ *ptr = (char *) malloc(str_m = (size_t)str_l + 1); if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } else { int str_l2; va_start(ap, fmt); str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); va_end(ap); assert(str_l2 == str_l); } return str_l; } #endif #if defined(NEED_VASPRINTF) int vasprintf(char **ptr, const char *fmt, va_list ap) { size_t str_m; int str_l; *ptr = NULL; { va_list ap2; va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ va_end(ap2); } assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ *ptr = (char *) malloc(str_m = (size_t)str_l + 1); if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } else { int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); assert(str_l2 == str_l); } return str_l; } #endif #if defined(NEED_ASNPRINTF) int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) { va_list ap; int str_l; *ptr = NULL; va_start(ap, fmt); /* measure the required size */ str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); va_end(ap); assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ if (str_m == 0) { /* not interested in resulting string, just return size */ } else { *ptr = (char *) malloc(str_m); if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } else { int str_l2; va_start(ap, fmt); str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); va_end(ap); assert(str_l2 == str_l); } } return str_l; } #endif #if defined(NEED_VASNPRINTF) int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) { int str_l; *ptr = NULL; { va_list ap2; va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ va_end(ap2); } assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ if (str_m == 0) { /* not interested in resulting string, just return size */ } else { *ptr = (char *) malloc(str_m); if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } else { int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); assert(str_l2 == str_l); } } return str_l; } #endif /* * If the system does have snprintf and the portable routine is not * specifically required, this module produces no code for snprintf/vsnprintf. */ #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) #if !defined(NEED_SNPRINTF_ONLY) int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { va_list ap; int str_l; va_start(ap, fmt); str_l = portable_vsnprintf(str, str_m, fmt, ap); va_end(ap); return str_l; } #endif #if defined(NEED_SNPRINTF_ONLY) int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { #else int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { #endif #if defined(NEED_SNPRINTF_ONLY) va_list ap; #endif size_t str_l = 0; const char *p = fmt; /* In contrast with POSIX, the ISO C99 now says * that str can be NULL and str_m can be 0. * This is more useful than the old: if (str_m < 1) return -1; */ #if defined(NEED_SNPRINTF_ONLY) va_start(ap, fmt); #endif if (!p) p = ""; while (*p) { if (*p != '%') { /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */ /* but the following code achieves better performance for cases * where format string is long and contains few conversions */ const char *q = strchr(p+1,'%'); size_t n = !q ? strlen(p) : (q-p); if (str_l < str_m) { size_t avail = str_m-str_l; fast_memcpy(str+str_l, p, (n>avail?avail:n)); } p += n; str_l += n; } else { const char *starting_p; size_t min_field_width = 0, precision = 0; int zero_padding = 0, precision_specified = 0, justify_left = 0; int alternate_form = 0, force_sign = 0; int space_for_positive = 1; /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored. */ char length_modifier = '\0'; /* allowed values: \0, h, l, L */ char tmp[32];/* temporary buffer for simple numeric->string conversion */ const char *str_arg; /* string address in case of string argument */ size_t str_arg_l; /* natural field width of arg without padding and sign */ unsigned char uchar_arg; /* unsigned char argument value - only defined for c conversion. N.B. standard explicitly states the char argument for the c conversion is unsigned */ size_t number_of_zeros_to_pad = 0; /* number of zeros to be inserted for numeric conversions as required by the precision or minimal field width */ size_t zero_padding_insertion_ind = 0; /* index into tmp where zero padding is to be inserted */ char fmt_spec = '\0'; /* current conversion specifier character */ str_arg = NULL; starting_p = p; p++; /* skip '%' */ /* parse flags */ while (*p == '0' || *p == '-' || *p == '+' || *p == ' ' || *p == '#' || *p == '\'') { switch (*p) { case '0': zero_padding = 1; break; case '-': justify_left = 1; break; case '+': force_sign = 1; space_for_positive = 0; break; case ' ': force_sign = 1; /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ #ifdef PERL_COMPATIBLE /* ... but in Perl the last of ' ' and '+' applies */ space_for_positive = 1; #endif break; case '#': alternate_form = 1; break; case '\'': break; } p++; } /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */ /* parse field width */ if (*p == '*') { int j; p++; j = va_arg(ap, int); if (j >= 0) min_field_width = j; else { min_field_width = -j; justify_left = 1; } } else if (isdigit((int)(*p))) { /* size_t could be wider than unsigned int; make sure we treat argument like common implementations do */ unsigned int uj = *p++ - '0'; while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); min_field_width = uj; } /* parse precision */ if (*p == '.') { p++; precision_specified = 1; if (*p == '*') { int j = va_arg(ap, int); p++; if (j >= 0) precision = j; else { precision_specified = 0; precision = 0; /* NOTE: * Solaris 2.6 man page claims that in this case the precision * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page * claim that this case should be treated as unspecified precision, * which is what we do here. */ } } else if (isdigit((int)(*p))) { /* size_t could be wider than unsigned int; make sure we treat argument like common implementations do */ unsigned int uj = *p++ - '0'; while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); precision = uj; } } /* parse 'h', 'l' and 'll' length modifiers */ if (*p == 'h' || *p == 'l') { length_modifier = *p; p++; if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ #ifdef SNPRINTF_LONGLONG_SUPPORT length_modifier = '2'; /* double l encoded as '2' */ #else length_modifier = 'l'; /* treat it as a single 'l' */ #endif p++; } } fmt_spec = *p; /* common synonyms: */ switch (fmt_spec) { case 'i': fmt_spec = 'd'; break; case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; default: break; } /* get parameter value, do initial processing */ switch (fmt_spec) { case '%': /* % behaves similar to 's' regarding flags and field widths */ case 'c': /* c behaves similar to 's' regarding flags and field widths */ case 's': length_modifier = '\0'; /* wint_t and wchar_t not supported */ /* the result of zero padding flag with non-numeric conversion specifier*/ /* is undefined. Solaris and HPUX 10 does zero padding in this case, */ /* Digital Unix and Linux does not. */ #if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE) zero_padding = 0; /* turn zero padding off for string conversions */ #endif str_arg_l = 1; switch (fmt_spec) { case '%': str_arg = p; break; case 'c': { int j = va_arg(ap, int); uchar_arg = (unsigned char) j; /* standard demands unsigned char */ str_arg = (const char *) &uchar_arg; break; } case 's': str_arg = va_arg(ap, const char *); if (!str_arg) str_arg_l = 0; /* make sure not to address string beyond the specified precision !!! */ else if (!precision_specified) str_arg_l = strlen(str_arg); /* truncate string if necessary as requested by precision */ else if (precision == 0) str_arg_l = 0; else { const char *q = memchr(str_arg, '\0', precision); str_arg_l = !q ? precision : (q-str_arg); } break; default: break; } break; case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { /* NOTE: the u, o, x, X and p conversion specifiers imply the value is unsigned; d implies a signed value */ int arg_sign = 0; /* 0 if numeric argument is zero (or if pointer is NULL for 'p'), +1 if greater than zero (or nonzero for unsigned arguments), -1 if negative (unsigned argument is never negative) */ int int_arg = 0; unsigned int uint_arg = 0; /* only defined for length modifier h, or for no length modifiers */ long int long_arg = 0; unsigned long int ulong_arg = 0; /* only defined for length modifier l */ void *ptr_arg = NULL; /* pointer argument value -only defined for p conversion */ #ifdef SNPRINTF_LONGLONG_SUPPORT long long int long_long_arg = 0; unsigned long long int ulong_long_arg = 0; /* only defined for length modifier ll */ #endif if (fmt_spec == 'p') { /* HPUX 10: An l, h, ll or L before any other conversion character * (other than d, i, u, o, x, or X) is ignored. * Digital Unix: * not specified, but seems to behave as HPUX does. * Solaris: If an h, l, or L appears before any other conversion * specifier (other than d, i, u, o, x, or X), the behavior * is undefined. (Actually %hp converts only 16-bits of address * and %llp treats address as 64-bit data which is incompatible * with (void *) argument on a 32-bit system). */ #ifdef SOLARIS_COMPATIBLE # ifdef SOLARIS_BUG_COMPATIBLE /* keep length modifiers even if it represents 'll' */ # else if (length_modifier == '2') length_modifier = '\0'; # endif #else length_modifier = '\0'; #endif ptr_arg = va_arg(ap, void *); if (ptr_arg != NULL) arg_sign = 1; } else if (fmt_spec == 'd') { /* signed */ switch (length_modifier) { case '\0': case 'h': /* It is non-portable to specify a second argument of char or short * to va_arg, because arguments seen by the called function * are not char or short. C converts char and short arguments * to int before passing them to a function. */ int_arg = va_arg(ap, int); if (int_arg > 0) arg_sign = 1; else if (int_arg < 0) arg_sign = -1; break; case 'l': long_arg = va_arg(ap, long int); if (long_arg > 0) arg_sign = 1; else if (long_arg < 0) arg_sign = -1; break; #ifdef SNPRINTF_LONGLONG_SUPPORT case '2': long_long_arg = va_arg(ap, long long int); if (long_long_arg > 0) arg_sign = 1; else if (long_long_arg < 0) arg_sign = -1; break; #endif } } else { /* unsigned */ switch (length_modifier) { case '\0': case 'h': uint_arg = va_arg(ap, unsigned int); if (uint_arg) arg_sign = 1; break; case 'l': ulong_arg = va_arg(ap, unsigned long int); if (ulong_arg) arg_sign = 1; break; #ifdef SNPRINTF_LONGLONG_SUPPORT case '2': ulong_long_arg = va_arg(ap, unsigned long long int); if (ulong_long_arg) arg_sign = 1; break; #endif } } str_arg = tmp; str_arg_l = 0; /* NOTE: * For d, i, u, o, x, and X conversions, if precision is specified, * the '0' flag should be ignored. This is so with Solaris 2.6, * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. */ #ifndef PERL_COMPATIBLE if (precision_specified) zero_padding = 0; #endif if (fmt_spec == 'd') { if (force_sign && arg_sign >= 0) tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; /* leave negative numbers for sprintf to handle, to avoid handling tricky cases like (short int)(-32768) */ #ifdef LINUX_COMPATIBLE } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) { tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; #endif } else if (alternate_form) { if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } /* alternate form should have no effect for p conversion, but ... */ #ifdef HPUX_COMPATIBLE else if (fmt_spec == 'p' /* HPUX 10: for an alternate form of p conversion, * a nonzero result is prefixed by 0x. */ #ifndef HPUX_BUG_COMPATIBLE /* Actually it uses 0x prefix even for a zero value. */ && arg_sign != 0 #endif ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; } #endif } zero_padding_insertion_ind = str_arg_l; if (!precision_specified) precision = 1; /* default precision is 1 */ if (precision == 0 && arg_sign == 0 #if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE) && fmt_spec != 'p' /* HPUX 10 man page claims: With conversion character p the result of * converting a zero value with a precision of zero is a null string. * Actually HP returns all zeroes, and Linux returns "(nil)". */ #endif ) { /* converted to null string */ /* When zero value is formatted with an explicit precision 0, the resulting formatted string is empty (d, i, u, o, x, X, p). */ } else { char f[5]; int f_l = 0; f[f_l++] = '%'; /* construct a simple format string for sprintf */ if (!length_modifier) { } else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } else f[f_l++] = length_modifier; f[f_l++] = fmt_spec; f[f_l++] = '\0'; if (fmt_spec == 'p') { str_arg_l += strlen(_ultoa((unsigned long)(ptr_arg), tmp+str_arg_l, 16)); // str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); } else if (fmt_spec == 'd') { /* signed */ switch (length_modifier) { case '\0': case 'h': //str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break; str_arg_l += strlen(_ltoa(int_arg, tmp+str_arg_l, 10)); break; case 'l': //str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break; str_arg_l += strlen(_ltoa(long_arg, tmp+str_arg_l, 10)); break; #ifdef SNPRINTF_LONGLONG_SUPPORT #error not translated case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break; #endif } } else { /* unsigned */ int base = 10; if (fmt_spec == 'x' || fmt_spec == 'X') { base = 16; } if (fmt_spec == 'o') { base = 8; } switch (length_modifier) { case '\0': case 'h': //str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break; str_arg_l += strlen(_ultoa(uint_arg, tmp+str_arg_l, base)); break; case 'l': //str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break; str_arg_l += strlen(_ultoa(ulong_arg, tmp+str_arg_l, base)); break; #ifdef SNPRINTF_LONGLONG_SUPPORT #error not translated case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; #endif } } /* include the optional minus sign and possible "0x" in the region before the zero padding insertion point */ if (zero_padding_insertion_ind < str_arg_l && tmp[zero_padding_insertion_ind] == '-') { zero_padding_insertion_ind++; } if (zero_padding_insertion_ind+1 < str_arg_l && tmp[zero_padding_insertion_ind] == '0' && (tmp[zero_padding_insertion_ind+1] == 'x' || tmp[zero_padding_insertion_ind+1] == 'X') ) { zero_padding_insertion_ind += 2; } } { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; if (alternate_form && fmt_spec == 'o' #ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */ && (str_arg_l > 0) #endif #ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */ #else /* unless zero is already the first character */ && !(zero_padding_insertion_ind < str_arg_l && tmp[zero_padding_insertion_ind] == '0') #endif ) { /* assure leading zero for alternate-form octal numbers */ if (!precision_specified || precision < num_of_digits+1) { /* precision is increased to force the first character to be zero, except if a zero value is formatted with an explicit precision of zero */ precision = num_of_digits+1; precision_specified = 1; } } /* zero padding to specified precision? */ if (num_of_digits < precision) number_of_zeros_to_pad = precision - num_of_digits; } /* zero padding to specified minimal field width? */ if (!justify_left && zero_padding) { int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); if (n > 0) number_of_zeros_to_pad += n; } break; } default: /* unrecognized conversion specifier, keep format string as-is*/ zero_padding = 0; /* turn zero padding off for non-numeric convers. */ #ifndef DIGITAL_UNIX_COMPATIBLE justify_left = 1; min_field_width = 0; /* reset flags */ #endif #if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE) /* keep the entire format string unchanged */ str_arg = starting_p; str_arg_l = p - starting_p; /* well, not exactly so for Linux, which does something inbetween, * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */ #else /* discard the unrecognized conversion, just keep * * the unrecognized conversion character */ str_arg = p; str_arg_l = 0; #endif if (*p) str_arg_l++; /* include invalid conversion specifier unchanged if not at end-of-string */ break; } if (*p) p++; /* step over the just processed conversion specifier */ /* insert padding to the left as requested by min_field_width; this does not include the zero padding in case of numerical conversions*/ if (!justify_left) { /* left padding with blank or zero */ int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n)); } str_l += n; } } /* zero padding as requested by the precision or by the minimal field width * for numeric conversions required? */ if (number_of_zeros_to_pad <= 0) { /* will not copy first part of numeric right now, * * force it to be copied later in its entirety */ zero_padding_insertion_ind = 0; } else { /* insert first part of numerics (sign or '0x') before zero padding */ int n = zero_padding_insertion_ind; if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); } str_l += n; } /* insert zero padding as requested by the precision or min field width */ n = number_of_zeros_to_pad; if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memset(str+str_l, '0', (n>avail?avail:n)); } str_l += n; } } /* insert formatted string * (or as-is conversion specifier for unknown conversions) */ { int n = str_arg_l - zero_padding_insertion_ind; if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, (n>avail?avail:n)); } str_l += n; } } /* insert right padding */ if (justify_left) { /* right blank padding to the field width */ int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memset(str+str_l, ' ', (n>avail?avail:n)); } str_l += n; } } } } #if defined(NEED_SNPRINTF_ONLY) va_end(ap); #endif if (str_m > 0) { /* make sure the string is null-terminated even at the expense of overwriting the last character (shouldn't happen, but just in case) */ str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; } /* Return the number of characters formatted (excluding trailing null * character), that is, the number of characters that would have been * written to the buffer if it were large enough. * * The value of str_l should be returned, but str_l is of unsigned type * size_t, and snprintf is int, possibly leading to an undetected * integer overflow, resulting in a negative return value, which is illegal. * Both XSH5 and ISO C99 (at least the draft) are silent on this issue. * Should errno be set to EOVERFLOW and EOF returned in this case??? */ return (int) str_l; } #endif ================================================ FILE: cgadraw/snprintf.h ================================================ #ifndef _PORTABLE_SNPRINTF_H_ #define _PORTABLE_SNPRINTF_H_ extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); #define snprintf portable_snprintf #define vsnprintf portable_vsnprintf #endif ================================================ FILE: cgadraw/string.c ================================================ #include #include #include #include "config.h" #include "snprintf.h" #include "caca.h" #include "cacaint.h" int caca_gotoxy(caca_canvas_t *cv, int x, int y) { cv->frames[cv->frame].x = x; cv->frames[cv->frame].y = y; return 0; } int caca_wherex(caca_canvas_t const *cv) { return cv->frames[cv->frame].x; } int caca_wherey(caca_canvas_t const *cv) { return cv->frames[cv->frame].y; } int caca_put_char(caca_canvas_t *cv, int x, int y, uint32_t ch) { cv->c[x + y * cv->width].c = caca_utf32_to_cp437(ch); cv->c[x + y * cv->width].attr = caca_attr_to_ansi(cv->curattr); return 1; } uint32_t caca_get_char(caca_canvas_t const *cv, int x, int y) { if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height) return ' '; return caca_cp437_to_utf32(cv->c[x + y * cv->width].c); } int caca_put_str(caca_canvas_t *cv, int x, int y, char const *s) { int len; if (y < 0 || y >= (int)cv->height || x >= (int)cv->width) { return strlen(s); } for (len = 0; *s; len++) { caca_put_char(cv, x + len, y, *s++); } return len; } int caca_printf(caca_canvas_t *cv, int x, int y, char const *format, ...) { va_list args; int ret; va_start(args, format); ret = caca_vprintf(cv, x, y, format, args); va_end(args); return ret; } int caca_vprintf(caca_canvas_t *cv, int x, int y, char const *format, va_list args) { char tmp[256]; char *buf = tmp; int bufsize = sizeof(tmp), ret; if(cv->width - x + 1 > sizeof(tmp)) { return -1; } vsnprintf(buf, bufsize, format, args); buf[bufsize - 1] = '\0'; ret = caca_put_str(cv, x, y, buf); return ret; } int caca_clear_canvas(caca_canvas_t *cv) { uint32_t attr = cv->curattr; int n; for(n = cv->width * cv->height; n--; ) { cv->c[n].c = ' '; cv->c[n].attr = caca_attr_to_ansi(attr); } return 0; } int caca_set_canvas_handle(caca_canvas_t *cv, int x, int y) { cv->frames[cv->frame].handlex = x; cv->frames[cv->frame].handley = y; return 0; } int caca_get_canvas_handle_x(caca_canvas_t const *cv) { return cv->frames[cv->frame].handlex; } int caca_get_canvas_handle_y(caca_canvas_t const *cv) { return cv->frames[cv->frame].handley; } int caca_blit(caca_canvas_t *dst, int x, int y, caca_canvas_t const *src, caca_canvas_t const *mask) { return -1; } int caca_set_canvas_boundaries(caca_canvas_t *cv, int x, int y, int w, int h) { return -1; } ================================================ FILE: cgadraw/transfrm.c ================================================ /* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /* * This file contains horizontal and vertical flipping routines. */ #include "config.h" #include "caca.h" #include "cacaint.h" static uint32_t flipchar(uint32_t ch); static uint32_t flopchar(uint32_t ch); static uint32_t rotatechar(uint32_t ch); static uint32_t leftchar(uint32_t ch); static uint32_t rightchar(uint32_t ch); static void leftpair(uint32_t pair[2]); static void rightpair(uint32_t pair[2]); int caca_invert(caca_canvas_t *cv) { return -1; } int caca_flip(caca_canvas_t *cv) { return -1; } int caca_flop(caca_canvas_t *cv) { return -1; } int caca_rotate_180(caca_canvas_t *cv) { return -1; } int caca_rotate_left(caca_canvas_t *cv) { return -1; } int caca_rotate_right(caca_canvas_t *cv) { return -1; } int caca_stretch_left(caca_canvas_t *cv) { return -1; } int caca_stretch_right(caca_canvas_t *cv) { return -1; } /* FIXME: as the lookup tables grow bigger, use a log(n) lookup instead * of linear lookup. */ static uint32_t flipchar(uint32_t ch) { int i; static uint32_t const noflip[] = { /* ASCII */ ' ', '"', '#', '\'', '-', '.', '*', '+', ':', '=', '0', '8', 'A', 'H', 'I', 'M', 'O', 'T', 'U', 'V', 'W', 'X', 'Y', '^', '_', 'i', 'o', 'v', 'w', 'x', '|', /* CP437 and box drawing */ 0x2591, 0x2592, 0x2593, 0x2588, 0x2584, 0x2580, /* ░ ▒ ▓ █ ▄ ▀ */ 0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */ 0x252c, 0x2534, 0x2533, 0x253b, 0x2566, 0x2569, /* ┬ ┴ ┳ ┻ ╦ ╩ */ 0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */ 0x2575, 0x2577, 0x2579, 0x257b, /* ╵ ╷ ╹ ╻ */ 0 }; static uint32_t const pairs[] = { /* ASCII */ '(', ')', '/', '\\', '<', '>', '[', ']', 'b', 'd', 'p', 'q', '{', '}', /* ASCII-Unicode */ ';', 0x204f, /* ; ⁏ */ '`', 0x00b4, /* ` ´ */ ',', 0x02ce, /* , ˎ */ '1', 0x07c1, /* 1 ߁ */ 'B', 0x10412,/* B 𐐒 */ 'C', 0x03fd, /* C Ͻ */ 'D', 0x15e1, /* D ᗡ */ 'E', 0x018e, /* E Ǝ */ 'J', 0x1490, /* J ᒐ */ 'L', 0x2143, /* L ⅃ */ 'N', 0x0418, /* N И */ 'P', 0x1040b,/* P 𐐋 */ 'R', 0x042f, /* R Я */ 'S', 0x01a7, /* S Ƨ */ 'c', 0x0254, /* c ɔ */ 'e', 0x0258, /* e ɘ */ /* CP437 */ 0x258c, 0x2590, /* ▌ ▐ */ 0x2596, 0x2597, /* ▖ ▗ */ 0x2598, 0x259d, /* ▘ ▝ */ 0x2599, 0x259f, /* ▙ ▟ */ 0x259a, 0x259e, /* ▚ ▞ */ 0x259b, 0x259c, /* ▛ ▜ */ 0x25ba, 0x25c4, /* ► ◄ */ 0x2192, 0x2190, /* → ← */ 0x2310, 0xac, /* ⌐ ¬ */ /* Box drawing */ 0x250c, 0x2510, /* ┌ ┐ */ 0x2514, 0x2518, /* └ ┘ */ 0x251c, 0x2524, /* ├ ┤ */ 0x250f, 0x2513, /* ┏ ┓ */ 0x2517, 0x251b, /* ┗ ┛ */ 0x2523, 0x252b, /* ┣ ┫ */ 0x2552, 0x2555, /* ╒ ╕ */ 0x2558, 0x255b, /* ╘ ╛ */ 0x2553, 0x2556, /* ╓ ╖ */ 0x2559, 0x255c, /* ╙ ╜ */ 0x2554, 0x2557, /* ╔ ╗ */ 0x255a, 0x255d, /* ╚ ╝ */ 0x255e, 0x2561, /* ╞ ╡ */ 0x255f, 0x2562, /* ╟ ╢ */ 0x2560, 0x2563, /* ╠ ╣ */ 0x2574, 0x2576, /* ╴ ╶ */ 0x2578, 0x257a, /* ╸ ╺ */ /* Misc Unicode */ 0x22f2, 0x22fa, /* ⋲ ⋺ */ 0x22f3, 0x22fb, /* ⋳ ⋻ */ 0x2308, 0x2309, /* ⌈ ⌉ */ 0x230a, 0x230b, /* ⌊ ⌋ */ 0x230c, 0x230d, /* ⌌ ⌍ */ 0x230e, 0x230f, /* ⌎ ⌏ */ 0x231c, 0x231d, /* ⌜ ⌝ */ 0x231e, 0x231f, /* ⌞ ⌟ */ 0x2326, 0x232b, /* ⌦ ⌫ */ 0x2329, 0x232a, /* 〈 〉 */ 0x2341, 0x2342, /* ⍁ ⍂ */ 0x2343, 0x2344, /* ⍃ ⍄ */ 0x2345, 0x2346, /* ⍅ ⍆ */ 0x2347, 0x2348, /* ⍇ ⍈ */ 0x233f, 0x2340, /* ⌿ ⍀ */ 0x239b, 0x239e, /* ⎛ ⎞ */ 0x239c, 0x239f, /* ⎜ ⎟ */ 0x239d, 0x23a0, /* ⎝ ⎠ */ 0x23a1, 0x23a4, /* ⎡ ⎤ */ 0x23a2, 0x23a5, /* ⎢ ⎥ */ 0x23a3, 0x23a6, /* ⎣ ⎦ */ 0x23a7, 0x23ab, /* ⎧ ⎫ */ 0x23a8, 0x23ac, /* ⎨ ⎬ */ 0x23a9, 0x23ad, /* ⎩ ⎭ */ 0x23b0, 0x23b1, /* ⎰ ⎱ */ 0x23be, 0x23cb, /* ⎾ ⏋ */ 0x23bf, 0x23cc, /* ⎿ ⏌ */ 0 }; for(i = 0; noflip[i]; i++) if(ch == noflip[i]) return ch; for(i = 0; pairs[i]; i++) if(ch == pairs[i]) return pairs[i ^ 1]; return ch; } static uint32_t flopchar(uint32_t ch) { int i; static uint32_t const noflop[] = { /* ASCII */ ' ', '(', ')', '*', '+', '-', '0', '3', '8', ':', '<', '=', '>', 'B', 'C', 'D', 'E', 'H', 'I', 'K', 'O', 'X', '[', ']', 'c', 'o', '{', '|', '}', /* CP437 and box drawing */ 0x2591, 0x2592, 0x2593, 0x2588, 0x258c, 0x2590, /* ░ ▒ ▓ █ ▌ ▐ */ 0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */ 0x251c, 0x2524, 0x2523, 0x252b, 0x2560, 0x2563, /* ├ ┤ ┣ ┫ ╠ ╣ */ 0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */ 0x2574, 0x2576, 0x2578, 0x257a, /* ╴ ╶ ╸ ╺ */ /* Misc Unicode */ 0x22f2, 0x22fa, 0x22f3, 0x22fb, 0x2326, 0x232b, /* ⋲ ⋺ ⋳ ⋻ ⌦ ⌫ */ 0x2329, 0x232a, 0x2343, 0x2344, 0x2345, 0x2346, /* 〈 〉 ⍃ ⍄ ⍅ ⍆ */ 0x2347, 0x2348, 0x239c, 0x239f, 0x23a2, 0x23a5, /* ⍇ ⍈ ⎜ ⎟ ⎢ ⎥ */ 0x23a8, 0x23ac, /* ⎨ ⎬ */ 0 }; static uint32_t const pairs[] = { /* ASCII */ '/', '\\', 'M', 'W', ',', '`', 'b', 'p', 'd', 'q', 'p', 'q', 'f', 't', '.', '\'', /* ASCII-Unicode */ '_', 0x203e, /* _ ‾ */ '!', 0x00a1, /* ! ¡ */ 'A', 0x2200, /* A ∀ */ 'J', 0x1489, /* J ᒉ */ 'L', 0x0413, /* L Г */ 'N', 0x0418, /* N И */ 'P', 0x042c, /* P Ь */ 'R', 0x0281, /* R ʁ */ 'S', 0x01a7, /* S Ƨ */ 'U', 0x0548, /* U Ո */ 'V', 0x039b, /* V Λ */ 'Y', 0x2144, /* Y ⅄ */ 'h', 0x03bc, /* h μ */ 'i', 0x1d09, /* i ᴉ */ 'j', 0x1e37, /* j ḷ */ 'l', 0x0237, /* l ȷ */ 'v', 0x028c, /* v ʌ */ 'w', 0x028d, /* w ʍ */ 'y', 0x03bb, /* y λ */ /* Not perfect, but better than nothing */ '"', 0x201e, /* " „ */ 'm', 0x026f, /* m ɯ */ 'n', 'u', /* CP437 */ 0x2584, 0x2580, /* ▄ ▀ */ 0x2596, 0x2598, /* ▖ ▘ */ 0x2597, 0x259d, /* ▗ ▝ */ 0x2599, 0x259b, /* ▙ ▛ */ 0x259f, 0x259c, /* ▟ ▜ */ 0x259a, 0x259e, /* ▚ ▞ */ /* Box drawing */ 0x250c, 0x2514, /* ┌ └ */ 0x2510, 0x2518, /* ┐ ┘ */ 0x252c, 0x2534, /* ┬ ┴ */ 0x250f, 0x2517, /* ┏ ┗ */ 0x2513, 0x251b, /* ┓ ┛ */ 0x2533, 0x253b, /* ┳ ┻ */ 0x2554, 0x255a, /* ╔ ╚ */ 0x2557, 0x255d, /* ╗ ╝ */ 0x2566, 0x2569, /* ╦ ╩ */ 0x2552, 0x2558, /* ╒ ╘ */ 0x2555, 0x255b, /* ╕ ╛ */ 0x2564, 0x2567, /* ╤ ╧ */ 0x2553, 0x2559, /* ╓ ╙ */ 0x2556, 0x255c, /* ╖ ╜ */ 0x2565, 0x2568, /* ╥ ╨ */ 0x2575, 0x2577, /* ╵ ╷ */ 0x2579, 0x257b, /* ╹ ╻ */ /* Misc Unicode */ 0x2308, 0x230a, /* ⌈ ⌊ */ 0x2309, 0x230b, /* ⌉ ⌋ */ 0x230c, 0x230e, /* ⌌ ⌎ */ 0x230d, 0x230f, /* ⌍ ⌏ */ 0x231c, 0x231e, /* ⌜ ⌞ */ 0x231d, 0x231f, /* ⌝ ⌟ */ 0x2341, 0x2342, /* ⍁ ⍂ */ 0x233f, 0x2340, /* ⌿ ⍀ */ 0x239b, 0x239d, /* ⎛ ⎝ */ 0x239e, 0x23a0, /* ⎞ ⎠ */ 0x23a1, 0x23a3, /* ⎡ ⎣ */ 0x23a4, 0x23a6, /* ⎤ ⎦ */ 0x23a7, 0x23a9, /* ⎧ ⎩ */ 0x23ab, 0x23ad, /* ⎫ ⎭ */ 0x23b0, 0x23b1, /* ⎰ ⎱ */ 0x23be, 0x23bf, /* ⎾ ⎿ */ 0x23cb, 0x23cc, /* ⏋ ⏌ */ 0 }; for(i = 0; noflop[i]; i++) if(ch == noflop[i]) return ch; for(i = 0; pairs[i]; i++) if(ch == pairs[i]) return pairs[i ^ 1]; return ch; } static uint32_t rotatechar(uint32_t ch) { int i; static uint32_t const norotate[] = { /* ASCII */ ' ', '*', '+', '-', '/', '0', '8', ':', '=', 'H', 'I', 'N', 'O', 'S', 'X', 'Z', '\\', 'o', 's', 'x', 'z', '|', /* Unicode */ 0x2591, 0x2592, 0x2593, 0x2588, 0x259a, 0x259e, /* ░ ▒ ▓ █ ▚ ▞ */ 0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */ 0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */ /* Misc Unicode */ 0x233f, 0x2340, 0x23b0, 0x23b1, /* ⌿ ⍀ ⎰ ⎱ */ 0 }; static uint32_t const pairs[] = { /* ASCII */ '(', ')', '<', '>', '[', ']', '{', '}', '.', '\'', '6', '9', 'M', 'W', 'b', 'q', 'd', 'p', 'n', 'u', /* ASCII-Unicode */ '_', 0x203e, /* _ ‾ */ ',', 0x00b4, /* , ´ */ ';', 0x061b, /* ; ؛ */ '`', 0x02ce, /* ` ˎ */ '&', 0x214b, /* & ⅋ */ '!', 0x00a1, /* ! ¡ */ '?', 0x00bf, /* ? ¿ */ '3', 0x0190, /* 3 Ɛ */ '4', 0x152d, /* 4 ᔭ */ 'A', 0x2200, /* A ∀ */ 'B', 0x10412,/* B 𐐒 */ 'C', 0x03fd, /* C Ͻ */ 'D', 0x15e1, /* D ᗡ */ 'E', 0x018e, /* E Ǝ */ 'F', 0x2132, /* F Ⅎ -- 0x07c3 looks better, but is RTL */ 'G', 0x2141, /* G ⅁ */ 'J', 0x148b, /* J ᒋ */ 'L', 0x2142, /* L ⅂ */ 'P', 0x0500, /* P Ԁ */ 'Q', 0x038c, /* Q Ό */ 'R', 0x1d1a, /* R ᴚ */ 'T', 0x22a5, /* T ⊥ */ 'U', 0x0548, /* U Ո */ 'V', 0x039b, /* V Λ */ 'Y', 0x2144, /* Y ⅄ */ 'a', 0x0250, /* a ɐ */ 'c', 0x0254, /* c ɔ */ 'e', 0x01dd, /* e ǝ */ 'f', 0x025f, /* f ɟ */ 'g', 0x1d77, /* g ᵷ */ 'h', 0x0265, /* h ɥ */ 'i', 0x1d09, /* i ᴉ */ 'j', 0x1e37, /* j ḷ */ 'k', 0x029e, /* k ʞ */ 'l', 0x0237, /* l ȷ */ 'm', 0x026f, /* m ɯ */ 'r', 0x0279, /* r ɹ */ 't', 0x0287, /* t ʇ */ 'v', 0x028c, /* v ʌ */ 'w', 0x028d, /* w ʍ */ 'y', 0x028e, /* y ʎ */ /* Unicode-ASCII to match third-party software */ 0x0183, 'g', /* ƃ g */ 0x0259, 'e', /* ə e */ 0x027e, 'j', /* ɾ j */ 0x02d9, '.', /* ˙ . */ 0x05df, 'l', /* ן l */ /* Not perfect, but better than nothing */ '"', 0x201e, /* " „ */ /* Misc Unicode */ 0x00e6, 0x1d02, /* æ ᴂ */ 0x0153, 0x1d14, /* œ ᴔ */ 0x03b5, 0x025c, /* ε ɜ */ 0x025b, 0x025c, /* ɛ ɜ */ /* CP437 */ 0x258c, 0x2590, /* ▌ ▐ */ 0x2584, 0x2580, /* ▄ ▀ */ 0x2596, 0x259d, /* ▖ ▝ */ 0x2597, 0x2598, /* ▗ ▘ */ 0x2599, 0x259c, /* ▙ ▜ */ 0x259f, 0x259b, /* ▟ ▛ */ /* Box drawing */ 0x250c, 0x2518, /* ┌ ┘ */ 0x2510, 0x2514, /* ┐ └ */ 0x251c, 0x2524, /* ├ ┤ */ 0x252c, 0x2534, /* ┬ ┴ */ 0x250f, 0x251b, /* ┏ ┛ */ 0x2513, 0x2517, /* ┓ ┗ */ 0x2523, 0x252b, /* ┣ ┫ */ 0x2533, 0x253b, /* ┳ ┻ */ 0x2554, 0x255d, /* ╔ ╝ */ 0x2557, 0x255a, /* ╗ ╚ */ 0x2560, 0x2563, /* ╠ ╣ */ 0x2566, 0x2569, /* ╦ ╩ */ 0x2552, 0x255b, /* ╒ ╛ */ 0x2555, 0x2558, /* ╕ ╘ */ 0x255e, 0x2561, /* ╞ ╡ */ 0x2564, 0x2567, /* ╤ ╧ */ 0x2553, 0x255c, /* ╓ ╜ */ 0x2556, 0x2559, /* ╖ ╙ */ 0x255f, 0x2562, /* ╟ ╢ */ 0x2565, 0x2568, /* ╥ ╨ */ 0x2574, 0x2576, /* ╴ ╶ */ 0x2575, 0x2577, /* ╵ ╷ */ 0x2578, 0x257a, /* ╸ ╺ */ 0x2579, 0x257b, /* ╹ ╻ */ /* Misc Unicode */ 0x22f2, 0x22fa, /* ⋲ ⋺ */ 0x22f3, 0x22fb, /* ⋳ ⋻ */ 0x2308, 0x230b, /* ⌈ ⌋ */ 0x2309, 0x230a, /* ⌉ ⌊ */ 0x230c, 0x230f, /* ⌌ ⌏ */ 0x230d, 0x230e, /* ⌍ ⌎ */ 0x231c, 0x231f, /* ⌜ ⌟ */ 0x231d, 0x231e, /* ⌝ ⌞ */ 0x2326, 0x232b, /* ⌦ ⌫ */ 0x2329, 0x232a, /* 〈 〉 */ 0x2343, 0x2344, /* ⍃ ⍄ */ 0x2345, 0x2346, /* ⍅ ⍆ */ 0x2347, 0x2348, /* ⍇ ⍈ */ 0x239b, 0x23a0, /* ⎛ ⎠ */ 0x239c, 0x239f, /* ⎜ ⎟ */ 0x239e, 0x239d, /* ⎞ ⎝ */ 0x23a1, 0x23a6, /* ⎡ ⎦ */ 0x23a2, 0x23a5, /* ⎢ ⎥ */ 0x23a4, 0x23a3, /* ⎤ ⎣ */ 0x23a7, 0x23ad, /* ⎧ ⎭ */ 0x23a8, 0x23ac, /* ⎨ ⎬ */ 0x23ab, 0x23a9, /* ⎫ ⎩ */ 0x23be, 0x23cc, /* ⎾ ⏌ */ 0x23cb, 0x23bf, /* ⏋ ⎿ */ 0 }; for(i = 0; norotate[i]; i++) if(ch == norotate[i]) return ch; for(i = 0; pairs[i]; i++) if(ch == pairs[i]) return pairs[i ^ 1]; return ch; } static uint32_t const leftright2[] = { /* ASCII */ '/', '\\', '|', '-', '|', '_', /* This is all right because there was already a '|' before */ /* ASCII-Unicode */ '|', 0x203e, /* | ‾ */ /* Misc Unicode */ 0x2571, 0x2572, /* ╱ ╲ */ /* Box drawing */ 0x2500, 0x2502, /* ─ │ */ 0x2501, 0x2503, /* ━ ┃ */ 0x2550, 0x2551, /* ═ ║ */ 0, 0 }; static uint32_t const leftright4[] = { /* ASCII */ '<', 'v', '>', '^', ',', '.', '\'', '`', /* ASCII / Unicode */ '(', 0x203f, ')', 0x2040, /* ( ‿ ) ⁀ */ /* Misc Unicode */ 0x256d, 0x2570, 0x256f, 0x256e, /* ╭ ╰ ╯ ╮ */ /* CP437 */ 0x258c, 0x2584, 0x2590, 0x2580, /* ▌ ▄ ▐ ▀ */ 0x2596, 0x2597, 0x259d, 0x2598, /* ▖ ▗ ▝ ▘ */ 0x2599, 0x259f, 0x259c, 0x259b, /* ▙ ▟ ▜ ▛ */ /* Box drawing */ 0x250c, 0x2514, 0x2518, 0x2510, /* ┌ └ ┘ ┐ */ 0x250f, 0x2517, 0x251b, 0x2513, /* ┏ ┗ ┛ ┓ */ 0x251c, 0x2534, 0x2524, 0x252c, /* ├ ┴ ┤ ┬ */ 0x2523, 0x253b, 0x252b, 0x2533, /* ┣ ┻ ┫ ┳ */ 0x2552, 0x2559, 0x255b, 0x2556, /* ╒ ╙ ╛ ╖ */ 0x2553, 0x2558, 0x255c, 0x2555, /* ╓ ╘ ╜ ╕ */ 0x2554, 0x255a, 0x255d, 0x2557, /* ╔ ╚ ╝ ╗ */ 0x255e, 0x2568, 0x2561, 0x2565, /* ╞ ╨ ╡ ╥ */ 0x255f, 0x2567, 0x2562, 0x2564, /* ╟ ╧ ╢ ╤ */ 0x2560, 0x2569, 0x2563, 0x2566, /* ╠ ╩ ╣ ╦ */ 0x2574, 0x2577, 0x2576, 0x2575, /* ╴ ╷ ╶ ╵ */ 0x2578, 0x257b, 0x257a, 0x2579, /* ╸ ╻ ╺ ╹ */ 0, 0, 0, 0 }; static uint32_t leftchar(uint32_t ch) { int i; for(i = 0; leftright2[i]; i++) if(ch == leftright2[i]) return leftright2[(i & ~1) | ((i + 1) & 1)]; for(i = 0; leftright4[i]; i++) if(ch == leftright4[i]) return leftright4[(i & ~3) | ((i + 1) & 3)]; return ch; } static uint32_t rightchar(uint32_t ch) { int i; for(i = 0; leftright2[i]; i++) if(ch == leftright2[i]) return leftright2[(i & ~1) | ((i - 1) & 1)]; for(i = 0; leftright4[i]; i++) if(ch == leftright4[i]) return leftright4[(i & ~3) | ((i - 1) & 3)]; return ch; } static uint32_t const leftright2x2[] = { /* ASCII / Unicode */ '-', '-', 0x4e28, CACA_MAGIC_FULLWIDTH, /* -- 丨 */ '|', '|', 0x2f06, CACA_MAGIC_FULLWIDTH, /* || ⼆ */ /* Unicode */ 0x2584, 0x2580, 0x2580, 0x2584, /* ▄▀ ▀▄ */ 0, 0, 0, 0 }; static uint32_t const leftright2x4[] = { /* ASCII */ ':', ' ', '.', '.', ' ', ':', '\'', '\'', /* ASCII / Unicode */ ' ', '`', 0x00b4, ' ', 0x02ce, ' ', ' ', ',', /* ` ´ ˎ , */ ' ', '`', '\'', ' ', '.', ' ', ' ', ',', /* fallback ASCII */ '`', ' ', ',', ' ', ' ', 0x00b4, ' ', 0x02ce, /* ` , ˎ ´ */ '`', ' ', ',', ' ', ' ', '.', ' ', '\'', /* fallback ASCII */ '/', ' ', '-', 0x02ce, ' ', '/', '`', '-', /* / -ˎ / `- */ '/', ' ', '-', '.', ' ', '/', '\'', '-', /* fallback ASCII */ '\\', ' ', ',', '-', ' ', '\\', '-', 0x00b4, /* \ ,- \ -´ */ '\\', ' ', '.', '-', ' ', '\\', '-', '\'', /* fallback ASCII */ '\\', ' ', '_', ',', ' ', '\\', 0x00b4, 0x203e, /* \ _, \ ´‾ */ '\\', '_', '_', '/', 0x203e, '\\', '/', 0x203e, /* \_ _/ ‾\ /‾ */ '_', '\\', 0x203e, '/', '\\', 0x203e, '/', '_', /* _\ ‾/ \‾ /_ */ '|', ' ', '_', '_', ' ', '|', 0x203e, 0x203e, /* | __ | ‾‾ */ '_', '|', 0x203e, '|', '|', 0x203e, '|', '_', /* _| ‾| |‾ |_ */ '|', '_', '_', '|', 0x203e, '|', '|', 0x203e, /* |_ _| ‾| |‾ */ '_', ' ', ' ', 0x2577, ' ', 0x203e, 0x2575, ' ', /* _ ╷ ‾ ╵ */ ' ', '_', ' ', 0x2575, 0x203e, ' ', 0x2577, ' ', /* _ ╵ ‾ ╷ */ '.', '_', '.', 0x2575, 0x203e, '\'', 0x2577, '\'', /* ._ .╵ ‾' ╷' */ '(', '_', 0x203f, '|', 0x203e, ')', '|', 0x2040, /* (_ ‿| ‾) |⁀ */ '(', 0x203e, '|', 0x203f, '_', ')', 0x2040, '|', /* (‾ |‿ _) ⁀| */ '\\', '/', 0xff1e, CACA_MAGIC_FULLWIDTH, '/', '\\', 0xff1c, CACA_MAGIC_FULLWIDTH, /* \/ > /\ < */ ')', ' ', 0xfe35, CACA_MAGIC_FULLWIDTH, ' ', '(', 0xfe36, CACA_MAGIC_FULLWIDTH, /* ) ︵ ( ︶ */ '}', ' ', 0xfe37, CACA_MAGIC_FULLWIDTH, ' ', '{', 0xfe38, CACA_MAGIC_FULLWIDTH, /* } ︷ { ︸ */ /* Not perfect, but better than nothing */ '(', ' ', 0x02ce, ',', ' ', ')', 0x00b4, '`', /* ( ˎ, ) ´` */ ' ', 'v', '>', ' ', 0x028c, ' ', ' ', '<', /* v > ʌ < */ ' ', 'V', '>', ' ', 0x039b, ' ', ' ', '<', /* V > Λ < */ 'v', ' ', '>', ' ', ' ', 0x028c, ' ', '<', /* v > ʌ < */ 'V', ' ', '>', ' ', ' ', 0x039b, ' ', '<', /* V > Λ < */ '\\', '|', 0xff1e, CACA_MAGIC_FULLWIDTH, '|', '\\', 0xff1c, CACA_MAGIC_FULLWIDTH, /* \| > |\ < */ '|', '/', 0xff1e, CACA_MAGIC_FULLWIDTH, '/', '|', 0xff1c, CACA_MAGIC_FULLWIDTH, /* |/ > /| < */ /* Unicode */ 0x2584, ' ', ' ', 0x2584, ' ', 0x2580, 0x2580, ' ', /* ▄ ▄ ▀ ▀ */ 0x2588, ' ', 0x2584, 0x2584, ' ', 0x2588, 0x2580, 0x2580, /* █ ▄▄ █ ▀▀ */ 0x2588, 0x2584, 0x2584, 0x2588, 0x2580, 0x2588, 0x2588, 0x2580, /* █▄ ▄█ ▀█ █▀ */ /* TODO: Braille */ /* Not perfect, but better than nothing */ 0x2591, ' ', 0x28e4, 0x28e4, ' ', 0x2591, 0x281b, 0x281b, /* ░ ⣤⣤ ░ ⠛⠛ */ 0x2592, ' ', 0x28f6, 0x28f6, ' ', 0x2592, 0x283f, 0x283f, /* ▒ ⣶⣶ ▒ ⠿⠿ */ 0, 0, 0, 0, 0, 0, 0, 0 }; static void leftpair(uint32_t pair[2]) { int i; for(i = 0; leftright2x2[i]; i += 2) if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1]) { pair[0] = leftright2x2[(i & ~3) | ((i + 2) & 3)]; pair[1] = leftright2x2[((i & ~3) | ((i + 2) & 3)) + 1]; return; } for(i = 0; leftright2x4[i]; i += 2) if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1]) { pair[0] = leftright2x4[(i & ~7) | ((i + 2) & 7)]; pair[1] = leftright2x4[((i & ~7) | ((i + 2) & 7)) + 1]; return; } } static void rightpair(uint32_t pair[2]) { int i; for(i = 0; leftright2x2[i]; i += 2) if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1]) { pair[0] = leftright2x2[(i & ~3) | ((i - 2) & 3)]; pair[1] = leftright2x2[((i & ~3) | ((i - 2) & 3)) + 1]; return; } for(i = 0; leftright2x4[i]; i += 2) if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1]) { pair[0] = leftright2x4[(i & ~7) | ((i - 2) & 7)]; pair[1] = leftright2x4[((i & ~7) | ((i - 2) & 7)) + 1]; return; } } ================================================ FILE: cgadraw/triangle.c ================================================ /* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * All Rights Reserved * * This library is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. */ /* * This file contains triangle drawing functions, both filled and outline. */ #include "config.h" #if !defined(__KERNEL__) # include #endif #include "caca.h" #include "cacaint.h" /** \brief Draw a triangle on the canvas using the given character. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x1 X coordinate of the first point. * \param y1 Y coordinate of the first point. * \param x2 X coordinate of the second point. * \param y2 Y coordinate of the second point. * \param x3 X coordinate of the third point. * \param y3 Y coordinate of the third point. * \param ch UTF-32 character to be used to draw the triangle outline. * \return This function always returns 0. */ int caca_draw_triangle(caca_canvas_t * cv, int x1, int y1, int x2, int y2, int x3, int y3, uint32_t ch) { caca_draw_line(cv, x1, y1, x2, y2, ch); caca_draw_line(cv, x2, y2, x3, y3, ch); caca_draw_line(cv, x3, y3, x1, y1, ch); return 0; } /** \brief Draw a thin triangle on the canvas. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x1 X coordinate of the first point. * \param y1 Y coordinate of the first point. * \param x2 X coordinate of the second point. * \param y2 Y coordinate of the second point. * \param x3 X coordinate of the third point. * \param y3 Y coordinate of the third point. * \return This function always returns 0. */ int caca_draw_thin_triangle(caca_canvas_t * cv, int x1, int y1, int x2, int y2, int x3, int y3) { caca_draw_thin_line(cv, x1, y1, x2, y2); caca_draw_thin_line(cv, x2, y2, x3, y3); caca_draw_thin_line(cv, x3, y3, x1, y1); return 0; } /** \brief Fill a triangle on the canvas using the given character. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x1 X coordinate of the first point. * \param y1 Y coordinate of the first point. * \param x2 X coordinate of the second point. * \param y2 Y coordinate of the second point. * \param x3 X coordinate of the third point. * \param y3 Y coordinate of the third point. * \param ch UTF-32 character to be used to fill the triangle. * \return This function always returns 0. */ int caca_fill_triangle(caca_canvas_t * cv, int x1, int y1, int x2, int y2, int x3, int y3, uint32_t ch) { int x, y, xmin, xmax, ymin, ymax; int xx1, xx2, xa, xb, sl21, sl31, sl32; /* Bubble-sort y1 <= y2 <= y3 */ if (y1 > y2) return caca_fill_triangle(cv, x2, y2, x1, y1, x3, y3, ch); if (y2 > y3) return caca_fill_triangle(cv, x1, y1, x3, y3, x2, y2, ch); /* Compute slopes and promote precision */ sl21 = (y2 == y1) ? 0 : (x2 - x1) * 0x10000 / (y2 - y1); sl31 = (y3 == y1) ? 0 : (x3 - x1) * 0x10000 / (y3 - y1); sl32 = (y3 == y2) ? 0 : (x3 - x2) * 0x10000 / (y3 - y2); x1 *= 0x10000; x2 *= 0x10000; x3 *= 0x10000; ymin = y1 < 0 ? 0 : y1; ymax = y3 + 1 < cv->height ? y3 + 1 : cv->height; if (ymin < y2) { xa = x1 + sl21 * (ymin - y1); xb = x1 + sl31 * (ymin - y1); } else if (ymin == y2) { xa = x2; xb = (y1 == y3) ? x3 : x1 + sl31 * (ymin - y1); } else /* (ymin > y2) */ { xa = x3 + sl32 * (ymin - y3); xb = x3 + sl31 * (ymin - y3); } /* Rasterize our triangle */ for (y = ymin; y < ymax; y++) { /* Rescale xa and xb, recentering the division */ if (xa < xb) { xx1 = (xa + 0x800) / 0x10000; xx2 = (xb + 0x801) / 0x10000; } else { xx1 = (xb + 0x800) / 0x10000; xx2 = (xa + 0x801) / 0x10000; } xmin = xx1 < 0 ? 0 : xx1; xmax = xx2 + 1 < cv->width ? xx2 + 1 : cv->width; for (x = xmin; x < xmax; x++) caca_put_char(cv, x, y, ch); xa += y < y2 ? sl21 : sl32; xb += sl31; } return 0; } /* This function actually renders the triangle, but is not exported due to sam's pedantic will. */ static int caca_fill_triangle_textured_l(caca_canvas_t * cv, int x1, int y1, int x2, int y2, int x3, int y3, caca_canvas_t * tex, float u1, float v1, float u2, float v2, float u3, float v3) { float y2y1, y3y1, y3y2; float sl12, sl13, sl23; float usl12, usl13, usl23, vsl12, vsl13, vsl23; float xa, xb, ua, va, ub, vb, u, v; uint32_t savedattr; int tw, th, x, y, s; #define SWAP_F(a, b) {float c = a; a = b; b = c; } /* (very) Naive and (very) float-based affine and (very) non-clipped and (very) non-corrected triangle mapper Accepts arbitrary texture sizes Coordinates clamped to [0.0 - 1.0] (no repeat) */ if (!cv || !tex) return -1; /* Bubble-sort y1 <= y2 <= y3 */ if (y1 > y2) return caca_fill_triangle_textured_l(cv, x2, y2, x1, y1, x3, y3, tex, u2, v2, u1, v1, u3, v3); if (y2 > y3) return caca_fill_triangle_textured_l(cv, x1, y1, x3, y3, x2, y2, tex, u1, v1, u3, v3, u2, v2); savedattr = caca_get_attr(cv, -1, -1); /* Clip texture coordinates */ if (u1 < 0.0f) u1 = 0.0f; else if (u1 > 1.0f) u1 = 1.0f; if (u2 < 0.0f) u2 = 0.0f; else if (u2 > 1.0f) u2 = 1.0f; if (u3 < 0.0f) u3 = 0.0f; else if (u3 > 1.0f) u3 = 1.0f; if (v1 < 0.0f) v1 = 0.0f; else if (v1 > 1.0f) v1 = 1.0f; if (v2 < 0.0f) v2 = 0.0f; else if (v2 > 1.0f) v2 = 1.0f; if (v3 < 0.0f) v3 = 0.0f; else if (v3 > 1.0f) v3 = 1.0f; /* Convert relative tex coordinates to absolute */ tw = caca_get_canvas_width(tex); th = caca_get_canvas_height(tex); u1 *= (float)tw; u2 *= (float)tw; u3 *= (float)tw; v1 *= (float)th; v2 *= (float)th; v3 *= (float)th; y2y1 = (float)(y2 - y1); y3y1 = (float)(y3 - y1); y3y2 = (float)(y3 - y2); /* Compute slopes, making sure we don't divide by zero */ /* (in this case, we don't need the value anyway) */ /* FIXME : only compute needed slopes */ sl12 = ((float)x2 - x1) / (y2y1 == 0 ? 1 : y2y1); sl13 = ((float)x3 - x1) / (y3y1 == 0 ? 1 : y3y1); sl23 = ((float)x3 - x2) / (y3y2 == 0 ? 1 : y3y2); usl12 = (u2 - u1) / (y2y1 == 0 ? 1 : y2y1); usl13 = (u3 - u1) / (y3y1 == 0 ? 1 : y3y1); usl23 = (u3 - u2) / (y3y2 == 0 ? 1 : y3y2); vsl12 = (v2 - v1) / (y2y1 == 0 ? 1 : y2y1); vsl13 = (v3 - v1) / (y3y1 == 0 ? 1 : y3y1); vsl23 = (v3 - v2) / (y3y2 == 0 ? 1 : y3y2); xa = (float)x1; xb = (float)x1; ua = u1; ub = u1; va = v1; vb = v1; s = 0; /* Top */ for (y = y1; y < y2; y++) { float tus, tvs; if (xb < xa) { SWAP_F(xb, xa); SWAP_F(sl13, sl12); SWAP_F(ua, ub); SWAP_F(va, vb); SWAP_F(usl13, usl12); SWAP_F(vsl13, vsl12); s = 1; } tus = (ub - ua) / (xb - xa); tvs = (vb - va) / (xb - xa); v = va; u = ua; /* scanline */ for (x = xa; x < xb; x++) { uint32_t attr, c; u += tus; v += tvs; /* FIXME: use caca_get_canvas_attrs / caca_get_canvas_chars */ attr = caca_get_attr(tex, u, v); c = caca_get_char(tex, u, v); caca_set_attr(cv, attr); caca_put_char(cv, x, y, c); } xa += sl13; xb += sl12; ua += usl13; va += vsl13; ub += usl12; vb += vsl12; } if (s) { SWAP_F(xb, xa); SWAP_F(sl13, sl12); SWAP_F(ua, ub); SWAP_F(va, vb); SWAP_F(usl13, usl12); SWAP_F(vsl13, vsl12); } /* Bottom */ xb = (float)x2; /* These variables are set by 'top' routine and are in an incorrect state if we only draw the bottom part */ if (y1 == y2) { ua = u1; ub = u2; va = v1; vb = v2; } for (y = y2; y < y3; y++) { float tus, tvs; if (xb <= xa) { SWAP_F(xb, xa); SWAP_F(sl13, sl23); SWAP_F(ua, ub); SWAP_F(va, vb); SWAP_F(usl13, usl23); SWAP_F(vsl13, vsl23); } tus = (ub - ua) / ((float)xb - xa); tvs = (vb - va) / ((float)xb - xa); u = ua; v = va; /* scanline */ for (x = xa; x < xb; x++) { uint32_t attr, c; u += tus; v += tvs; /* FIXME, can be heavily optimised */ attr = caca_get_attr(tex, u, v); c = caca_get_char(tex, u, v); caca_set_attr(cv, attr); caca_put_char(cv, x, y, c); } xa += sl13; xb += sl23; ua += usl13; va += vsl13; ub += usl23; vb += vsl23; } caca_set_attr(cv, savedattr); return 0; } /** \brief Fill a triangle on the canvas using an arbitrary-sized texture. * * This function fails if one or both the canvas are missing * * \param cv The handle to the libcaca canvas. * \param coords The coordinates of the triangle (3{x,y}) * \param tex The handle of the canvas texture. * \param uv The coordinates of the texture (3{u,v}) * \return This function return 0 if ok, -1 if canvas or texture are missing. */ int caca_fill_triangle_textured(caca_canvas_t * cv, int coords[6], caca_canvas_t * tex, float uv[6]) { return caca_fill_triangle_textured_l(cv, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], tex, uv[0], uv[1], uv[2], uv[3], uv[4], uv[5]); } ================================================ FILE: config.h ================================================ #ifndef __CONFIG_H #define __CONFIG_H /* Warnings I don't care about */ #pragma warning(disable : 4001) // single-line comments #pragma warning(disable : 4096) // varargs are always cdecl #pragma warning(disable : 4127) // conditional expression constant #pragma warning(disable : 4704) // in-line assembler #pragma warning(disable : 4505) // unreferenced local function // Warnings i don't mind unless i'm debugging. #ifdef NDEBUG # pragma warning(disable : 4100) // unreferenced formal parameter # pragma warning(disable : 4101) // unreferenced local variable #endif // Dont create logfiles for release builds. #ifdef RELEASE # define DEBUG_LOGFILE NULL #else // You can also set "CON" to show messages on stdout. # define DEBUG_LOGFILE "H:\\DEBUG.LOG" #endif // Unfortunately we can't use stdio in addins, because the crt uses malloc. #define HAVE_SNPRINTF #define PREFER_PORTABLE_SNPRINTF #include "snprintf.h" #define snprintf portable_snprintf #define vsnprintf portable_vsnprintf #define true 1 #define false 0 // In text mode, the screen resolution is queried via int 10,f: // https://stanislavs.org/helppc/int_10-f.html // // The number of columns is returned in AL, so it cannot possibly // exceed 256, because that's only an 8bit register. #define MAX_COLS 256 // The selectors for the BIOS Data Area and CGA Framebuffer. We // can't access these directly, we need to request 123 installs // an LDT entry for us. #define BIOS_DATA_AREA 0x0040 #define CGA_FRAMEBUF 0xb800 #endif ================================================ FILE: debug/Makefile ================================================ DFLAGS=-quiet -dumb ASFLAGS=/m3 /q /z /w0 CPPFLAGS=/Iinclude WARNLEVEL=4 CFLAGS=/AL /nologo /NDAAA /Od /G3 /Gs /Gc /Zi /FPi87 /Zp1 /Zl /Gf /f- /W$(WARNLEVEL) /WX LDFLAGS=/b /nologo /onerror:noexe /nod /noe LDLIBS=LLIBC7 EFLAGS=/verbose /nologo .PHONY: clean # MS-DOS doesn't allow long commandlines, so params are written to a "response" # file. %.obj: %.c @rm -f $@ printf "$(CPPFLAGS) $(CFLAGS)\r\n" > $(@:.obj=.cl) dosemu $(DFLAGS) -E "IN G:/ CL /c @$(@:.obj=.cl) /Fo$@ $<" @rm -f $(@:.obj=.cl) %.obj: %.asm @rm -f $@ dosemu $(DFLAGS) -E "IN G:/ TASM32 /c $(ASFLAGS) $<" all: l13vcgad.obj l13vcgaf.obj logcalls.obj logcbs.obj clean: rm -f *.exe *.obj *.dld *.map *.pdb *.lnk *.cl chmod u=rw,go=r *.asm *.names *.idb ================================================ FILE: debug/exports.inc ================================================ GLOBAL GetDisplayInfo GLOBAL DriverInit GLOBAL DriverTerminate GLOBAL ResetDisplay GLOBAL SetGraphicsMode GLOBAL MoveCursor GLOBAL WriteLmbcsStringWithAttributes GLOBAL WritePaddedLmbcsStringWithAttributes GLOBAL SetRegionBgAttributes GLOBAL ClearRegionForeground GLOBAL ClearRegionForegroundKeepBg GLOBAL BlockRegionCopy GLOBAL CalcSizeTranslatedString GLOBAL FitTranslatedString GLOBAL SetCursorInvisible GLOBAL SetCursorVisible GLOBAL LockCursorAttributes GLOBAL QQCalledBeforeGraphicsMode GLOBAL QQLotusApiJustCallsFunc_1 GLOBAL MoveCursor2 GLOBAL QQLotusApiJustCallsFunc_0 GLOBAL ComplicatedNop GLOBAL zerosub_0 GLOBAL LotusApiPassedPtr GLOBAL LotusApiJustCallsFunc GLOBAL nullsub_5 GLOBAL exprt_scan_linx GLOBAL exprt_fill_rect GLOBAL exprt_thin_diag_line GLOBAL exprt_thin_vert_line GLOBAL exprt_shade_rect GLOBAL exprt_fill_scan_list ================================================ FILE: debug/l13vcgad.asm ================================================ ; vim: set ft=tasm: version m520 .model large,stdcall .486 DOSSEG CODESEG extern _wrap_DriverInit: proc extern _wrap_GetDisplayInfo: proc extern _wrap_DriverTerminate: proc extern _wrap_ResetDisplay: proc extern _wrap_SetGraphicsMode: proc extern _wrap_MoveCursor: proc extern _wrap_WriteLmbcsStringWithAttributes: proc extern _wrap_WritePaddedLmbcsStringWithAttributes: proc extern _wrap_SetRegionBgAttributes: proc extern _wrap_ClearRegionForeground: proc extern _wrap_ClearRegionForegroundKeepBg: proc extern _wrap_BlockRegionCopy: proc extern _wrap_CalcSizeTranslatedString: proc extern _wrap_FitTranslatedString: proc extern _wrap_SetCursorInvisible: proc extern _wrap_SetCursorVisible: proc extern _wrap_LockCursorAttributes: proc extern _wrap_QQCalledBeforeGraphicsMode: proc extern _wrap_QQLotusApiJustCallsFunc_1: proc extern _wrap_MoveCursor2: proc extern _wrap_QQLotusApiJustCallsFunc_0: proc extern _wrap_ComplicatedNop: proc extern _wrap_zerosub_0: proc extern _wrap_LotusApiPassedPtr: proc extern _wrap_LotusApiJustCallsFunc: proc extern _wrap_nullsub_5: proc start dd _wrap_GetDisplayInfo dd _wrap_DriverInit dd _wrap_DriverTerminate dd _wrap_ResetDisplay dd _wrap_SetGraphicsMode dd _wrap_MoveCursor dd _wrap_WriteLmbcsStringWithAttributes dd _wrap_WritePaddedLmbcsStringWithAttributes dd _wrap_SetRegionBgAttributes dd _wrap_ClearRegionForeground dd _wrap_ClearRegionForegroundKeepBg dd _wrap_BlockRegionCopy dd _wrap_CalcSizeTranslatedString dd _wrap_FitTranslatedString dd _wrap_SetCursorInvisible dd _wrap_SetCursorVisible dd _wrap_LockCursorAttributes dd _wrap_QQCalledBeforeGraphicsMode dd _wrap_QQLotusApiJustCallsFunc_1 dd _wrap_MoveCursor2 dd _wrap_QQLotusApiJustCallsFunc_0 dd _wrap_ComplicatedNop dd _wrap_zerosub_0 dd _wrap_LotusApiPassedPtr dd _wrap_LotusApiJustCallsFunc dd _wrap_nullsub_5 dw seg _DATA ; This is just here to satisfy the crt, it should never be called. global _main: proc _main proc far mov ax, 4141h int 3 _main endp ; There is an object file that screws up my alignment in the CRT, and ; I don't even need it. ; ; If you define a symbol, and then use /NOE, the linkers uses this one, ; so this stops that nuisance object from being linked into my executable. global __nullcheck: proc __nullcheck proc far mov ax, 4242h int 3 __nullcheck endp end start ================================================ FILE: debug/logcalls.c ================================================ #include #include #include #include #include #include #include #include "config.h" #include "debug.h" #include "idatypes.h" #include "lottypes.h" #include "lotcalls.h" // This is a debugging shim that logs what a real driver is doing. // // The idea is that I can compare the logs with what my driver is doing, and // check where they disagree. // // Because 123 uses callee clears calling convention, unfortunately I // have to know the correct parameters, and can't just use a generic // logger. struct LOTUSFUNCS far *callbacks; struct LOTUSFUNCS cbwrappers; extern void InstallCallbackWrapper(struct LOTUSFUNCS *cbarray); extern int __pascal GetDisplayInfo(struct DISPLAYINFO far *dspinfo); int __pascal _wrap_GetDisplayInfo(struct DISPLAYINFO far *dspinfo) { int result; traceent("GetDisplayInfo"); traceptr(dspinfo); result = GetDisplayInfo(dspinfo); tracebuf(dspinfo, sizeof *dspinfo); return result; } extern int __pascal DriverInit(void *drvapi, void *vbdptr, char *cfgname, char deflmbcsgrp); static unsigned d; int __pascal _wrap_DriverInit(void *drvapi, void *vbdptr, char *cfgname, char deflmbcsgrp) { int result; openlog("H:\\CORRECT.LOG"); traceent("DriverInit"); traceptr(drvapi); traceptr(vbdptr); tracestr(cfgname); tracechr(deflmbcsgrp); // Save a pointer to real callbacks. callbacks = drvapi; // Make a copy of these callbacks so that I can patch them and replace // with debugging wrappers. memcpy(&cbwrappers, callbacks, sizeof *callbacks); tracebuf(callbacks, sizeof *callbacks); InstallCallbackWrapper(&cbwrappers); result = DriverInit(&cbwrappers, vbdptr, cfgname, deflmbcsgrp); traceint(result); trace("XXX"); callbacks->GetDescriptor(&d, 0, 0x100); traceint(d); return result; } extern int __pascal DriverTerminate(); int __pascal _wrap_DriverTerminate() { int result; traceent("DriverTerminate"); //closelog(); result = DriverTerminate(); trace("XXX"); callbacks->FreeDescriptor(d); traceint(result); return result; } extern int __pascal ResetDisplay(); int __pascal _wrap_ResetDisplay() { traceent("ResetDisplay"); return ResetDisplay(); } extern int __pascal SetGraphicsMode(); int __pascal _wrap_SetGraphicsMode() { traceent("SetGraphicsMode"); return SetGraphicsMode(); } void __pascal _wrap_MoveCursor(int col, int line) { traceent("MoveCursor"); traceint(col); traceint(line); MoveCursor(col, line); return; } extern int __pascal WriteLmbcsStringWithAttributes(int byteslen, char far *lmbcsptr, char attrs); int __pascal _wrap_WriteLmbcsStringWithAttributes(int byteslen, char far *lmbcsptr, char attrs) { int result; traceent("WriteLmbcsStringWithAttributes"); traceint(byteslen); tracestrlen(lmbcsptr, byteslen); traceval("%x", attrs); result = WriteLmbcsStringWithAttributes(byteslen, lmbcsptr, attrs); traceint(result); return result; } extern int __pascal WritePaddedLmbcsStringWithAttributes(int byteslen, char far *lmbcsptr, char attrs, int startpad, int endpad); int __pascal _wrap_WritePaddedLmbcsStringWithAttributes(int byteslen, char far *lmbcsptr, char attrs, int startpad, int endpad) { int result; traceent("WritePaddedLmbcsStringWithAttributes"); traceint(byteslen); tracestrlen(lmbcsptr, byteslen); traceval("%x", attrs); traceint(startpad); traceint(endpad); result = WritePaddedLmbcsStringWithAttributes(byteslen, lmbcsptr, attrs, startpad, endpad); traceint(result); return result; } extern int __pascal SetRegionBgAttributes(int cols, int lines, char attrs); int __pascal _wrap_SetRegionBgAttributes(int cols, int lines, char attrs) { traceent("SetRegionBgAttributes"); traceint(cols); traceint(lines); traceval("%x", attrs); return SetRegionBgAttributes(cols, lines, attrs); } void __pascal _wrap_ClearRegionForeground(int cols, int lines, char attrs) { traceent("ClearRegionForeground"); traceint(cols); traceint(lines); traceval("%x", attrs); ClearRegionForeground(cols, lines, attrs); return; } extern int __pascal ClearRegionForegroundKeepBg(int cols, int lines); int __pascal _wrap_ClearRegionForegroundKeepBg(int cols, int lines) { traceent("ClearRegionForegroundKeepBg"); traceint(cols); traceint(lines); return ClearRegionForegroundKeepBg(cols, lines); } extern int __pascal BlockRegionCopy(); int __pascal _wrap_BlockRegionCopy() { traceent("BlockRegionCopy"); __debugbreak(); return 0; } extern int __pascal CalcSizeTranslatedString(int argstrlen, char far *argstr); int __pascal _wrap_CalcSizeTranslatedString(int argstrlen, char far *argstr) { int result; traceent("CalcSizeTranslatedString"); tracestrlen(argstr, argstrlen); traceint(argstr); result = CalcSizeTranslatedString(argstrlen, argstr); traceint(result); return result; } extern int __pascal FitTranslatedString(int strarglen, char far *strarg, int ncols, int far *bytesneeded); int __pascal _wrap_FitTranslatedString(int strarglen, char far *strarg, int ncols, int far *bytesneeded) { int result; traceent("FitTranslatedString"); traceint(strarglen); tracestrlen(strarg, strarglen); traceint(ncols); traceptr(bytesneeded); result = FitTranslatedString(strarglen, strarg, ncols, bytesneeded); traceint(result); traceint(*bytesneeded); return result; } extern int __pascal SetCursorInvisible(); int __pascal _wrap_SetCursorInvisible() { traceent("SetCursorInvisible"); return SetCursorInvisible(); } extern int __pascal SetCursorVisible(); int __pascal _wrap_SetCursorVisible() { traceent("SetCursorVisible"); return SetCursorVisible(); } extern int __pascal LockCursorAttributes(); int __pascal _wrap_LockCursorAttributes() { traceent("LockCursorAttributes"); return LockCursorAttributes(); } extern int __pascal QQCalledBeforeGraphicsMode(void far *p); int __pascal _wrap_QQCalledBeforeGraphicsMode(void far *p) { int result; traceent("QQCalledBeforeGraphicsMode"); traceptr(p); result = QQCalledBeforeGraphicsMode(p); return result; } extern int __pascal QQLotusApiJustCallsFunc_1(int strarglen, void far *strarg); int __pascal _wrap_QQLotusApiJustCallsFunc_1(int strarglen, void far *strarg) { int result; traceent("QQLotusApiJustCallsFunc_1"); traceint(strarglen); tracebuf(strarg, strarglen); result = QQLotusApiJustCallsFunc_1(strarglen, strarg); traceint(result); return result; } extern int __pascal MoveCursor2(int col, int line); int __pascal _wrap_MoveCursor2(int col, int line) { int result; traceent("MoveCursor2"); traceint(col); traceint(line); result = MoveCursor2(col, line); traceint(result); return result; } extern int __pascal QQLotusApiJustCallsFunc_0(void far *ptr); int __pascal _wrap_QQLotusApiJustCallsFunc_0(void far *ptr) { int result; traceent("QQLotusApiJustCallsFunc_0"); traceptr(ptr); tracebuf(ptr, 16); result = QQLotusApiJustCallsFunc_0(ptr); traceint(result); tracebuf(ptr, 16); return result; } extern int __pascal ComplicatedNop(); int __pascal _wrap_ComplicatedNop() { traceent("ComplicatedNop"); __debugbreak(); return 0; } extern int __pascal zerosub_0(); int __pascal _wrap_zerosub_0() { traceent("zerosub_0"); __debugbreak(); return 0; } extern int __pascal LotusApiPassedPtr(void far *ptr); int __pascal _wrap_LotusApiPassedPtr(void far *ptr) { traceent("LotusApiPassedPtr"); traceptr(ptr); return LotusApiPassedPtr(ptr); } extern int __pascal LotusApiJustCallsFunc(int a, void far *ptra, int d, void far *ptrb); int __pascal _wrap_LotusApiJustCallsFunc(int a, void far *ptra, int d, void far *ptrb) { int result; traceent("LotusApiJustCallsFunc"); traceint(a); traceptr(ptra); tracebuf(ptra, 16); traceint(d); traceptr(ptrb); tracebuf(ptrb, 16); result = LotusApiJustCallsFunc(a, ptra, d, ptrb); traceint(result); return result; } extern int __pascal nullsub_5(); int __pascal _wrap_nullsub_5() { traceent("nullsub_5"); __debugbreak(); return 0; } ================================================ FILE: debug/logcbs.c ================================================ #include #include #include #include #include #include #include #include "config.h" #include "debug.h" #include "idatypes.h" #include "lottypes.h" #include "lotcalls.h" extern void __pascal __far exprt_scan_linx(int, int, int, int); extern void __pascal __far exprt_fill_rect(int, int, int, int, int); extern void __pascal __far exprt_thin_diag_line(int, int, int, int, int); extern void __pascal __far exprt_thin_vert_line(int, int, int, int); extern int __pascal __far exprt_shade_rect(int, int, int, int, void far *, int); extern void __pascal __far exprt_fill_scan_list(); // This is a debugging shim that logs what a real driver is doing. static void far * __pascal _wrap_AllocMem(int a, int size, int vmr) { void far *result; trace("callbacks->AllocMem"); traceint(a); traceint(size); traceint(vmr); result = callbacks->AllocMem(a, size, vmr); traceptr(result); tracebuf(result, size); return result; } static void __pascal _wrap_lfield_8(void far *ptr, int size) { trace("callbacks->lfield_8"); traceptr(ptr); traceint(size); tracebuf(ptr, size); callbacks->lfield_8(ptr, size); tracebuf(ptr, size); return; } static void far * __pascal _wrap_Allocate(int tag, int size) { void *result; trace("callbacks->Allocate"); traceint(tag); traceint(size); result = callbacks->Allocate(tag, size); traceptr(result); tracebuf(result, size); return result; } static int __pascal _wrap_Free(void *addr, int unknown, int size) { int result; trace("callbacks->Free"); traceptr(addr); traceint(unknown); traceint(size); tracebuf(addr, size); result = callbacks->Free(addr, unknown, size); traceint(result); return result; } static void * __pascal _wrap_MapMemVmr(void far *ptr, int v) { void *result; trace("callbacks->MapMem"); traceptr(ptr); traceint(v); result = callbacks->MapMemVmr(ptr, v); traceptr(result); return result; } static void far * __pascal _wrap_LoadVmr(int vmr) { void *result = 0; trace("callbacks->LoadVmr"); traceint(vmr); result = callbacks->LoadVmr(vmr); traceptr(result); return result; } static void __pascal _wrap_UnregisterDevdata(int hdl) { trace("callbacks->UnregisterDevdata"); traceint(hdl); callbacks->UnregisterDevdata(hdl); return; } static unsigned __pascal _wrap_GetDescriptor(unsigned far *ptr, int base, int limit) { int result; traceptr(ptr); traceval("%#x", *ptr); traceint(base); traceint(limit); trace("callbacks->GetDescriptor"); result = callbacks->GetDescriptor(ptr, base, limit); traceint(*ptr); traceint(result); return result; } static void __pascal _wrap_FreeDescriptor(int seg) { trace("callbacks->FreeDescriptor"); traceint(seg); callbacks->FreeDescriptor(seg); return; } static void __pascal _wrap_exprt_scan_linx(int a, int b, int c, int d) { trace("devfuncs->exprt_scan_linx"); traceint(a); traceint(b); traceint(c); traceint(d); exprt_scan_linx(a, b, c, d); return; } static void __pascal _wrap_exprt_fill_rect(int a, int b, int c, int d, int e) { trace("devfuncs->wrap_exprt_fill_rect"); traceint(a); traceint(b); traceint(c); traceint(d); traceint(e); exprt_fill_rect(a, b, c, d, e); return; } static void __pascal _wrap_exprt_thin_diag_line(int a, int b, int c, int d, int e) { trace("devfuncs->exprt_thin_diag_line"); traceint(a); traceint(b); traceint(c); traceint(d); traceint(e); exprt_thin_diag_line(a, b, c, d, e); return; } static void __pascal _wrap_exprt_thin_vert_line(int a, int b, int c, int d) { trace("devfuncs->exprt_thin_vert_line"); traceint(a); traceint(b); traceint(c); traceint(d); exprt_thin_vert_line(a, b, c, d); return; } static void __pascal _wrap_exprt_shade_rect(int a, int b, int c, int d, void far *e, int f) { trace("devfuncs->exprt_shade_rect"); traceint(a); traceint(b); traceint(c); traceint(d); traceptr(e); traceint(f); exprt_shade_rect(a, b, c, d, e, f); return; } static void __pascal _wrap_exprt_fill_scan_list() { trace("devfuncs->exprt_fill_scan_list"); exprt_fill_scan_list(); return; } static int __pascal _wrap_RegisterDevdataStruct(struct DEVDATA far *a, int far *b) { int result; int i; trace("callbacks->RegisterDevdataStruct"); traceptr(a); traceptr(b); tracebuf(a, sizeof *a); traceint(a->ShowMeFlag); traceint(a->RasterHeight); traceint(a->AspectX); traceint(a->AspectY); traceint(a->RHmusPerTHmu); traceint(a->RHmuPerGHmus); traceint(a->RVmusPerTVmu); traceint(a->RVmuPerGVmus); for (i = 0; i < 12; i++) { traceint(a->FillPatts[i].pattsize.pty); traceint(a->FillPatts[i].pattsize.ptx); traceint(a->FillPatts[i].patthotSpot.pty); traceint(a->FillPatts[i].patthotSpot.ptx); traceptr(a->FillPatts[i].pattptr); tracebuf(a->FillPatts[i].pattptr, 16); traceint(a->FillPatts[i].pattSecOffset.pty); traceint(a->FillPatts[i].pattSecOffset.ptx); } tracebuf(a->ColorMap, sizeof a->ColorMap); tracebuf(a->SrcPatD, sizeof a->SrcPatD); tracebuf(a->SrcPatS, sizeof a->SrcPatS); traceint(a->FIndexCnt); traceptr(a->FontIndex); traceint(a->FNamesCnt); traceptr(a->FontNames); // Install our wrappers. a->DevFuncs.fill_scan_list = _wrap_exprt_fill_scan_list; a->DevFuncs.shade_rect = _wrap_exprt_shade_rect; a->DevFuncs.thin_vert_line = _wrap_exprt_thin_vert_line; a->DevFuncs.thin_diag_line = _wrap_exprt_thin_diag_line; a->DevFuncs.fill_rect = _wrap_exprt_fill_rect; a->DevFuncs.scan_linx = _wrap_exprt_scan_linx; result = callbacks->RegisterDevdataStruct(a, b); traceint(result); traceint(*b); return result; } static int __pascal _wrap_field_64(int a, void far *ptr) { int result; trace("callbacks->field_64"); traceint(a); traceptr(ptr); tracebuf(ptr, 16); __breakpoint(0x297, 0x23d5); result = callbacks->field_64(a, ptr); __debugbreak(); traceint(result); tracebuf(ptr, 16); return result; } static void __pascal _wrap_field_58(int a, int b) { trace("callbacks->field_58"); traceint(a); traceint(b); callbacks->field_58(a, b); return; } static int __pascal _wrap_field_5C(int a, void far *b) { int result; trace("callbacks->field_5C"); traceint(a); traceptr(b); result = callbacks->field_5C(a, b); traceint(result); return result; } static int __pascal _wrap_field_68(int a, int b, void far *ptr1, void far *ptr2) { int result; trace("callbacks->field_68"); traceint(a); traceint(b); traceptr(ptr1); traceptr(ptr2); result = callbacks->field_68(a, b, ptr1, ptr2); traceint(result); return result; } static int __pascal _wrap_field_6C(int a, int b, void *c, int d, void far *e, void far *f) { int result; trace("callbacks->field_6C"); traceint(a); traceint(b); traceptr(c); traceint(d); traceptr(e); traceptr(f); result = callbacks->field_6C(a, b, c, d, e, f); traceint(result); return result; } void InstallCallbackWrapper(struct LOTUSFUNCS *cbarray) { memset(cbarray, 0xcc, sizeof *cbarray); cbarray->AllocMem = _wrap_AllocMem; cbarray->lfield_8 = _wrap_lfield_8; cbarray->Allocate = _wrap_Allocate; cbarray->Free = _wrap_Free; cbarray->MapMemVmr= _wrap_MapMemVmr; cbarray->LoadVmr = _wrap_LoadVmr; cbarray->RegisterDevdataStruct = _wrap_RegisterDevdataStruct; cbarray->field_58 = _wrap_field_58; cbarray->field_5C = _wrap_field_5C; cbarray->UnregisterDevdata = _wrap_UnregisterDevdata; cbarray->field_64 = _wrap_field_64; cbarray->field_68 = _wrap_field_68; cbarray->field_6C = _wrap_field_6C; cbarray->GetDescriptor = _wrap_GetDescriptor; cbarray->FreeDescriptor = _wrap_FreeDescriptor; } ================================================ FILE: debug.c ================================================ #include #include #include #include #include #include #include #include #include "config.h" #include "debug.h" static int logfile = -1; int openlog(const char *name) { // Name not specified. if (name == NULL) return 1; // Logfile already open. if (logfile != -1) return 1; if (_dos_creat(name, _A_NORMAL, &logfile) != 0) { logmsg("_dos_creat failed to open logfile %s", name); return 1; } return 0; } int closelog(void) { // If we're not logging, just return. if (logfile == -1) { return 1; } _dos_close(logfile); logfile = -1; return 0; } int logstr(const char *fmt, ...) { va_list ap; unsigned len; char line[128]; // If we're not logging, just return. if (logfile == -1) return 1; va_start(ap, fmt); portable_vsnprintf(line, sizeof(line), fmt, ap); va_end(ap); _dos_write(logfile, line, strlen(line), &len); return 0; } int logmsg(const char *fmt, ...) { va_list ap; unsigned len; char line[128]; // If we're not logging, just return. if (logfile == -1) return 1; va_start(ap, fmt); portable_vsnprintf(line, sizeof(line), fmt, ap); va_end(ap); _dos_write(logfile, line, strlen(line), &len); _dos_write(logfile, "\r\n", 2, &len); return 0; } void loghex(const void far *ptr, int buflen) { const unsigned char *buf = ptr; int i, j; // If we're not logging, just return. if (logfile == -1) return; for (i = 0; i < buflen; i += 16) { logstr("%06x: ", i); for (j = 0; j < 16; j++) if (i + j < buflen) logstr("%02x ", buf[i+j]); else logstr(" "); logstr(" "); for (j = 0; j < 16; j++) { if (i + j < buflen) { if (buf[i+j] >= ' ' && buf[i+j] <= '~') { logstr("%c", buf[i+j]); } else { logstr("."); } } } logstr("\r\n"); } } ================================================ FILE: debug.h ================================================ #ifndef __DEBUG_H #define __DEBUG_H // We have to do a lot of printf debugging, so a few macros to help. #ifndef NDEBUG # define trace(str) logmsg("%s:%d: TRACE %s", __FILE__, __LINE__, (str)) # define traceval(fmt, val) \ logmsg("%s:%d: TRACE %s " fmt, __FILE__, __LINE__, #val, (val)) # define traceint(val) \ logmsg("%s:%d: TRACE %s %d", __FILE__, __LINE__, #val, (val)) # define tracestr(val) \ logmsg("%s:%d: TRACE %s \"%s\"", __FILE__, __LINE__, #val, (val)) # define tracestrlen(val, len) \ logmsg("%s:%d: TRACE %s \"%.*s\"", __FILE__, __LINE__, #val, (len), (val)) # define traceptr(val) \ logmsg("%s:%d: TRACE %s %p", __FILE__, __LINE__, #val, (val)) # define tracechr(val) \ logmsg("%s:%d: TRACE %s %hd", __FILE__, __LINE__, #val, (val)) # define tracebuf(ptr, len) do { \ logmsg("%s:%d: TRACE %s (%d bytes):", __FILE__, __LINE__, #ptr, (len)); \ loghex(ptr, len); \ } while (false) # define traceent(name) do { \ int ra, rs; \ __asm { mov ax, word ptr [bp+2] }; \ __asm { mov ra, ax }; \ __asm { mov ax, word ptr [bp+4] }; \ __asm { mov rs, ax }; \ logmsg("ENTER %s, caller %hx:%hx", (name), rs, ra); \ } while (false) #else # define trace(str) # define traceval(fmt, val) # define traceint(val) # define tracestr(val) # define traceptr(val) # define tracechr(val) # define traceent(name) # define tracebuf(ptr, len) # define tracestrlen(val, len) #endif // Put some useful values in registers so dumps are useful. #define __debugbreak() do { \ int line = __LINE__; \ char *file = __FILE__; \ __asm { mov si, line }; \ __asm { les di, file }; \ __asm { int 3 }; \ } while (false) // I cant use conditional breakpoints, this will have to do. #define __breakafter(n) do { \ static int count; \ if (++count >= (n)) \ __asm { int 3 }; \ } while (false) // These get replaced on overlay switch, but sometimes handy. #define __breakpoint(s, o) do { \ unsigned char far *bpint = MK_FP(s, o); \ *bpint = 0xCC; \ } while (false) int openlog(const char *name); int closelog(void); int logmsg(const char *fmt, ...); int logstr(const char *fmt, ...); void loghex(const void far *ptr, int buflen); #endif ================================================ FILE: draw.c ================================================ #include #include #include #include #include #include #include #include #include "config.h" #include "debug.h" #include "idatypes.h" #include "lottypes.h" #include "lotcalls.h" #include "bundle.h" #include "lmbcs.h" #include "attr.h" #include "caca.h" #include "draw.h" // The canvas used for drawing ascii-art graphics. extern caca_canvas_t *cv; void __pascal exprt_scan_linx(int x, int y, int width, int attr) { traceent("exprt_scan_linx"); traceint(x); traceint(y); traceint(width); traceint(attr); caca_set_color_ansi(cv, attr, attr); caca_draw_line(cv, x, y, x + width, y, '-'); return; } void __pascal exprt_fill_rect(int x, int y, int width, int height, int attr) { traceent("exprt_fill_rect"); traceint(x); traceint(y); traceint(width); traceint(height); traceint(attr); traceptr(cv); caca_set_color_ansi(cv, attr, CACA_WHITE); caca_fill_box(cv, x, y, width, height, ' '); return; } void __pascal exprt_thin_diag_line(int x1, int y1, int x2, int y2, int attr) { traceent("exprt_thin_diag_line"); traceint(x1); traceint(y1); traceint(x2); traceint(y2); traceint(attr); caca_set_color_ansi(cv, attr, attr); caca_draw_thin_line(cv, x1, y1, x2, y2); return; } void __pascal exprt_thin_vert_line(int x, int y, int height, int attr) { traceent("exprt_thin_vert_line"); traceint(x); traceint(y); traceint(height); traceint(attr); caca_set_color_ansi(cv, attr, attr); caca_draw_line(cv, x, y, x, y + height, '|'); return; } void __pascal exprt_shade_rect(int a, int b, int c, int d, void far *e, int f) { traceent("exprt_shade_rect"); traceint(a); traceint(b); traceint(c); traceint(d); traceptr(e); traceint(f); return; } void __pascal exprt_fill_scan_list() { traceent("exprt_fill_scan_list"); return; } ================================================ FILE: draw.h ================================================ #ifndef __DRAW_H #define __DRAW_H extern void __pascal exprt_scan_linx(int a, int b, int c, int d); extern void __pascal exprt_fill_rect(int a, int b, int c, int d, int e); extern void __pascal exprt_thin_diag_line(int a, int b, int c, int d, int e); extern void __pascal exprt_thin_vert_line(int a, int b, int c, int d); extern void __pascal exprt_shade_rect(int a, int b, int c, int d, void far *e, int f); extern void __pascal exprt_fill_scan_list(); extern caca_canvas_t *cv; #endif ================================================ FILE: idatypes.h ================================================ #ifndef __IDATYPES_H #define __IDATYPES_H // Compatability with IDA generated code. typedef long __int32; typedef unsigned char bool; #endif ================================================ FILE: l13vdemu.asm ================================================ ; vim: set ft=tasm: version m520 .model large,stdcall .486 DOSSEG CODESEG extern drv_disp_info: proc extern drv_disp_open: proc extern drv_disp_close: proc extern drv_disp_text: proc extern drv_disp_graph: proc extern drv_disp_set_pos: proc extern drv_disp_write: proc extern drv_disp_zone: proc extern drv_disp_set_bg: proc extern drv_disp_clear: proc extern drv_disp_fg_clear: proc extern drv_disp_copy: proc extern drv_disp_size: proc extern drv_disp_fit: proc extern drv_disp_curs_off: proc extern drv_disp_curs_on: proc extern drv_disp_curs_type: proc extern drv_disp_grph_process: proc extern drv_disp_grph_txt_size: proc extern drv_disp_set_pos_hpu: proc extern drv_disp_grph_compute_view: proc extern drv_disp_unlock: proc extern drv_disp_lock: proc extern drv_disp_grph_set_cur_view: proc extern drv_disp_grph_txt_fit: proc extern drv_disp_sync: proc start dd drv_disp_info dd drv_disp_open dd drv_disp_close dd drv_disp_text dd drv_disp_graph dd drv_disp_set_pos dd drv_disp_write dd drv_disp_zone dd drv_disp_set_bg dd drv_disp_clear dd drv_disp_fg_clear dd drv_disp_copy dd drv_disp_size dd drv_disp_fit dd drv_disp_curs_off dd drv_disp_curs_on dd drv_disp_curs_type dd drv_disp_grph_process dd drv_disp_grph_txt_size dd drv_disp_set_pos_hpu dd drv_disp_grph_compute_view dd drv_disp_unlock dd drv_disp_lock dd drv_disp_grph_set_cur_view dd drv_disp_grph_txt_fit dd drv_disp_sync dw seg _DATA ; This is just here to satisfy the crt, it should never be called. global _main: proc _main proc far mov ax, 4141h int 3 _main endp ; There is an object file that screws up my alignment in the CRT, and ; I don't even need it. ; ; If you define a symbol, and then use /NOE, the linkers uses this one, ; so this stops that nuisance object from being linked into my executable. global __nullcheck: proc __nullcheck proc far mov ax, 4242h int 3 __nullcheck endp end start ================================================ FILE: l13vdemu.def ================================================ LIBRARY L13VDEMU DESCRIPTION 'DOSEMU2 DISPLAY DRIVER FOR LOTUS 123R4D' HEAPSIZE 0 STACKSIZE 0 DATA MULTIPLE EXPORTS drv_disp_info @1 drv_disp_open @2 drv_disp_close @3 drv_disp_text @4 drv_disp_graph @5 drv_disp_set_pos @6 drv_disp_write @7 drv_disp_zone @8 drv_disp_set_bg @9 drv_disp_clear @10 drv_disp_fg_clear @11 drv_disp_copy @12 drv_disp_size @13 drv_disp_fit @14 drv_disp_curs_off @15 drv_disp_curs_on @16 drv_disp_curs_type @17 drv_disp_grph_process @18 drv_disp_grph_txt_size @19 drv_disp_set_pos_hpu @20 drv_disp_grph_compute_view @21 drv_disp_unlock @22 drv_disp_lock @23 drv_disp_grph_set_cur_view @24 drv_disp_grph_txt_fit @25 drv_disp_sync @26 ================================================ FILE: lmbcs.c ================================================ #include #include #include #include #include #include #include #include #include "config.h" #include "debug.h" static const unsigned char lmbcs_group1_table[256] = { 0xB1, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xB1, 0x7E, 0xF8, 0x5E, 0x60, 0x27, 0x22, 0x27, 0xB1, 0x2D, 0xC4, 0xB1, 0xB1, 0xB1, 0x3C, 0x3E, 0xB1, 0x7E, 0xF8, 0x5E, 0x60, 0x27, 0xB1, 0x2C, 0x22, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0x98, 0xB1, 0xB1, 0xB1, 0xC6, 0xC7, 0xDD, 0xDE, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xB5, 0xB6, 0xB7, 0xB8, 0xBD, 0xBE, 0xCF, 0xB1, 0xB1, 0xB1, 0xB1, 0xFC, 0x6C, 0x4C, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0x4B, 0xA9, 0x9C, 0x9E, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x6F, 0x9C, 0x4F, 0x78, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0x54, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0x41, 0x41, 0x41, 0x63, 0xB9, 0xBA, 0xBB, 0xBC, 0x9B, 0x9D, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0x61, 0x41, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xB1, 0x64, 0x44, 0x45, 0x45, 0x45, 0x69, 0x49, 0x49, 0x49, 0xD9, 0xDA, 0xDB, 0xDC, 0x7C, 0x49, 0xDF, 0x4F, 0xE1, 0x4F, 0x4F, 0x6F, 0x4F, 0xE6, 0x70, 0x50, 0x55, 0x55, 0x55, 0x79, 0x59, 0x5F, 0x27, 0x2D, 0xF1, 0x3D, 0xB1, 0x14, 0x15, 0xF6, 0x2C, 0xF8, 0x22, 0xF9, 0x31, 0x33, 0xFD, 0xFE, 0xFF }; // Add more code pages here, as needed, these are these tables: // https://en.wikipedia.org/wiki/Lmbcs#Group_1 static const unsigned char * lmbcs_tables[31] = { NULL, // 0 lmbcs_group1_table, // 1 }; // This is the table from https://en.wikipedia.org/wiki/Lmbcs#Encodings static const int lmbcs_group_length[] = { 1, // NUL 2, // Code page 850 (DOS Latin-1) 2, // Code page 851 (DOS Greek) 2, // Code page 1255 (Windows Hebrew) 2, // Code page 1256 (Windows Arabic) 2, // Code page 1251 (Windows Cyrillic) 2, // Code page 852 (DOS Latin-2) 1, // BEL 2, // Code page 1254 (Windows Turkish) 1, // TAB 1, // LF 2, // Code page 874 (Thai) 2, // Reserved 1, // CR 2, // Reserved 2, // Remapped C0/C1 control codes 3, // Code page 932/ 3, // Code page 949/ 3, // Code page 950 3, // Code page 936/ 3, // UTF-16 (Unicode) 3, // Reserved 3, // Reserved 3, // Reserved 3, // Reserved 1, // Lotus 1-2-3 system range 3, // Reserved 3, // Reserved 3, // Reserved 3, // Reserved 3, // Reserved 3, // Reserved }; // maxlen == 0 means count chars, but dont write. int translate_lmbcs(const void *src, unsigned char *dst, int maxdst, int maxsrc, int defgroup) { const unsigned char *p; // current lmbcs byte position int count; // how many characters weve translated p = src; trace("translate_lmbcs"); traceint(maxdst); traceint(maxsrc); if (lmbcs_tables[defgroup] == NULL) { trace("no translation table for default group"); __debugbreak(); } if (maxsrc == -1) { maxsrc = strlen(src); traceint(maxsrc); } loghex(src, maxsrc); for (count = 0; p < (unsigned char *) src + maxsrc; count++) { // Literal character if (*p >= 0x20 && *p <= 0x7f) { if (count < maxdst) { *dst++ = *p; } p++; // 1byte continue; } // Nul terminator if (*p == 0x00) { if (count < maxdst) { *dst++ = *p; } break; } if (*p < 0x20 && *p != 0x00) { if (lmbcs_tables[*p] == NULL) { logmsg("lmbcs string uses group %hd, no table available", *p); if (count < maxdst) { *dst++ = '?'; } } else { // Lookup in specified table. if (count < maxdst) { *dst++ = lmbcs_tables[p[0]][p[1]]; } } // Lookup how long this groups characters are. p += lmbcs_group_length[*p]; continue; } // Lookup in default table. if (count < maxdst) { *dst++ = lmbcs_tables[defgroup][*p]; } p++; // code } logmsg("translated characters is %d", count); return count; } ================================================ FILE: lmbcs.h ================================================ #ifndef __LMBCS_H #define __LMBCS_H int translate_lmbcs(const void *src, unsigned char *dst, int maxdst, int maxsrc, int defgroup); #endif ================================================ FILE: lotcalls.h ================================================ #ifndef __LOTCALLS_H #define __LOTCALLS_H #pragma pack(1) #define __lotapi __pascal __far struct LOTUSFUNCS { int (__lotapi *drv_max_segsize)(void); void far *(__lotapi *alloc_mptr)(int vmr, int size, int tag); void (__lotapi *free_mptr)(void far *ptr, int size); void far *(__lotapi *alloc_fixed_block)(int size); int (__lotapi *free_fixed_block)(void far *ptr, int, int size); void far *(__lotapi *drv_map_mptr)(void far *m, int v); void far *(__lotapi *drv_get_vmr)(int v); void far *drv_unmap_vmr; void far *drv_yield_test; void far *file_name_parse; void far *file_exist; void far *file_find; void far *file_access_read; void far *file_get_fileinfo; void far *file_get_filepointer; void far *file_lseek; void far *file_read; void far *file_access_finished; void far *file_access_write; void far *file_write; int (__lotapi *open_rasterizer)(struct DEVDATA far *, int far *); void far *rast_init_device; void (__lotapi *set_strip)(int, int); int (__lotapi *raster)(int, void far *ptr); void (__lotapi *close_rasterizer)(int hdl); int (__lotapi *rast_compute_view)(int, void far *ptr); int (__lotapi *rast_txt_size)(int, int, void far *ptr1, void far *ptr2); int (__lotapi *rast_txt_fit)(int, int, void *a, int, void far *b, void far *c); void far *p_link_init; void far *p_link_transmit; void far *p_link_term; void far *p_link_start_job; void far *p_link_end_job; void far *p_signal_user; void far *p_print_link_alert; void far *p_print_link_message; void far *country_reduce_string; void far *country_convert_string; void far *dyload; void far *dyunload; void far *drv_wait_nticks; unsigned (__lotapi *set_disp_buf)(unsigned far *selector, int base, int limit); void far *circle_position; void far *drv_display_reset; void far *open_system_file; void far *drv_largest_avail; void far *quit_now; void far *is_encoded; void far *command; void far *x_get_date; void far *x_get_time; void far *x_sysbeep; void far *convert_from_table; void far *reduce_from_table; void far *country_text_cnv; void (__lotapi *drop_disp_buf)(int seg); void far *x_cntry_tbl_release2; void far *char_size; }; extern struct LOTUSFUNCS far *callbacks; extern void __pascal drv_disp_clear(unsigned cols, unsigned lines, unsigned char attrs); extern void __pascal drv_disp_set_pos(int col, int line); #endif ================================================ FILE: lotdemu.c ================================================ #include #include #include #include #include #include #include #include #include "config.h" #include "debug.h" #include "idatypes.h" #include "lottypes.h" #include "lotcalls.h" #include "bundle.h" #include "lmbcs.h" #include "attr.h" #include "caca.h" #include "draw.h" #include "raster.h" // You can either disable labels on graphs, try to draw them graphically, or // try to draw them as text. // If you set WANT_GRAPH_LABELS, the 1-2-3 rasterizer will try to drawn them. // This is unlikely to look good at low resolutions. // If you set WANT_TEXT_LABELS, then we will try to parse the rasterizer // bytecode to figure out where the labels are, and then draw them ourselves. // If you set neither, no labels will get drawn. #define WANT_GRAPH_LABELS 0 // Note: not working yet #define WANT_TEXT_LABELS 0 // Note: nearly working! // The current cursor position. static int curx; static int cury; // Selectors we need to release on exit. unsigned fbseg; unsigned vbseg; // This will point into the CGA framebuffer. static unsigned char far * framebuffer; // These two pointers reference values in the BDA (BIOS Data Area). static unsigned int far * bdcols; static unsigned char far * bdrows; // There is an "optimized" LMBCS encoding that skips the group byte // and just uses a default value. 123 tells us what the default should // be at startup. static unsigned char lmbcsgroup; // A handle returned when opening the rasterizer. static unsigned rasthdl; static unsigned char blanks[MAX_COLS]; // This will point at fixed (not vmr) memory. vmrs are 123's // abstraction of XMS/EMS handles, they need to be mapped before // you can touch them. static unsigned char far *attrmap; // Not sure what this ptr is yet. static void far *curview; static struct DISPLAYINFO dpinfo = { 80, // num_text_cols 25, // num_text_rows true, // graphics true, // full_screen_graph 1, // hpu_per_col 320, // graph_cols 200, // graph_rows 1, // graph_col_res 1, // graph_row_res 100, // view_set_size true, // iscolor }; struct FONTINFO fontinfo = { { 'd', 'e', 'f', 'a', 'u', 'l', 't' }, 0, // angle 1, // em_pix_hgt 1, // em_pix_wid }; static unsigned char fillpatts[] = { 0x18, 0x24, 0x42, 0x81, 0x81, 0x42, 0x24, 0x18, 0x09, 0x12, 0x24, 0x48, 0x90, 0x21, 0x42, 0x84, 0x01, 0x24, 0x02, 0x48, 0x04, 0x90, 0x09, 0x20, 0x12, 0x40, 0x24, 0x80, 0x49, 0x00, 0x92, 0x00, 0x24, 0x04, 0x48, 0x08, 0x90, 0x10, 0x20, 0x24, 0x40, 0x48, 0x80, 0x90, 0x01, 0x80, 0x02, 0x40, 0x04, 0x20, 0x08, 0x10, 0x10, 0x08, 0x20, 0x04, 0x40, 0x02, 0x80, 0x01, 0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x10, 0x08, 0x08, 0x10, 0x04, 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x09, 0x00, 0x12, 0x00, 0x24, 0x00, 0x48, 0x00, 0x90, 0x01, 0x20, 0x02, 0x40, 0x04, 0x80, 0x09, 0x00, 0x12, 0x00, 0x24, 0x00, 0x48, 0x00, 0x90, 0x00, 0x20, 0x01, 0x40, 0x02, 0x80, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xee, 0xee, 0x55, 0x55, 0xbb, 0xbb, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x44, 0x44, 0xaa, 0xaa, 0x11, 0x11, 0xaa, 0xaa, 0x00, 0x00, 0xaa, 0xaa, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00 }; // I think an mu must be a "movement unit"? static struct DEVDATA devdata = { 1, // ShowMeFlag 200, // RasterHeight 1, // AspectX 1, // AspectY 1, // RHmusPerTHmu 1, // RHmuPerGHmus 1, // RVmusPerTVmu 1, // RVmuPerGVmus { { { 8, 8 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 0 { { 8, 8 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 1 { { 14, 14 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 2 { { 16, 16 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 3 { { 16, 16 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 4 { { 14, 14 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 5 { { 4, 16 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 6 { { 4, 16 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 7 { { 4, 16 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 8 { { 4, 16 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 9 { { 4, 16 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 10 { { 4, 16 }, { 0, 0 }, &fillpatts[ 0], { 0, 0 } }, // 11 }, // FillPatts { CACA_MAGENTA, CACA_MAGENTA, CACA_CYAN, CACA_BLACK, CACA_MAGENTA, CACA_CYAN, CACA_BLACK, CACA_BLACK, CACA_WHITE, CACA_MAGENTA, CACA_CYAN, CACA_BLACK, CACA_MAGENTA, CACA_CYAN, CACA_WHITE, CACA_BLACK, }, // ColorMap { 0xFFFF, 0xAAAA, 0xCCCC, 0xCACA, 0xC0C0, 0x00FF, 0xFF24, 0x0000 }, // SrcPatD { 0x0001, 0x0006, 0x000C, 0x0012, 0x0018, 0x001E, 0x0024, 0x002A }, // SrcPatS 26, // FIndexCnt "ABCDEFGHIJKLMNOPQRSTUVWXYZ", // FontIndex 1, // FNamesCnt &fontinfo, // FontNames { exprt_scan_linx, exprt_fill_rect, exprt_thin_diag_line, exprt_thin_vert_line, exprt_shade_rect, exprt_fill_scan_list, }, // DevFuncs }; struct LOTUSFUNCS far *callbacks; // The canvas used for drawing ascii-art graphics. caca_canvas_t *cv; void __pascal drv_disp_info(struct DISPLAYINFO far *dspinfo) { traceent("drv_disp_info"); traceptr(dspinfo); memcpy(dspinfo, &dpinfo, sizeof dpinfo); tracebuf(dspinfo, sizeof dpinfo); return; } static int start_lotus_rasterizer() { int result; void *destptr; void *vmrptr; trace("starting rasterizer..."); tracebuf(&devdata, sizeof devdata); // Allocate a VMR buffer to give to 123. destptr = callbacks->alloc_mptr(0x27, sizeof devdata, 1); traceptr(destptr); // Copy out buffer over. memcpy(destptr, &devdata, sizeof devdata); vmrptr = callbacks->drv_get_vmr(1); traceptr(vmrptr); // Register it, we release this handle in our drv_disp_close(). rasthdl = callbacks->open_rasterizer(vmrptr, &result); traceint(rasthdl); traceint(result); return 0; } int __pascal drv_disp_open(void far *drvapi, struct BDLHDR far *vbdptr, const char far *cfgname, char deflmbcsgrp) { // If requested, log all calls openlog(DEBUG_LOGFILE); traceent("drv_disp_open"); traceptr(drvapi); traceptr(vbdptr); tracestr(cfgname); tracechr(deflmbcsgrp); // Save this pointer for calling lotus functions. callbacks = drvapi; // Descriptors we want installed. vbseg = BIOS_DATA_AREA; fbseg = CGA_FRAMEBUF; tracebuf(callbacks, sizeof *callbacks); // Record the default lmbcs group lmbcsgroup = deflmbcsgrp; // Create a line of blanks for quickly clearing a row. memset(blanks, ' ', sizeof blanks); // Request Bios Data Area access. callbacks->set_disp_buf(&vbseg, 0, 0x100); traceint(vbseg); // See https://stanislavs.org/helppc/bios_data_area.html bdrows = MK_FP(vbseg, 0x84); bdcols = MK_FP(vbseg, 0x4A); traceint(*bdrows); traceint(*bdcols); dpinfo.num_text_cols = *bdcols; dpinfo.num_text_rows = *bdrows + 1; dpinfo.graph_cols = dpinfo.num_text_cols; dpinfo.graph_rows = dpinfo.num_text_rows; devdata.RasterHeight = dpinfo.graph_rows; devdata.AspectX = 2; devdata.AspectY = 1; traceint(dpinfo.num_text_cols); traceint(dpinfo.num_text_rows); // Request an LDT for framebuffer access. callbacks->set_disp_buf(&fbseg, 0, dpinfo.num_text_cols * dpinfo.num_text_rows * 2); traceint(fbseg); framebuffer = MK_FP(fbseg, 0); // Dump a few bytes just to make sure this look sane. tracebuf(framebuffer, 32); // For some reason the order of these operations is important, otherwise // DEVDATA gets unmapped at inconvenient times. start_lotus_rasterizer(); // Setup the CGA <=> 123 attribute map. attrmap = callbacks->alloc_mptr(0x27, dpinfo.num_text_cols * dpinfo.num_text_rows, 0); //attrmap = callbacks->Allocate(dpinfo.num_text_cols * dpinfo.num_text_rows, 0); //callbacks->LoadVmr(0); // Out of memory? if (attrmap == NULL) { logmsg("failed to allocate space for the attribute map"); return 1; } traceptr(attrmap); // Initialize to white on black. memset(attrmap, 0, dpinfo.num_text_cols * dpinfo.num_text_rows); // Parse out configuration bundle. ParseConfig(vbdptr); // Clear the screen ready for 123. drv_disp_clear(dpinfo.num_text_cols, dpinfo.num_text_rows, 0); // Initialize a canvas for ASCII-art graphics. cv = caca_create_canvas(dpinfo.num_text_cols, dpinfo.num_text_rows, fbseg, 0); return 0; } int __pascal drv_disp_close() { traceent("drv_disp_close"); // Clean up the screen. drv_disp_set_pos(0, 0); drv_disp_clear(dpinfo.num_text_cols, dpinfo.num_text_rows, 0); // Free our attribute map. callbacks->free_fixed_block(attrmap, 0, dpinfo.num_text_cols * dpinfo.num_text_rows); // Release any segment descriptors. callbacks->drop_disp_buf(vbseg); callbacks->drop_disp_buf(fbseg); // No more rasterization needed. callbacks->close_rasterizer(rasthdl); // Decrement canvas reference count. caca_free_canvas(cv); // If we were logging, close it. //closelog(); return 0; } // This is called when 1-2-3 exits graphics mode. int __pascal drv_disp_text() { traceent("drv_disp_text"); drv_disp_set_pos(0, 0); return 1; } // This is called when a graph is requested (F10). int __pascal drv_disp_graph() { traceent("SetGraphicsMode"); return 1; } void __pascal drv_disp_set_pos(int col, int line) { traceent("drv_disp_set_pos"); traceint(col); traceint(line); curx = col; cury = line; return; } // Write string str to the current cursor position, str is not a lmbcs // string, just a literal sequence of bytes to write. It might not be NUL // terminated. // // len is the number of bytes to write, I don't think we have to worry about // linewrap, 123 will never ask us to write past the right column. // // attrs is a 6bit value, 3bits referring to the BG and three bits referring // to the FG, these are defined by Lotus and must be translated into CGA. // // Mask is required because sometimes Lotus wants us to update the FG, but // leave the BG intact, so in that case you would use MASK_FG. It's possible // both, none or either are specified. static int WriteStringToFramebuffer(unsigned char far *str, unsigned len, unsigned char attrs, unsigned char mask) { unsigned char far *line; unsigned char far *attr; unsigned i; trace("WriteStringToFramebuffer"); tracestrlen(str, len); traceint(len); traceptr(str); traceint(curx); traceint(cury); // It's easier to parse lotus attributes in octal, because they're // three bit values. traceval("%03ho", mask); traceval("%03ho", attrs); // That doesn't work for CGA attributes, hex is better. traceval("%#hx", LATTR(attrs)); for (i = 0; i < len; i++) { // Current line in framebuffer. line = (framebuffer + cury * dpinfo.num_text_cols * 2); attr = (attrmap + cury * dpinfo.num_text_cols * 1); // Optionally write character, it's possible the caller // only wanted to change attributes. if (str) { line[curx * 2 + 0] = str[i]; } // If specified, change the character attributes. // // - Sometimes 123 wants to change the FG/BG attributes, but leave the // other unchanged, so a mask can be specified. // ATTR_ALL will change both // ATTR_NONE will change none // ATTR_FG will change FG, leaving BG // ATTR_BG will change BG, leaving FG // // - The parameter is a *LOTUS* attribute, we need to translate it // to a CGA attribute before writing it to the framebuffer. if (mask == ATTR_ALL) { // The easy case, overwrite everything. line[curx * 2 + 1] = LATTR(attr[curx] = attrs); } else if (mask != ATTR_NONE) { unsigned char curattr = attr[curx]; // Now remove the bits we're changing. curattr &= ~mask; // Now remove any bits from new attributes we dont want. attrs &= mask; // Combine them. attrs |= curattr; // Translate and write them back. line[curx * 2 + 1] = LATTR(attr[curx] = attrs); } // Advance cursor position. curx++; } return curx; } int __pascal drv_disp_write(unsigned byteslen, unsigned char far *lmbcsptr, unsigned char attrs) { unsigned char dest[MAX_COLS]; int result; traceent("drv_disp_write"); traceint(byteslen); tracestrlen(lmbcsptr, byteslen); traceval("%03ho", attrs); result = translate_lmbcs(lmbcsptr, dest, sizeof dest, byteslen, lmbcsgroup); WriteStringToFramebuffer(dest, result, MKFG(attrs), ATTR_FG); traceint(result); return result; } int __pascal drv_disp_zone(unsigned byteslen, char far *lmbcsptr, unsigned char attrs, unsigned startpad, unsigned endpad) { unsigned char dest[MAX_COLS]; int result; traceent("drv_disp_zone"); traceint(byteslen); tracestrlen(lmbcsptr, byteslen); traceval("%03ho", attrs); traceint(startpad); traceint(endpad); memset(dest, ' ', sizeof dest); result = translate_lmbcs(lmbcsptr, dest + startpad, sizeof(dest) - startpad, byteslen, lmbcsgroup); traceint(result); // Write the string WriteStringToFramebuffer(dest, endpad, MKFG(attrs), ATTR_FG); return endpad; } // The region is a rectangle: // // curx,cury curx+cols,cury // +------------------------+ // | | // | | // | | // +------------------------+ // curx,cury+lines curx+cols,cury+lines // // Does not appear to change current cursor position. void __pascal drv_disp_set_bg(unsigned cols, unsigned lines, char attrs) { unsigned y; int origx; int origy; traceent("drv_disp_set_bg"); traceint(cols); traceint(lines); traceint(curx); traceint(cury); traceval("%03ho", attrs); origx = curx; origy = cury; for (y = 0; y <= lines; y++) { WriteStringToFramebuffer(NULL, cols, MKBG(attrs), ATTR_BG); drv_disp_set_pos(origx, origy + y); } drv_disp_set_pos(origx, origy); return; } void __pascal drv_disp_clear(unsigned cols, unsigned lines, unsigned char attrs) { unsigned y; unsigned origx; unsigned origy; traceent("drv_disp_clear"); traceint(cols); traceint(lines); traceint(curx); traceint(cury); traceval("%03ho", attrs); origx = curx; origy = cury; for (y = 0; y <= lines; y++) { WriteStringToFramebuffer(blanks, cols, MKBG(attrs), ATTR_ALL); drv_disp_set_pos(origx, origy + y); } drv_disp_set_pos(origx, origy); return; } void __pascal drv_disp_fg_clear(unsigned cols, unsigned lines) { unsigned y; unsigned origx; unsigned origy; traceent("drv_disp_fg_clear"); traceint(cols); traceint(lines); traceint(curx); traceint(cury); origx = curx; origy = cury; for (y = 0; y <= lines; y++) { WriteStringToFramebuffer(blanks, cols, 0000, ATTR_FG); drv_disp_set_pos(origx, origy + y); } drv_disp_set_pos(origx, origy); return; } #ifndef RELEASE // Just for debugging purposes, show where a square is on the screen. static void DrawSquare(int topx, int topy, int bottomx, int bottomy, char c) { unsigned char line[MAX_COLS]; unsigned height; unsigned width; unsigned i; height = bottomy - topy; width = bottomx - topx; memset(line, c, sizeof line); drv_disp_set_pos(topx, topy); WriteStringToFramebuffer(line, width, 7, ATTR_ALL); drv_disp_set_pos(topx, bottomy); WriteStringToFramebuffer(line, width, 7, ATTR_ALL); for (i = 0; i < height; i++) { drv_disp_set_pos(topx, topy + i); WriteStringToFramebuffer(line, 1, 7, ATTR_ALL); drv_disp_set_pos(topx + width, topy + i); WriteStringToFramebuffer(line, 1, 7, ATTR_ALL); } return; } #endif // Move a rectangular block from the current cursor, including attributes. // // The region starts at curx,cury and the size is given by width and height. // // The source and destination region might overlap, and we have to handle that. void __pascal drv_disp_copy(int width, int height, int dstx, int dsty) { unsigned char far *srcline; unsigned char far *srcattr; unsigned char far *dstline; unsigned char far *dstattr; int i; traceent("drv_disp_copy"); traceint(width); traceint(height); traceint(dstx); traceint(dsty); traceint(curx); traceint(cury); // Use this to show the source for debugging. //DrawSquare(curx, cury, curx + width, cury + height, '#'); // Recall that regions can overlap, so we need to be careful about // our start position so we don't clobber a part we haven't processed // yet. // If we're moving a region *up* we want to start at the bottom. // If we're moving a region *down* we want to start at top. // // It doesn't matter if it's moving sideways, that's memmoves problem :) // // Note: I am not sure I have this logic correct. if (dsty <= cury) { trace("region vertical direction is down or sideways"); // We're moving down, start at the top. for (i = 0; i < height; i++) { srcline = (framebuffer + (cury + i) * dpinfo.num_text_cols * 2); srcattr = (attrmap + (cury + i) * dpinfo.num_text_cols * 1); dstline = (framebuffer + (dsty + i) * dpinfo.num_text_cols * 2); dstattr = (attrmap + (dsty + i) * dpinfo.num_text_cols * 1); memmove(&dstline[dstx * 2], &srcline[curx * 2], width * 2); memmove(&dstattr[dstx], &srcattr[curx], width); } } else { trace("region vertical direction is up"); // We're moving up, start at the bottom. for (i = height - 1; i >= 0; i--) { srcline = (framebuffer + (cury + i) * dpinfo.num_text_cols * 2); srcattr = (attrmap + (cury + i) * dpinfo.num_text_cols * 1); dstline = (framebuffer + (dsty + i) * dpinfo.num_text_cols * 2); dstattr = (attrmap + (dsty + i) * dpinfo.num_text_cols * 1); memmove(&dstline[dstx * 2], &srcline[curx * 2], width * 2); memmove(&dstattr[dstx], &srcattr[curx], width); } } return; } int __pascal drv_disp_size(int argstrlen, char far *argstr) { int result; traceent("drv_disp_size"); tracestrlen(argstr, argstrlen); traceptr(argstr); result = translate_lmbcs(argstr, NULL, 0, argstrlen, lmbcsgroup); traceint(result); return result; } int __pascal drv_disp_fit(int strarglen, char far *strarg, int ncols, int far *bytesneeded) { traceent("drv_disp_fit"); traceint(strarglen); tracestrlen(strarg, strarglen); traceint(ncols); traceptr(bytesneeded); *bytesneeded = translate_lmbcs(strarg, NULL, 0, strarglen, lmbcsgroup); traceint(*bytesneeded); // Truncate if necessary. return *bytesneeded = __min(*bytesneeded, ncols); } void __pascal drv_disp_curs_off() { traceent("drv_disp_curs_off"); // https://stanislavs.org/helppc/int_10-1.html // // To hide the cursor, you simply set bit 5 of ch. int 10,1 // supports configuring the size of the cursor too, but that // doesn't work in terminal mode - it's all or nothing. __asm { mov ah, 1 mov ch, 1 << 5 mov cl, 0 int 0x10 } return; } void __pascal drv_disp_curs_on() { traceent("drv_disp_curs_on"); // First we make sure the "hide cursor" bit isn't set, see the // implementation of drv_disp_curs_off() for details. __asm { mov ah, 1 mov ch, 0 mov cl, 0 int 0x10 } // Now we need to put it in the right place, you can set the position with // int 10,2. https://stanislavs.org/helppc/int_10-2.html __asm { mov ah, 2 mov bx, cury mov cx, curx mov dl, cl mov dh, bl int 0x10 } return; } // I'm not sure what I'm supposed to do here. void __pascal drv_disp_curs_type(int unused, int cursmodenum) { traceent("drv_disp_curs_type"); traceint(unused); traceint(cursmodenum); return; } #if WANT_GRAPH_LABELS && WANT_TEXT_LABELS # error you cant have graphical and text labels on graphs, choose one. #endif // Lotus calls this just before it sends bytecode to the rasterizer. // // The interesting thing is we have an opportunity to parse // the bytecode and patch it, which could let us extract labels and // paint them directly on graphs instead of trying to rasterize them. int __pascal drv_disp_grph_process(struct GRAPH far *graph) { void far *entry; void far *exit; int result; struct RASTEROPS rops; traceent("drv_disp_grph_process"); traceptr(graph); tracebuf(graph, 64); callbacks->drv_map_mptr(graph, 0); graph = callbacks->drv_get_vmr(0); memcpy(&rops, &graph->rops, sizeof rops); traceptr(rops.screeninfo); // contains fontdata, resolution, etc. traceptr(rops.bytecode); // Rasterizer bytecode tracebuf(rops.bytecode, 32); traceint(rops.rfield_8); // Boolean Flag, no ops if zero traceint(rops.rfield_A); // Some ops do change it (inc or dec), dunno what it is. callbacks->set_strip(rasthdl, 0); // Record the bytecode entrypoint. entry = rops.bytecode; // Run the rasterizer. result = callbacks->raster(rasthdl, &rops); traceint(result); // Now we know where the bytecode ends. exit = rops.bytecode; #if WANT_TEXT_LABELS // If bytecode was executed, we can trace through it and locate and // extracts labels. That's useful, because otherwise we have rasterized // labels which dont look good on a terminal because they're tiny little // lines that can't be drawn accurately with libcaca. if (result) { int opcnt = decode_raster_ops(entry, exit); traceint(opcnt); } #endif callbacks->drv_map_mptr(graph, 0); graph = callbacks->drv_get_vmr(0); // Copy the results over. memcpy(&graph->rops, &rops, sizeof rops); // Dump the next bytecode instructions. traceptr(rops.bytecode); tracebuf(rops.bytecode, 32); return result; } int __pascal drv_disp_grph_txt_size(int strarglen, void far *strarg) { int result = 0; traceent("drv_disp_grph_txt_size"); traceint(strarglen); traceptr(strarg); tracebuf(strarg, strarglen); #if WANT_GRAPH_LABELS result = callbacks->rast_txt_size(rasthdl, strarglen, strarg, curview); #elif WANT_TEXT_LABELS // XXX: See the discssion in drv_disp_grph_txt_fit. result = strarglen; #endif traceint(result); return result; } void __pascal drv_disp_set_pos_hpu(int col, int line) { traceent("drv_disp_set_pos_hpu"); drv_disp_set_pos(col, line); return; } int __pascal drv_disp_grph_compute_view(void far *ptr) { int result; traceent("drv_disp_grph_compute_view"); traceptr(ptr); tracebuf(ptr, 16); traceint(rasthdl); traceptr(cv); result = callbacks->rast_compute_view(rasthdl, ptr); traceint(result); tracebuf(ptr, 16); return true; } int __pascal drv_disp_unlock() { traceent("drv_disp_unlock"); return 0; } int __pascal drv_disp_lock() { traceent("drv_disp_lock"); return 0; } int __pascal drv_disp_grph_set_cur_view(void far *view) { traceent("drv_disp_grph_set_cur_view"); traceptr(view); curview = view; return 0; } int __pascal drv_disp_grph_txt_fit(int len, char far *str, int maxlen, int far *used) { int result; traceent("drv_disp_grph_txt_fit"); traceint(len); traceptr(str); tracestrlen(str, len); traceval("%#x", maxlen); traceptr(used); result = *used = 0; #if WANT_GRAPH_LABELS result = callbacks->rast_txt_fit(rasthdl, len, str, maxlen, used, curview); #elif WANT_TEXT_LABELS // XXX: I want to tell the rasterizer how much space I need for the text, // but if it's vertical it takes me much more space, because the terminal // aspect ratio is 2:1 (pixels are taller than they are wide). // // I don't know how it wants to draw this, should I use worst or best case? // // I think I should tell it *best* case, and then I can re-do the fit // operation later when I'm parsing the bytecode and know the alignment and // angle. I think this doesn't work yet, but there are raster opcodes like // SetTextAngle and SetTextAlignment, so it seems plausible. result = *used = __min(len, maxlen); #endif traceint(result); return result; } int __pascal drv_disp_sync(int a, int b) { traceent("drv_disp_sync"); traceptr(a); traceptr(b); return 0; } ================================================ FILE: lottypes.h ================================================ #ifndef __LOTTYPES_H #define __LOTTYPES_H #pragma pack(1) #include "idatypes.h" struct DISPLAYINFO { int num_text_cols; int num_text_rows; int graphics; int full_screen_graph; int hpu_per_col; int graph_cols; int graph_rows; int graph_col_res; int graph_row_res; int view_set_size; int iscolor; }; struct BDLHDR { int bdllen; int bdlver; int driver_id; int bundle_id; }; struct BDLRECHDR { int bdltype; int reclen; }; struct DEVPRIM { void (__pascal __far *scan_linx)(int, int, int, int); void (__pascal __far *fill_rect)(int, int, int, int, int); void (__pascal __far *thin_diag_line)(int, int, int, int, int); void (__pascal __far *thin_vert_line)(int, int, int, int); void (__pascal __far *shade_rect)(int, int, int, int, void far *, int); void (__pascal __far *fill_scan_list)(); }; typedef struct DEVPRIM DEVPRIM; struct POINT { int pty; int ptx; }; typedef struct POINT POINT; struct PATT { POINT pattsize; POINT patthotSpot; void far *pattptr; POINT pattSecOffset; }; typedef struct PATT PATT; struct DEVDATA { int ShowMeFlag; int RasterHeight; int AspectX; int AspectY; int RHmusPerTHmu; int RHmuPerGHmus; int RVmusPerTVmu; int RVmuPerGVmus; PATT FillPatts[12]; unsigned char ColorMap[16]; int SrcPatD[8]; int SrcPatS[8]; int FIndexCnt; void far *FontIndex; int FNamesCnt; void far *FontNames; DEVPRIM DevFuncs; }; // I'm still a bit lost on what all these fields are. // I have no idea how 123 knows where the opcodes end. struct GRAPH { char unknown0; char unknown1; char far *screeninfo; // Seems to contain resolution and font data. char far *rasterops; // Sometimes a duplicate of the bytecode ptr? int unknown2; int unknown3; int unknown4; int unknown5; int unknown6; char unknown7; struct RASTEROPS { void far *screeninfo; void far *bytecode; int rfield_8; // Always seems to be 1 int rfield_A; } rops; }; struct FONTINFO { char name[9]; int angle; int em_pix_hgt; int em_pix_wid; }; #endif ================================================ FILE: raster.c ================================================ #include #include #include #include #include #include #include #include #include "config.h" #include "debug.h" #include "idatypes.h" #include "lottypes.h" #include "lotcalls.h" #include "bundle.h" #include "lmbcs.h" #include "attr.h" #include "caca.h" #include "draw.h" #include "raster.h" static const char far *raster_op_name(unsigned op) { char far *raster_op_names[] = { "Jump", "Nop", "SetLineColor", "SetLinePattern", "SetLineWidth", "SetFillColor", "SetFillPatten", "undefined", "SetTextSize", "SetTextAlign", "SetTextAngle", "SetTextStyle", "SetMarkerSize", "SetMarkerSymbol", "ClearRectangle", "DrawSingleLine", "DrawRectangleEdges", "DrawRectangleInterior", "DrawRactangle", "DrawSliceEdges", "DrawSliceInterior", "DrawSlice", "DrawArc", "DrawPolyLine", "DrawPolygonInterior", "DrawPolygon", "DrawText", "DrawMarker", }; if (op < RASTER_OP_NUMOPS) { return raster_op_names[op]; } return NULL; } int decode_raster_ops(char far *startaddr, char far *endaddr) { int opcnt; int oplen; char type; char opcode; char param; char far *addr; char far *rasterop; trace("decode_raster_ops"); traceptr(startaddr); traceptr(endaddr); for (opcnt = 0, rasterop = startaddr; rasterop != endaddr; opcnt++) { traceptr(rasterop); tracebuf(rasterop, 16); type = *rasterop++; opcode = (char)((type & 0x7f) - 1); param = *rasterop++; addr = *(char far **) rasterop; // Only used for JMP? oplen = 0; traceint(type); traceint(opcode); traceint(param); traceptr(addr); logmsg("opcode %02x %s@", opcode, raster_op_name(opcode)); if (opcode == RASTER_OP_DRAWTEXT) { struct DRAWTEXT *label = (void far *) rasterop; traceptr(label); traceval("%u", label->xpos); traceval("%u", label->ypos); traceint(label->len); traceptr(label->str); tracestrlen(label->str, label->len); } // This is a jmp to a far pointer. if (param < 0 && opcode == RASTER_OP_JMP) { traceptr(addr); tracebuf(addr, 32); rasterop = addr; continue; } if (type < 0) { // The top bit indicates the instruction is variable width. oplen = param & 0x1f; // If the 1-byte length is zero, then it's a 2-byte width. if (oplen == 0) { oplen = *(int *) rasterop; } rasterop += oplen; } } return opcnt; } ================================================ FILE: raster.h ================================================ #ifndef __RASTER_H #define __RASTER_H int decode_raster_ops(char far *startaddr, char far *endaddr); // I think these are the rasterizer opcode names, I pulled them out of the // symbols from the SysV UNIX port of 1-2-3. There may be mistakes, I haven't // verified they are all correct. enum { RASTER_OP_JMP = 0, RASTER_OP_NOP, RASTER_OP_SETLINECOLOR, RASTER_OP_SETLINEPATTERN, RASTER_OP_SETLINEWIDTH, RASTER_OP_SETFILLCOLOR, RASTER_OP_SETFILLPATTERN, RASTER_OP_UNDEFINED, RASTER_OP_SETTEXTSIZE, RASTER_OP_SETTEXTALIGN, RASTER_OP_SETTEXTANGLE, RASTER_OP_SETTEXTSTYLE, RASTER_OP_SETMARKERSIZE, RASTER_OP_SETMARKERSYMBOL, RASTER_OP_CLEARRECTANGLE, RASTER_OP_DRAWSINGLELINE, RASTER_OP_DRAWRECTANGLEEDGES, RASTER_OP_DRAWRECTANGLEINTERIOR, RASTER_OP_DRAWRECTANGLE, RASTER_OP_DRAWSLICEEDGES, RASTER_OP_DRAWSLICEINTERIOR, RASTER_OP_DRAWSLICE, RASTER_OP_DRAWARC, RASTER_OP_DRAWPOLYLINE, RASTER_OP_DRAWPOLYGONINTERIOR, RASTER_OP_DRAWPOLYGON, RASTER_OP_DRAWTEXT, RASTER_OP_DRAWMARKER, RASTER_OP_NUMOPS, }; // The operands to a DRAWTEXT instruction. struct DRAWTEXT { int xpos; int ypos; int len; char far *str; }; #endif ================================================ FILE: snprintf.c ================================================ /* * snprintf.c - a portable implementation of snprintf * * AUTHOR * Mark Martinec , April 1999. * * Copyright 1999, Mark Martinec. All rights reserved. * * TERMS AND CONDITIONS * This program is free software; you can redistribute it and/or modify * it under the terms of the "Frontier Artistic License" which comes * with this Kit. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Frontier Artistic License for more details. * * You should have received a copy of the Frontier Artistic License * with this Kit in the file named LICENSE.txt . * If not, I'll be glad to provide one. * * FEATURES * - careful adherence to specs regarding flags, field width and precision; * - good performance for large string handling (large format, large * argument or large paddings). Performance is similar to system's sprintf * and in several cases significantly better (make sure you compile with * optimizations turned on, tell the compiler the code is strict ANSI * if necessary to give it more freedom for optimizations); * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); * - written in standard ISO/ANSI C - requires an ANSI C compiler. * * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES * * This snprintf only supports the following conversion specifiers: * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) * with flags: '-', '+', ' ', '0' and '#'. * An asterisk is supported for field width as well as precision. * * Length modifiers 'h' (short int), 'l' (long int), * and 'll' (long long int) are supported. * NOTE: * If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the * length modifier 'll' is recognized but treated the same as 'l', * which may cause argument value truncation! Defining * SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also * handles length modifier 'll'. long long int is a language extension * which may not be portable. * * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) * with length modifiers (none or h, l, ll) is left to the system routine * sprintf, but all handling of flags, field width and precision as well as * c and s conversions is done very carefully by this portable routine. * If a string precision (truncation) is specified (e.g. %.8s) it is * guaranteed the string beyond the specified precision will not be referenced. * * Length modifiers h, l and ll are ignored for c and s conversions (data * types wint_t and wchar_t are not supported). * * The following common synonyms for conversion characters are supported: * - i is a synonym for d * - D is a synonym for ld, explicit length modifiers are ignored * - U is a synonym for lu, explicit length modifiers are ignored * - O is a synonym for lo, explicit length modifiers are ignored * The D, O and U conversion characters are nonstandard, they are supported * for backward compatibility only, and should not be used for new code. * * The following is specifically NOT supported: * - flag ' (thousands' grouping character) is recognized but ignored * - numeric conversion specifiers: f, e, E, g, G and synonym F, * as well as the new a and A conversion specifiers * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) * - wide character/string conversions: lc, ls, and nonstandard * synonyms C and S * - writeback of converted string length: conversion character n * - the n$ specification for direct reference to n-th argument * - locales * * It is permitted for str_m to be zero, and it is permitted to specify NULL * pointer for resulting string argument if str_m is zero (as per ISO C99). * * The return value is the number of characters which would be generated * for the given input, excluding the trailing null. If this value * is greater or equal to str_m, not all characters from the result * have been stored in str, output bytes beyond the (str_m-1) -th character * are discarded. If str_m is greater than zero it is guaranteed * the resulting string will be null-terminated. * * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, * but is different from some older and vendor implementations, * and is also different from XPG, XSH5, SUSv2 specifications. * For historical discussion on changes in the semantics and standards * of snprintf see printf(3) man page in the Linux programmers manual. * * Routines asprintf and vasprintf return a pointer (in the ptr argument) * to a buffer sufficiently large to hold the resulting string. This pointer * should be passed to free(3) to release the allocated storage when it is * no longer needed. If sufficient space cannot be allocated, these functions * will return -1 and set ptr to be a NULL pointer. These two routines are a * GNU C library extensions (glibc). * * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 * characters into the allocated output string, the last character in the * allocated buffer then gets the terminating null. If the formatted string * length (the return value) is greater than or equal to the str_m argument, * the resulting string was truncated and some of the formatted characters * were discarded. These routines present a handy way to limit the amount * of allocated memory to some sane value. * * AVAILABILITY * http://www.ijs.si/software/snprintf/ * * REVISION HISTORY * 1999-04 V0.9 Mark Martinec * - initial version, some modifications after comparing printf * man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10, * and checking how Perl handles sprintf (differently!); * 1999-04-09 V1.0 Mark Martinec * - added main test program, fixed remaining inconsistencies, * added optional (long long int) support; * 1999-04-12 V1.1 Mark Martinec * - support the 'p' conversion (pointer to void); * - if a string precision is specified * make sure the string beyond the specified precision * will not be referenced (e.g. by strlen); * 1999-04-13 V1.2 Mark Martinec * - support synonyms %D=%ld, %U=%lu, %O=%lo; * - speed up the case of long format string with few conversions; * 1999-06-30 V1.3 Mark Martinec * - fixed runaway loop (eventually crashing when str_l wraps * beyond 2^31) while copying format string without * conversion specifiers to a buffer that is too short * (thanks to Edwin Young for * spotting the problem); * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) * to snprintf.h * 2000-02-14 V2.0 (never released) Mark Martinec * - relaxed license terms: The Artistic License now applies. * You may still apply the GNU GENERAL PUBLIC LICENSE * as was distributed with previous versions, if you prefer; * - changed REVISION HISTORY dates to use ISO 8601 date format; * - added vsnprintf (patch also independently proposed by * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01) * 2000-06-27 V2.1 Mark Martinec * - removed POSIX check for str_m<1; value 0 for str_m is * allowed by ISO C99 (and GNU C library 2.1) - (pointed out * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie). * Besides relaxed license this change in standards adherence * is the main reason to bump up the major version number; * - added nonstandard routines asnprintf, vasnprintf, asprintf, * vasprintf that dynamically allocate storage for the * resulting string; these routines are not compiled by default, * see comments where NEED_V?ASN?PRINTF macros are defined; * - autoconf contributed by Caolan McNamara * 2000-10-06 V2.2 Mark Martinec * - BUG FIX: the %c conversion used a temporary variable * that was no longer in scope when referenced, * possibly causing incorrect resulting character; * - BUG FIX: make precision and minimal field width unsigned * to handle huge values (2^31 <= n < 2^32) correctly; * also be more careful in the use of signed/unsigned/size_t * internal variables - probably more careful than many * vendor implementations, but there may still be a case * where huge values of str_m, precision or minimal field * could cause incorrect behaviour; * - use separate variables for signed/unsigned arguments, * and for short/int, long, and long long argument lengths * to avoid possible incompatibilities on certain * computer architectures. Also use separate variable * arg_sign to hold sign of a numeric argument, * to make code more transparent; * - some fiddling with zero padding and "0x" to make it * Linux compatible; * - systematically use macros fast_memcpy and fast_memset * instead of case-by-case hand optimization; determine some * breakeven string lengths for different architectures; * - terminology change: 'format' -> 'conversion specifier', * 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")', * 'alternative form' -> 'alternate form', * 'data type modifier' -> 'length modifier'; * - several comments rephrased and new ones added; * - make compiler not complain about 'credits' defined but * not used; */ /* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf. * * If HAVE_SNPRINTF is defined this module will not produce code for * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well, * causing this portable version of snprintf to be called portable_snprintf * (and portable_vsnprintf). */ #define HAVE_SNPRINTF /* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and * vsnprintf but you would prefer to use the portable routine(s) instead. * In this case the portable routine is declared as portable_snprintf * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf') * is defined to expand to 'portable_v?snprintf' - see file snprintf.h . * Defining this macro is only useful if HAVE_SNPRINTF is also defined, * but does does no harm if defined nevertheless. */ #define PREFER_PORTABLE_SNPRINTF /* Define SNPRINTF_LONGLONG_SUPPORT if you want to support * data type (long long int) and length modifier 'll' (e.g. %lld). * If undefined, 'll' is recognized but treated as a single 'l'. * * If the system's sprintf does not handle 'll' * the SNPRINTF_LONGLONG_SUPPORT must not be defined! * * This is off by default as (long long int) is a language extension. */ /* #define SNPRINTF_LONGLONG_SUPPORT */ /* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf. * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly, * otherwise both snprintf and vsnprintf routines will be defined * and snprintf will be a simple wrapper around vsnprintf, at the expense * of an extra procedure call. */ /* #define NEED_SNPRINTF_ONLY */ /* Define NEED_V?ASN?PRINTF macros if you need library extension * routines asprintf, vasprintf, asnprintf, vasnprintf respectively, * and your system library does not provide them. They are all small * wrapper routines around portable_vsnprintf. Defining any of the four * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY * and turns on PREFER_PORTABLE_SNPRINTF. * * Watch for name conflicts with the system library if these routines * are already present there. * * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as * specified by C99, to be able to traverse the same list of arguments twice. * I don't know of any other standard and portable way of achieving the same. * With some versions of gcc you may use __va_copy(). You might even get away * with "ap2 = ap", in this case you must not call va_end(ap2) ! * #define va_copy(ap2,ap) ap2 = ap */ /* #define NEED_ASPRINTF */ /* #define NEED_ASNPRINTF */ /* #define NEED_VASPRINTF */ /* #define NEED_VASNPRINTF */ /* Define the following macros if desired: * SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE, * HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE, * DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE, * PERL_COMPATIBLE, PERL_BUG_COMPATIBLE, * * - For portable applications it is best not to rely on peculiarities * of a given implementation so it may be best not to define any * of the macros that select compatibility and to avoid features * that vary among the systems. * * - Selecting compatibility with more than one operating system * is not strictly forbidden but is not recommended. * * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE . * * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is * documented in a sprintf man page on a given operating system * and actually adhered to by the system's sprintf (but not on * most other operating systems). It may also refer to and enable * a behaviour that is declared 'undefined' or 'implementation specific' * in the man page but a given implementation behaves predictably * in a certain way. * * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf * that contradicts the sprintf man page on the same operating system. * * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE * conditionals take into account all idiosyncrasies of a particular * implementation, there may be other incompatibilities. */ /* ============================================= */ /* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */ /* ============================================= */ #define PORTABLE_SNPRINTF_VERSION_MAJOR 2 #define PORTABLE_SNPRINTF_VERSION_MINOR 2 #if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) # if defined(NEED_SNPRINTF_ONLY) # undef NEED_SNPRINTF_ONLY # endif # if !defined(PREFER_PORTABLE_SNPRINTF) # define PREFER_PORTABLE_SNPRINTF # endif #endif #if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE) #define SOLARIS_COMPATIBLE #endif #if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE) #define HPUX_COMPATIBLE #endif #if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE) #define DIGITAL_UNIX_COMPATIBLE #endif #if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE) #define PERL_COMPATIBLE #endif #if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE) #define LINUX_COMPATIBLE #endif #include #include #include #include #include #include #include #ifdef isdigit #undef isdigit #endif #define isdigit(c) ((c) >= '0' && (c) <= '9') /* For copying strings longer or equal to 'breakeven_point' * it is more efficient to call memcpy() than to do it inline. * The value depends mostly on the processor architecture, * but also on the compiler and its optimization capabilities. * The value is not critical, some small value greater than zero * will be just fine if you don't care to squeeze every drop * of performance out of the code. * * Small values favor memcpy, large values favor inline code. */ #if defined(__alpha__) || defined(__alpha) # define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ #endif #if defined(__i386__) || defined(__i386) # define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ #endif #if defined(__hppa) # define breakeven_point 10 /* HP-PA - gcc */ #endif #if defined(__sparc__) || defined(__sparc) # define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ #endif /* some other values of possible interest: */ /* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ /* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ #ifndef breakeven_point # define breakeven_point 6 /* some reasonable one-size-fits-all value */ #endif #define fast_memcpy(d,s,n) \ { register size_t nn = (size_t)(n); \ if (nn >= breakeven_point) memcpy((d), (s), nn); \ else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ register char *dd; register const char *ss; \ for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } #define fast_memset(d,c,n) \ { register size_t nn = (size_t)(n); \ if (nn >= breakeven_point) memset((d), (int)(c), nn); \ else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ register char *dd; register const int cc=(int)(c); \ for (dd=(d); nn>0; nn--) *dd++ = cc; } } /* prototypes */ #if defined(NEED_ASPRINTF) int asprintf (char **ptr, const char *fmt, /*args*/ ...); #endif #if defined(NEED_VASPRINTF) int vasprintf (char **ptr, const char *fmt, va_list ap); #endif #if defined(NEED_ASNPRINTF) int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); #endif #if defined(NEED_VASNPRINTF) int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap); #endif #if defined(HAVE_SNPRINTF) /* declare our portable snprintf routine under name portable_snprintf */ /* declare our portable vsnprintf routine under name portable_vsnprintf */ #else /* declare our portable routines under names snprintf and vsnprintf */ #define portable_snprintf snprintf #if !defined(NEED_SNPRINTF_ONLY) #define portable_vsnprintf vsnprintf #endif #endif #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); #if !defined(NEED_SNPRINTF_ONLY) int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); #endif #endif /* declarations */ #if 0 static char credits[] = "\n\ @(#)snprintf.c, v2.2: Mark Martinec, \n\ @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; #endif #if defined(NEED_ASPRINTF) int asprintf(char **ptr, const char *fmt, /*args*/ ...) { va_list ap; size_t str_m; int str_l; *ptr = NULL; va_start(ap, fmt); /* measure the required size */ str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); va_end(ap); assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ *ptr = (char *) malloc(str_m = (size_t)str_l + 1); if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } else { int str_l2; va_start(ap, fmt); str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); va_end(ap); assert(str_l2 == str_l); } return str_l; } #endif #if defined(NEED_VASPRINTF) int vasprintf(char **ptr, const char *fmt, va_list ap) { size_t str_m; int str_l; *ptr = NULL; { va_list ap2; va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ va_end(ap2); } assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ *ptr = (char *) malloc(str_m = (size_t)str_l + 1); if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } else { int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); assert(str_l2 == str_l); } return str_l; } #endif #if defined(NEED_ASNPRINTF) int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) { va_list ap; int str_l; *ptr = NULL; va_start(ap, fmt); /* measure the required size */ str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); va_end(ap); assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ if (str_m == 0) { /* not interested in resulting string, just return size */ } else { *ptr = (char *) malloc(str_m); if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } else { int str_l2; va_start(ap, fmt); str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); va_end(ap); assert(str_l2 == str_l); } } return str_l; } #endif #if defined(NEED_VASNPRINTF) int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) { int str_l; *ptr = NULL; { va_list ap2; va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ va_end(ap2); } assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ if (str_m == 0) { /* not interested in resulting string, just return size */ } else { *ptr = (char *) malloc(str_m); if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } else { int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); assert(str_l2 == str_l); } } return str_l; } #endif /* * If the system does have snprintf and the portable routine is not * specifically required, this module produces no code for snprintf/vsnprintf. */ #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) #if !defined(NEED_SNPRINTF_ONLY) int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { va_list ap; int str_l; va_start(ap, fmt); str_l = portable_vsnprintf(str, str_m, fmt, ap); va_end(ap); return str_l; } #endif #if defined(NEED_SNPRINTF_ONLY) int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { #else int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { #endif #if defined(NEED_SNPRINTF_ONLY) va_list ap; #endif size_t str_l = 0; const char *p = fmt; /* In contrast with POSIX, the ISO C99 now says * that str can be NULL and str_m can be 0. * This is more useful than the old: if (str_m < 1) return -1; */ #if defined(NEED_SNPRINTF_ONLY) va_start(ap, fmt); #endif if (!p) p = ""; while (*p) { if (*p != '%') { /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */ /* but the following code achieves better performance for cases * where format string is long and contains few conversions */ const char *q = strchr(p+1,'%'); size_t n = !q ? strlen(p) : (q-p); if (str_l < str_m) { size_t avail = str_m-str_l; fast_memcpy(str+str_l, p, (n>avail?avail:n)); } p += n; str_l += n; } else { const char *starting_p; size_t min_field_width = 0, precision = 0; int zero_padding = 0, precision_specified = 0, justify_left = 0; int alternate_form = 0, force_sign = 0; int space_for_positive = 1; /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored. */ char length_modifier = '\0'; /* allowed values: \0, h, l, L */ char tmp[32];/* temporary buffer for simple numeric->string conversion */ const char *str_arg; /* string address in case of string argument */ size_t str_arg_l; /* natural field width of arg without padding and sign */ unsigned char uchar_arg; /* unsigned char argument value - only defined for c conversion. N.B. standard explicitly states the char argument for the c conversion is unsigned */ size_t number_of_zeros_to_pad = 0; /* number of zeros to be inserted for numeric conversions as required by the precision or minimal field width */ size_t zero_padding_insertion_ind = 0; /* index into tmp where zero padding is to be inserted */ char fmt_spec = '\0'; /* current conversion specifier character */ str_arg = NULL; starting_p = p; p++; /* skip '%' */ /* parse flags */ while (*p == '0' || *p == '-' || *p == '+' || *p == ' ' || *p == '#' || *p == '\'') { switch (*p) { case '0': zero_padding = 1; break; case '-': justify_left = 1; break; case '+': force_sign = 1; space_for_positive = 0; break; case ' ': force_sign = 1; /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ #ifdef PERL_COMPATIBLE /* ... but in Perl the last of ' ' and '+' applies */ space_for_positive = 1; #endif break; case '#': alternate_form = 1; break; case '\'': break; } p++; } /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */ /* parse field width */ if (*p == '*') { int j; p++; j = va_arg(ap, int); if (j >= 0) min_field_width = j; else { min_field_width = -j; justify_left = 1; } } else if (isdigit((int)(*p))) { /* size_t could be wider than unsigned int; make sure we treat argument like common implementations do */ unsigned int uj = *p++ - '0'; while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); min_field_width = uj; } /* parse precision */ if (*p == '.') { p++; precision_specified = 1; if (*p == '*') { int j = va_arg(ap, int); p++; if (j >= 0) precision = j; else { precision_specified = 0; precision = 0; /* NOTE: * Solaris 2.6 man page claims that in this case the precision * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page * claim that this case should be treated as unspecified precision, * which is what we do here. */ } } else if (isdigit((int)(*p))) { /* size_t could be wider than unsigned int; make sure we treat argument like common implementations do */ unsigned int uj = *p++ - '0'; while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); precision = uj; } } /* parse 'h', 'l' and 'll' length modifiers */ if (*p == 'h' || *p == 'l') { length_modifier = *p; p++; if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ #ifdef SNPRINTF_LONGLONG_SUPPORT length_modifier = '2'; /* double l encoded as '2' */ #else length_modifier = 'l'; /* treat it as a single 'l' */ #endif p++; } } fmt_spec = *p; /* common synonyms: */ switch (fmt_spec) { case 'i': fmt_spec = 'd'; break; case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; default: break; } /* get parameter value, do initial processing */ switch (fmt_spec) { case '%': /* % behaves similar to 's' regarding flags and field widths */ case 'c': /* c behaves similar to 's' regarding flags and field widths */ case 's': length_modifier = '\0'; /* wint_t and wchar_t not supported */ /* the result of zero padding flag with non-numeric conversion specifier*/ /* is undefined. Solaris and HPUX 10 does zero padding in this case, */ /* Digital Unix and Linux does not. */ #if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE) zero_padding = 0; /* turn zero padding off for string conversions */ #endif str_arg_l = 1; switch (fmt_spec) { case '%': str_arg = p; break; case 'c': { int j = va_arg(ap, int); uchar_arg = (unsigned char) j; /* standard demands unsigned char */ str_arg = (const char *) &uchar_arg; break; } case 's': str_arg = va_arg(ap, const char *); if (!str_arg) str_arg_l = 0; /* make sure not to address string beyond the specified precision !!! */ else if (!precision_specified) str_arg_l = strlen(str_arg); /* truncate string if necessary as requested by precision */ else if (precision == 0) str_arg_l = 0; else { const char *q = memchr(str_arg, '\0', precision); str_arg_l = !q ? precision : (q-str_arg); } break; default: break; } break; case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { /* NOTE: the u, o, x, X and p conversion specifiers imply the value is unsigned; d implies a signed value */ int arg_sign = 0; /* 0 if numeric argument is zero (or if pointer is NULL for 'p'), +1 if greater than zero (or nonzero for unsigned arguments), -1 if negative (unsigned argument is never negative) */ int int_arg = 0; unsigned int uint_arg = 0; /* only defined for length modifier h, or for no length modifiers */ long int long_arg = 0; unsigned long int ulong_arg = 0; /* only defined for length modifier l */ void *ptr_arg = NULL; /* pointer argument value -only defined for p conversion */ #ifdef SNPRINTF_LONGLONG_SUPPORT long long int long_long_arg = 0; unsigned long long int ulong_long_arg = 0; /* only defined for length modifier ll */ #endif if (fmt_spec == 'p') { /* HPUX 10: An l, h, ll or L before any other conversion character * (other than d, i, u, o, x, or X) is ignored. * Digital Unix: * not specified, but seems to behave as HPUX does. * Solaris: If an h, l, or L appears before any other conversion * specifier (other than d, i, u, o, x, or X), the behavior * is undefined. (Actually %hp converts only 16-bits of address * and %llp treats address as 64-bit data which is incompatible * with (void *) argument on a 32-bit system). */ #ifdef SOLARIS_COMPATIBLE # ifdef SOLARIS_BUG_COMPATIBLE /* keep length modifiers even if it represents 'll' */ # else if (length_modifier == '2') length_modifier = '\0'; # endif #else length_modifier = '\0'; #endif ptr_arg = va_arg(ap, void *); if (ptr_arg != NULL) arg_sign = 1; } else if (fmt_spec == 'd') { /* signed */ switch (length_modifier) { case '\0': case 'h': /* It is non-portable to specify a second argument of char or short * to va_arg, because arguments seen by the called function * are not char or short. C converts char and short arguments * to int before passing them to a function. */ int_arg = va_arg(ap, int); if (int_arg > 0) arg_sign = 1; else if (int_arg < 0) arg_sign = -1; break; case 'l': long_arg = va_arg(ap, long int); if (long_arg > 0) arg_sign = 1; else if (long_arg < 0) arg_sign = -1; break; #ifdef SNPRINTF_LONGLONG_SUPPORT case '2': long_long_arg = va_arg(ap, long long int); if (long_long_arg > 0) arg_sign = 1; else if (long_long_arg < 0) arg_sign = -1; break; #endif } } else { /* unsigned */ switch (length_modifier) { case '\0': case 'h': uint_arg = va_arg(ap, unsigned int); if (uint_arg) arg_sign = 1; break; case 'l': ulong_arg = va_arg(ap, unsigned long int); if (ulong_arg) arg_sign = 1; break; #ifdef SNPRINTF_LONGLONG_SUPPORT case '2': ulong_long_arg = va_arg(ap, unsigned long long int); if (ulong_long_arg) arg_sign = 1; break; #endif } } str_arg = tmp; str_arg_l = 0; /* NOTE: * For d, i, u, o, x, and X conversions, if precision is specified, * the '0' flag should be ignored. This is so with Solaris 2.6, * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. */ #ifndef PERL_COMPATIBLE if (precision_specified) zero_padding = 0; #endif if (fmt_spec == 'd') { if (force_sign && arg_sign >= 0) tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; /* leave negative numbers for sprintf to handle, to avoid handling tricky cases like (short int)(-32768) */ #ifdef LINUX_COMPATIBLE } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) { tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; #endif } else if (alternate_form) { if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } /* alternate form should have no effect for p conversion, but ... */ #ifdef HPUX_COMPATIBLE else if (fmt_spec == 'p' /* HPUX 10: for an alternate form of p conversion, * a nonzero result is prefixed by 0x. */ #ifndef HPUX_BUG_COMPATIBLE /* Actually it uses 0x prefix even for a zero value. */ && arg_sign != 0 #endif ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; } #endif } zero_padding_insertion_ind = str_arg_l; if (!precision_specified) precision = 1; /* default precision is 1 */ if (precision == 0 && arg_sign == 0 #if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE) && fmt_spec != 'p' /* HPUX 10 man page claims: With conversion character p the result of * converting a zero value with a precision of zero is a null string. * Actually HP returns all zeroes, and Linux returns "(nil)". */ #endif ) { /* converted to null string */ /* When zero value is formatted with an explicit precision 0, the resulting formatted string is empty (d, i, u, o, x, X, p). */ } else { char f[5]; int f_l = 0; f[f_l++] = '%'; /* construct a simple format string for sprintf */ if (!length_modifier) { } else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } else f[f_l++] = length_modifier; f[f_l++] = fmt_spec; f[f_l++] = '\0'; if (fmt_spec == 'p') { str_arg_l += strlen(_ultoa((unsigned long)(ptr_arg), tmp+str_arg_l, 16)); // str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); } else if (fmt_spec == 'd') { /* signed */ switch (length_modifier) { case '\0': case 'h': //str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break; str_arg_l += strlen(_ltoa(int_arg, tmp+str_arg_l, 10)); break; case 'l': //str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break; str_arg_l += strlen(_ltoa(long_arg, tmp+str_arg_l, 10)); break; #ifdef SNPRINTF_LONGLONG_SUPPORT #error not translated case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break; #endif } } else { /* unsigned */ int base = 10; if (fmt_spec == 'x' || fmt_spec == 'X') { base = 16; } if (fmt_spec == 'o') { base = 8; } switch (length_modifier) { case '\0': case 'h': //str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break; str_arg_l += strlen(_ultoa(uint_arg, tmp+str_arg_l, base)); break; case 'l': //str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break; str_arg_l += strlen(_ultoa(ulong_arg, tmp+str_arg_l, base)); break; #ifdef SNPRINTF_LONGLONG_SUPPORT #error not translated case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; #endif } } /* include the optional minus sign and possible "0x" in the region before the zero padding insertion point */ if (zero_padding_insertion_ind < str_arg_l && tmp[zero_padding_insertion_ind] == '-') { zero_padding_insertion_ind++; } if (zero_padding_insertion_ind+1 < str_arg_l && tmp[zero_padding_insertion_ind] == '0' && (tmp[zero_padding_insertion_ind+1] == 'x' || tmp[zero_padding_insertion_ind+1] == 'X') ) { zero_padding_insertion_ind += 2; } } { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; if (alternate_form && fmt_spec == 'o' #ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */ && (str_arg_l > 0) #endif #ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */ #else /* unless zero is already the first character */ && !(zero_padding_insertion_ind < str_arg_l && tmp[zero_padding_insertion_ind] == '0') #endif ) { /* assure leading zero for alternate-form octal numbers */ if (!precision_specified || precision < num_of_digits+1) { /* precision is increased to force the first character to be zero, except if a zero value is formatted with an explicit precision of zero */ precision = num_of_digits+1; precision_specified = 1; } } /* zero padding to specified precision? */ if (num_of_digits < precision) number_of_zeros_to_pad = precision - num_of_digits; } /* zero padding to specified minimal field width? */ if (!justify_left && zero_padding) { int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); if (n > 0) number_of_zeros_to_pad += n; } break; } default: /* unrecognized conversion specifier, keep format string as-is*/ zero_padding = 0; /* turn zero padding off for non-numeric convers. */ #ifndef DIGITAL_UNIX_COMPATIBLE justify_left = 1; min_field_width = 0; /* reset flags */ #endif #if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE) /* keep the entire format string unchanged */ str_arg = starting_p; str_arg_l = p - starting_p; /* well, not exactly so for Linux, which does something inbetween, * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */ #else /* discard the unrecognized conversion, just keep * * the unrecognized conversion character */ str_arg = p; str_arg_l = 0; #endif if (*p) str_arg_l++; /* include invalid conversion specifier unchanged if not at end-of-string */ break; } if (*p) p++; /* step over the just processed conversion specifier */ /* insert padding to the left as requested by min_field_width; this does not include the zero padding in case of numerical conversions*/ if (!justify_left) { /* left padding with blank or zero */ int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n)); } str_l += n; } } /* zero padding as requested by the precision or by the minimal field width * for numeric conversions required? */ if (number_of_zeros_to_pad <= 0) { /* will not copy first part of numeric right now, * * force it to be copied later in its entirety */ zero_padding_insertion_ind = 0; } else { /* insert first part of numerics (sign or '0x') before zero padding */ int n = zero_padding_insertion_ind; if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); } str_l += n; } /* insert zero padding as requested by the precision or min field width */ n = number_of_zeros_to_pad; if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memset(str+str_l, '0', (n>avail?avail:n)); } str_l += n; } } /* insert formatted string * (or as-is conversion specifier for unknown conversions) */ { int n = str_arg_l - zero_padding_insertion_ind; if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, (n>avail?avail:n)); } str_l += n; } } /* insert right padding */ if (justify_left) { /* right blank padding to the field width */ int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); if (n > 0) { if (str_l < str_m) { size_t avail = str_m-str_l; fast_memset(str+str_l, ' ', (n>avail?avail:n)); } str_l += n; } } } } #if defined(NEED_SNPRINTF_ONLY) va_end(ap); #endif if (str_m > 0) { /* make sure the string is null-terminated even at the expense of overwriting the last character (shouldn't happen, but just in case) */ str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; } /* Return the number of characters formatted (excluding trailing null * character), that is, the number of characters that would have been * written to the buffer if it were large enough. * * The value of str_l should be returned, but str_l is of unsigned type * size_t, and snprintf is int, possibly leading to an undetected * integer overflow, resulting in a negative return value, which is illegal. * Both XSH5 and ISO C99 (at least the draft) are silent on this issue. * Should errno be set to EOVERFLOW and EOF returned in this case??? */ return (int) str_l; } #endif ================================================ FILE: snprintf.h ================================================ #ifndef _PORTABLE_SNPRINTF_H_ #define _PORTABLE_SNPRINTF_H_ #define PORTABLE_SNPRINTF_VERSION_MAJOR 2 #define PORTABLE_SNPRINTF_VERSION_MINOR 2 #ifdef HAVE_SNPRINTF #include #else extern int snprintf(char *, size_t, const char *, /*args*/ ...); extern int vsnprintf(char *, size_t, const char *, va_list); #endif #if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF) extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); #define snprintf portable_snprintf #define vsnprintf portable_vsnprintf #endif extern int asprintf (char **ptr, const char *fmt, /*args*/ ...); extern int vasprintf (char **ptr, const char *fmt, va_list ap); extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap); #endif