aboutsummaryrefslogtreecommitdiff
path: root/kitten.c
diff options
context:
space:
mode:
Diffstat (limited to 'kitten.c')
-rw-r--r--kitten.c696
1 files changed, 696 insertions, 0 deletions
diff --git a/kitten.c b/kitten.c
new file mode 100644
index 0000000..f63cd96
--- /dev/null
+++ b/kitten.c
@@ -0,0 +1,696 @@
+/* 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 */
+
+/*
+ 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
+*/
+
+#ifndef NO_KITTEN
+
+#include <stdio.h> /* sprintf */
+#ifndef _MICROC_
+#include <stdlib.h> /* getenv */
+#include <string.h> /* strchr */
+#include <dos.h>
+#ifndef __PACIFIC__
+#include <fcntl.h>
+#else
+#define O_RDONLY 0
+#define O_TEXT 0
+#endif
+#else
+#include <intr.h>
+#include <file.h>
+#define O_RDONLY READONLY
+#define O_TEXT 0
+#endif
+/* assert we are running in small model */
+/* else pointer below has to be done correctly */
+/* char verify_small_pointers[sizeof(void*) == 2 ? 1 : -1]; */
+
+#include "kitten.h"
+
+char catcontents[8192];
+
+struct catstring
+{
+ char key1;
+ char key2;
+ char *text;
+};
+
+/* Micro-C does not support typedef */
+#define catstring_t struct catstring
+
+catstring_t catpoints[128];
+
+
+/* Local prototypes */
+
+int catread (char *catfile); /* Reads a catfile into the hash */
+char *processEscChars (char *line); /* Converts c escape sequences to chars */
+
+int get_char (int file); /* not meant for external use */
+/* external use would cause consistency problems if */
+/* value or related file of the file handle changes */
+
+int mystrtoul (char *src, int base, int size);
+
+
+/* Globals */
+
+nl_catd _kitten_catalog = 0; /* _kitten_catalog descriptor or 0 */
+char catfile[128]; /* full path to _kitten_catalog */
+
+char getlbuf[8192]; /* read buffer for better speed */
+char *getlp; /* current point in buffer */
+int getlrem = -1; /* remaining bytes in buffer */
+char lastcr = 0; /* for 2byte CR LF sequences */
+
+
+#ifndef _MICROC_
+#ifndef __DJGPP__
+
+/* DOS handle based file usage */
+
+int
+dos_open (char *filename)
+{
+ union REGS r;
+ struct SREGS s;
+
+ 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 : (int) 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);
+}
+
+#endif /*DJGPP*/
+#endif /*Micro-C */
+/* 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'.
+ */
+char *
+kittengets (int setnum, int msgnum, char *message)
+{
+/* In Micro-C, variables must be defined at the start of the
+ * function and may not be immediately assigned a value
+ */
+#ifdef _MICROC_
+ int i;
+ i = 0;
+#else
+ int i = 0;
+#endif
+
+ 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);
+}
+
+
+/**
+ * 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;
+#ifdef _MICROC_
+ char *tok;
+ int toklen;
+#endif
+
+ /* 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';
+
+ /* 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 && nlsptr[0])
+ {
+#ifdef _MICROC_
+ tok = strchr (nlsptr, ';');
+#else
+ char *tok = strchr (nlsptr, ';');
+ int toklen;
+#endif
+
+ if (tok == NULL)
+ toklen = strlen (nlsptr); /* last segment */
+ else
+ toklen = 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);
+}
+
+
+/**
+ * Load a message catalog into memory.
+ */
+
+int
+catread (char *catfile)
+{
+ int file; /* pointer to the catfile */
+ int i;
+ char *where;
+ char *tok;
+#ifdef _MICROC_
+ char *msg;
+ char *key;
+ int key1;
+ int key2;
+#endif
+
+ /* Get the whole catfile into a buffer and parse it */
+
+ file = open (catfile, O_RDONLY | O_TEXT);
+ if (file < 0)
+ /* Cannot open the file. Return failure */
+ return 0;
+
+ for (i = 0; i < 128; i++)
+ catpoints[i].text = NULL;
+
+ for (i = 0; (unsigned int) 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
+ {
+#ifndef _MICROC_
+ char *msg;
+ char *key;
+ int key1 = 0;
+ int key2 = 0;
+#else
+ key1 = 0;
+ key2 = 0;
+#endif
+
+ 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);
+#ifdef __PACIFIC__
+ return 0;
+#endif
+}
+
+
+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)
+{
+#ifdef _MICROC_
+ int ret;
+ int digit;
+ int ch;
+ ret = 0;
+#else
+ int ret = 0;
+#endif
+
+ for (; size > 0; size--)
+ {
+#ifdef _MICROC_
+ ch = *src;
+#else
+ int digit;
+ int ch = *src;
+#endif
+ 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)
+{
+ /* used when converting \xdd and \ddd (hex or octal) characters */
+ char ch;
+#ifdef _MICROC_
+ char *src;
+ char *dst;
+ int chx;
+ src = line;
+ dst = line;
+#else
+ char *src = line;
+ char *dst = line; /* possible as dst is shorter than src */
+#endif
+
+ 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 */
+ {
+#ifdef _MICROC_
+ chx = mystrtoul (src, 16, 2); /* get value */
+#else
+ int chx = mystrtoul (src, 16, 2); /* get value */
+#endif
+ 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 */
+ {
+#ifdef _MICROC_
+ chx = mystrtoul (src, 8, 3); /* get value */
+#else
+ int chx = mystrtoul (src, 8, 3); /* get value */
+#endif
+ 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)
+{
+#ifdef _MICROC_
+ int rval;
+ rval = -1;
+#else
+ int rval = -1;
+#endif
+
+ 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 *str, int size)
+{
+ int ch;
+#ifdef _MICROC_
+ int success;
+ success = 0;
+#else
+ int success = 0;
+#endif
+
+ if ((size == 0) || (str == NULL))
+ { /* re-init get_line buffers */
+ getlp = getlbuf;
+ getlrem = -1;
+ lastcr = 0;
+ return 0;
+ }
+
+ str[0] = '\0';
+
+ while ((size > 0) && (success == 0))
+ {
+ ch = get_char (file);
+ if (ch < 0)
+ break; /* (can cause fail if no \n found yet) */
+
+ if (ch == '\r')
+ ch = get_char (file); /* ignore \r */
+
+ str[0] = ch;
+
+ if ((ch == '\n') || (ch == '\r'))
+ { /* done? */
+ str[0] = '\0';
+ return 1; /* success */
+ }
+
+ str++;
+ size--;
+
+ } /* while */
+
+ str[0] = '\0'; /* terminate buffer */
+
+ return success;
+
+}
+
+#endif /*NO_KITTEN */