/*===========================================================================*
* *
* sfllang.c - *
* *
* Copyright (c) 1991-2003 iMatix Corporation *
* *
* ------------------ GPL Licensed Source Code ------------------ *
* iMatix makes this software available under the GNU General *
* Public License (GPL) license for open source projects. For *
* details of the GPL license please see www.gnu.org or read the *
* file license.gpl provided in this package. *
* *
* 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 in the file 'license.gpl'; if *
* not, write to the Free Software Foundation, Inc., 59 Temple *
* Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* You can also license this software under iMatix's General Terms *
* of Business (GTB) for commercial projects. If you have not *
* explicitly licensed this software under the iMatix GTB you may *
* only use it under the terms of the GNU General Public License. *
* *
* For more information, send an email to info@imatix.com. *
* -------------------------------------------------------------- *
*===========================================================================*/
#include "prelude.h" /* Universal header file */
#include "sflstr.h" /* String functions */
#include "sfldate.h" /* Date functions */
#include "sfllist.h" /* Memory-allocation functions */
#include "sflmem.h" /* Memory-allocation functions */
#include "sfllang.h" /* Function prototypes */
static char
*language_str [] = {
"--",
"DA", "DE", "EN", "ES", "FB", "FR",
"IS", "IT", "NL", "NO", "PO", "SV"
};
static char
*DA_units [] = {
"nul", "en", "to", "tre", "fire", "fem", "seks", "syv", "otte",
"ni", "ti", "elve", "tolv", "tretten", "fjorten", "femten",
"seksten", "sytten", "atten", "nitten"
},
*DE_units [] = {
"null", "ein", "zwei", "drei", "vier", "fnf", "sechs", "sieben",
"acht", "neun", "zehn", "elf", "zw/Uolf", "dreizehn", "vierzehn",
"f/Uunfzehn", "sechzehn", "siebzehn", "achtzehn", "neunzehn"
},
*EN_units [] = {
"zero", "one", "two", "three", "four", "five", "six", "seven",
"eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
},
*ES_units [] = {
"zero", "uno", "dos", "tres", "cuatro", "cinco", "seis", "siete",
"ocho", "nueve", "diez", "once", "doce", "trece", "catorce", "quince",
"dieciseis", "diecisiete", "dieciocho", "diecinueve"
},
*FR_units [] = {
"zero", "un", "deux", "trois", "quatre", "cinq", "six", "sept",
"huit", "neuf", "dix", "onze", "douze", "treize", "quatorze",
"quinze", "seize", "dix-sept", "dix-huit", "dix-neuf"
},
*IS_units [] = {
"null", "einn", "tweir", "thrir", "fjorir", "fimm", "sex", "sj/Uo",
"atta", "niu", "tiu", "ellefu", "tolf", "threttan", "fjortan",
"fimtan", "sextan", "seytjan", "atjan", "nitjan"
},
*IT_units [] = {
"zero", "uno", "due", "tre", "quattro", "cinque", "sei", "sette",
"otto", "nove", "dieci", "undici", "dodici", "tredici", "quatrodici",
"quindici", "sedici", "diciassette", "diciotto", "dicianove"
},
*NL_units [] = {
"nul", "een", "twee", "drie", "vier", "vijf", "zes", "zeven", "acht",
"negen", "tien", "elf", "twaalf", "dertien", "veertien", "vijftien",
"zestien", "zeventien", "achttien", "negentien"
},
*NO_units [] = {
"null", "en", "to", "tre", "fire", "fem", "seks", "syv", "/Ratte",
"ni", "ti", "elleve", "tolv", "tretten", "fjorten", "femten",
"seksten", "sytten", "atten", "nitten"
},
*PO_units [] = {
"zero", "um", "dois", "tres", "quatro", "cinco", "seis", "sete",
"oito", "nove", "dez", "onze", "doze", "treze", "catorze",
"quinze", "dezasseis", "dezassete", "dezoito", "dezanove"
},
*SV_units [] = {
"noll", "en", "tv/Ra", "tre", "fyra", "fem", "sex", "sju", "/Ratta",
"nio", "tio", "elva", "tolv", "tretton", "fjorton", "femton",
"sexton", "sjutton", "atton", "nitton"
};
static char
*DA_tens [] = {
"ti", "tyve", "tredive", "fyrre", "halvtreds",
"treds", "halvfjerds", "firs", "halvfems"
},
*DE_tens [] = {
"zehn", "zwanzig", "drei/Bsig", "vierzig", "f/Uunfzig",
"sechzig", "siebzig", "achtzig", "neunzig"
},
*EN_tens [] = {
"ten", "twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety"
},
*ES_tens [] = {
"diez", "veinti", "treinta", "cuarenta", "cincuenta",
"sesenta", "setenta", "ochenta", "noventa"
},
*FB_tens [] = {
"dix", "vingt", "trente", "quarante", "cinquante",
"soixante", "septante", "quatre-vingt", "nonante"
},
*FR_tens [] = {
"dix", "vingt", "trente", "quarante", "cinquante",
"soixante", "soixante", "quatre-vingt", "quatre-vingt"
},
*IS_tens [] = {
"tiu", "tuttugu", "thrjatiu", "fj/Uorutiu", "fumtiu",
"sextiu", "sj/Uotiu", "attatiu", "niutiu"
},
*IT_tens [] = {
"dieci", "venti", "trenta", "quaranta", "cinquanta",
"sessanta", "settanta", "ottanta", "novanta"
},
*NL_tens [] = {
"tien", "twintig", "dertig", "veertig", "vijftig",
"zestig", "zeventig", "tachtig", "negentig"
},
*NO_tens [] = {
"ti", "tyve", "tredve", "f/Sorti", "femti",
"seksti", "sytti", "/Ratti", "nitti"
},
*PO_tens [] = {
"dez", "vinte", "trinta", "quarenta", "cinquenta",
"sessenta", "setenta", "oitenta", "noventa"
},
*SV_tens [] = {
"tio", "tjugo", "trettio", "fyrtio", "femtio",
"sextio", "sjuttio", "/Rattio", "nittio"
};
static char
*DA_months [] = {
"januar", "februar", "marts", "april", "maj", "juni",
"juli", "august", "september", "oktober", "november", "december"
},
*DE_months [] = {
"Januar", "Februar", "Marsch", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"
},
*EN_months [] = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
},
*ES_months [] = {
"enero", "febrero", "marzo", "abril", "mayo", "junio",
"julio", "agosto", "setiembre", "octubre", "noviembre", "diciembre"
},
*FR_months [] = {
"janvier", "fevrier", "mars", "avril", "mai", "juin",
"juillet", "ao/Cut", "septembre", "octobre", "novembre", "decembre"
},
*IS_months [] = {
"januar", "februar", "marz", "april", "mai", "juni",
"juli", "agust", "september", "oktober", "november", "desember"
},
*IT_months [] = {
"gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno",
"luglio", "agosto", "settembre", "ottobre", "novembre", "dicembre"
},
*NL_months [] = {
"januari", "februari", "mars", "april", "mei", "juni",
"juli", "augustus", "september", "oktober", "november", "december"
},
*NO_months [] = {
"januar", "februar", "mars", "april", "mai", "juni",
"juli", "august", "september", "oktober", "november", "desember"
},
*PO_months [] = {
"janeiro", "fevereiro", "maro", "abril", "maio", "junho",
"julho", "agosto", "setembro", "outubro", "novembro", "dezembro"
},
*SV_months [] = {
"januari", "februari", "mars", "april", "maj", "juni",
"juli", "augusti", "september", "oktober", "november", "december"
};
static char
*EN_days [] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
},
*FR_days [] = {
"Dimanche", "Lundi", "Mardi", "Mercredi",
"Jeudi", "Vendredi", "Samedi"
},
*NL_days [] = {
"Zondag", "Maandag", "Dinsdag", "Woensdag",
"Donderdag", "Vrijdag", "Zaterdag"
};
static int
user_language = 0; /* 0 = default language */
static Bool
use_accents = TRUE;
static char
**units_table = EN_units,
**tens_table = EN_tens,
**day_table = EN_days,
**month_table = EN_months;
/* Local function prototypes */
static char *handle_accents (char *string);
/* ---------------------------------------------------------------------[<]-
Function: set_userlang
Synopsis: Sets language used for date and numeric translation.
The valid user languages are:
USERLANG_DEFAULT Default language (use hard-coded values)
USERLANG_DA Danish
USERLANG_DE German
USERLANG_EN English
USERLANG_ES Castillian Spanish
USERLANG_FB Belgian or Swiss French
USERLANG_FR French
USERLANG_IS Icelandic
USERLANG_IT Italian
USERLANG_NL Dutch
USERLANG_NO Norwegian
USERLANG_PO Portuguese
USERLANG_SV Swedish
Returns 0 if okay, -1 if an unsupported language was specified.
---------------------------------------------------------------------[>]-*/
int
set_userlang (int language)
{
/* Order of this table is not critical */
static struct {
int language;
char **units;
char **tens;
char **days;
char **months;
} languages [] =
{
{ USERLANG_DEFAULT, EN_units, EN_tens, EN_days, EN_months },
{ USERLANG_DA, DA_units, DA_tens, EN_days, DA_months },
{ USERLANG_DE, DE_units, DE_tens, EN_days, DE_months },
{ USERLANG_EN, EN_units, EN_tens, EN_days, EN_months },
{ USERLANG_ES, ES_units, ES_tens, EN_days, ES_months },
{ USERLANG_FB, FR_units, FB_tens, FR_days, FR_months },
{ USERLANG_FR, FR_units, FR_tens, FR_days, FR_months },
{ USERLANG_IS, IS_units, IS_tens, EN_days, IS_months },
{ USERLANG_IT, IT_units, IT_tens, EN_days, IT_months },
{ USERLANG_NL, NL_units, NL_tens, NL_days, NL_months },
{ USERLANG_NO, NO_units, NO_tens, EN_days, NO_months },
{ USERLANG_PO, PO_units, PO_tens, EN_days, PO_months },
{ USERLANG_SV, SV_units, SV_tens, EN_days, SV_months },
{ -1, NULL, NULL, NULL, NULL }
};
int
index;
for (index = 0; languages [index].language != -1; index++)
if (languages [index].language == language)
{
user_language = language;
units_table = languages [index].units;
tens_table = languages [index].tens;
day_table = languages [index].days;
month_table = languages [index].months;
return (0);
}
return (-1);
}
/* ---------------------------------------------------------------------[<]-
Function: set_userlang_str
Synopsis: Sets language used for date and numeric translation, using a
string representation of the language. The valid user languages are:
"" Default language (use hard-coded values)
"--" Alternative form for default language
"DA" Danish
"DE" German
"EN" English
"ES" Castillian Spanish
"FB" Belgian or Swiss French
"FR" French
"IS" Icelandic
"IT" Italian
"NL" Dutch
"NO" Norwegian
"PO" Portuguese
"SV" Swedish
Returns 0 if okay, -1 if an unsupported language was specified.
---------------------------------------------------------------------[>]-*/
int
set_userlang_str (const char *language)
{
int
index;
if (strnull (language))
return (set_userlang (USERLANG_DEFAULT));
for (index = 0; index < USERLANG_TOP; index++)
if (streq (language, language_str [index]))
return (set_userlang (index));
return (-1);
}
/* ---------------------------------------------------------------------[<]-
Function: get_userlang
Synopsis: Returns the current user language code.
---------------------------------------------------------------------[>]-*/
int
get_userlang (void)
{
return (user_language);
}
/* ---------------------------------------------------------------------[<]-
Function: get_userlang_str
Synopsis: Returns the current user language as a 2-character string.
---------------------------------------------------------------------[>]-*/
char *
get_userlang_str (void)
{
return (language_str [user_language]);
}
/* ---------------------------------------------------------------------[<]-
Function: set_accents
Synopsis: Enables or disables native-language accents. If enabled,
accented characters in translated words are produced in the current
system character set, if possible. Otherwise, suitable translations
are made into the 26-letter English alphabet. By default, accents are
enabled.
---------------------------------------------------------------------[>]-*/
int
set_accents (Bool accents)
{
use_accents = accents;
return (0);
}
/* ---------------------------------------------------------------------[<]-
Function: get_accents
Synopsis: Returns TRUE if accents are enabled, FALSE if not.
---------------------------------------------------------------------[>]-*/
Bool
get_accents (void)
{
return (use_accents);
}
/* ---------------------------------------------------------------------[<]-
Function: get_units_name
Synopsis: Returns the name for the specified units, which is a value from
zero to 19. Accented characters are formatted according to the current
accents setting.
---------------------------------------------------------------------[>]-*/
char *
get_units_name (int units)
{
ASSERT (units >= 0 && units <= 19);
return (handle_accents (units_table [units]));
}
/* ---------------------------------------------------------------------[<]-
Function: get_tens_name
Synopsis: Returns the name for the specified tens, which is a value
from 10 to 90; it is rounded as required. Accented characters are
formatted according to the current accents setting.
---------------------------------------------------------------------[>]-*/
char *
get_tens_name (int tens)
{
ASSERT (tens >= 10 && tens < 100);
return (handle_accents (tens_table [tens / 10 - 1]));
}
/* ---------------------------------------------------------------------[<]-
Function: get_day_name
Synopsis: Returns the name for the specified day, which must be a
value from 0 (Sunday) to 6 (Saturday). Accented characters are
formatted according to the current accents setting.
---------------------------------------------------------------------[>]-*/
char *
get_day_name (int day)
{
ASSERT (day >= 0 && day <= 6);
return (handle_accents (day_table [day]));
}
/* ---------------------------------------------------------------------[<]-
Function: get_day_abbrev
Synopsis: Returns the abbreviation for the specified day, which must be
a value from 0 (Sunday) to 6 (Saturday). The abbreviation (3 letters)
is converted into uppercase if the 'upper' argument is true. Accented
characters are formatted according to the current accents setting.
---------------------------------------------------------------------[>]-*/
char *
get_day_abbrev (int day, Bool upper)
{
char
abbrev [4];
ASSERT (day >= 0 && day <= 6);
strncpy (abbrev, day_table [day], 3);
abbrev [3] = '\0';
if (upper)
strupc (abbrev);
return (handle_accents (abbrev));
}
/* ---------------------------------------------------------------------[<]-
Function: get_month_name
Synopsis: Returns the name for the specified month, which must be a
value from 1 to 12. Accented characters are handled as per the current
accents setting.
---------------------------------------------------------------------[>]-*/
char *
get_month_name (int month)
{
ASSERT (month >= 1 && month <= 12);
return (handle_accents (month_table [month - 1]));
}
/* ---------------------------------------------------------------------[<]-
Function: get_month_abbrev
Synopsis: Returns the abbreviation for the specified month, which must
be a value from 1 to 12. The abbreviation (3 letters) is converted into
uppercase if the 'upper' argument is true. Accented characters are
formatted according to the current accents setting.
---------------------------------------------------------------------[>]-*/
char *
get_month_abbrev (int month, Bool upper)
{
char
abbrev [4];
ASSERT (month >= 1 && month <= 12);
strncpy (abbrev, month_table [month - 1], 3);
abbrev [3] = '\0';
if (upper)
strupc (abbrev);
return (handle_accents (abbrev));
}
/* handle_accents -- internal
*
* Accepts string containing escaped accented characters and converts into
* local values. Currently handles DOS and Unix character sets and this
* (reduced) set of accented characters:
*
* /Uu u umlaut
* /UU U umlaut
* /Uo o umlaut
* /UO O umlaut
* /Ra a ring
* /RA A ring
* /So slash o
* /SO slash O
* /Bs scharfes S
* /BS scharfes S
* /Cu circumflex u
* /CU circumflex U
*/
static char *
handle_accents (char *string)
{
#if (defined (__UNIX__))
# define CHAR_a_ring '\345'
# define CHAR_A_ring '\305'
# define CHAR_o_uml '\366'
# define CHAR_O_uml '\326'
# define CHAR_u_circ '\373'
# define CHAR_u_uml '\374'
# define CHAR_U_uml '\334'
#elif (defined (__MSDOS__))
# define CHAR_a_ring '\206'
# define CHAR_A_ring '\217'
# define CHAR_o_uml '\224'
# define CHAR_O_uml '\231'
# define CHAR_u_circ '\226'
# define CHAR_u_uml '\201'
# define CHAR_U_uml '\232'
#else
# define CHAR_a_ring 'a'
# define CHAR_A_ring 'A'
# define CHAR_o_uml 'o'
# define CHAR_O_uml 'O'
# define CHAR_u_circ 'u'
# define CHAR_u_uml 'u'
# define CHAR_U_uml 'U'
#endif
return (string); /* To be implemented */
}
/* ---------------------------------------------------------------------[<]-
Function: timestamp_string
Synopsis: Formats a timestamp according to a user-supplied pattern. The
result is returned in a buffer supplied by the caller; if this argument
is NULL, allocates a buffer and returns that (the caller must then free
the buffer using mem_free()). The pattern consists of arbitrary text
mixed with insertion symbols indicated by '%':
%y day of year, 001-366
%yy year 2 digits, 00-99
%yyyy year 4 digits, 0100-9999
%mm month, 01-12
%mmm month, Jan
%mmmm month, January
%MMM month, JAN
%MMMM month, JANUARY
%dd day, 01-31
%ddd day of week, Sun
%dddd day of week, Sunday
%DDD day of week, SUN
%DDDD day of week, SUNDAY
%w day of week, 1-7 (1=Sunday)
%ww week of year, 01-53
%q year quarter, 1-4
%h hour, 00-23
%m minute, 00-59
%s second, 00-59
%c centisecond, 00-99
%% literal character %
---------------------------------------------------------------------[>]-*/
char *
timestamp_string (char *buffer, const char *pattern)
{
long
date, /* Current date */
time; /* and time */
int
century, /* Century component of date */
year, /* Year component of date */
month, /* Month component of date */
day, /* Day component of date */
hour, /* Hour component of time */
minute, /* Minute component of time */
second, /* Second component of time */
centi, /* 1/100 sec component of time */
cursize; /* Size of current component */
char
*dest, /* Store formatted data here */
ch; /* Next character in picture */
date = date_now ();
time = time_now ();
century = GET_CENTURY (date);
year = GET_YEAR (date);
month = GET_MONTH (date);
day = GET_DAY (date);
hour = GET_HOUR (time);
minute = GET_MINUTE (time);
second = GET_SECOND (time);
centi = GET_CENTI (time);
if (buffer == NULL)
buffer = mem_alloc (strlen (pattern) * 2);
/* Scan through picture, converting each component */
dest = buffer;
*dest = 0; /* String is empty */
while (*pattern)
{
ch = *pattern++;
if (ch == '%' && *pattern)
{
ch = *pattern++; /* Count size of pattern after % */
for (cursize = 1; *pattern == ch; cursize++)
pattern++;
}
else
{
*dest++ = ch; /* Something else - store and next */
*dest = 0; /* Terminate the string nicely */
continue;
}
/* Now process pattern itself */
switch (ch)
{
case 'y':
if (cursize == 1) /* y day of year, 001-366 */
sprintf (dest, "%03d", julian_date (date));
else
if (cursize == 2) /* yy year 2 digits, 00-99 */
sprintf (dest, "%02d", year);
else
if (cursize == 4) /* yyyy year 4 digits, 0100-9999 */
sprintf (dest, "%02d%02d", century, year);
break;
case 'm':
if (cursize == 1) /* m minute, 00-59 */
sprintf (dest, "%02d", minute);
else
if (cursize == 2) /* mm month, 01-12 */
sprintf (dest, "%02d", month);
else
if (cursize == 3) /* mmm month, 3 letters */
strcpy (dest, get_month_abbrev (month, FALSE));
else
if (cursize == 4) /* mmmm month, full name */
strcpy (dest, get_month_name (month));
break;
case 'M':
if (cursize == 3) /* MMM month, 3-letters, ucase */
strcpy (dest, get_month_abbrev (month, TRUE));
else
if (cursize == 4) /* MMMM month, full name, ucase */
{
strcpy (dest, get_month_name (month));
strupc (dest);
}
break;
case 'd':
if (cursize == 2) /* dd day, 01-31 */
sprintf (dest, "%02d", day);
else
if (cursize == 3) /* ddd day of week, Sun */
strcpy (dest, get_day_abbrev (day_of_week (date), FALSE));
else
if (cursize == 4) /* dddd day of week, Sunday */
strcpy (dest, get_day_name (day_of_week (date)));
break;
case 'D':
if (cursize == 3) /* DDD day of week, SUN */
strcpy (dest, get_day_abbrev (day_of_week (date), TRUE));
else
if (cursize == 4) /* DDDD day of week, SUNDAY */
{
strcpy (dest, get_day_name (day_of_week (date)));
strupc (dest);
}
break;
case 'w':
if (cursize == 1) /* w day of week, 1-7 (1=Sun) */
sprintf (dest, "%d", day_of_week (date) + 1);
else
if (cursize == 2) /* ww week of year, 01-53 */
sprintf (dest, "%d", week_of_year (date));
break;
case 'q':
if (cursize == 1) /* q year quarter, 1-4 */
sprintf (dest, "%d", year_quarter (date));
break;
case 'h':
if (cursize == 1) /* h hour, 00-23 */
sprintf (dest, "%02d", hour);
break;
case 's':
if (cursize == 1) /* s second, 00-59 */
sprintf (dest, "%02d", second);
break;
case 'c':
if (cursize == 1) /* c centisecond, 00-99 */
sprintf (dest, "%02d", centi);
break;
case '%':
if (cursize == 1) /* % literal '%' */
strcpy (dest, "%");
break;
}
if (*dest) /* If something was output, */
while (*dest) /* skip to end of string */
dest++;
else
{
while (cursize--) /* Else output ch once or more */
*dest++ = ch; /* and bump dest pointer */
*dest = 0; /* Terminate the string nicely */
}
}
return (buffer);
}