/* Functions that emulate UNIX catgets */ /* Copyright (C) 1999,2000,2001 Jim Hall */ /* 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 /* sprintf */ #ifndef _MICROC_ #include /* getenv */ #include /* strchr */ #include #ifndef __PACIFIC__ #include #else #define O_RDONLY 0 #define O_TEXT 0 #endif #else #include #include #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[_MAX_PATH]; /* 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); } /** * Obtains path to current exec. */ static char * get_cmd_path(char *buf) { char cmd_drive[_MAX_DRIVE]; char cmd_dir[_MAX_DIR]; _splitpath(__argv[0], cmd_drive, cmd_dir, NULL, NULL); _makepath(buf, cmd_drive, cmd_dir, "", NULL); return 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; #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'; /* 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); /* 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 && 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 */