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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
#include "fileserv.h"
#include <string.h>
struct lang_pref {
char const *lang;
size_t len;
float pref;
};
static int
lang_pref_cmp(const void *a, const void *b)
{
struct lang_pref const *lpa = a;
struct lang_pref const *lpb = b;
if (lpa->pref < lpb->pref)
return 1;
if (lpa->pref > lpb->pref)
return -1;
return 0;
}
static int
parse_accepted_language(const char *str, struct lang_pref **lpv, size_t *lpc)
{
size_t count = 1;
const char *p;
struct lang_pref *lp;
size_t len;
size_t i;
for (p = str; *p; p++)
if (*p == ',')
++count;
lp = calloc(count, sizeof(lp[0]));
if (!lp) {
alloc_warn();
return -1;
}
i = 0;
while (i < count) {
while (*str && (*str == ' ' || *str == '\t'))
++str;
if (!*str)
break;
len = strcspn(str, ",;");
lp[i].lang = str;
lp[i].len = len;
str += len;
if (*str == ';') {
while (*++str && (*str == ' ' || *str == '\t'))
;
if (str[0] == 'q' && str[1] == '=') {
lp[i].pref = strtod(str + 2, (char**) &str);
while (*str && (*str == ' ' || *str == '\t'))
++str;
}
} else {
lp[i].pref = 1.0;
}
if (*str == ',') {
++str;
++i;
} else {
if (*str == 0)
i++;
break;
}
}
qsort(lp, i, sizeof(lp[0]), lang_pref_cmp);
*lpv = lp;
*lpc = i;
return 0;
}
static ssize_t
match_lang(struct lang_pref const *lpv, size_t lpc, char const *lang)
{
size_t llen = strlen(lang);
ssize_t i, wildcard = -1;
for (i = 0; i < lpc; i++) {
if (lpv[i].len == 1 && lpv[i].lang[0] == '*')
wildcard = i;
else if ((lpv[i].len == llen
|| (lpv[i].len > llen && lpv[i].lang[llen] == '-'))
&& memcmp(lpv[i].lang, lang, llen) == 0)
return i;
}
return wildcard;
}
LANG_VAR *
select_language(LANG_VAR_LIST *lst, char const *acc)
{
struct lang_pref *lpv;
size_t lpc;
LANG_VAR *res = NULL;
ssize_t n;
LANG_VAR *lv;
if (parse_accepted_language(acc ? acc : "en,*;q=0.5", &lpv, &lpc)
== 0) {
STAILQ_FOREACH(lv, lst, next) {
ssize_t j = match_lang(lpv, lpc, lv->lang);
if (j >= 0
&& (!res || lpv[j].pref > lpv[n].pref)) {
res = lv;
n = j;
}
}
free(lpv);
}
if (res == NULL) {
res = STAILQ_FIRST(lst);
STAILQ_FOREACH(lv, lst, next) {
if (strcmp(lv->lang, "en") == 0) {
res = lv;
break;
}
}
}
return res;
}
|