2 * ion/ioncore/strings.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
9 #include <libtu/output.h>
10 #include <libtu/misc.h>
19 /*{{{ String scanning */
22 wchar_t str_wchar_at(char *p
, int max
)
25 if(mbtowc(&wc
, p
, max
)>0)
31 char *str_stripws(char *p
)
39 memset(&ps
, 0, sizeof(ps
));
42 ret
=mbrtowc(&wc
, p
+pos
, n
-pos
, &ps
);
51 memmove(p
, p
+pos
, n
-pos
+1);
59 ret
=mbrtowc(&wc
, p
+pos
, n
-pos
, &ps
);
78 int str_prevoff(const char *p
, int pos
)
81 return (pos
>0 ? 1 : 0);
83 if(ioncore_g
.enc_utf8
){
88 if((p
[pos
]&0xC0)!=0x80)
94 assert(ioncore_g
.use_mb
);
100 memset(&ps
, 0, sizeof(ps
));
103 l
=mbrlen(p
+prev
, pos
-prev
, &ps
);
105 warn(TR("Invalid multibyte string."));
117 int str_nextoff(const char *p
, int opos
)
120 return (*(p
+opos
)=='\0' ? 0 : 1);
122 if(ioncore_g
.enc_utf8
){
127 if((p
[pos
]&0xC0)!=0x80)
133 assert(ioncore_g
.use_mb
);
137 memset(&ps
, 0, sizeof(ps
));
139 l
=mbrlen(p
+opos
, strlen(p
+opos
), &ps
);
141 warn(TR("Invalid multibyte string."));
149 int str_len(const char *p
)
154 if(ioncore_g
.enc_utf8
){
158 if(((*p
)&0xC0)!=0x80)
165 assert(ioncore_g
.use_mb
);
168 int len
=0, bytes
=strlen(p
), l
;
169 memset(&ps
, 0, sizeof(ps
));
172 l
=mbrlen(p
, bytes
, &ps
);
174 warn(TR("Invalid multibyte string."));
189 /*{{{ Title shortening */
202 static SR
*shortenrules
=NULL
;
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
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
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. \\
232 bool ioncore_defshortening(const char *rx
, const char *rule
, bool always
)
236 #define ERRBUF_SIZE 256
237 static char errbuf
[ERRBUF_SIZE
];
239 if(rx
==NULL
|| rule
==NULL
)
247 ret
=regcomp(&(si
->re
), rx
, REG_EXTENDED
);
251 regerror(ret
, &(si
->re
), errbuf
, ERRBUF_SIZE
);
252 warn(TR("Error compiling regular expression: %s"), errbuf
);
256 si
->rule
=scopy(rule
);
262 LINK_ITEM(shortenrules
, si
, next
, prev
);
274 static char *shorten(GrBrush
*brush
, const char *str
, uint maxw
,
275 const char *rule
, int nmatch
, regmatch_t
*pmatch
)
278 int rulelen
, slen
, i
, j
, k
, ll
;
283 /* Ensure matches are at character boundaries */
284 if(!ioncore_g
.enc_sb
){
285 int pos
=0, len
, strl
;
287 memset(&ps
, 0, sizeof(ps
));
292 len
=mbrtowc(NULL
, str
+pos
, strl
-pos
, &ps
);
294 /* Invalid multibyte string */
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
)
309 /* Stupid alloc rule that wastes space */
310 rulelen
=strlen(rule
);
313 for(i
=0; i
<nmatch
; i
++){
314 if(pmatch
[i
].rm_so
==-1)
316 slen
+=(pmatch
[i
].rm_eo
-pmatch
[i
].rm_so
);
319 s
=ALLOC_N(char, slen
);
330 for(i
=0; i
<rulelen
; i
++){
362 if(rule
[i
]>='0' && rule
[i
]<='9'){
363 k
=(int)(rule
[i
]-'0');
366 if(pmatch
[k
].rm_so
==-1)
368 ll
=(pmatch
[k
].rm_eo
-pmatch
[k
].rm_so
);
369 strncpy(s
+j
, str
+pmatch
[k
].rm_so
, ll
);
382 uint bl
=grbrush_get_text_width(brush
, s
, i
);
383 uint el
=grbrush_get_text_width(brush
, s
+j
, slen
-j
);
386 /* el+bl may not be the actual length, but close enough. */
388 memmove(s
+i
, s
+j
, slen
-j
+1);
393 ll
=str_prevoff(s
, i
);
397 bl
=grbrush_get_text_width(brush
, s
, i
);
399 ll
=str_nextoff(s
, j
);
403 el
=grbrush_get_text_width(brush
, s
+j
, slen
-j
);
415 char *grbrush_make_label(GrBrush
*brush
, const char *str
, uint maxw
)
418 regmatch_t pmatch
[10];
424 if(grbrush_get_text_width(brush
, str
, strlen(str
))<=maxw
)
427 /*return scopy(str);*/
429 for(rule
=shortenrules
; rule
!=NULL
; rule
=rule
->next
){
430 if(fits
&& !rule
->always
)
432 ret
=regexec(&(rule
->re
), str
, nmatch
, pmatch
, 0);
435 retstr
=shorten(brush
, str
, maxw
, rule
->rule
, nmatch
, pmatch
);
443 pmatch
[0].rm_eo
=strlen(str
)-1;
444 retstr
=shorten(brush
, str
, maxw
, "$1$<...", 1, pmatch
);