/* 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 */ #include /* sprintf */ #include /* getenv */ #include /* strchr */ #include #include #if !defined(__TURBOC__) #include #include #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; static catstring_t catpoints[128]; /* max 128 strings can be defined... */ /* 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); /* not meant for external use */ /* external use would cause consistency problems if */ /* value or related file of the file handle changes */ int mystrtoul(char *, int, int); /* 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 #endif /* 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) { 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); } /** * 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'; /* 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); } /** * 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= '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) { int ret = 0; 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 ((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 */ } if (ch == '\r') ch = get_char (file); /* ignore \r */ str[0] = ch; if ( (ch == '\n') || (ch == '\r') || (ch < 0) ) { /* done? */ str[0] = '\0'; return 1; /* success */ } str++; size--; } /* while */ str[0] = '\0'; /* terminate buffer */ 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; }