2 * ion/ioncore/strings.c
4 * Copyright (c) Tuomo Valkonen 1999-2008.
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."));
187 /*{{{ Title shortening */
190 static char *scatn3(const char *p1, int l1,
191 const char *p2, int l2,
192 const char *p3, int l3)
194 char *p=ALLOC_N(char, l1+l2+l3+1);
214 static SR *shortenrules=NULL;
218 * Add a rule describing how too long titles should be shortened to fit in tabs.
219 * The regular expression \var{rx} (POSIX, not Lua!) is used to match titles
220 * and when \var{rx} matches, \var{rule} is attempted to use as a replacement
221 * for title. If \var{always} is set, the rule is used even if no shortening
224 * Similarly to sed's 's' command, \var{rule} may contain characters that are
225 * inserted in the resulting string and specials as follows:
227 * \begin{tabularx}{\linewidth}{lX}
228 * \tabhead{Special & Description}
229 * \$0 & Place the original string here. \\
230 * \$1 to \$9 & Insert n:th capture here (as usual,captures are surrounded
231 * by parentheses in the regex). \\
232 * \$| & Alternative shortening separator. The shortening described
233 * before the first this kind of separator is tried first and
234 * if it fails to make the string short enough, the next is
235 * tried, and so on. \\
236 * \$< & Remove characters on the left of this marker to shorten the
238 * \$> & Remove characters on the right of this marker to shorten the
239 * string. Only the first \$< or \$> within an alternative
240 * shortening is used. \\
244 bool ioncore_defshortening(const char *rx, const char *rule, bool always)
248 #define ERRBUF_SIZE 256
249 static char errbuf[ERRBUF_SIZE];
251 if(rx==NULL || rule==NULL)
259 ret=regcomp(&(si->re), rx, REG_EXTENDED);
263 regerror(ret, &(si->re), errbuf, ERRBUF_SIZE);
264 warn(TR("Error compiling regular expression: %s"), errbuf);
268 si->rule=scopy(rule);
274 LINK_ITEM(shortenrules, si, next, prev);
286 static char *shorten(GrBrush *brush, const char *str, uint maxw,
287 const char *rule, int nmatch, regmatch_t *pmatch)
290 int rulelen, slen, i, j, k, ll;
295 /* Ensure matches are at character boundaries */
296 if(!ioncore_g.enc_sb){
297 int pos=0, len, strl;
299 memset(&ps, 0, sizeof(ps));
304 len=mbrtowc(NULL, str+pos, strl-pos, &ps);
306 /* Invalid multibyte string */
311 for(i=0; i<nmatch; i++){
312 if(pmatch[i].rm_so>pos && pmatch[i].rm_so<pos+len)
313 pmatch[i].rm_so=pos+len;
314 if(pmatch[i].rm_eo>pos && pmatch[i].rm_eo<pos+len)
321 /* Stupid alloc rule that wastes space */
322 rulelen=strlen(rule);
325 for(i=0; i<nmatch; i++){
326 if(pmatch[i].rm_so==-1)
328 slen+=(pmatch[i].rm_eo-pmatch[i].rm_so);
331 s=ALLOC_N(char, slen);
342 for(i=0; i<rulelen; i++){
374 if(rule[i]>='0' && rule[i]<='9'){
375 k=(int)(rule[i]-'0');
378 if(pmatch[k].rm_so==-1)
380 ll=(pmatch[k].rm_eo-pmatch[k].rm_so);
381 strncpy(s+j, str+pmatch[k].rm_so, ll);
394 uint bl=grbrush_get_text_width(brush, s, i);
395 uint el=grbrush_get_text_width(brush, s+j, slen-j);
398 /* el+bl may not be the actual length, but close enough. */
400 memmove(s+i, s+j, slen-j+1);
405 ll=str_prevoff(s, i);
409 bl=grbrush_get_text_width(brush, s, i);
411 ll=str_nextoff(s, j);
415 el=grbrush_get_text_width(brush, s+j, slen-j);
427 char *grbrush_make_label(GrBrush *brush, const char *str, uint maxw)
430 regmatch_t pmatch[10];
436 if(grbrush_get_text_width(brush, str, strlen(str))<=maxw)
439 /*return scopy(str);*/
441 for(rule=shortenrules; rule!=NULL; rule=rule->next){
442 if(fits && !rule->always)
444 ret=regexec(&(rule->re), str, nmatch, pmatch, 0);
447 retstr=shorten(brush, str, maxw, rule->rule, nmatch, pmatch);
455 pmatch[0].rm_eo=strlen(str)-1;
456 retstr=shorten(brush, str, maxw, "$1$<...", 1, pmatch);