Remove/mark some unused functions and parameters
[notion.git] / ioncore / strings.c
bloba4eb9b86e772288a30105c200bc880e4f044b0bf
1 /*
2 * ion/ioncore/strings.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <libtu/output.h>
10 #include <libtu/misc.h>
11 #include <string.h>
12 #include <regex.h>
13 #include "log.h"
14 #include "common.h"
15 #include "global.h"
16 #include "strings.h"
19 /*{{{ String scanning */
22 wchar_t str_wchar_at(char *p, int max)
24 wchar_t wc;
25 if(mbtowc(&wc, p, max)>0)
26 return wc;
27 return 0;
31 char *str_stripws(char *p)
33 mbstate_t ps;
34 wchar_t wc;
35 int first=-1, pos=0;
36 int n=strlen(p);
37 int ret;
39 memset(&ps, 0, sizeof(ps));
41 while(1){
42 ret=mbrtowc(&wc, p+pos, n-pos, &ps);
43 if(ret<=0)
44 break;
45 if(!iswspace(wc))
46 break;
47 pos+=ret;
50 if(pos!=0)
51 memmove(p, p+pos, n-pos+1);
53 if(ret<=0)
54 return p;
56 pos=ret;
58 while(1){
59 ret=mbrtowc(&wc, p+pos, n-pos, &ps);
60 if(ret<=0)
61 break;
62 if(iswspace(wc)){
63 if(first==-1)
64 first=pos;
65 }else{
66 first=-1;
68 pos+=ret;
71 if(first!=-1)
72 p[first]='\0';
74 return p;
78 int str_prevoff(const char *p, int pos)
80 if(ioncore_g.enc_sb)
81 return (pos>0 ? 1 : 0);
83 if(ioncore_g.enc_utf8){
84 int opos=pos;
86 while(pos>0){
87 pos--;
88 if((p[pos]&0xC0)!=0x80)
89 break;
91 return opos-pos;
94 assert(ioncore_g.use_mb);
96 /* *sigh* */
97 int l, prev=0;
98 mbstate_t ps;
100 memset(&ps, 0, sizeof(ps));
102 while(1){
103 l=mbrlen(p+prev, pos-prev, &ps);
104 if(l<0){
105 warn(TR("Invalid multibyte string."));
106 return 0;
108 if(prev+l>=pos)
109 return pos-prev;
110 prev+=l;
117 int str_nextoff(const char *p, int opos)
119 if(ioncore_g.enc_sb)
120 return (*(p+opos)=='\0' ? 0 : 1);
122 if(ioncore_g.enc_utf8){
123 int pos=opos;
125 while(p[pos]){
126 pos++;
127 if((p[pos]&0xC0)!=0x80)
128 break;
130 return pos-opos;
133 assert(ioncore_g.use_mb);
135 mbstate_t ps;
136 int l;
137 memset(&ps, 0, sizeof(ps));
139 l=mbrlen(p+opos, strlen(p+opos), &ps);
140 if(l<0){
141 warn(TR("Invalid multibyte string."));
142 return 0;
144 return l;
149 int str_len(const char *p)
151 if(ioncore_g.enc_sb)
152 return strlen(p);
154 if(ioncore_g.enc_utf8){
155 int len=0;
157 while(*p){
158 if(((*p)&0xC0)!=0x80)
159 len++;
160 p++;
162 return len;
165 assert(ioncore_g.use_mb);
167 mbstate_t ps;
168 int len=0, bytes=strlen(p), l;
169 memset(&ps, 0, sizeof(ps));
171 while(bytes>0){
172 l=mbrlen(p, bytes, &ps);
173 if(l<=0){
174 warn(TR("Invalid multibyte string."));
175 break;
177 len++;
178 bytes-=l;
179 p += l;
181 return len;
186 /*}}}*/
189 /*{{{ Title shortening */
192 INTRSTRUCT(SR);
194 DECLSTRUCT(SR){
195 regex_t re;
196 char *rule;
197 SR *next, *prev;
198 bool always;
202 static SR *shortenrules=NULL;
205 /*EXTL_DOC
206 * Add a rule describing how too long titles should be shortened to fit in tabs.
207 * The regular expression \var{rx} (POSIX, not Lua!) is used to match titles
208 * and when \var{rx} matches, \var{rule} is attempted to use as a replacement
209 * for title. If \var{always} is set, the rule is used even if no shortening
210 * is necessary.
212 * Similarly to sed's 's' command, \var{rule} may contain characters that are
213 * inserted in the resulting string and specials as follows:
215 * \begin{tabularx}{\linewidth}{lX}
216 * \tabhead{Special & Description}
217 * \$0 & Place the original string here. \\
218 * \$1 to \$9 & Insert n:th capture here (as usual,captures are surrounded
219 * by parentheses in the regex). \\
220 * \$| & Alternative shortening separator. The shortening described
221 * before the first this kind of separator is tried first and
222 * if it fails to make the string short enough, the next is
223 * tried, and so on. \\
224 * \$< & Remove characters on the left of this marker to shorten the
225 * string. \\
226 * \$> & Remove characters on the right of this marker to shorten the
227 * string. Only the first \$< or \$> within an alternative
228 * shortening is used. \\
229 * \end{tabularx}
231 EXTL_EXPORT
232 bool ioncore_defshortening(const char *rx, const char *rule, bool always)
234 SR *si;
235 int ret;
236 #define ERRBUF_SIZE 256
237 static char errbuf[ERRBUF_SIZE];
239 if(rx==NULL || rule==NULL)
240 return FALSE;
242 si=ALLOC(SR);
244 if(si==NULL)
245 return FALSE;
247 ret=regcomp(&(si->re), rx, REG_EXTENDED);
249 if(ret!=0){
250 errbuf[0]='\0';
251 regerror(ret, &(si->re), errbuf, ERRBUF_SIZE);
252 warn(TR("Error compiling regular expression: %s"), errbuf);
253 goto fail2;
256 si->rule=scopy(rule);
257 si->always=always;
259 if(si->rule==NULL)
260 goto fail;
262 LINK_ITEM(shortenrules, si, next, prev);
264 return TRUE;
266 fail:
267 regfree(&(si->re));
268 fail2:
269 free(si);
270 return FALSE;
274 static char *shorten(GrBrush *brush, const char *str, uint maxw,
275 const char *rule, int nmatch, regmatch_t *pmatch)
277 char *s;
278 int rulelen, slen, i, j, k, ll;
279 int strippt=0;
280 int stripdir=-1;
281 bool more=FALSE;
283 /* Ensure matches are at character boundaries */
284 if(!ioncore_g.enc_sb){
285 int pos=0, len, strl;
286 mbstate_t ps;
287 memset(&ps, 0, sizeof(ps));
289 strl=strlen(str);
291 while(pos<strl){
292 len=mbrtowc(NULL, str+pos, strl-pos, &ps);
293 if(len<0){
294 /* Invalid multibyte string */
295 return scopy("???");
297 if(len==0)
298 break;
299 for(i=0; i<nmatch; i++){
300 if(pmatch[i].rm_so>pos && pmatch[i].rm_so<pos+len)
301 pmatch[i].rm_so=pos+len;
302 if(pmatch[i].rm_eo>pos && pmatch[i].rm_eo<pos+len)
303 pmatch[i].rm_eo=pos;
305 pos+=len;
309 /* Stupid alloc rule that wastes space */
310 rulelen=strlen(rule);
311 slen=rulelen;
313 for(i=0; i<nmatch; i++){
314 if(pmatch[i].rm_so==-1)
315 continue;
316 slen+=(pmatch[i].rm_eo-pmatch[i].rm_so);
319 s=ALLOC_N(char, slen);
321 if(s==NULL)
322 return NULL;
325 more=FALSE;
326 j=0;
327 strippt=0;
328 stripdir=-1;
330 for(i=0; i<rulelen; i++){
331 if(rule[i]!='$'){
332 s[j++]=rule[i];
333 continue;
336 i++;
338 if(rule[i]=='|'){
339 rule=rule+i+1;
340 rulelen=rulelen-i-1;
341 more=TRUE;
342 break;
345 if(rule[i]=='$'){
346 s[j++]='$';
347 continue;
350 if(rule[i]=='<'){
351 strippt=j;
352 stripdir=-1;
353 continue;
356 if(rule[i]=='>'){
357 strippt=j;
358 stripdir=1;
359 continue;
362 if(rule[i]>='0' && rule[i]<='9'){
363 k=(int)(rule[i]-'0');
364 if(k>=nmatch)
365 continue;
366 if(pmatch[k].rm_so==-1)
367 continue;
368 ll=(pmatch[k].rm_eo-pmatch[k].rm_so);
369 strncpy(s+j, str+pmatch[k].rm_so, ll);
370 j+=ll;
374 slen=j;
375 s[slen]='\0';
377 i=strippt;
378 j=strippt;
380 /* shorten */
382 uint bl=grbrush_get_text_width(brush, s, i);
383 uint el=grbrush_get_text_width(brush, s+j, slen-j);
385 while(1){
386 /* el+bl may not be the actual length, but close enough. */
387 if(el+bl<=maxw){
388 memmove(s+i, s+j, slen-j+1);
389 return s;
392 if(stripdir==-1){
393 ll=str_prevoff(s, i);
394 if(ll==0)
395 break;
396 i-=ll;
397 bl=grbrush_get_text_width(brush, s, i);
398 }else{
399 ll=str_nextoff(s, j);
400 if(ll==0)
401 break;
402 j+=ll;
403 el=grbrush_get_text_width(brush, s+j, slen-j);
407 }while(more);
409 free(s);
411 return NULL;
415 char *grbrush_make_label(GrBrush *brush, const char *str, uint maxw)
417 size_t nmatch=10;
418 regmatch_t pmatch[10];
419 SR *rule;
420 int ret;
421 char *retstr;
422 bool fits=FALSE;
424 if(grbrush_get_text_width(brush, str, strlen(str))<=maxw)
425 fits=TRUE;
427 /*return scopy(str);*/
429 for(rule=shortenrules; rule!=NULL; rule=rule->next){
430 if(fits && !rule->always)
431 continue;
432 ret=regexec(&(rule->re), str, nmatch, pmatch, 0);
433 if(ret!=0)
434 continue;
435 retstr=shorten(brush, str, maxw, rule->rule, nmatch, pmatch);
436 goto rettest;
439 if(fits){
440 retstr=scopy(str);
441 }else{
442 pmatch[0].rm_so=0;
443 pmatch[0].rm_eo=strlen(str)-1;
444 retstr=shorten(brush, str, maxw, "$1$<...", 1, pmatch);
447 rettest:
448 if(retstr!=NULL)
449 return retstr;
450 return scopy("");
454 /*}}}*/