From 1b9e3118374a13dc12652d87b1dd3d1ccfecbbf3 Mon Sep 17 00:00:00 2001 From: Javier Date: Thu, 3 Sep 2026 23:49:29 +0200 Subject: embed language & unicode tables into main executables use KITTENC to embed all NLS files into executable KITTENC does not allow loading external files anymore... embed the cp???uni.tbl (codepage -> unicode conversion tables) into the main executable too. however, we will still try to find them in PATH if user cp is missing from the internal ones. this means the distribution zip/flp now contain much fewer files. con: some utilities now need to built in the host (kittenc & unitbl2c) this may make the build less portable; so far unix and dos/386 still work. --- kitten.c | 756 ++++++++++++------------------------------------------------- kitten.h | 50 ++-- kittenc.c | 548 ++++++++++++++++++++++++++++++++++++++++++++ kittenc.h | 44 ++++ makefile | 36 ++- mousmain.c | 4 +- sfmain.c | 28 +-- unitbl2c.c | 206 +++++++++++++++++ 8 files changed, 1007 insertions(+), 665 deletions(-) create mode 100644 kittenc.c create mode 100644 kittenc.h create mode 100644 unitbl2c.c diff --git a/kitten.c b/kitten.c index 3e7f9d9..421ed81 100644 --- a/kitten.c +++ b/kitten.c @@ -1,180 +1,61 @@ /* Functions that emulate UNIX catgets */ -/* Copyright (C) 1999,2000,2001 Jim Hall */ -/* Kitten version 2003 by Tom Ehlert, heavily modified by Eric Auer 2003 */ +/* Kitten version 2021 by Tom Ehlert, */ /* - 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; + This software is free software; free to use, + modify, pass to others, whatever -static catstring_t catpoints[128]; /* max 128 strings can be defined... */ - - -/* Local prototypes */ + use it at your own risk +*/ -int catread (char *catfile); /* Reads a catfile into the hash */ -char *processEscChars(char *line); /* Converts c escape sequences to chars */ +/* Minor modifications to use internal static buffer to keep API compatible + * with regular kitten. */ -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 */ +#ifndef NO_KITTEN -int mystrtoul(char *, int, int); +#include /* sprintf */ +#include /* getenv */ +#include /* sprintf */ +#include /* sprintf */ +#include /* sprintf */ +#include "kitten.h" +#include "kittenc.h" /* 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 +static char global_nls_buffer[4096] = {0}; /* 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) +const char * +_kittengets (int msgID, const char *message) { + struct message_header *message_header; // if this is != 0 then init was done - 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); -} + if (global_nls_buffer[0] == 0) + return message; -/** - * Obtains path to current exec. - */ -static char * get_cmd_path(char *buf) -{ - char cmd_drive[_MAX_DRIVE]; - char cmd_dir[_MAX_DIR]; + message_header = (struct message_header *)global_nls_buffer; + + for ( ; message_header->len != 0; message_header = (struct message_header *)((char*)message_header + message_header->len)) + { + if (message_header->id== msgID) + { + return (const char*)message_header + sizeof(struct message_header); + } - _splitpath(__argv[0], cmd_drive, cmd_dir, NULL, NULL); - _makepath(buf, cmd_drive, cmd_dir, "", NULL); + } - return buf; + return message; } @@ -182,483 +63,128 @@ static char * get_cmd_path(char *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; - - /* 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); - - /* maybe exe_path\nls\cat.%LANG% ? */ - get_cmd_path(catfile); - strcat(catfile, "NLS\\"); - 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 != 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); -} +#define _toupper(c) (c & ~0x20) - -/** - * 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) +nl_catd +kittenopen (const char *name) { - int ret = 0; + int fd; + struct message_end message_end; + struct content content[KITTEN_MAX_RESOURCES]; + char *language; + int i; + long len; + char exename[_MAX_PATH]; + long fileendnow, seekoffset; + + //printf("kittenopen %s\n", name); - 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 ((fd = open(name, _O_RDONLY | _O_BINARY)) < 0) + { + sprintf(exename, "%s.exe", name); + if ((fd = open(exename, _O_RDONLY | _O_BINARY)) < 0) + { + printf("can't open %s\n", name); // should never happen + return 0; + } + } + + fileendnow = lseek(fd, 0, SEEK_END); + + lseek(fd, - (int)sizeof(struct message_end), SEEK_END); + + read(fd, &message_end, sizeof(struct message_end)); + + if (memcmp(message_end.ID, "KITTENC", 8) != 0) + { + //printf("no KITTENC record found\n"); + return 0; + } + + if (message_end.resource_count > KITTEN_MAX_RESOURCES) + { + printf("resource > %d\n", KITTEN_MAX_RESOURCES); + return 0; + } + + seekoffset = fileendnow - message_end.fileend_orig; + + if (lseek(fd, message_end.filepos+seekoffset, SEEK_SET) != message_end.filepos+seekoffset) + { + printf("can't seek 1\n"); + return 0; + } + + + read(fd, &content, sizeof(content[0]) * message_end.resource_count); + + language = getenv("LANG"); + + // printf("using language %s\n", language); + + if (message_end.resource_count == 1) + { + if (language && + _toupper(language[0]) == 'E' && + _toupper(language[0]) == 'N') + { + // set LANG=EN switches to internal strings + close(fd); + return 0; + } + // otherwise use the only compiled language + i = 0; + + goto found_my_language; + } + + if (language == NULL) // language not found + { // use internal strings + close(fd); + return 0; + } + + // see if we find our language in the existing resources + for (i = 0; i < message_end.resource_count; i++) + { + if(_toupper(content[i].language[0]) == _toupper(language[0]) && + _toupper(content[i].language[1]) == _toupper(language[1])) + { + goto found_my_language; + } + } + + // language not found + close(fd); + return 0; - 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 */ - } +found_my_language: + // found our language + // printf(" language %s found between %lx and %lx\n", language, content[i].filepos_start, content[i].filepos_end); - if (ch == '\r') - ch = get_char (file); /* ignore \r */ + // printf("seek %lu --> %lu\n", content[i].filepos_start, content[i].filepos_start+seekoffset); - str[0] = ch; + lseek(fd, content[i].filepos_start+seekoffset, SEEK_SET); + len = content[i].filepos_end - content[i].filepos_start; - if ( (ch == '\n') || (ch == '\r') || (ch < 0) ) { /* done? */ - str[0] = '\0'; - return 1; /* success */ - } + if (len > sizeof(global_nls_buffer)) + { + printf("nls_buffer too small(%d). we need at least %l\n", + sizeof(global_nls_buffer), len); + close(fd); + return 1; + } - str++; - size--; - } /* while */ + read(fd, global_nls_buffer, (int)len); - str[0] = '\0'; /* terminate buffer */ + close(fd); + return 0; +} - 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; -} +#endif /*NO_KITTEN */ diff --git a/kitten.h b/kitten.h index 3ce631d..b38b688 100644 --- a/kitten.h +++ b/kitten.h @@ -20,45 +20,43 @@ */ -#ifndef _CATGETS_H -#define _CATGETS_H +#ifndef KITTEN_H +#define KITTEN_H #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/* Data types */ +#define _(catalog,messageid,message) kittengets(catalog,messageid,message) + +#ifdef NO_KITTEN + +#define kittengets(x,y,z) (z) +#define kittenclose() +#define kittenopen(a) -#if defined(__GNUC__) -typedef int __attribute__((used)) nl_catd; #else -typedef int nl_catd; -#endif + +/* Data types */ + +#define nl_catd int /* Functions */ -#ifdef NOCATS /* #define NOCATS to disable completely */ -#define catgets(c,x,y,s) s -#define catopen(x,y) 1 -#define catclose(x) -#define _(x,y,s) s -#else -#define catgets(catalog,set,message_number,message) kittengets(set,message_number,message) -#define catopen(name,flag) kittenopen(name) -#define catclose(catalog) kittenclose() -#define _(set,message_number,message) kittengets(set,message_number,message) -#endif -const char *kittengets(int setnum, int msgnum, const char *message); -nl_catd kittenopen(char *name); -void kittenclose (void); +#define catgets(catalog, set,message_number,message) _kittengets((catalog << 8) + set,default_message) +#define catopen(name,flag) kittenopen(argv[0]) +#define catclose(catalog) -int get_line (int file, char *buffer, int size); +#define kittengets(catalog, messageid,message) _kittengets((catalog << 8) + messageid,message) +#define kittenclose(catalog) -int kitten_extract_response(const char *s, const char *delimiters, - char *responses, int num); + const char *_kittengets (int messageid, const char *message); + nl_catd kittenopen (const char *exename); +#endif /*NO_KITTEN */ #ifdef __cplusplus } #endif -#endif /* _CATGETS_H */ +#endif /* KITTEN_H */ diff --git a/kittenc.c b/kittenc.c new file mode 100644 index 0000000..79011ea --- /dev/null +++ b/kittenc.c @@ -0,0 +1,548 @@ +/* + KITTENC - compile kitten files and attach this to + executables in a way that the kitten library retrieves + the language resources, in the same way that + catgets/kittengets ever did + +*/ +/* + This software is free software; free to use, + modify, pass to others, whatever + + use it at your own risk +*/ +/* Minor modifications to compile on UNIX host */ + + +#include +#include +#include +#include +#include + +#include "kitten.h" +#include "kittenc.h" +#include "kitten.c" // Small hack to keep everything in one file + +void usage() + { + printf(kittengets(1,0,"KITTENC - KITTEN compiler\n")); + printf(kittengets(1,1,"usage\n")); + printf(kittengets(1,2,"KITTENC program.exe ATTACH NLS\\program.??\n")); + printf(kittengets(1,3,"KITTENC program.exe ATTACH NLS\\program.DE\n")); + printf(kittengets(1,4,"KITTENC program.exe ATTACH NLS\\program.DE NLS\\program.fr\n")); + printf(kittengets(1,5,"KITTENC program.exe INFO : show info about language resources\n")); + printf(kittengets(1,6,"KITTENC program.exe DUMP : recreate language resources\n")); + printf(kittengets(1,7,"KITTENC program.exe TRUNCATE : delete attached resources\n")); + } + + + +/** + * 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 special[] = "\\nrtvbafx"; +char translated[]= "\\\n\r\t\v\b\a\fx"; + + +char * +processEscChars (char *line) +{ + /* used when converting \xdd and \ddd (hex or octal) characters */ + char ch; + char *src = line; + char *dst = line; /* possible as dst is shorter than src */ + char *s; + int chx; + + + if (line == NULL) + return line; + + /* cycle through copying characters, except when a \ is encountered. */ + while (*src != '\0') + { + ch = *src; + src++; + + if (ch != '\\') + { + *dst++ = ch; + continue; + } + + ch = *src; /* what follows slash? */ + src++; + + s = strchr(special, ch); /* is it a special character?? */ + + if (s == NULL) /* no. store the character as is */ + { + *dst++ = ch; + continue; + } + ch = translated[s - special]; + + if (ch != 'x') /* newline, formfeed etc. */ + { + *dst++ = ch; + continue; + } + + 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++; + } + } /* while */ + + /* ensure '\0' terminated */ + *dst = '\0'; + + return line; +} + + +// +// process a single FIND.DE file +// + +int process_language_file(FILE *fd, char *filename) + { + FILE *fdres; + char linebuff[512]; + char msg[512]; + int id1, id2; + struct message_header message_header; + int len; + int linecount = 0; + + if ((fdres = fopen(filename, "r")) == 0) + { + printf("can't open %s\n", filename); + return 0; + } + + while (fgets(linebuff, sizeof(linebuff), fdres)) + { + char *s = linebuff; + + linecount++; + + for (len = strlen(s); len > 0; ) // delete trailing white space + { // from line + len--; + if (strchr(" \t\r\n", s[len]) == NULL) + break; + s[len] = '\0'; + } + if (len == 0) + continue; + + + if (linebuff[0] == '#') + { + // Skip comment line + } + else if (sscanf(linebuff, " %u . %u :%s", &id1, &id2, msg) != 3) + { + if (sscanf(linebuff, " %c", &id1) == 0 || // empty line + id1 == '#') // # comment line + continue; + + printf("%3u:%10.10s: missing 'num, num :' \n", linecount, linebuff); + continue; + } + s = strchr(linebuff, ':'); + if (s == NULL) continue; + + s++; + + processEscChars(s); + + if (strlen(s) > 250) + { + printf("%10.10s: lines must not be longer then 250 characters, is %u\n", linebuff, strlen(s)); + s[250] = 0; + } + + /* now here we have destilled id1, id2, message s */ + + message_header.len = strlen(s) // the message + + 1 // the trailing \0 + + sizeof(message_header); + message_header.id = (id1 << 8) + id2; + + fwrite(&message_header, 1, sizeof(message_header), fd); + fwrite(s, strlen(s)+1,1,fd); + + } + + + message_header.len = 0; // END message + message_header.id = 0; + + fwrite(&message_header, 1, sizeof(message_header), fd); + + return linecount; + } + + +// this is the 'compiler' part of KITTENC +// +// attach("FIND.EXE", "NLS\\FIND.??", 1) +// attach("FIND.EXE", "NLS\\FIND.DE", NLS\\FIND.ES", 2) +// + +int attach(char *progname, char *args[], int argc) + { + int i; + FILE *fd; + struct _finddata_t fileinfo; + long search_handle; + char *s; + char *filename; + char path_buffer[_MAX_PATH]; + struct content content[KITTEN_MAX_RESOURCES]; + struct message_end message_end; + int linecount; + + int resourcecount = 0; + + if ((fd = fopen(progname, "r+b")) == NULL) + { + printf(kittengets(2,1, "executable file <%s> can't be opened. because <%s>\n"), progname, strerror( errno )); + return 1; + } + fseek(fd, 0, SEEK_END); + + for (i = 0; i < argc; i++) + { + filename = args[i]; + +#ifdef __UNIX__ + /* no need to expand wildcards on unix */ + strcpy(path_buffer, filename); +#else + + if ((search_handle = _findfirst(args[i], &fileinfo)) == 0) + { + printf(kittengets(2,2,"%s: no such file\n"), args[i]); + return 1; + } + + do { /* the name is name only, without the path */ + + if ((s = strrchr(filename, '\\')) != NULL || + (s = strrchr(filename, '/')) != NULL || + (s = strrchr(filename, ':')) != NULL) + { + memcpy(path_buffer, filename, s-filename+1); + strcpy(path_buffer + (s-filename) +1, fileinfo.name); + } + else + strcpy(path_buffer, fileinfo.name); + +#endif + + printf("%s:", path_buffer); + + + if ((s = strrchr(path_buffer, '.')) == NULL || + strlen(path_buffer) - (s - path_buffer) != 3) + { + printf(kittengets(2,3,"file names MUST end with .EN\n")); + continue; + } + + if (resourcecount > KITTEN_MAX_RESOURCES) + { + printf(kittengets(2,4,"too many resourcefiles (max %d)\n"), KITTEN_MAX_RESOURCES); + continue; + } + + content[resourcecount].filepos_start = ftell(fd); + strcpy(content[resourcecount].language,s+1); + + + linecount = process_language_file(fd, path_buffer); + + content[resourcecount].filepos_end = ftell(fd); + content[resourcecount].reserved = 0; + + printf("%u messages, %lu byte\n", linecount, content[resourcecount].filepos_end - content[resourcecount].filepos_start); + + resourcecount++; + +#ifndef __UNIX__ + } while (_findnext(search_handle, &fileinfo) == 0); +#endif + } + + message_end.filepos = ftell(fd); + + /* make all absolute filepositions relative to file end + to make UPX support easier */ + + + fwrite(&content, sizeof(content[0]),resourcecount,fd); + + message_end.resource_count = resourcecount; + memcpy(message_end.ID, "KITTENC",8); + + message_end.fileend_orig = ftell(fd) + sizeof(message_end); + + fwrite(&message_end, sizeof(message_end),1,fd); + fclose(fd); + + return 0; + } + +enum { KITTEN_TRUNCATE, KITTEN_INFO, KITTEN_DUMP }; + +do_kitten_stuff(char *progname, int mode) + { + int fd; + struct message_end message_end; + struct content content[KITTEN_MAX_RESOURCES]; + int i; + long fileendnow, seekoffset; + + + + if ((fd = open(progname, _O_RDWR | _O_BINARY)) < 0) + { + printf("can't open %s because<%s>\n", progname, strerror(errno)); + return 0; + } + fileendnow = lseek(fd, 0, SEEK_END); + + lseek(fd, - (int)sizeof(struct message_end), SEEK_END); + + read(fd, &message_end, sizeof(struct message_end)); + + if (memcmp(message_end.ID, "KITTENC", 8) != 0) + { + printf("no KITTENC record found\n"); + goto RETURN; + } + + if (message_end.resource_count > KITTEN_MAX_RESOURCES) + { + printf("resource > %d\n", KITTEN_MAX_RESOURCES); + goto RETURN; + } + + seekoffset = fileendnow - message_end.fileend_orig; + + if (lseek(fd, message_end.filepos+seekoffset, SEEK_SET) != message_end.filepos+seekoffset) + { + printf("can't seek 1\n"); + goto RETURN; + } + + read(fd, &content, sizeof(content[0]) * message_end.resource_count); + + + if (mode == KITTEN_TRUNCATE) + { + _chsize(fd, content[0].filepos_start); // truncate file + goto RETURN; + } + if (mode == KITTEN_INFO) + { + printf("%s: %u languages supported\n", progname, message_end.resource_count); + + for (i = 0; i < message_end.resource_count; i++) + { + printf("%2.2s %lu byte\n", content[i].language, content[i].filepos_end - content[i].filepos_start); + } + + goto RETURN; + } + + if (mode == KITTEN_DUMP) + { + char resname[_MAX_PATH],*s; + char outname[_MAX_PATH]; + FILE *fdout; + int i; + int linecount; + + for (i = 0; i < message_end.resource_count; i++) // for each language + { + _splitpath(progname, NULL,NULL, resname, NULL); // reconstruct KITTENC.DE + + sprintf(outname, "%s.%c%c", resname, content[i].language[0], content[i].language[1]);; + + if ((fdout = fopen(outname, "w")) == NULL) // create KITTENC.DE + { + printf("can't write resourcefile %s\n", outname); + goto RETURN; + } + lseek(fd, content[i].filepos_start+seekoffset, SEEK_SET); + + printf("%s : ", outname); + + linecount= 0; + + while (1) + { + short len, id; char c; + + read(fd, &len, sizeof(short)); + + if (len == 0) + break; + + read(fd,&id, sizeof(short)); + + fprintf(fdout, "%u,%u:", id >> 8, id & 0xff); + + while (read(fd, &c, 1) == 1 && + c != 0) + { + if (c >= 0x20) + fputc(c, fdout); + else + { + if ((s = strchr(translated, c)) != NULL) /* \t \n \f */ + { + fprintf(fdout, "\\%c", special[s - translated]); + } + else + fprintf(fdout, "\\x%02x", c); + } + } // next character + + fprintf(fdout, "\n"); + linecount++; + } // next line + + printf("%u messages\n", linecount); + fclose(fdout); + } // next language + + + goto RETURN; + } + + + +RETURN: + close(fd); + return 0; + + } + + +main(int argc, char *argv[]) +{ + char *progname; + char buffer[_MAX_PATH]; + + kittenopen(argv[0]); + + printf("KITTENC 1.0\n"); + + //printf(kittengets(0,0,"hallo world\n")); + + if (argc < 3) + { + usage(); + exit(1); + } + + progname = argv[1]; +#ifndef __UNIX__ + strupr(progname); + + if (strstr(progname, ".EXE") == NULL && + strstr(progname, ".COM") == NULL) + { + sprintf(buffer,"%s.EXE", progname); + progname = buffer; + } +#endif + + if (stricmp(argv[2], "ATTACH") == 0) + { + do_kitten_stuff(progname, KITTEN_TRUNCATE); + return attach(progname, argv+3, argc-3); + + } + else if (stricmp(argv[2], "TRUNCATE") == 0) + { + return do_kitten_stuff(progname, KITTEN_TRUNCATE); + + + } + else if (stricmp(argv[2], "INFO") == 0) + { + return do_kitten_stuff(progname, KITTEN_INFO); + } + else if (stricmp(argv[2], "DUMP") == 0) + { + return do_kitten_stuff(progname, KITTEN_DUMP); + } + else { + printf("'%s' is wrong %s %s %s\n", argv[2], argv[0], argv[1], argv[2]); + usage(); + exit(1); + } + +} + + diff --git a/kittenc.h b/kittenc.h new file mode 100644 index 0000000..d42ec27 --- /dev/null +++ b/kittenc.h @@ -0,0 +1,44 @@ +/* + KITTENC - compile kitten files and attach this to + executables in a way that the kitten library retrieves + the language resources, in the same way that + catgets/kittengets ever did + +*/ +/* + This software is free software; free to use, + modify, pass to others, whatever + + use it at your own risk +*/ + +#ifndef KITTENC_H +#define KITTENC_H + +//internal stuff shared between kitten.c and kittenc.c +struct message_header +{ + short len; + short id; + // char message[]; +}; + +struct content +{ + char language[4]; + long filepos_start; // for this language + long filepos_end; + long reserved; // align on 16 byte to look better in hex editor +}; + +struct message_end +{ + long filepos; // points to content + long resource_count;// number of entries + long fileend_orig; // file end NOW because UPX + char ID[8]; // "KITTENC" +}; + +#define KITTEN_MAX_RESOURCES 16 + +#endif /* KITTENC_H */ diff --git a/makefile b/makefile index 0ae4f28..1383c12 100644 --- a/makefile +++ b/makefile @@ -37,16 +37,25 @@ w16dll_cflags = -bt=windows -bd -mc -zu -s -6 -osi -w3 -wcd=202 compile_dos = *wcc -fo=$^@ $(dos_cflags) $[@ compile_dostsr = *wcc -fo=$^@ $(dostsr_cflags) $[@ compile_w16dll = *wcc -fo=$^@ $(w16dll_cflags) $[@ +compile_link_host = *wcl386 -i=$(%originclude) -os -fe=$^@ $[@ + +!ifdef __UNIX__ +run_host = ./ +!else +run_host = +!endif .BEFORE: # We need DOS and Windows headers, not host platform's + set originclude=$(%include) set include=$(%watcom)/h/win;$(%watcom)/h all: $(bins) .SYMBOLIC # DOS mouse driver -vbmouse.exe: vbmouse.lnk $(mousedos_objs) +vbmouse.exe: vbmouse.lnk $(mousedos_objs) kittenc.exe *wlink @$[@ name $@ file { $(mousedos_objs) } + $(run_host)kittenc.exe $@ attach nls/vbmouse.* mousetsr.obj: mousetsr.c .AUTODEPEND $(compile_dostsr) @@ -62,13 +71,14 @@ mousew16.obj: mousew16.c .AUTODEPEND $(compile_w16dll) # DOS shared folders -vbsf.exe: vbsf.lnk $(sfdos_objs) +vbsf.exe: vbsf.lnk $(sfdos_objs) kittenc.exe *wlink @$[@ name $@ file { $(sfdos_objs) } + $(run_host)kittenc.exe $@ attach nls/vbsf.* sftsr.obj: sftsr.c .AUTODEPEND $(compile_dostsr) -sfmain.obj: sfmain.c .AUTODEPEND +sfmain.obj: sfmain.c unitbl.h .AUTODEPEND $(compile_dos) # Auxiliary object files @@ -78,6 +88,10 @@ vbox.obj: vbox.c .AUTODEPEND kitten.obj: kitten.c .AUTODEPEND $(compile_dos) +# embed all unitbl/*.tbl files into a single unitbl.h +unitbl.h: unitbl2c.exe $(unitbls) + $(run_host)unitbl2c.exe $@ unitbl/*.tbl + # Test programs moustest_objs = moustest.obj @@ -87,16 +101,22 @@ moustest.exe: moustest.obj moustest.obj: moustest.c .AUTODEPEND $(compile_dos) -mc -zu +# Programs to build for the host +host_bins = unitbl2c.exe kittenc.exe +unitbl2c.exe: unitbl2c.c + $(compile_link_host) + +kittenc.exe: kittenc.c + $(compile_link_host) + # Other targets clean: .SYMBOLIC - rm -f vbmouse.exe vbmouse.drv vbsf.exe moustest.exe - rm -f *.obj *.map rm -f vbados.zip vbados.flp vbasrc.zip + rm -f $(bins) $(host_bins) + rm -f *.obj *.map vbados.flp: mformat -C -f 1440 -v VBADOS -i $^@ :: - mcopy -i $^@ unitbl/*.tbl :: - mcopy -i $^@ nls/vbsf.* nls/vbmouse.* :: # Build a floppy image containing the driver flp: vbados.flp $(bins) $(infs) .SYMBOLIC @@ -104,7 +124,7 @@ flp: vbados.flp $(bins) $(infs) .SYMBOLIC # Build a zip with the driver binaries zip: vbmouse.exe vbmouse.drv oemsetup.inf vbsf.exe .SYMBOLIC - zip --DOS-names -fz- -j vbados.zip unitbl/*.tbl nls/vbsf.* nls/vbmouse.* + zip --DOS-names -fz- -j vbados.zip nls/vbsf.* nls/vbmouse.* zip --DOS-names -fz- vbados.zip $(bins) $(infs) srczip: .SYMBOLIC diff --git a/mousmain.c b/mousmain.c index 02540de..24801b7 100644 --- a/mousmain.c +++ b/mousmain.c @@ -35,8 +35,6 @@ #include "dostsr.h" #include "mousetsr.h" -static nl_catd cat; - #if USE_WHEEL static bool detect_ps2_wheel(LPTSRDATA data) { @@ -761,7 +759,7 @@ int main(int argc, const char *argv[]) LPTSRDATA data = get_tsr_data(true); int err, argi = 1; - cat = kittenopen("vbmouse"); + kittenopen(argv[0]); printf(_(1, 20, "\nVBMouse %x.%x (reporting as MSMOUSE %x.%x)\n"), VERSION_MAJOR, VERSION_MINOR, REPORTED_VERSION_MAJOR, REPORTED_VERSION_MINOR); diff --git a/sfmain.c b/sfmain.c index 59acbca..4f102bb 100644 --- a/sfmain.c +++ b/sfmain.c @@ -30,10 +30,9 @@ #include "vboxshfl.h" #include "dostsr.h" #include "sftsr.h" +#include "unitbl.h" #include "unicode.h" -static nl_catd cat; - static char get_drive_letter(const char *path) { if (!path || path[0] == '\0') return '\0'; if (path[1] == '\0' || (path[1] == ':' && path[2] == '\0')) { @@ -169,13 +168,12 @@ static void close_openfiles(LPTSRDATA data, int drive) static int mount_shfl(LPTSRDATA data, int drive, const char *folder) { int32_t err; - SHFLSTRING_WITH_BUF(str, SHFL_MAX_LEN); + SHFLSTRING_WITH_BUF(utf8name, SHFL_MAX_LEN); SHFLROOT root = SHFL_ROOT_NIL; - unsigned flags = SHFL_MIQF_DRIVE_LETTER, version = 0; - shflstring_strcpy(&str.shflstr, folder); + utf8name.shflstr.u16Length = local_to_utf8(data, utf8name.buf, folder, utf8name.shflstr.u16Size); - err = vbox_shfl_map_folder(&data->vb, data->hgcm_client_id, &str.shflstr, &root); + err = vbox_shfl_map_folder(&data->vb, data->hgcm_client_id, &utf8name.shflstr, &root); if (err) { fprintf(stderr, _(3, 3, "Cannot mount shared folder '%s', err=%d\n"), folder, err); return -1; @@ -237,7 +235,7 @@ static bool is_mounted_drive(LPTSRDATA data, char drive_letter, int drive) return true; } -static int mount(LPTSRDATA data, char *folder, char drive_letter, MOUNTOPTS *opts) +static int mount(LPTSRDATA data, const char *folder, char drive_letter, MOUNTOPTS *opts) { int drive = drive_letter_to_index(drive_letter); DOSLOL __far *lol = dos_get_list_of_lists(); @@ -270,7 +268,6 @@ static int mount(LPTSRDATA data, char *folder, char drive_letter, MOUNTOPTS *opt // By setting the physical flag, we also let DOS know the drive is present cds->flags = DOS_CDS_FLAG_NETWORK | DOS_CDS_FLAG_PHYSICAL; - (void)utf8_to_local(data, folder, folder, NULL); printf(_(1, 3, "Shared folder '%s' mounted as drive %c:\n"), folder, drive_letter); return EXIT_SUCCESS; @@ -481,7 +478,8 @@ static void load_unicode_table(uint16_t far *unicode_table) char fullpath[_MAX_PATH]; char buffer[256]; FILE *f; - int i, ret; + const uint16_t *builtin_tbl; + int ret; // get current Code Page // @@ -502,6 +500,12 @@ static void load_unicode_table(uint16_t far *unicode_table) goto error; } + // Try one of the builtin tables first + if ((builtin_tbl = get_uni_tbl(r.x.bx))) { + _fmemcpy(unicode_table, builtin_tbl, 256); + return; + } + sprintf(filename, r.x.bx > 999 ? "c%duni.tbl" : "cp%duni.tbl", r.x.bx); // Search in the same directory as the executable first @@ -812,9 +816,8 @@ int main(int argc, const char *argv[]) { LPTSRDATA data = get_tsr_data(true); int err, argi = 1; - SHFLSTRING_WITH_BUF(utf8name, SHFL_MAX_LEN); - cat = kittenopen("vbsf"); + kittenopen(argv[0]); if (argi >= argc || stricmp(argv[argi], "install") == 0) { uint8_t hash_chars = DEF_HASH_CHARS; @@ -890,8 +893,7 @@ int main(int argc, const char *argv[]) err = parse_mountopts(&opts, &argi, argc, argv); if (err) return err; - local_to_utf8(data, utf8name.buf, folder, utf8name.shflstr.u16Size); - return mount(data, utf8name.buf, drive, &opts); + return mount(data, folder, drive, &opts); } else if (stricmp(argv[argi], "umount") == 0 || stricmp(argv[argi], "unmount") == 0) { char drive; if (!data) return driver_not_found(); diff --git a/unitbl2c.c b/unitbl2c.c new file mode 100644 index 0000000..f2e76cc --- /dev/null +++ b/unitbl2c.c @@ -0,0 +1,206 @@ +/* + * VBSF - generate C file with codepage -> unicode tables based on uni*.tbl files + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +/** size of codepage -> unicode table */ +#define TBL_SIZE 128 /* in 16bit words */ +/** maximum number of codepages in the same C file */ +#define MAX_CPS 32 + +static FILE *fout; +static unsigned all_cps[MAX_CPS] = {0}; +static unsigned num_cps = 0; + +static void print_table(const uint16_t *table, unsigned cp) +{ + int i; + int col = 0; + + fprintf(fout, "static const uint16_t cp%utbl[%u] = {\n ", cp, TBL_SIZE); + + for (i = 0; i < TBL_SIZE; i++) + { + if (col) + { + fprintf(fout, ", "); + } + if (col >= 8) + { + fprintf(fout, "\n "); + col = 0; + } + fprintf(fout, "0x%04X", table[i]); + col++; + } + + fprintf(fout, "\n};\n\n"); +} + +static int process_table(FILE *fp, const char *base, unsigned cp) +{ + char buffer[TBL_SIZE*2]; + int err; + + // Read "Description of table" (terminated by \r\n) + if (!fgets(buffer, sizeof(buffer), fp)) { + return -1; + } + + fprintf(fout, "// %s", buffer); + + // Read "Table format designator" (currently only format 1 is supported) + err = fread(buffer, 1, 1, fp); + if (err != 1 || buffer[0] != 1) { + return -1; + } + + // Now read the actual table (256 bytes in format 1) + if ((err = fread(buffer, 1, sizeof(buffer), fp)) != sizeof(buffer)) { + return -1; + } + + print_table((const uint16_t *)buffer, cp); + + all_cps[num_cps++] = cp; + + return 0; +} + +static int process_file(char *filename) +{ + FILE *fp = fopen(filename, "rb"); + char *base = basename(filename); + unsigned cp = 0; + + if (!fp) { + fprintf(stderr, "Cannot open file '%s'\n", filename); + return -1; + } + + if (strnicmp(&base[0], "CP", 2) == 0 + && isdigit(base[2]) && isdigit(base[3]) && isdigit(base[4]) + && strnicmp(&base[5], "UNI.TBL", 7) == 0) { + cp = atoi(&base[2]); + } else if (strnicmp(&base[0], "C", 2) == 0 + && isdigit(base[1]) && isdigit(base[2]) && isdigit(base[3]) && isdigit(base[4]) + && strnicmp(&base[5], "UNI.TBL", 7) == 0) { + cp = atoi(&base[1]); + } + + if (!cp) { + fprintf(stderr, "Cannot guess codepage number from filename '%s'\n", base); + fclose(fp); + return -1; + } + + if (process_table(fp, base, cp) != 0) { + fprintf(stderr, "Cannot read table format from '%s'\n", base); + fclose(fp); + return -1; + } + + fclose(fp); + return 0; +} + +#ifdef __UNIX__ +// no need to process wildcards on UNIX +static int process_arg(char *arg) +{ + return process_file(arg); +} +#else +#include + +static int process_arg(char *arg) +{ + struct _finddata_t fileinfo; + intptr_t fh = _findfirst(arg, &fileinfo); + + if (!fh) { + fprintf(stderr, "Cannot find '%s'\n", arg); + return -1; + } + + do { + // Name of file in fileinfo will be without path + // If so copy the path from the arg + char namebuf[_MAX_PATH]; + char *s; + + if ((s = strrchr(arg, '\\')) || (s = strrchr(arg, '/')) || (s = strrchr(arg, ':'))) { + memcpy(namebuf, arg, s-arg+1); + strcpy(namebuf + (s-arg) +1, fileinfo.name); + } else { + strcpy(namebuf, fileinfo.name); + } + + if (process_file(namebuf) != 0) { + return -1; + } + } while (_findnext(fh, &fileinfo) == 0); + + return 0; +} +#endif + +int main(int argc, char **argv) +{ + int i; + + if (argc < 3) + { + fprintf(stderr, + "Usage: bin2c \n"); + return 1; + } + + fout = fopen(argv[1], "wt"); + fprintf(fout, "#include \n"); + + for (i = 2; i < argc; i++) { + if (process_arg(argv[i])) { + return EXIT_FAILURE; + } + } + + fprintf(fout, "static const uint16_t * get_uni_tbl(unsigned cp) {\n"); + fprintf(fout, " switch (cp) {\n"); + for (i = 0; i < num_cps; i++) { + unsigned cp = all_cps[i]; + fprintf(fout, " case %u:\n", cp); + fprintf(fout, " return cp%utbl;\n", cp); + } + fprintf(fout, " default:\n"); + fprintf(fout, " return 0;\n"); + fprintf(fout, " }\n"); + fprintf(fout, "}\n\n"); + + fclose(fout); + + printf("Processed %u tables\n", num_cps); + + return EXIT_SUCCESS; +} -- cgit v1.2.3