2 * ion/ioncore/strings.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
12 #include <libtu/output.h>
13 #include <libtu/misc.h>
21 /*{{{ String scanning */
24 wchar_t str_wchar_at(char *p, int max)
27 if(mbtowc(&wc, p, max)>0)
33 char *str_stripws(char *p)
41 memset(&ps, 0, sizeof(ps));
44 ret=mbrtowc(&wc, p+pos, n-pos, &ps);
53 memmove(p, p+pos, n-pos+1);
61 ret=mbrtowc(&wc, p+pos, n-pos, &ps);
80 int str_prevoff(const char *p, int pos)
83 return (pos>0 ? 1 : 0);
85 if(ioncore_g.enc_utf8){
90 if((p[pos]&0xC0)!=0x80)
96 assert(ioncore_g.use_mb);
102 memset(&ps, 0, sizeof(ps));
105 l=mbrlen(p+prev, pos-prev, &ps);
107 warn(TR("Invalid multibyte string."));
119 int str_nextoff(const char *p, int opos)
122 return (*(p+opos)=='\0' ? 0 : 1);
124 if(ioncore_g.enc_utf8){
129 if((p[pos]&0xC0)!=0x80)
135 assert(ioncore_g.use_mb);
139 memset(&ps, 0, sizeof(ps));
141 l=mbrlen(p+opos, strlen(p+opos), &ps);
143 warn(TR("Invalid multibyte string."));
151 int str_len(const char *p)
156 if(ioncore_g.enc_utf8){
160 if(((*p)&0xC0)!=0x80)
167 assert(ioncore_g.use_mb);
170 int len=0, bytes=strlen(p), l;
171 memset(&ps, 0, sizeof(ps));
174 l=mbrlen(p, bytes, &ps);
176 warn(TR("Invalid multibyte string."));
190 /*{{{ Title shortening */
193 static char *scatn3(const char *p1, int l1,
194 const char *p2, int l2,
195 const char *p3, int l3)
197 char *p=ALLOC_N(char, l1+l2+l3+1);
217 static SR *shortenrules=NULL;
221 * Add a rule describing how too long titles should be shortened to fit in tabs.
222 * The regular expression \var{rx} (POSIX, not Lua!) is used to match titles
223 * and when \var{rx} matches, \var{rule} is attempted to use as a replacement
224 * for title. If \var{always} is set, the rule is used even if no shortening
227 * Similarly to sed's 's' command, \var{rule} may contain characters that are
228 * inserted in the resulting string and specials as follows:
230 * \begin{tabularx}{\linewidth}{lX}
231 * \tabhead{Special & Description}
232 * \$0 & Place the original string here. \\
233 * \$1 to \$9 & Insert n:th capture here (as usual,captures are surrounded
234 * by parentheses in the regex). \\
235 * \$| & Alternative shortening separator. The shortening described
236 * before the first this kind of separator is tried first and
237 * if it fails to make the string short enough, the next is
238 * tried, and so on. \\
239 * \$< & Remove characters on the left of this marker to shorten the
241 * \$> & Remove characters on the right of this marker to shorten the
242 * string. Only the first \$< or \$> within an alternative
243 * shortening is used. \\
247 bool ioncore_defshortening(const char *rx, const char *rule, bool always)
251 #define ERRBUF_SIZE 256
252 static char errbuf[ERRBUF_SIZE];
254 if(rx==NULL || rule==NULL)
262 ret=regcomp(&(si->re), rx, REG_EXTENDED);
266 regerror(ret, &(si->re), errbuf, ERRBUF_SIZE);
267 warn(TR("Error compiling regular expression: %s"), errbuf);
271 si->rule=scopy(rule);
277 LINK_ITEM(shortenrules, si, next, prev);
289 static char *shorten(GrBrush *brush, const char *str, uint maxw,
290 const char *rule, int nmatch, regmatch_t *pmatch)
293 int rulelen, slen, i, j, k, ll;
298 /* Ensure matches are at character boundaries */
299 if(!ioncore_g.enc_sb){
300 int pos=0, len, strl;
302 memset(&ps, 0, sizeof(ps));
307 len=mbrtowc(NULL, str+pos, strl-pos, &ps);
309 /* Invalid multibyte string */
314 for(i=0; i<nmatch; i++){
315 if(pmatch[i].rm_so>pos && pmatch[i].rm_so<pos+len)
316 pmatch[i].rm_so=pos+len;
317 if(pmatch[i].rm_eo>pos && pmatch[i].rm_eo<pos+len)
324 /* Stupid alloc rule that wastes space */
325 rulelen=strlen(rule);
328 for(i=0; i<nmatch; i++){
329 if(pmatch[i].rm_so==-1)
331 slen+=(pmatch[i].rm_eo-pmatch[i].rm_so);
334 s=ALLOC_N(char, slen);
345 for(i=0; i<rulelen; i++){
377 if(rule[i]>='0' && rule[i]<='9'){
378 k=(int)(rule[i]-'0');
381 if(pmatch[k].rm_so==-1)
383 ll=(pmatch[k].rm_eo-pmatch[k].rm_so);
384 strncpy(s+j, str+pmatch[k].rm_so, ll);
397 uint bl=grbrush_get_text_width(brush, s, i);
398 uint el=grbrush_get_text_width(brush, s+j, slen-j);
401 /* el+bl may not be the actual length, but close enough. */
403 memmove(s+i, s+j, slen-j+1);
408 ll=str_prevoff(s, i);
412 bl=grbrush_get_text_width(brush, s, i);
414 ll=str_nextoff(s, j);
418 el=grbrush_get_text_width(brush, s+j, slen-j);
430 char *grbrush_make_label(GrBrush *brush, const char *str, uint maxw)
433 regmatch_t pmatch[10];
439 if(grbrush_get_text_width(brush, str, strlen(str))<=maxw)
442 /*return scopy(str);*/
444 for(rule=shortenrules; rule!=NULL; rule=rule->next){
445 if(fits && !rule->always)
447 ret=regexec(&(rule->re), str, nmatch, pmatch, 0);
450 retstr=shorten(brush, str, maxw, rule->rule, nmatch, pmatch);
458 pmatch[0].rm_eo=strlen(str)-1;
459 retstr=shorten(brush, str, maxw, "$1$<...", 1, pmatch);