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 */
192 static char *scatn3(const char *p1
, int l1
,
193 const char *p2
, int l2
,
194 const char *p3
, int l3
)
196 char *p
=ALLOC_N(char, l1
+l2
+l3
+1);
216 static SR
*shortenrules
=NULL
;
220 * Add a rule describing how too long titles should be shortened to fit in tabs.
221 * The regular expression \var{rx} (POSIX, not Lua!) is used to match titles
222 * and when \var{rx} matches, \var{rule} is attempted to use as a replacement
223 * for title. If \var{always} is set, the rule is used even if no shortening
226 * Similarly to sed's 's' command, \var{rule} may contain characters that are
227 * inserted in the resulting string and specials as follows:
229 * \begin{tabularx}{\linewidth}{lX}
230 * \tabhead{Special & Description}
231 * \$0 & Place the original string here. \\
232 * \$1 to \$9 & Insert n:th capture here (as usual,captures are surrounded
233 * by parentheses in the regex). \\
234 * \$| & Alternative shortening separator. The shortening described
235 * before the first this kind of separator is tried first and
236 * if it fails to make the string short enough, the next is
237 * tried, and so on. \\
238 * \$< & Remove characters on the left of this marker to shorten the
240 * \$> & Remove characters on the right of this marker to shorten the
241 * string. Only the first \$< or \$> within an alternative
242 * shortening is used. \\
246 bool ioncore_defshortening(const char *rx
, const char *rule
, bool always
)
250 #define ERRBUF_SIZE 256
251 static char errbuf
[ERRBUF_SIZE
];
253 if(rx
==NULL
|| rule
==NULL
)
261 ret
=regcomp(&(si
->re
), rx
, REG_EXTENDED
);
265 regerror(ret
, &(si
->re
), errbuf
, ERRBUF_SIZE
);
266 warn(TR("Error compiling regular expression: %s"), errbuf
);
270 si
->rule
=scopy(rule
);
276 LINK_ITEM(shortenrules
, si
, next
, prev
);
288 static char *shorten(GrBrush
*brush
, const char *str
, uint maxw
,
289 const char *rule
, int nmatch
, regmatch_t
*pmatch
)
292 int rulelen
, slen
, i
, j
, k
, ll
;
297 /* Ensure matches are at character boundaries */
298 if(!ioncore_g
.enc_sb
){
299 int pos
=0, len
, strl
;
301 memset(&ps
, 0, sizeof(ps
));
306 len
=mbrtowc(NULL
, str
+pos
, strl
-pos
, &ps
);
308 /* Invalid multibyte string */
313 for(i
=0; i
<nmatch
; i
++){
314 if(pmatch
[i
].rm_so
>pos
&& pmatch
[i
].rm_so
<pos
+len
)
315 pmatch
[i
].rm_so
=pos
+len
;
316 if(pmatch
[i
].rm_eo
>pos
&& pmatch
[i
].rm_eo
<pos
+len
)
323 /* Stupid alloc rule that wastes space */
324 rulelen
=strlen(rule
);
327 for(i
=0; i
<nmatch
; i
++){
328 if(pmatch
[i
].rm_so
==-1)
330 slen
+=(pmatch
[i
].rm_eo
-pmatch
[i
].rm_so
);
333 s
=ALLOC_N(char, slen
);
344 for(i
=0; i
<rulelen
; i
++){
376 if(rule
[i
]>='0' && rule
[i
]<='9'){
377 k
=(int)(rule
[i
]-'0');
380 if(pmatch
[k
].rm_so
==-1)
382 ll
=(pmatch
[k
].rm_eo
-pmatch
[k
].rm_so
);
383 strncpy(s
+j
, str
+pmatch
[k
].rm_so
, ll
);
396 uint bl
=grbrush_get_text_width(brush
, s
, i
);
397 uint el
=grbrush_get_text_width(brush
, s
+j
, slen
-j
);
400 /* el+bl may not be the actual length, but close enough. */
402 memmove(s
+i
, s
+j
, slen
-j
+1);
407 ll
=str_prevoff(s
, i
);
411 bl
=grbrush_get_text_width(brush
, s
, i
);
413 ll
=str_nextoff(s
, j
);
417 el
=grbrush_get_text_width(brush
, s
+j
, slen
-j
);
429 char *grbrush_make_label(GrBrush
*brush
, const char *str
, uint maxw
)
432 regmatch_t pmatch
[10];
438 if(grbrush_get_text_width(brush
, str
, strlen(str
))<=maxw
)
441 /*return scopy(str);*/
443 for(rule
=shortenrules
; rule
!=NULL
; rule
=rule
->next
){
444 if(fits
&& !rule
->always
)
446 ret
=regexec(&(rule
->re
), str
, nmatch
, pmatch
, 0);
449 retstr
=shorten(brush
, str
, maxw
, rule
->rule
, nmatch
, pmatch
);
457 pmatch
[0].rm_eo
=strlen(str
)-1;
458 retstr
=shorten(brush
, str
, maxw
, "$1$<...", 1, pmatch
);