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>
18 /*{{{ String scanning */
21 wchar_t str_wchar_at(char *p
, int max
)
24 if(mbtowc(&wc
, p
, max
)>0)
30 char *str_stripws(char *p
)
38 memset(&ps
, 0, sizeof(ps
));
41 ret
=mbrtowc(&wc
, p
+pos
, n
-pos
, &ps
);
50 memmove(p
, p
+pos
, n
-pos
+1);
58 ret
=mbrtowc(&wc
, p
+pos
, n
-pos
, &ps
);
77 int str_prevoff(const char *p
, int pos
)
80 return (pos
>0 ? 1 : 0);
82 if(ioncore_g
.enc_utf8
){
87 if((p
[pos
]&0xC0)!=0x80)
93 assert(ioncore_g
.use_mb
);
99 memset(&ps
, 0, sizeof(ps
));
102 l
=mbrlen(p
+prev
, pos
-prev
, &ps
);
104 warn(TR("Invalid multibyte string."));
116 int str_nextoff(const char *p
, int opos
)
119 return (*(p
+opos
)=='\0' ? 0 : 1);
121 if(ioncore_g
.enc_utf8
){
126 if((p
[pos
]&0xC0)!=0x80)
132 assert(ioncore_g
.use_mb
);
136 memset(&ps
, 0, sizeof(ps
));
138 l
=mbrlen(p
+opos
, strlen(p
+opos
), &ps
);
140 warn(TR("Invalid multibyte string."));
148 int str_len(const char *p
)
153 if(ioncore_g
.enc_utf8
){
157 if(((*p
)&0xC0)!=0x80)
164 assert(ioncore_g
.use_mb
);
167 int len
=0, bytes
=strlen(p
), l
;
168 memset(&ps
, 0, sizeof(ps
));
171 l
=mbrlen(p
, bytes
, &ps
);
173 warn(TR("Invalid multibyte string."));
188 /*{{{ Title shortening */
191 static char *scatn3(const char *p1
, int l1
,
192 const char *p2
, int l2
,
193 const char *p3
, int l3
)
195 char *p
=ALLOC_N(char, l1
+l2
+l3
+1);
215 static SR
*shortenrules
=NULL
;
219 * Add a rule describing how too long titles should be shortened to fit in tabs.
220 * The regular expression \var{rx} (POSIX, not Lua!) is used to match titles
221 * and when \var{rx} matches, \var{rule} is attempted to use as a replacement
222 * for title. If \var{always} is set, the rule is used even if no shortening
225 * Similarly to sed's 's' command, \var{rule} may contain characters that are
226 * inserted in the resulting string and specials as follows:
228 * \begin{tabularx}{\linewidth}{lX}
229 * \tabhead{Special & Description}
230 * \$0 & Place the original string here. \\
231 * \$1 to \$9 & Insert n:th capture here (as usual,captures are surrounded
232 * by parentheses in the regex). \\
233 * \$| & Alternative shortening separator. The shortening described
234 * before the first this kind of separator is tried first and
235 * if it fails to make the string short enough, the next is
236 * tried, and so on. \\
237 * \$< & Remove characters on the left of this marker to shorten the
239 * \$> & Remove characters on the right of this marker to shorten the
240 * string. Only the first \$< or \$> within an alternative
241 * shortening is used. \\
245 bool ioncore_defshortening(const char *rx
, const char *rule
, bool always
)
249 #define ERRBUF_SIZE 256
250 static char errbuf
[ERRBUF_SIZE
];
252 if(rx
==NULL
|| rule
==NULL
)
260 ret
=regcomp(&(si
->re
), rx
, REG_EXTENDED
);
264 regerror(ret
, &(si
->re
), errbuf
, ERRBUF_SIZE
);
265 warn(TR("Error compiling regular expression: %s"), errbuf
);
269 si
->rule
=scopy(rule
);
275 LINK_ITEM(shortenrules
, si
, next
, prev
);
287 static char *shorten(GrBrush
*brush
, const char *str
, uint maxw
,
288 const char *rule
, int nmatch
, regmatch_t
*pmatch
)
291 int rulelen
, slen
, i
, j
, k
, ll
;
296 /* Ensure matches are at character boundaries */
297 if(!ioncore_g
.enc_sb
){
298 int pos
=0, len
, strl
;
300 memset(&ps
, 0, sizeof(ps
));
305 len
=mbrtowc(NULL
, str
+pos
, strl
-pos
, &ps
);
307 /* Invalid multibyte string */
312 for(i
=0; i
<nmatch
; i
++){
313 if(pmatch
[i
].rm_so
>pos
&& pmatch
[i
].rm_so
<pos
+len
)
314 pmatch
[i
].rm_so
=pos
+len
;
315 if(pmatch
[i
].rm_eo
>pos
&& pmatch
[i
].rm_eo
<pos
+len
)
322 /* Stupid alloc rule that wastes space */
323 rulelen
=strlen(rule
);
326 for(i
=0; i
<nmatch
; i
++){
327 if(pmatch
[i
].rm_so
==-1)
329 slen
+=(pmatch
[i
].rm_eo
-pmatch
[i
].rm_so
);
332 s
=ALLOC_N(char, slen
);
343 for(i
=0; i
<rulelen
; i
++){
375 if(rule
[i
]>='0' && rule
[i
]<='9'){
376 k
=(int)(rule
[i
]-'0');
379 if(pmatch
[k
].rm_so
==-1)
381 ll
=(pmatch
[k
].rm_eo
-pmatch
[k
].rm_so
);
382 strncpy(s
+j
, str
+pmatch
[k
].rm_so
, ll
);
395 uint bl
=grbrush_get_text_width(brush
, s
, i
);
396 uint el
=grbrush_get_text_width(brush
, s
+j
, slen
-j
);
399 /* el+bl may not be the actual length, but close enough. */
401 memmove(s
+i
, s
+j
, slen
-j
+1);
406 ll
=str_prevoff(s
, i
);
410 bl
=grbrush_get_text_width(brush
, s
, i
);
412 ll
=str_nextoff(s
, j
);
416 el
=grbrush_get_text_width(brush
, s
+j
, slen
-j
);
428 char *grbrush_make_label(GrBrush
*brush
, const char *str
, uint maxw
)
431 regmatch_t pmatch
[10];
437 if(grbrush_get_text_width(brush
, str
, strlen(str
))<=maxw
)
440 /*return scopy(str);*/
442 for(rule
=shortenrules
; rule
!=NULL
; rule
=rule
->next
){
443 if(fits
&& !rule
->always
)
445 ret
=regexec(&(rule
->re
), str
, nmatch
, pmatch
, 0);
448 retstr
=shorten(brush
, str
, maxw
, rule
->rule
, nmatch
, pmatch
);
456 pmatch
[0].rm_eo
=strlen(str
)-1;
457 retstr
=shorten(brush
, str
, maxw
, "$1$<...", 1, pmatch
);