aboutsummaryrefslogtreecommitdiff
path: root/kitten.c
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2026-09-03 23:49:29 +0200
committerJavier <dev.git@javispedro.com>2026-09-04 00:06:23 +0200
commit1b9e3118374a13dc12652d87b1dd3d1ccfecbbf3 (patch)
tree0c5f7b5aef76806a449d799dba333d41eaa60aa2 /kitten.c
parent906193586d30a382d2b982419d92f80c07a65286 (diff)
downloadvbados-1b9e3118374a13dc12652d87b1dd3d1ccfecbbf3.tar.gz
vbados-1b9e3118374a13dc12652d87b1dd3d1ccfecbbf3.zip
embed language & unicode tables into main executables
use KITTENC to embed all NLS files into executable KITTENC does not allow loading external files anymore... embed the cp???uni.tbl (codepage -> unicode conversion tables) into the main executable too. however, we will still try to find them in PATH if user cp is missing from the internal ones. this means the distribution zip/flp now contain much fewer files. con: some utilities now need to built in the host (kittenc & unitbl2c) this may make the build less portable; so far unix and dos/386 still work.
Diffstat (limited to 'kitten.c')
-rw-r--r--kitten.c756
1 files changed, 141 insertions, 615 deletions
diff --git a/kitten.c b/kitten.c
index 3e7f9d9..421ed81 100644
--- a/kitten.c
+++ b/kitten.c
@@ -1,180 +1,61 @@
/* Functions that emulate UNIX catgets */
-/* Copyright (C) 1999,2000,2001 Jim Hall <jhall@freedos.org> */
-/* Kitten version 2003 by Tom Ehlert, heavily modified by Eric Auer 2003 */
+/* Kitten version 2021 by Tom Ehlert, */
/*
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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 GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
-
-#include <stdio.h> /* sprintf */
-#include <stdlib.h> /* getenv */
-#include <string.h> /* strchr */
-#include <fcntl.h>
-#include <dos.h>
-#if !defined(__TURBOC__)
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
-
-#include "kitten.h"
-
-#if defined(__TURBOC__)
-int dos_open(char *filename, int mode);
-#define open(filename,mode) dos_open(filename,mode)
-
-int dos_read(int file, void *ptr, unsigned count);
-#define read(file, ptr, count) dos_read(file,ptr,count)
-
-int dos_write(int file, void *ptr, unsigned count);
-#define write(file, ptr, count) dos_write(file,ptr,count)
-
-void dos_close(int file);
-#define close(file) dos_close(file)
-#endif
-
-static char catcontents[6144]; /* *** Maximum size of PROGRAM.%LANG% file *** */
-
-struct catstring{
- char key1;
- char key2;
- char *text;
-};
-
-typedef struct catstring catstring_t;
+ This software is free software; free to use,
+ modify, pass to others, whatever
-static catstring_t catpoints[128]; /* max 128 strings can be defined... */
-
-
-/* Local prototypes */
+ use it at your own risk
+*/
-int catread (char *catfile); /* Reads a catfile into the hash */
-char *processEscChars(char *line); /* Converts c escape sequences to chars */
+/* Minor modifications to use internal static buffer to keep API compatible
+ * with regular kitten. */
-int get_char(int); /* not meant for external use */
-/* external use would cause consistency problems if */
-/* value or related file of the file handle changes */
+#ifndef NO_KITTEN
-int mystrtoul(char *, int, int);
+#include <stdio.h> /* sprintf */
+#include <stdlib.h> /* getenv */
+#include <io.h> /* sprintf */
+#include <fcntl.h> /* sprintf */
+#include <string.h> /* sprintf */
+#include "kitten.h"
+#include "kittenc.h"
/* Globals */
-static nl_catd _kitten_catalog = 0; /* _kitten_catalog descriptor or 0 */
-static char catfile[128]; /* full path to _kitten_catalog */
-
-static char getlbuf[1024]; /* read buffer (better speed) -- 7/2004: 1k, not 8k */
-static char *getlp; /* current point in buffer */
-static int getlrem = -1; /* remaining bytes in buffer */
-
-
-#if defined(__TURBOC__)
-/* DOS handle based file usage */
-
-int dos_open(char *filename, int mode)
-{
- union REGS r;
- struct SREGS s;
- (void)mode; /* mode ignored - readonly supported */
- r.h.ah = 0x3d;
- r.h.al = 0; /* read mode only supoported now !! */
- r.x.dx = FP_OFF(filename);
- s.ds = FP_SEG(filename);
- intdosx(&r,&r,&s);
- return ( (r.x.cflag) ? -1 : r.x.ax );
-}
-
-
-int dos_read(int file, void *ptr, unsigned count)
-{
- union REGS r;
- struct SREGS s;
- r.h.ah = 0x3f;
- r.x.bx = file;
- r.x.cx = count;
- r.x.dx = FP_OFF(ptr);
- s.ds = FP_SEG(ptr);
- intdosx(&r,&r,&s);
- return ( (r.x.cflag) ? 0 : r.x.ax );
-}
-
-
-int dos_write(int file, void *ptr, unsigned count)
-{
- union REGS r;
- struct SREGS s;
- r.h.ah = 0x40;
- r.x.bx = file;
- r.x.cx = count;
- r.x.dx = FP_OFF(ptr);
- s.ds = FP_SEG(ptr);
- intdosx(&r,&r,&s);
- return ( (r.x.cflag) ? 0 : r.x.ax );
-}
-
-
-void dos_close(int file)
-{
- union REGS r;
- r.h.ah = 0x3e;
- r.x.bx = file;
- intdos(&r,&r);
-}
-#else
-#include <unistd.h>
-#endif
+static char global_nls_buffer[4096] = {0};
/* Functions */
-
/**
* On success, catgets() returns a pointer to an internal
* buffer area containing the null-terminated message string.
* On failure, catgets() returns the value 'message'.
*/
-const char *kittengets(int setnum, int msgnum, const char *message)
+const char *
+_kittengets (int msgID, const char *message)
{
+ struct message_header *message_header; // if this is != 0 then init was done
- int i = 0;
-
- while ((catpoints[i].key1 != setnum) || (catpoints[i].key2 != msgnum)) {
- if ((catpoints[i].text == NULL) || (i>127)) /* at EOF */
- return message;
- i++;
- }
-
- if (catpoints[i].text == NULL)
- return message;
- else
- return (catpoints[i].text);
-}
+ if (global_nls_buffer[0] == 0)
+ return message;
-/**
- * Obtains path to current exec.
- */
-static char * get_cmd_path(char *buf)
-{
- char cmd_drive[_MAX_DRIVE];
- char cmd_dir[_MAX_DIR];
+ message_header = (struct message_header *)global_nls_buffer;
+
+ for ( ; message_header->len != 0; message_header = (struct message_header *)((char*)message_header + message_header->len))
+ {
+ if (message_header->id== msgID)
+ {
+ return (const char*)message_header + sizeof(struct message_header);
+ }
- _splitpath(__argv[0], cmd_drive, cmd_dir, NULL, NULL);
- _makepath(buf, cmd_drive, cmd_dir, "", NULL);
+ }
- return buf;
+ return message;
}
@@ -182,483 +63,128 @@ static char * get_cmd_path(char *buf)
* Initialize kitten for program (name).
*/
-nl_catd kittenopen(char *name)
-{
- /* catopen() returns a message _kitten_catalog descriptor *
- * of type nl_catd on success. On failure, it returns -1. */
-
- char catlang[3]; /* from LANG environment var. */
- char *nlsptr; /* ptr to NLSPATH */
- char *lang; /* ptr to LANG */
- int i;
-
- /* Open the _kitten_catalog file */
- /* The value of `_kitten_catalog' will be set based on catread */
-
- if (_kitten_catalog) { /* Already one open */
- write(1,"cat already open\r\n",strlen("cat already open\r\n"));
- return (-1);
- }
-
- for (i=0; i<128; i++)
- catpoints[i].text = NULL;
-
- if (strchr (name, '\\')) {
- /* unusual case: 'name' is a filename */
- write(1,"found \\\r\n",9);
- _kitten_catalog = catread (name);
- if (_kitten_catalog)
- return (_kitten_catalog);
- }
-
- /* If the message _kitten_catalog file name does not contain a directory *
- * separator, then we need to try to locate the message _kitten_catalog. */
-
- /* We will need the value of LANG, and may need a 2-letter abbrev of
- LANG later on, so get it now. */
-
- lang = getenv ("LANG");
-
- if (lang == NULL) {
- /* printf("no lang= found\n"); */ /* not fatal, though */
- /* Return failure - we won't be able to locate the cat file */
- return (-1);
- }
-
- if ( ( strlen(lang) < 2 ) ||
- ( (strlen(lang) > 2) && (lang[2] != '-') ) ) {
- /* Return failure - we won't be able to locate the cat file */
- return (-1);
- }
-
- memcpy(catlang, lang, 2);
- /* we copy the full LANG value or the part before "-" if "-" found */
- catlang[2] = '\0';
-
- /* first try to find catalog file in the same path as exe */
- get_cmd_path(catfile);
- strcat(catfile, name);
- strcat(catfile, ".");
- strcat(catfile, catlang);
- _kitten_catalog = catread (catfile);
- if (_kitten_catalog)
- return (_kitten_catalog);
-
- /* maybe exe_path\nls\cat.%LANG% ? */
- get_cmd_path(catfile);
- strcat(catfile, "NLS\\");
- strcat(catfile, name);
- strcat(catfile, ".");
- strcat(catfile, catlang);
- _kitten_catalog = catread (catfile);
- if (_kitten_catalog)
- return (_kitten_catalog);
-
- /* otherwise step through NLSPATH */
- nlsptr = getenv ("NLSPATH");
-
- if (nlsptr == NULL) {
- /* printf("no NLSPATH= found\n"); */ /* not fatal either */
- /* Return failure - we won't be able to locate the cat file */
- return (-1);
- }
-
- catfile[0] = '\0';
-
- while (nlsptr != NULL) {
- char *tok = strchr(nlsptr, ';');
- int toklen;
-
- if (tok == NULL)
- toklen = strlen(nlsptr); /* last segment */
- else
- toklen = (int)(tok - nlsptr); /* segment terminated by ';' */
-
- /* catfile = malloc(toklen+1+strlen(name)+1+strlen(lang)+1); */
- /* Try to find the _kitten_catalog file in each path from NLSPATH */
-
- if ((toklen+6+strlen(name)) > sizeof(catfile)) {
- write(1,"NLSPATH overflow\r\n",strlen("NLSPATH overflow\r\n"));
- return 0; /* overflow in NLSPATH, should never happen */
- }
-
- /* Rule #1: %NLSPATH%\%LANG%\cat */
-
- memcpy(catfile, nlsptr, toklen);
- strcpy(catfile+toklen,"\\");
- strcat(catfile,catlang);
- strcat(catfile,"\\");
- strcat(catfile,name);
- _kitten_catalog = catread (catfile);
- if (_kitten_catalog)
- return (_kitten_catalog);
-
- /* Rule #2: %NLSPATH%\cat.%LANG% */
-
- /* memcpy(catfile, nlsptr, toklen); */
- strcpy(catfile+toklen,"\\");
- strcat(catfile,name);
- strcat(catfile,".");
- strcat(catfile,catlang);
- _kitten_catalog = catread (catfile);
- if (_kitten_catalog)
- return (_kitten_catalog);
-
- /* Grab next tok for the next while iteration */
-
- nlsptr = tok;
- if (nlsptr) nlsptr++;
-
- } /* while tok */
-
- /* We could not find it. Return failure. */
-
- return (-1);
-}
+#define _toupper(c) (c & ~0x20)
-
-/**
- * Load a message catalog into memory.
- */
-
-int catread (char *catfile)
-{
- int file; /* pointer to the catfile */
- int i;
- char *where;
- char *tok;
-
- /* Get the whole catfile into a buffer and parse it */
-
- file = open (catfile, O_RDONLY);
- if (file < 0)
- /* Cannot open the file. Return failure */
- return 0;
-
- for (i=0; i<128; i++)
- catpoints[i].text = NULL;
-
- for (i=0; i<sizeof(catcontents); i++)
- catcontents[i] = '\0';
-
- /* Read the file into memory */
- i = read (file, catcontents, sizeof(catcontents)-1);
-
- if ((i == sizeof(catcontents)-1) || (i < 1))
- return 0; /* file was too big or too small */
-
- where = catcontents;
- i = 0; /* catpoints entry */
-
- do {
- char *msg;
- char *key;
- int key1 = 0;
- int key2 = 0;
-
- tok = strchr(where, '\n');
-
- if (tok == NULL) { /* done? */
- close(file);
- return 1; /* success */
- }
-
- tok[0] = '\0'; /* terminate here */
- tok--; /* guess: \r before \n */
- if (tok[0] != '\r')
- tok++; /* if not, go back */
- else {
- tok[0] = '\0'; /* terminate here already */
- tok++;
- }
- tok++; /* this is where the next line starts */
-
- if ( (where[0] >= '0') && (where[0] <= '9') &&
- ( (msg = strchr(where,':')) != NULL) ) {
- /* Skip everything which starts with # or with no digit */
- /* Entries look like "1.2:This is a message" */
-
- msg[0] = '\0'; /* remove : */
- msg++; /* go past the : */
-
- if ((key = strchr (where, '.')) != NULL) {
- key[0] = '\0'; /* turn . into terminator */
- key++; /* go past the . */
- key1 = mystrtoul(where, 10, strlen(where));
- key2 = mystrtoul(key, 10, strlen(key));
-
- if ((key1 >= 0) && (key2 >= 0)) {
- catpoints[i].key1 = key1;
- catpoints[i].key2 = key2;
- catpoints[i].text = processEscChars(msg);
- if (catpoints[i].text == NULL) /* ESC parse error */
- catpoints[i].text = msg;
- i++; /* next entry! */
- } /* valid keys */
-
- } /* . found */
-
- } /* : and digit found */
-
- where = tok; /* go to next line */
-
- } while (1);
-}
-
-
-void kittenclose (void)
-{
- /* close a message _kitten_catalog */
- _kitten_catalog = 0;
-}
-
-
-/**
- * Parse a string that represents an unsigned integer.
- * Returns -1 if an error is found. The first size
- * chars of the string are parsed.
- */
-
-int mystrtoul(char *src, int base, int size)
+nl_catd
+kittenopen (const char *name)
{
- int ret = 0;
+ int fd;
+ struct message_end message_end;
+ struct content content[KITTEN_MAX_RESOURCES];
+ char *language;
+ int i;
+ long len;
+ char exename[_MAX_PATH];
+ long fileendnow, seekoffset;
+
+ //printf("kittenopen %s\n", name);
- for (; size > 0; size--) {
- int digit;
- int ch = *src;
- src++;
-
- if (ch >= '0' && ch <= '9')
- digit = ch - '0';
- else if (ch >= 'A' && ch <= 'Z')
- digit = ch - 'A' + 10;
- else if (ch >= 'a' && ch <= 'z')
- digit = ch - 'a' + 10;
- else
- return -1;
-
- if (digit >= base)
- return -1;
-
- ret = ret * base + digit;
- } /* for */
-
- return ret;
-}
-
-
-/**
- * Process strings, converting \n, \t, \v, \b, \r, \f, \\,
- * \ddd, \xdd and \x0dd to actual chars.
- * (Note: \x is an extension to support hexadecimal)
- * This is used to allow the messages to use c escape sequences.
- * Modifies the line in-place (always same size or shorter).
- * Returns a pointer to input string.
- */
-
-char *processEscChars(char *line)
-{
- char *src = line;
- char *dst = line; /* possible as dst is shorter than src */
-
- /* used when converting \xdd and \ddd (hex or octal) characters */
- char ch;
-
- if (line == NULL)
- return line;
-
- /* cycle through copying characters, except when a \ is encountered. */
- while (*src != '\0') {
- ch = *src;
- src++;
-
- if (ch == '\\') {
- ch = *src; /* what follows slash? */
- src++;
-
- switch (ch) {
- case '\\': /* a single slash */
- *dst = '\\';
- dst++;
- break;
- case 'n': /* a newline (linefeed) */
- *dst = '\n';
- dst++;
- break;
- case 'r': /* a carriage return */
- *dst = '\r';
- dst++;
- break;
- case 't': /* a horizontal tab */
- *dst = '\t';
- dst++;
- break;
- case 'v': /* a vertical tab */
- *dst = '\v';
- dst++;
- break;
- case 'b': /* a backspace */
- *dst = '\b';
- dst++;
- break;
- case 'a': /* alert */
- *dst = '\a';
- dst++;
- break;
- case 'f': /* formfeed */
- *dst = '\f';
- dst++;
- break;
- case 'x': /* extension supporting hex numbers \xdd or \x0dd */
- {
- int chx = mystrtoul(src, 16, 2); /* get value */
- if (chx >= 0) { /* store character */
- *dst = chx;
- dst++;
- src += 2;
- } else /* error so just store x (loose slash) */
- {
- *dst = *src;
- dst++;
- }
- }
- break;
- default: /* just store letter (loose slash) or handle octal */
- {
- int chx = mystrtoul(src, 8, 3); /* get value */
- if (chx >= 0) {/* store character */
- *dst = chx;
- dst++;
- src += 3;
- } else
- {
- *dst = *src;
- dst++;
- }
- }
- break;
- } /* switch */
- } /* if backslash */
- else
- {
- *dst = ch;
- dst++;
- }
- } /* while */
-
- /* ensure '\0' terminated */
- *dst = '\0';
-
- return line;
-}
-
-
-int get_char(int file) {
- int rval = -1;
-
- if (getlrem <= 0) { /* (re)init buffer */
- getlrem = read(file, getlbuf, sizeof(getlbuf));
- if (getlrem <= 0)
- return -1; /* fail: read error / EOF */
- getlp = getlbuf; /* init pointer */
- }
-
- if (getlrem > 0) { /* consume byte from buffer */
- rval = getlp[0];
- getlp++;
- getlrem--;
- }
-
- return rval;
-}
-
-
-/**
- * Read a line of text from file. You must call this with
- * a null buffer or null size to flush buffers when you are
- * done with a file before using it on the next file. Cannot
- * be used for 2 files at the same time.
- */
-
-int get_line (int file, char *orig_str, int size)
-{
- int ch;
- char *str = orig_str;
+ if ((fd = open(name, _O_RDONLY | _O_BINARY)) < 0)
+ {
+ sprintf(exename, "%s.exe", name);
+ if ((fd = open(exename, _O_RDONLY | _O_BINARY)) < 0)
+ {
+ printf("can't open %s\n", name); // should never happen
+ return 0;
+ }
+ }
+
+ fileendnow = lseek(fd, 0, SEEK_END);
+
+ lseek(fd, - (int)sizeof(struct message_end), SEEK_END);
+
+ read(fd, &message_end, sizeof(struct message_end));
+
+ if (memcmp(message_end.ID, "KITTENC", 8) != 0)
+ {
+ //printf("no KITTENC record found\n");
+ return 0;
+ }
+
+ if (message_end.resource_count > KITTEN_MAX_RESOURCES)
+ {
+ printf("resource > %d\n", KITTEN_MAX_RESOURCES);
+ return 0;
+ }
+
+ seekoffset = fileendnow - message_end.fileend_orig;
+
+ if (lseek(fd, message_end.filepos+seekoffset, SEEK_SET) != message_end.filepos+seekoffset)
+ {
+ printf("can't seek 1\n");
+ return 0;
+ }
+
+
+ read(fd, &content, sizeof(content[0]) * message_end.resource_count);
+
+ language = getenv("LANG");
+
+ // printf("using language %s\n", language);
+
+ if (message_end.resource_count == 1)
+ {
+ if (language &&
+ _toupper(language[0]) == 'E' &&
+ _toupper(language[0]) == 'N')
+ {
+ // set LANG=EN switches to internal strings
+ close(fd);
+ return 0;
+ }
+ // otherwise use the only compiled language
+ i = 0;
+
+ goto found_my_language;
+ }
+
+ if (language == NULL) // language not found
+ { // use internal strings
+ close(fd);
+ return 0;
+ }
+
+ // see if we find our language in the existing resources
+ for (i = 0; i < message_end.resource_count; i++)
+ {
+ if(_toupper(content[i].language[0]) == _toupper(language[0]) &&
+ _toupper(content[i].language[1]) == _toupper(language[1]))
+ {
+ goto found_my_language;
+ }
+ }
+
+ // language not found
+ close(fd);
+ return 0;
- if ((size == 0) || (str == NULL)) { /* re-init get_line buffers */
- getlp = getlbuf;
- getlrem = -1;
- return 0;
- }
-
- str[0] = '\0';
+
- while (size > 0) {
- ch = get_char (file);
- if (ch < 0) {
- /* encountered EOF / read error, so no more content*/
- str[0] = '\0';
- /* test if the pointers are equal, there is not content in the buffer */
- if(str == orig_str)
- return 0; /* fail, no more content */
-
- return 1; /* success */
- }
+found_my_language:
+ // found our language
+ // printf(" language %s found between %lx and %lx\n", language, content[i].filepos_start, content[i].filepos_end);
- if (ch == '\r')
- ch = get_char (file); /* ignore \r */
+ // printf("seek %lu --> %lu\n", content[i].filepos_start, content[i].filepos_start+seekoffset);
- str[0] = ch;
+ lseek(fd, content[i].filepos_start+seekoffset, SEEK_SET);
+ len = content[i].filepos_end - content[i].filepos_start;
- if ( (ch == '\n') || (ch == '\r') || (ch < 0) ) { /* done? */
- str[0] = '\0';
- return 1; /* success */
- }
+ if (len > sizeof(global_nls_buffer))
+ {
+ printf("nls_buffer too small(%d). we need at least %l\n",
+ sizeof(global_nls_buffer), len);
+ close(fd);
+ return 1;
+ }
- str++;
- size--;
- } /* while */
+ read(fd, global_nls_buffer, (int)len);
- str[0] = '\0'; /* terminate buffer */
+ close(fd);
+ return 0;
+}
- return 0; /* fail, because the line is longer than the buffer? */
-}
-/*
- * input
- * s : input string to be parsed
- * delimiters : start, intermediate and end
- * responses : output buffer (has to be long enough for num chars)
- * num : required number of responses
- *
- * return
- * 0 on failure
- * 1 on success
- */
-int kitten_extract_response(const char *s, const char *delimiters,
- char *responses, int num) {
- int slen = strlen(s);
- const char *p, *q;
- int i;
-
- /* Response parsing requires substring usually of the form '(%c/%c)' */
- p = strchr(s, delimiters[0]);
- if (!p)
- return 0;
-
- /* Each response requires letter followed by a delimiter e.g. 'Y/' or 'N)' */
- if (p + 1 - s > slen - 2 * num)
- return 0;
-
- for (i = 0; i < num; i++) {
- q = p + 2 + i * 2;
- if (*q != delimiters[1] && *q != delimiters[2])
- return 0;
- responses[i] = *(p + 1 + i * 2);
- }
-
- return 1;
-}
+#endif /*NO_KITTEN */