Go to most recent revision | Blame | Last modification | View Log | RSS feed
/* 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, int mode)
{
union REGS r;
struct SREGS s;
#ifndef __WATCOMC__
if (mode); /* mode ignored - readonly supported */
#endif
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 */