|
Packit Service |
a2489d |
/*
|
|
Packit Service |
a2489d |
* plural word form chooser for i18n
|
|
Packit Service |
a2489d |
*
|
|
Packit Service |
a2489d |
* Copyright (c) 1998,2016 by Alexander V. Lukyanov (lav@yars.free.net)
|
|
Packit Service |
a2489d |
*
|
|
Packit Service |
a2489d |
* This file is in public domain.
|
|
Packit Service |
a2489d |
*/
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
/*
|
|
Packit Service |
a2489d |
* This file provides a function to transform a string with all plural forms
|
|
Packit Service |
a2489d |
* of a word to a string with concrete form depending on a number.
|
|
Packit Service |
a2489d |
* It uses a rule encoded in special string.
|
|
Packit Service |
a2489d |
*/
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
/* TODO:
|
|
Packit Service |
a2489d |
* allow to select number of argument
|
|
Packit Service |
a2489d |
*/
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
#include <config.h>
|
|
Packit Service |
a2489d |
#include <string.h>
|
|
Packit Service |
a2489d |
#include <stdio.h>
|
|
Packit Service |
a2489d |
#include <stdarg.h>
|
|
Packit Service |
a2489d |
#include <stdlib.h>
|
|
Packit Service |
a2489d |
#include "plural.h"
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
static int choose_plural_form(const char *rule,int num)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
int res=0;
|
|
Packit Service |
a2489d |
int match=1;
|
|
Packit Service |
a2489d |
int n=num;
|
|
Packit Service |
a2489d |
char c;
|
|
Packit Service |
a2489d |
int arg,arg_len;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
for(;;)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
switch(c=*rule)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
case '=':
|
|
Packit Service |
a2489d |
case '!':
|
|
Packit Service |
a2489d |
case '>':
|
|
Packit Service |
a2489d |
case '<':
|
|
Packit Service |
a2489d |
case '%':
|
|
Packit Service |
a2489d |
if(sscanf(rule+1,"%d%n",&arg,&arg_len)<1)
|
|
Packit Service |
a2489d |
return -1;
|
|
Packit Service |
a2489d |
rule+=arg_len;
|
|
Packit Service |
a2489d |
if(c=='%')
|
|
Packit Service |
a2489d |
n%=arg;
|
|
Packit Service |
a2489d |
if((c=='=' && !(n==arg))
|
|
Packit Service |
a2489d |
|| (c=='!' && !(n!=arg))
|
|
Packit Service |
a2489d |
|| (c=='>' && !(n> arg))
|
|
Packit Service |
a2489d |
|| (c=='<' && !(n< arg)))
|
|
Packit Service |
a2489d |
match=0;
|
|
Packit Service |
a2489d |
break;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
case '|':
|
|
Packit Service |
a2489d |
case ' ':
|
|
Packit Service |
a2489d |
case '\0':
|
|
Packit Service |
a2489d |
if(match)
|
|
Packit Service |
a2489d |
return res;
|
|
Packit Service |
a2489d |
if(c=='\0')
|
|
Packit Service |
a2489d |
return res+1;
|
|
Packit Service |
a2489d |
if(c=='|')
|
|
Packit Service |
a2489d |
match=1;
|
|
Packit Service |
a2489d |
if(c==' ') /* next rule */
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
n=num;
|
|
Packit Service |
a2489d |
res++;
|
|
Packit Service |
a2489d |
match=1;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
break;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
rule++;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
/* return res;*/
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
/*
|
|
Packit Service |
a2489d |
* The function takes _untranslated_ string with $form1|form2|form3...$
|
|
Packit Service |
a2489d |
* inserts, and a list of integer numbers. It uses gettext on the string.
|
|
Packit Service |
a2489d |
* Using "translated" rule and the list of numbers it choose appropriate
|
|
Packit Service |
a2489d |
* plural forms.
|
|
Packit Service |
a2489d |
*
|
|
Packit Service |
a2489d |
* If the string or the rule cannot be translated, it uses untranslated
|
|
Packit Service |
a2489d |
* string and default (english) rule.
|
|
Packit Service |
a2489d |
*
|
|
Packit Service |
a2489d |
* Returns pointer to static storage, copy if needed.
|
|
Packit Service |
a2489d |
*/
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
const char *plural(const char *format,...)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
static char *res=0;
|
|
Packit Service |
a2489d |
static size_t res_size=0;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
/* This is the rule for plural form choice (last condition can be omitted) */
|
|
Packit Service |
a2489d |
/* Operators: = > < ! | % */
|
|
Packit Service |
a2489d |
const char *rule=N_("=1 =0|>1");
|
|
Packit Service |
a2489d |
const char *s;
|
|
Packit Service |
a2489d |
char *store;
|
|
Packit Service |
a2489d |
int index,plural_index;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
const char *new_format=gettext(format);
|
|
Packit Service |
a2489d |
const char *new_rule=gettext(rule);
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
va_list va;
|
|
Packit Service |
a2489d |
long arg;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
va_start(va,format);
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
if(new_format!=format && new_rule!=rule) /* there is translation */
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
rule=new_rule; /* use "translated" rule */
|
|
Packit Service |
a2489d |
format=new_format; /* and translated format */
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
if(res==0)
|
|
Packit Service |
a2489d |
res=malloc(res_size=256);
|
|
Packit Service |
a2489d |
if(res==0)
|
|
Packit Service |
a2489d |
goto va_end_out;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
store=res;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
for(s=format; *s; s++)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
#define ALLOCATE \
|
|
Packit Service |
a2489d |
if(store-res+1>=res_size) \
|
|
Packit Service |
a2489d |
{ \
|
|
Packit Service |
a2489d |
int dif=store-res; \
|
|
Packit Service |
a2489d |
res=realloc(res,res_size*=2); \
|
|
Packit Service |
a2489d |
if(res==0) \
|
|
Packit Service |
a2489d |
goto va_end_out; \
|
|
Packit Service |
a2489d |
store=res+dif; \
|
|
Packit Service |
a2489d |
} /* end ALLOCATE */
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
if(*s=='$' && s[1])
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
s++;
|
|
Packit Service |
a2489d |
if(*s=='$')
|
|
Packit Service |
a2489d |
goto plain;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
/* check options */
|
|
Packit Service |
a2489d |
if(*s=='#')
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
s++;
|
|
Packit Service |
a2489d |
if(*s=='l') /* long */
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
s++;
|
|
Packit Service |
a2489d |
if(*s=='l') /* long long */
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
s++;
|
|
Packit Service |
a2489d |
arg=va_arg(va,long long)%1000000;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
else
|
|
Packit Service |
a2489d |
arg=va_arg(va,long);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
else
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
arg=va_arg(va,int);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
if(*s=='#') /* end of options */
|
|
Packit Service |
a2489d |
s++;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
else
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
arg=va_arg(va,int);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
if(arg<0)
|
|
Packit Service |
a2489d |
arg=-arg;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
plural_index=choose_plural_form(rule,arg);
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
index=0;
|
|
Packit Service |
a2489d |
while(index!=plural_index)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
/* skip plural form */
|
|
Packit Service |
a2489d |
while(*s!='$' && *s!='|' && *s)
|
|
Packit Service |
a2489d |
s++;
|
|
Packit Service |
a2489d |
if(*s==0)
|
|
Packit Service |
a2489d |
goto out;
|
|
Packit Service |
a2489d |
if(*s=='$')
|
|
Packit Service |
a2489d |
break;
|
|
Packit Service |
a2489d |
s++;
|
|
Packit Service |
a2489d |
index++;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
if(index==plural_index)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
/* insert the form */
|
|
Packit Service |
a2489d |
while(*s!='$' && *s!='|' && *s)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
ALLOCATE;
|
|
Packit Service |
a2489d |
*store++=*s++;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
while(*s!='$' && *s)
|
|
Packit Service |
a2489d |
s++;
|
|
Packit Service |
a2489d |
if(*s==0)
|
|
Packit Service |
a2489d |
goto out;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
else
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
plain:
|
|
Packit Service |
a2489d |
ALLOCATE;
|
|
Packit Service |
a2489d |
*store++=*s;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
out:
|
|
Packit Service |
a2489d |
ALLOCATE;
|
|
Packit Service |
a2489d |
*store=0;
|
|
Packit Service |
a2489d |
va_end_out:
|
|
Packit Service |
a2489d |
va_end(va);
|
|
Packit Service |
a2489d |
return res;
|
|
Packit Service |
a2489d |
}
|