Subversion Repositories SvarDOS

Compare Revisions

No changes between revisions

Ignore whitespace Rev 611 → Rev 636

/localcfg/tags/20220203/Makefile
0,0 → 1,58
#
# localcfg Makefile for OpenWatcom
#
 
all: localcfg.com
 
localcfg.com: localcfg.c country.c deflang.c
wcl -0 -y -cc -wx -mt -lr -zp1 -we -d0 -ox localcfg.c country.c deflang.c svarlang.lib/svarlngs.lib
del *.obj
upx --8086 -9 localcfg.com
 
deflang.c: nls_lang\*.txt
cd nls_lang
utf8tocp maz pl_utf8.txt > pl.txt
..\svarlang.lib\tlumacz en pl
move out.lng ..\localcfg.lng
move deflang.c ..\
cd ..
 
clean: .SYMBOLIC
del *.obj
del *.com
del deflang.c
del localcfg.lng
 
buildpkg: localcfg.com .SYMBOLIC
mkdir appinfo
mkdir source
mkdir source\localcfg
mkdir source\localcfg\nls_lang
mkdir source\localcfg\svarlang.lib
mkdir bin
mkdir doc
mkdir doc\localcfg
mkdir nls
copy localcfg.lsm appinfo
copy localcfg.com bin
copy localcfg.lng nls
copy svarlang.lib\*.* source\localcfg\svarlang.lib
del source\localcfg\svarlang.lib\*.lib
del source\localcfg\svarlang.lib\*.exe
copy *.txt doc\localcfg
copy *.c source\localcfg
copy *.h source\localcfg
copy *.txt source\localcfg
copy makefile source\localcfg
copy nls_lang\*.* source\localcfg\nls_lang
if exist localcfg.zip del localcfg.zip
zip -9rkDX -m localcfg.zip appinfo bin doc nls source
rmdir appinfo
rmdir source\localcfg\svarlang.lib
rmdir source\localcfg\nls_lang
rmdir source\localcfg
rmdir source
rmdir bin
rmdir doc\localcfg
rmdir doc
rmdir nls
/localcfg/tags/20220203/country.c
0,0 → 1,335
/*
* functions that reads/writes from/to the localcfg country.sys-like file.
* Copyright (C) Mateusz Viste 2015-2022
*/
 
#include <stdio.h>
#include <string.h>
 
#include "country.h"
 
 
struct funchdr {
unsigned char funcname[8];
unsigned short funcsiz;
};
 
static unsigned char filebuff[1024];
 
 
/* fills a country struct with default values */
static void country_default(struct country *countrydata) {
 
/* first clear the memory */
bzero(countrydata, sizeof(struct country));
 
/* fill in CTYINFO fields (non-zero values only) */
countrydata->CTYINFO.id = 1;
countrydata->CTYINFO.codepage = 437;
/* countrydata->CTYINFO.datefmt = COUNTRY_DATE_MDY;
countrydata->CTYINFO.timefmt = COUNTRY_TIME12; */
countrydata->CTYINFO.currsym[0] = '$';
countrydata->CTYINFO.decimal[0] = '.';
countrydata->CTYINFO.thousands[0] = ',';
countrydata->CTYINFO.datesep[0] = '/';
countrydata->CTYINFO.timesep[0] = ':';
countrydata->CTYINFO.currprec = 2;
/* countrydata->CTYINFO.currencydecsym = 0; */
/* countrydata->CTYINFO.currencyspace = 0; */
/* countrydata->CTYINFO.currencypos = 0; */
 
/* fill in YESNO fields (non-zero values only) */
countrydata->YESNO.yes[0] = 'Y';
countrydata->YESNO.no[0] = 'N';
}
 
 
/* Loads data from a country.sys file into a country struct.
* Returns 0 on success, non-zero otherwise. */
int country_read(struct country *countrydata, const char *fname) {
short firstentryoffs;
unsigned char *subfunctions[16] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
short filesize;
short subfunctionscount;
unsigned char *functiondata;
int x;
FILE *fd;
 
/* preload the country struct with default values */
country_default(countrydata);
 
/* load the file into buff, if file exists */
fd = fopen(fname, "rb");
if (fd == NULL) return(0); /* "file doesn't exist" is not an error condition */
filesize = fread(filebuff, 1, sizeof(filebuff), fd);
fclose(fd);
 
/* check that it's a country file - should start with 0xFF COUNTRY 0x00 */
if (memcmp(filebuff, "\377COUNTRY\0", 9) != 0) return(COUNTRY_ERR_INV_FORMAT);
 
/* check that it's one of my country.sys files - should contain a trailer */
if (memcmp(filebuff + filesize - 8, "LOCALCFG", 8) != 0) return(COUNTRY_ERR_NOT_LOCALCFG);
 
/* read the offset of the entries index - must be at least 23 */
functiondata = filebuff + 19;
firstentryoffs = *((unsigned short *)functiondata);
if ((firstentryoffs < 23) || (firstentryoffs >= filesize)) return(-4);
functiondata = filebuff + firstentryoffs;
 
/* how many entries do we have? I expect exactly one. */
if (*((unsigned short *)functiondata) != 1) return(-5);
/* skip to the first country entry */
functiondata += 2;
 
/* skip directly to the subfunctions of the first country */
/* ddwords: size, country, codepage, reserved, reserved, offset */
/* printf("Size = %d\n", READSHORT(functiondata)); */
functiondata += 2; /* skip size */
/* printf("Country = %d\n", READSHORT(functiondata[0]); */
functiondata += 2; /* skip country */
/* printf("Codepage = %d\n", READSHORT(functiondata)); */
functiondata += 2; /* skip codepage */
functiondata += 4; /* skip reserved fields */
firstentryoffs = *((unsigned short *)functiondata); /* read offset of the subfunctions index */
functiondata = filebuff + firstentryoffs;
 
/* read all subfunctions, but no more than 15 */
subfunctionscount = *((unsigned short *)functiondata);
/* printf("Found %d subfunctions\n", subfunctionscount); */
functiondata += 2;
for (x = 0; (x < 15) && (x < subfunctionscount); x++) {
short size = *((unsigned short *)functiondata);
functiondata += 2;
functiondata += 2; /* skip ID of the subfunction */
subfunctions[x] = filebuff + *((unsigned short *)functiondata);
/* printf("subfunction %d at 0x%p\n", x, subfunctions[x]); */
functiondata += size - 2;
}
 
/* load every subfunction, and feed the country struct with data */
for (x = 0; subfunctions[x] != NULL; x++) {
struct funchdr *hdr = (void *)(subfunctions[x]);
functiondata = subfunctions[x] + 10;
/* */
if ((memcmp(hdr->funcname, "\377YESNO ", 8) == 0) && (hdr->funcsiz == 4)) {
memcpy(&(countrydata->YESNO), functiondata, hdr->funcsiz);
} else if ((memcmp(hdr->funcname, "\377CTYINFO", 8) == 0) && (hdr->funcsiz == 22)) {
memcpy(&(countrydata->CTYINFO), functiondata, hdr->funcsiz);
}
}
 
return(0);
}
 
 
#define MSB(x) (((x) >> 8) & 0xff)
#define LSB(x) ((x) & 0xff)
 
 
/* Computes a new country.sys file based on data from a country struct.
* Returns 0 on success, non-zero otherwise. */
int country_write(const char *fname, struct country *c) {
short filesize = 0;
FILE *fd;
int x;
short subfunction_id[7] = {1,2,4,5,6,7,35};
short subfunction_ptr[7];
 
const unsigned char ucase_437[128] = {128, 154, 69, 65, 142, 65, 143, 128,
69, 69, 69, 73, 73, 73, 142, 143,
144, 146, 146, 79, 153, 79, 85, 85,
89, 153, 154, 155, 156, 157, 158, 159,
65, 73, 79, 85, 165, 165, 166, 167,
168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183,
184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215,
216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231,
232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247,
248, 249, 250, 251, 252, 253, 254, 255};
 
const unsigned char collate_437[256] = { 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 123, 124, 125, 126, 127,
67, 85, 69, 65, 65, 65, 65, 67,
69, 69, 69, 73, 73, 73, 65, 65,
69, 65, 65, 79, 79, 79, 85, 85,
89, 79, 85, 36, 36, 36, 36, 36,
65, 73, 79, 85, 78, 78, 166, 167,
63, 169, 170, 171, 172, 33, 34, 34,
176, 177, 178, 179, 180, 181, 182, 183,
184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215,
216, 217, 218, 219, 220, 221, 222, 223,
224, 83, 226, 227, 228, 229, 230, 231,
232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247,
248, 249, 250, 251, 252, 253, 254, 255};
 
/* zero out filebuff */
bzero(filebuff, sizeof(filebuff));
 
/* compute the country.sys structures */
memcpy(filebuff, "\377COUNTRY\0\0\0\0\0\0\0\0\1\0\1", 19); /* header */
filesize = 19;
/* first entry offset (always current offset+4) */
filesize += 4;
memcpy(filebuff + filesize - 4, &filesize, sizeof(filesize));
/* number of entries */
filebuff[filesize] = 1;
filesize += 2;
/* first (and only) entry / size, country, codepage, reserved(2), offset */
filebuff[filesize++] = 12; /* size LSB */
filebuff[filesize++] = 0; /* size MSB */
filebuff[filesize++] = LSB(c->CTYINFO.id); /* country LSB */
filebuff[filesize++] = MSB(c->CTYINFO.id); /* country MSB */
filebuff[filesize++] = LSB(c->CTYINFO.codepage); /* codepage LSB */
filebuff[filesize++] = MSB(c->CTYINFO.codepage); /* codepage MSB */
filesize += 4; /* reserved bytes */
 
filesize += 4;
memcpy(filebuff + filesize - 4, &filesize, sizeof(filesize));
 
/* index of subfunctions */
filebuff[filesize] = 7; /* there are 7 subfunctions */
filesize += 2;
for (x = 0; x < 7; x++) { /* dump each subfunction (size, id, offset) */
/* size is always 6 */
filebuff[filesize] = 6;
filesize += 2;
/* id of the subfunction */
filebuff[filesize++] = LSB(subfunction_id[x]);
filebuff[filesize++] = MSB(subfunction_id[x]);
/* remember the offset of the subfunction pointer for later */
subfunction_ptr[x] = filesize;
filesize += 4;
}
 
/* write the CTYINFO subfunction */
memcpy(filebuff + subfunction_ptr[0], &filesize, sizeof(filesize));
 
/* subfunction header */
memcpy(filebuff + filesize, "\377CTYINFO", 8);
filesize += 8;
/* subfunction size */
filebuff[filesize] = 22;
filesize += 2;
 
/* country preferences */
memcpy(filebuff + filesize, &(c->CTYINFO), 22);
filesize += 22;
 
/* write the UCASE subfunction (used for LCASE, too) */
memcpy(filebuff + subfunction_ptr[1], &filesize, sizeof(filesize));
memcpy(filebuff + subfunction_ptr[2], &filesize, sizeof(filesize));
 
/* subfunction header */
memcpy(filebuff + filesize, "\377UCASE ", 8);
filesize += 8;
/* subfunction size */
filebuff[filesize++] = 128;
filebuff[filesize++] = 0;
/* UCASE table */
memcpy(filebuff + filesize, ucase_437, 128);
filesize += 128;
 
/* write the FCHAR subfunction (filename terminator table) */
memcpy(filebuff + subfunction_ptr[3], &filesize, sizeof(filesize));
 
/* subfunction header */
memcpy(filebuff + filesize, "\377FCHAR ", 8);
filesize += 8;
/* subfunction size */
filebuff[filesize++] = 22;
filebuff[filesize++] = 0;
/* values here are quite obscure, dumped from country.sys */
filebuff[filesize++] = 142;
filebuff[filesize++] = 0;
filebuff[filesize++] = 255;
filebuff[filesize++] = 65;
filebuff[filesize++] = 0;
filebuff[filesize++] = 32;
filebuff[filesize++] = 238;
/* list of characters that terminates a filename */
filebuff[filesize++] = 14; /* how many of them */
filebuff[filesize++] = 46; /* . */
filebuff[filesize++] = 34; /* " */
filebuff[filesize++] = 47; /* / */
filebuff[filesize++] = 92; /* \ */
filebuff[filesize++] = 91; /* [ */
filebuff[filesize++] = 93; /* ] */
filebuff[filesize++] = 58; /* : */
filebuff[filesize++] = 124; /* | */
filebuff[filesize++] = 60; /* < */
filebuff[filesize++] = 62; /* > */
filebuff[filesize++] = 43; /* + */
filebuff[filesize++] = 61; /* = */
filebuff[filesize++] = 59; /* ; */
filebuff[filesize++] = 44; /* , */
 
/* write the COLLATE subfunction */
memcpy(filebuff + subfunction_ptr[4], &filesize, sizeof(filesize));
 
/* subfunction header */
memcpy(filebuff + filesize, "\377COLLATE", 8);
filesize += 8;
/* subfunction size */
filebuff[filesize++] = LSB(256);
filebuff[filesize++] = MSB(256);
/* collation for standard CP437 */
memcpy(filebuff + filesize, collate_437, 256);
filesize += 256;
 
/* write the DBCS subfunction */
memcpy(filebuff + subfunction_ptr[5], &filesize, sizeof(filesize));
/* subfunction header */
memcpy(filebuff + filesize, "\377DBCS ", 8);
filesize += 8;
/* subfunction size */
filebuff[filesize++] = 0;
filebuff[filesize++] = 0;
/* table terminator (must be there even if no lenght is zero */
filebuff[filesize++] = 0;
filebuff[filesize++] = 0;
 
/* write the YESNO subfunction */
memcpy(filebuff + subfunction_ptr[6], &filesize, sizeof(filesize));
memcpy(filebuff + filesize, "\377YESNO ", 8);
filesize += 8;
filebuff[filesize] = 4; /* size (LSB) */
filesize += 2;
memcpy(filebuff + filesize, &(c->YESNO), 4);
filesize += 4;
 
/* write the file trailer */
memcpy(filebuff + filesize, "LOCALCFG", 8);
filesize += 8;
 
/* write the buffer to file */
fd = fopen(fname, "wb");
if (fd == NULL) return(-1);
fwrite(filebuff, 1, filesize, fd);
fclose(fd);
 
return(0);
}
/localcfg/tags/20220203/country.h
0,0 → 1,57
/*
* functions that read/write from/to the localcfg country.sys-like file.
* Copyright (C) Mateusz Viste 2015-2022
*/
 
#ifndef country_h_sentinel
#define country_h_sentinel
 
enum COUNTRY_DATEFMT {
COUNTRY_DATE_MDY = 0, /* Month, Day, Year */
COUNTRY_DATE_DMY = 1, /* Day, Month, Year */
COUNTRY_DATE_YMD = 2 /* Year, Month, Day */
};
 
enum COUNTRY_TIMEFMT {
COUNTRY_TIME12 = 0, /* AM/PM format (like 6:32 PM) */
COUNTRY_TIME24 = 1 /* 24h format (like 18:32) */
};
 
#define COUNTRY_ERR_INV_FORMAT -86
#define COUNTRY_ERR_NOT_LOCALCFG -87
 
struct country {
 
struct {
unsigned short id; /* international id (48=PL, 33=FR, 01=US...) */
unsigned short codepage; /* usual codepage */
unsigned short datefmt; /* date format */
char currsym[5]; /* currency symbol */
char thousands[2]; /* thousands separator */
char decimal[2]; /* decimal separator (like . or ,) */
char datesep[2]; /* date separator (usually '-', '.' or '/') */
char timesep[2]; /* time separator (usually ':') */
unsigned char currpos:1; /* 0=currency precedes the value, 1=follows it */
unsigned char currspace:1; /* set if the currency symbol should be one space away from the value */
unsigned char currdecsym:1; /* set if the currency symbol should replace the decimal point */
unsigned char ZEROED:5;
unsigned char currprec; /* currency precision (2 = 0.12) */
unsigned char timefmt; /* time format */
} CTYINFO;
 
struct {
char yes[2];
char no[2];
} YESNO;
 
};
 
/* Loads data from a country.sys file into a country struct.
* Returns 0 on success, non-zero otherwise. */
int country_read(struct country *countrydata, const char *fname);
 
/* Computes a new country.sys file based on data from a country struct.
* Returns 0 on success, non-zero otherwise. */
int country_write(const char *fname, struct country *countrydata);
 
#endif
/localcfg/tags/20220203/history.txt
0,0 → 1,16
localcfg history file
 
* localcfg ver 20220203
- NLS (translations) support through the SvarLANG library
- memory usage optimizations
- extra error messages
 
* localcfg ver 20220202 (merged into the SvarDOS project)
- country.sys path is optional, defaults to %DOSDIR%\CFG\COUNTRY.SYS
- COUNTRY=xxx example contains the actual codepage and path to COUNTRY.SYS
- improved loading/saving routines so they rely on memory structures
- relicensed from BSD 2-clause to MIT
- moved from Turbo C to OpenWatcom
 
* localcfg v0.90 [02 June 2015]
- first public release
/localcfg/tags/20220203/localcfg.c
0,0 → 1,474
/*
* Locales configuration for SvarDOS
*
* Copyright (C) Mateusz Viste 2015-2022
*
* MIT license
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
 
#include <stdio.h>
#include <stdlib.h> /* atoi() */
#include <string.h> /* strchr */
 
#include "svarlang.lib/svarlang.h"
 
#include "country.h"
 
#define PVER "20220203"
#define PDATE "2015-2022"
 
 
enum NLS_STRINGS {
NLS_HLP_VER = 0x0000,
NLS_HLP_DESC = 0x0001,
NLS_HLP_USAGE = 0x0002,
NLS_HLP_OPTIONS = 0x0003,
NLS_HLP_COUNTRY = 0x000A,
NLS_HLP_CP = 0x000B,
NLS_HLP_DECIM = 0x000C,
NLS_HLP_THOUS = 0x000D,
NLS_HLP_DATESEP = 0x000E,
NLS_HLP_DATEFMT = 0x000F,
NLS_HLP_TIMESEP = 0x0010,
NLS_HLP_TIMEFMT = 0x0011,
NLS_HLP_CURR = 0x0012,
NLS_HLP_CURRPOS0 = 0x0013,
NLS_HLP_CURRPOS1 = 0x0014,
NLS_HLP_CURRPOS2 = 0x0015,
NLS_HLP_CURRSPC = 0x0016,
NLS_HLP_CURRPREC = 0x0017,
NLS_HLP_YESNO = 0x0018,
NLS_HLP_INFOLOC1 = 0x0032,
NLS_HLP_INFOLOC2 = 0x0033,
 
NLS_INFO_COUNTRY = 0x0700,
NLS_INFO_CODEPAGE = 0x0701,
NLS_INFO_DECSEP = 0x0702,
NLS_INFO_THOUSEP = 0x0703,
NLS_INFO_DATEFMT = 0x0704,
NLS_INFO_TIMEFMT = 0x0705,
NLS_INFO_YESNO = 0x0706,
NLS_INFO_CURREXAMPLE = 0x0707,
NLS_MAKESURE = 0x0709,
 
NLS_ERR_FILEPATHTWICE = 0x0900,
NLS_ERR_BADPATH = 0x0901,
NLS_ERR_READFAIL = 0x0902,
NLS_ERR_INVPARAM = 0x0903,
NLS_ERR_INVFORMAT = 0x0904,
NLS_ERR_NOTLOCALCFG = 0x0905
};
 
 
static void output(const char *s) {
_asm {
/* set cx to strlen(s) */
push ds
pop es
mov di, s
xor al, al
cld
mov cx, 0xff
repne scasb /* compare ES:DI with AL, inc DI until match */
mov cx, di
sub cx, s
dec cx
/* output via DOS */
mov ah, 0x40 /* write to handle */
mov bx, 1 /* 1=stdout */
mov dx, s
int 0x21
}
}
 
 
static void crlf(void) {
output("\r\n");
}
 
 
static void outputnl(const char *s) {
output(s);
crlf();
}
 
 
static void nls_put(enum NLS_STRINGS id) {
output(svarlang_strid(id));
}
 
 
static void nls_puts(enum NLS_STRINGS id) {
nls_put(id);
crlf();
}
 
 
static void about(void) {
output("localcfg ");
nls_put(NLS_HLP_VER);
outputnl(" " PVER ", (C) " PDATE " Mateusz Viste");
nls_puts(NLS_HLP_DESC);
crlf();
nls_puts(NLS_HLP_USAGE);
crlf();
nls_puts(NLS_HLP_OPTIONS);
crlf();
nls_puts(NLS_HLP_COUNTRY);
nls_puts(NLS_HLP_CP);
nls_puts(NLS_HLP_DECIM);
nls_puts(NLS_HLP_THOUS);
nls_puts(NLS_HLP_DATESEP);
nls_puts(NLS_HLP_DATEFMT);
nls_puts(NLS_HLP_TIMESEP);
nls_puts(NLS_HLP_TIMEFMT);
nls_puts(NLS_HLP_CURR);
nls_puts(NLS_HLP_CURRPOS0);
nls_puts(NLS_HLP_CURRPOS1);
nls_puts(NLS_HLP_CURRPOS2);
nls_puts(NLS_HLP_CURRSPC);
nls_puts(NLS_HLP_CURRPREC);
nls_puts(NLS_HLP_YESNO);
crlf();
nls_puts(NLS_HLP_INFOLOC1);
nls_puts(NLS_HLP_INFOLOC2);
}
 
 
static char *datestring(char *result, struct country *c) {
switch (c->CTYINFO.datefmt) {
case COUNTRY_DATE_MDY:
sprintf(result, "12%c31%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
break;
case COUNTRY_DATE_DMY:
sprintf(result, "31%c12%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
break;
case COUNTRY_DATE_YMD:
default:
sprintf(result, "1990%c12%c31", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
break;
}
return(result);
}
 
 
static char *timestring(char *result, struct country *c) {
if (c->CTYINFO.timefmt == COUNTRY_TIME12) {
sprintf(result, "11%c59%c59 PM", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
} else {
sprintf(result, "23%c59%c59", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
}
return(result);
}
 
 
static char *currencystring(char *result, struct country *c) {
char decimalpart[16];
char space[2] = {0, 0};
char decsym[8];
char cursym[8];
decimalpart[0] = '1';
decimalpart[1] = '2';
decimalpart[2] = '3';
decimalpart[3] = '4';
decimalpart[4] = '5';
decimalpart[5] = '6';
decimalpart[6] = '7';
decimalpart[7] = '8';
decimalpart[8] = '9';
decimalpart[9] = 0;
/* prepare the decimal string first */
if (c->CTYINFO.currprec < 9) {
decimalpart[c->CTYINFO.currprec] = 0;
}
/* prepare the currency space string */
if (c->CTYINFO.currspace != 0) {
space[0] = ' ';
}
/* prepare the currency and decimal symbols */
if (c->CTYINFO.currdecsym != 0) { /* currency replaces the decimal point */
sprintf(decsym, "%s", c->CTYINFO.currsym);
cursym[0] = 0;
} else {
sprintf(decsym, "%c", c->CTYINFO.decimal[0]);
sprintf(cursym, "%s", c->CTYINFO.currsym);
}
if (c->CTYINFO.currprec == 0) decsym[0] = 0;
/* compute the final string */
if (c->CTYINFO.currpos == 0) { /* currency precedes value */
sprintf(result, "%s%s99%s%s", cursym, space, decsym, decimalpart);
} else { /* currency follows value or replaces decimal symbol */
sprintf(result, "99%s%s%s%s", decsym, decimalpart, space, cursym);
}
return(result);
}
 
 
/* checks if str starts with prefix. returns 0 if so, non-zero otherwise. */
static int stringstartswith(char *str, char *prefix) {
for (;;) {
/* end of prefix means success */
if (*prefix == 0) return(0);
/* otherwise there is no match */
if (*str != *prefix) return(-1);
/* if match good so far, look at next char */
str += 1;
prefix += 1;
}
}
 
 
/* processes an argument. returns 0 on success, non-zero otherwise. */
static int processarg(char *arg, struct country *c) {
char *value;
int intvalue;
/* an option must start with a '/' */
if (arg[0] != '/') return(-1);
arg += 1; /* skip the slash */
/* find where the value starts */
value = strchr(arg, ':');
/* if no value present, fail */
if (value == NULL) return(-2);
value += 1;
if (*value == 0) return(-3);
/* interpret the option now */
if (stringstartswith(arg, "country:") == 0) {
intvalue = atoi(value);
if ((intvalue > 0) && (intvalue < 1000)) {
c->CTYINFO.id = intvalue;
return(0);
}
} else if (stringstartswith(arg, "cp:") == 0) {
intvalue = atoi(value);
if ((intvalue > 0) && (intvalue < 1000)) {
c->CTYINFO.codepage = intvalue;
return(0);
}
} else if (stringstartswith(arg, "decim:") == 0) {
if (value[1] == 0) { /* value must be exactly one character */
c->CTYINFO.decimal[0] = *value;
return(0);
}
} else if (stringstartswith(arg, "thous:") == 0) {
if (value[1] == 0) { /* value must be exactly one character */
c->CTYINFO.thousands[0] = *value;
return(0);
}
} else if (stringstartswith(arg, "datesep:") == 0) {
if (value[1] == 0) { /* value must be exactly one character */
c->CTYINFO.datesep[0] = *value;
return(0);
}
} else if (stringstartswith(arg, "timesep:") == 0) {
if (value[1] == 0) { /* value must be exactly one character */
c->CTYINFO.timesep[0] = *value;
return(0);
}
} else if (stringstartswith(arg, "datefmt:") == 0) {
if (strcmp(value, "MDY") == 0) {
c->CTYINFO.datefmt = COUNTRY_DATE_MDY;
return(0);
} else if (strcmp(value, "DMY") == 0) {
c->CTYINFO.datefmt = COUNTRY_DATE_DMY;
return(0);
} else if (strcmp(value, "YMD") == 0) {
c->CTYINFO.datefmt = COUNTRY_DATE_YMD;
return(0);
}
} else if (stringstartswith(arg, "timefmt:") == 0) {
if (value[1] == 0) {
if ((value[0] >= '0') && (value[0] <= '1')) {
c->CTYINFO.timefmt = value[0] - '0';
return(0);
}
}
} else if (stringstartswith(arg, "curr:") == 0) {
if (strlen(value) <= 4) {
strcpy(c->CTYINFO.currsym, value);
return(0);
}
} else if (stringstartswith(arg, "currpos:") == 0) {
if (value[1] == 0) {
if (value[0] == '0') {
c->CTYINFO.currpos = 0;
return(0);
} else if (value[0] == '1') {
c->CTYINFO.currpos = 1;
return(0);
} else if (value[0] == '2') {
c->CTYINFO.currpos = 0;
c->CTYINFO.currdecsym = 1;
return(0);
}
}
} else if (stringstartswith(arg, "currspc:") == 0) {
if (value[1] == 0) {
if ((value[0] >= '0') && (value[0] <= '1')) {
c->CTYINFO.currspace = value[0] - '0';
return(0);
}
}
} else if (stringstartswith(arg, "currprec:") == 0) {
if (value[1] == 0) {
if ((value[0] >= '0') && (value[0] <= '9')) {
c->CTYINFO.currprec = value[0] - '0';
return(0);
}
}
} else if (stringstartswith(arg, "yesno:") == 0) {
/* string must be exactly 2 characters long */
if ((value[0] != 0) && (value[1] != 0) && (value[2] == 0)) {
c->YESNO.yes[0] = value[0];
c->YESNO.no[0] = value[1];
return(0);
}
}
/* if I'm here, something went wrong */
return(-4);
}
 
 
/* converts a path to its canonic representation, returns 0 on success
* or DOS err on failure (invalid drive) */
static unsigned short file_truename(const char *dst, char *src) {
unsigned short res = 0;
_asm {
push es
mov ah, 0x60 /* query truename, DS:SI=src, ES:DI=dst */
push ds
pop es
mov si, src
mov di, dst
int 0x21
jnc DONE
mov [res], ax
DONE:
pop es
}
return(res);
}
 
 
static void default_country_path(char *s) {
char *dosdir = getenv("DOSDIR");
size_t dosdirlen;
s[0] = 0;
if (dosdir == NULL) return;
dosdirlen = strlen(dosdir);
if (dosdirlen == 0) return;
/* drop trailing backslash if present */
if (dosdir[dosdirlen - 1] == '\\') dosdirlen--;
/* copy dosdir to s and append the rest of the path */
memcpy(s, dosdir, dosdirlen);
strcpy(s + dosdirlen, "\\CFG\\COUNTRY.SYS");
}
 
 
int main(int argc, char **argv) {
struct country cntdata;
int changedflag;
int x;
static char fname[130];
static char buff[64];
 
svarlang_autoload("localcfg");
 
/* scan argv looking for the path to country.sys */
for (x = 1; x < argc; x++) {
if (argv[x][0] != '/') {
if (fname[0] != 0) {
nls_puts(NLS_ERR_FILEPATHTWICE);
return(1);
}
/* */
if (file_truename(fname, argv[x]) != 0) {
nls_puts(NLS_ERR_BADPATH);
return(1);
}
} else if (strcmp(argv[x], "/?") == 0) { /* is it /? */
about();
return(1);
}
}
 
/* if no file path provided, look into %DOSDIR%\CFG\COUNTRY.SYS */
if (fname[0] == 0) default_country_path(fname);
 
x = country_read(&cntdata, fname);
if (x != 0) {
if (x == COUNTRY_ERR_INV_FORMAT) {
nls_puts(NLS_ERR_INVFORMAT);
} else if (x == COUNTRY_ERR_NOT_LOCALCFG) {
nls_puts(NLS_ERR_NOTLOCALCFG);
} else {
nls_puts(NLS_ERR_READFAIL);
}
return(2);
}
 
changedflag = 0;
 
/* process command line arguments */
for (x = 1; x < argc; x++) {
if (argv[x][0] != '/') continue; /* skip country.sys filename (processed earlier) */
changedflag++;
if (processarg(argv[x], &cntdata) != 0) {
nls_puts(NLS_ERR_INVPARAM);
return(3);
}
}
 
nls_put(NLS_INFO_COUNTRY);
sprintf(buff, " %03d", cntdata.CTYINFO.id);
outputnl(buff);
nls_put(NLS_INFO_CODEPAGE);
sprintf(buff, " %d", cntdata.CTYINFO.codepage);
outputnl(buff);
nls_put(NLS_INFO_DECSEP);
sprintf(buff, " %c", cntdata.CTYINFO.decimal[0]);
outputnl(buff);
nls_put(NLS_INFO_THOUSEP);
sprintf(buff, " %c", cntdata.CTYINFO.thousands[0]);
outputnl(buff);
nls_put(NLS_INFO_DATEFMT);
output(" ");
outputnl(datestring(buff, &cntdata));
nls_put(NLS_INFO_TIMEFMT);
output(" ");
outputnl(timestring(buff, &cntdata));
nls_put(NLS_INFO_YESNO);
sprintf(buff, " %c/%c", cntdata.YESNO.yes[0], cntdata.YESNO.no[0]);
outputnl(buff);
nls_put(NLS_INFO_CURREXAMPLE);
output(" ");
outputnl(currencystring(buff, &cntdata));
 
crlf();
nls_puts(NLS_MAKESURE);
sprintf(buff, "COUNTRY=%03d,%03d,", cntdata.CTYINFO.id, cntdata.CTYINFO.codepage);
output(buff);
outputnl(fname);
 
/* if anything changed, write the new file */
if (changedflag != 0) country_write(fname, &cntdata);
 
return(0);
}
/localcfg/tags/20220203/localcfg.lsm
0,0 → 1,2
version: 20220203
description: locales preferences configuration tool (country.sys-like)
/localcfg/tags/20220203/localcfg.txt
0,0 → 1,36
 
localcfg - SvarDOS locales editor
 
 
*** About ***
 
localcfg is a 16 bit DOS tool that provides an easy way of configurating system
locales by generating a custom COUNTRY.SYS file that reflects user's
preferences.
 
Run localcfg /? for usage instructions.
 
 
*** License ***
 
localcfg is written by Mateusz Viste, and published under the MIT license.
 
Copyright (C) 2015-2022 Mateusz Viste
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
/localcfg/tags/20220203/nls_lang/en.txt
0,0 → 1,55
#
# LOCALCFG TRANSLATION FILE
#
# LANGUAGE: ENGLISH
# TRANSLATOR: MATEUSZ VISTE
#
 
### HELP SCREEN ###########################################################
 
0.0:version
0.1:creates or edits COUNTRY.SYS local preferences
0.2:usage: localcfg [COUNTRY.SYS] [options]
0.3:options:
 
0.10:/country:XX set the country code to XX (1=USA, 33=France, 48=Poland, etc)
0.11:/cp:XXX adapts country data for codepage XXX (example: '437')
0.12:/decim:X reconfigures the decimal symbol to be 'X'
0.13:/thous:X reconfigures the thousands symbol to be 'X'
0.14:/datesep:X sets the date separator to 'X' (for example '/')
0.15:/datefmt:X sets the date format, can be: MDY, DMY or YMD
0.16:/timesep:X sets the time separator to 'X' (for example ':')
0.17:/timefmt:X sets the time format: 0=12h with AM/PM or 1=24h
0.18:/curr:XXX sets the currency to XXX (a string of 1 to 4 characters)
0.19:/currpos:X sets the currency symbol position to X, where X is either
0.20: 0=currency precedes the value, 1=currency follows the value and
0.21: 2=currency replaces the decimal sign
0.22:/currspc:X space between the currency and the value (0=no, 1=yes)
0.23:/currprec:X currency's precision (number of decimal digits, 0..9)
0.24:/yesno:XY sets the 'Yes/No' letter to XY (default: YN)
 
0.50:If COUNTRY.SYS location is not provided, then localcfg tries loading it
0.51:from %DOSDIR%\CFG\COUNTRY.SYS
 
 
### INFO SCREEN ###########################################################
 
7.0:Country code.......:
7.1:Codepage...........:
7.2:Decimal separator..:
7.3:Thousands separator:
7.4:Date format........:
7.5:Time format........:
7.6:Yes/No characters..:
7.7:Currency example...:
 
7.9:Make sure that your CONFIG.SYS contains this directive:
 
### ERROR MESSAGES ########################################################
 
9.0:ERROR: file path can be provided only once
9.1:ERROR: bad path to file
9.2:ERROR: failed to read the file
9.3:ERROR: invalid parameter syntax
9.4:ERROR: the file is not a valid COUNTRY.SYS
9.5:ERROR: the file has not been created with LOCALCFG
/localcfg/tags/20220203/nls_lang/pl_utf8.txt
0,0 → 1,55
#
# LOCALCFG TRANSLATION FILE
#
# LANGUAGE: POLISH
# TRANSLATOR: MATEUSZ VISTE
#
 
### HELP SCREEN ###########################################################
 
0.0:wersja
0.1:tworzy lub zmienia lokalne preferencje COUNTRY.SYS
0.2:użycie: localcfg [COUNTRY.SYS] [opcje]
0.3:opcje:
 
0.10:/country:XX ustawia kod kraju na XX (1=USA, 33=Francja, 48=Polska, itd)
0.11:/cp:XXX ustawia preferowaną stronę kodową na XXX (przykład: '437')
0.12:/decim:X ustawia separator dziesiętny 'X'
0.13:/thous:X ustawia separator tysięczny 'X'
0.14:/datesep:X ustawia separator daty 'X' (np. '/')
0.15:/datefmt:X ustawia jeden z formatów daty: MDY, DMY lub YMD
0.16:/timesep:X ustawia separator czasu 'X' (np. ':')
0.17:/timefmt:X ustawia format godziny: 0=12h z AM/PM lub 1=24h
0.18:/curr:XXX ustawia walutę XXX (ciąg od 1 do 4 znaków)
0.19:/currpos:X ustawia miejsce symbolu walutowego, gdzie X może być:
0.20: 0=symbol waluty przed sumą, 1=symbol waluty po sumie,
0.21: 2=symbol waluty zastępuje znak dziesiętny
0.22:/currspc:X spacja pomiędzy walutą a wartością (0=nie, 1=tak)
0.23:/currprec:X dokładność waluty (liczba pozycji dziesiętnych, 0..9)
0.24:/yesno:XY ustawia znaki 'Tak/Nie' (domyślnie: YN)
 
0.50:Jeśli nie podasz ścieżki do COUNTRY.SYS to localcfg spróbuje załadować plik
0.51:%DOSDIR%\CFG\COUNTRY.SYS
 
 
### INFO SCREEN ###########################################################
 
7.0:Kod kraju..........:
7.1:Strona kodowa......:
7.2:Znak dziesiętny....:
7.3:Separator tysięczny:
7.4:Format daty........:
7.5:Format godziny.....:
7.6:Znaki Tak/Nie......:
7.7:Przykład waluty....:
 
7.9:Upewnij się, że twój plik CONFIG.SYS zawiera taki wpis:
 
### ERROR MESSAGES ########################################################
 
9.0:BŁĄD: ścieżka do pliku może zostać podana tylko raz
9.1:BŁĄD: zła ścieżka do pliku
9.2:BŁĄD: nie zdołano wczytać pliku
9.3:BŁĄD: nieprawidłowa składnia parametru
9.4:BŁĄD: plik nie jest prawidłowym plikiem COUNTRY.SYS
9.5:BŁĄD: plik nie został stworzony za pomocą LOCALCFG
/localcfg/tags/20220203/svarlang.lib
0,0 → 1,0
link ../../svarlang.lib
Property changes:
Added: svn:special
+*
\ No newline at end of property
/localcfg/trunk/Makefile
45,6 → 45,7
copy *.txt source\localcfg
copy makefile source\localcfg
copy nls_lang\*.* source\localcfg\nls_lang
if exist localcfg.zip del localcfg.zip
zip -9rkDX -m localcfg.zip appinfo bin doc nls source
rmdir appinfo
rmdir source\localcfg\svarlang.lib