diff options
Diffstat (limited to 'kitten.c')
-rw-r--r-- | kitten.c | 696 |
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 */ |