source: fmtext.c @ b73bcbb

release-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since b73bcbb was 41c9a96, checked in by Anders Kaseorg <andersk@mit.edu>, 15 years ago
Reimplement search in terms of owl_regex. The current implementation of stristr has problems and there isn’t a good replacement available. This was its only caller, so we can get rid of it now. Also, this will make it possible to search with arbitrary regexes if someone feels like coming up with a syntax. Signed-off-by: Anders Kaseorg <andersk@mit.edu>
  • Property mode set to 100644
File size: 25.5 KB
Line 
1#include "owl.h"
2#include <stdlib.h>
3#include <string.h>
4
5/* initialize an fmtext with no data */
6void owl_fmtext_init_null(owl_fmtext *f)
7{
8  f->textlen = 0;
9  f->bufflen = 5;
10  f->textbuff = owl_malloc(5);
11  f->textbuff[0] = 0;
12  f->default_attrs = OWL_FMTEXT_ATTR_NONE;
13  f->default_fgcolor = OWL_COLOR_DEFAULT;
14  f->default_bgcolor = OWL_COLOR_DEFAULT;
15}
16
17/* Clear the data from an fmtext, but don't deallocate memory. This
18   fmtext can then be appended to again. */
19void owl_fmtext_clear(owl_fmtext *f)
20{
21  f->textlen = 0;
22  f->textbuff[0] = 0;
23  f->default_attrs = OWL_FMTEXT_ATTR_NONE;
24  f->default_fgcolor = OWL_COLOR_DEFAULT;
25  f->default_bgcolor = OWL_COLOR_DEFAULT;
26}
27
28void _owl_fmtext_realloc(owl_fmtext *f, int newlen) /*noproto*/
29{
30    if(newlen + 1 > f->bufflen) {
31      f->textbuff = owl_realloc(f->textbuff, newlen + 1);
32      f->bufflen = newlen+1;
33  }
34}
35
36int owl_fmtext_is_format_char(gunichar c)
37{
38  if ((c & ~OWL_FMTEXT_UC_ATTR_MASK) == OWL_FMTEXT_UC_ATTR) return 1;
39  if ((c & ~(OWL_FMTEXT_UC_ALLCOLOR_MASK)) == OWL_FMTEXT_UC_COLOR_BASE) return 1;
40  return 0;
41}
42/* append text to the end of 'f' with attribute 'attr' and color
43 * 'color'
44 */
45void owl_fmtext_append_attr(owl_fmtext *f, char *text, char attr, short fgcolor, short bgcolor)
46{
47  char attrbuff[6];
48  int newlen, a = 0, fg = 0, bg = 0;
49 
50  if (attr != OWL_FMTEXT_ATTR_NONE) a=1;
51  if (fgcolor != OWL_COLOR_DEFAULT) fg=1;
52  if (bgcolor != OWL_COLOR_DEFAULT) bg=1;
53
54  /* Plane-16 characters in UTF-8 are 4 bytes long. */
55  newlen = strlen(f->textbuff) + strlen(text) + (8 * (a + fg + bg));
56  _owl_fmtext_realloc(f, newlen);
57
58  /* Set attributes */
59  if (a) {
60    memset(attrbuff,0,6);
61    g_unichar_to_utf8(OWL_FMTEXT_UC_ATTR | attr, attrbuff);
62    strcat(f->textbuff, attrbuff);     
63  }
64  if (fg) {
65    memset(attrbuff,0,6);
66    g_unichar_to_utf8(OWL_FMTEXT_UC_FGCOLOR | fgcolor, attrbuff);
67    strcat(f->textbuff, attrbuff);     
68  }
69  if (bg) {
70    memset(attrbuff,0,6);
71    g_unichar_to_utf8(OWL_FMTEXT_UC_BGCOLOR | bgcolor, attrbuff);
72    strcat(f->textbuff, attrbuff);     
73  }
74 
75  strcat(f->textbuff, text);
76
77  /* Reset attributes */
78  if (bg) strcat(f->textbuff, OWL_FMTEXT_UTF8_BGDEFAULT);
79  if (fg) strcat(f->textbuff, OWL_FMTEXT_UTF8_FGDEFAULT);
80  if (a)  strcat(f->textbuff, OWL_FMTEXT_UTF8_ATTR_NONE);
81  f->textlen=newlen;
82}
83
84/* Append normal, uncolored text 'text' to 'f' */
85void owl_fmtext_append_normal(owl_fmtext *f, char *text)
86{
87  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
88}
89
90/* Append normal, uncolored text specified by format string to 'f' */
91void owl_fmtext_appendf_normal(owl_fmtext *f, char *fmt, ...)
92{
93  va_list ap;
94  char *buff;
95
96  va_start(ap, fmt);
97  buff = g_strdup_vprintf(fmt, ap);
98  va_end(ap);
99  if (!buff)
100    return;
101  owl_fmtext_append_attr(f, buff, OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
102}
103
104/* Append normal text 'text' to 'f' with color 'color' */
105void owl_fmtext_append_normal_color(owl_fmtext *f, char *text, int fgcolor, int bgcolor)
106{
107  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, fgcolor, bgcolor);
108}
109
110/* Append bold text 'text' to 'f' */
111void owl_fmtext_append_bold(owl_fmtext *f, char *text)
112{
113  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
114}
115
116/* Append reverse video text 'text' to 'f' */
117void owl_fmtext_append_reverse(owl_fmtext *f, char *text)
118{
119  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
120}
121
122/* Append reversed and bold, uncolored text 'text' to 'f' */
123void owl_fmtext_append_reversebold(owl_fmtext *f, char *text)
124{
125  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE | OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
126}
127
128/* Add the attribute 'attr' to the default atts for the text in 'f' */
129void owl_fmtext_addattr(owl_fmtext *f, char attr)
130{
131  /* add the attribute to all text */
132  f->default_attrs |= attr;
133}
134
135/* Set the default foreground color for this fmtext to 'color'.
136 * Only affects text that is colored default.
137 */
138void owl_fmtext_colorize(owl_fmtext *f, int color)
139{
140  f->default_fgcolor = color;
141}
142
143/* Set the default foreground color for this fmtext to 'color'.
144 * Only affects text that is colored default.
145 */
146void owl_fmtext_colorizebg(owl_fmtext *f, int color)
147{
148  f->default_bgcolor = color;
149}
150
151/* Internal function. Parse attrbute character. */
152void _owl_fmtext_update_attributes(gunichar c, char *attr, short *fgcolor, short *bgcolor) /*noproto*/
153{
154  if ((c & OWL_FMTEXT_UC_ATTR) == OWL_FMTEXT_UC_ATTR) {
155    *attr = c & OWL_FMTEXT_UC_ATTR_MASK;
156  }
157  else if ((c & OWL_FMTEXT_UC_COLOR_BASE) == OWL_FMTEXT_UC_COLOR_BASE) {
158    if ((c & OWL_FMTEXT_UC_BGCOLOR) == OWL_FMTEXT_UC_BGCOLOR) {
159      *bgcolor = (c == OWL_FMTEXT_UC_BGDEFAULT
160                  ? OWL_COLOR_DEFAULT
161                  : c & OWL_FMTEXT_UC_COLOR_MASK);
162    }
163    else if ((c & OWL_FMTEXT_UC_FGCOLOR) == OWL_FMTEXT_UC_FGCOLOR) {
164      *fgcolor = (c == OWL_FMTEXT_UC_FGDEFAULT
165                  ? OWL_COLOR_DEFAULT
166                  : c & OWL_FMTEXT_UC_COLOR_MASK);
167    }
168  }
169}
170
171/* Internal function. Scan for attribute characters. */
172void _owl_fmtext_scan_attributes(owl_fmtext *f, int start, char *attr, short *fgcolor, short *bgcolor) /*noproto*/
173{
174  char *p;
175  p = strchr(f->textbuff, OWL_FMTEXT_UC_STARTBYTE_UTF8);
176  while (p && p < f->textbuff + start) {
177    _owl_fmtext_update_attributes(g_utf8_get_char(p), attr, fgcolor, bgcolor);
178    p = strchr(p+1, OWL_FMTEXT_UC_STARTBYTE_UTF8);
179  }
180} 
181
182/* Internal function.  Append text from 'in' between index 'start' and
183 * 'stop', inclusive, to the end of 'f'. This function works with
184 * bytes.
185 */
186void _owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in, int start, int stop) /*noproto*/
187{
188  char attrbuff[6];
189  int newlen, a = 0, fg = 0, bg = 0;
190  char attr = 0;
191  short fgcolor = OWL_COLOR_DEFAULT;
192  short bgcolor = OWL_COLOR_DEFAULT;
193
194  _owl_fmtext_scan_attributes(in, start, &attr, &fgcolor, &bgcolor);
195  if (attr != OWL_FMTEXT_ATTR_NONE) a=1;
196  if (fgcolor != OWL_COLOR_DEFAULT) fg=1;
197  if (bgcolor != OWL_COLOR_DEFAULT) bg=1;
198
199  /* We will reset to defaults after appending the text. We may need
200     to set initial attributes. */
201  newlen=strlen(f->textbuff)+(stop-start+1) + (4 * (a + fg + bg)) + 12;
202  _owl_fmtext_realloc(f, newlen);
203
204  if (a) {
205    memset(attrbuff,0,6);
206    g_unichar_to_utf8(OWL_FMTEXT_UC_ATTR | attr, attrbuff);
207    strcat(f->textbuff, attrbuff);     
208  }
209  if (fg) {
210    memset(attrbuff,0,6);
211    g_unichar_to_utf8(OWL_FMTEXT_UC_FGCOLOR | fgcolor, attrbuff);
212    strcat(f->textbuff, attrbuff);     
213  }
214  if (bg) {
215    memset(attrbuff,0,6);
216    g_unichar_to_utf8(OWL_FMTEXT_UC_BGCOLOR | bgcolor, attrbuff);
217    strcat(f->textbuff, attrbuff);     
218  }
219
220  strncat(f->textbuff, in->textbuff+start, stop-start+1);
221
222  /* Reset attributes */
223  strcat(f->textbuff, OWL_FMTEXT_UTF8_BGDEFAULT);
224  strcat(f->textbuff, OWL_FMTEXT_UTF8_FGDEFAULT);
225  strcat(f->textbuff, OWL_FMTEXT_UTF8_ATTR_NONE);
226
227  f->textbuff[newlen]='\0';
228  f->textlen=newlen;
229}
230
231/* append fmtext 'in' to 'f' */
232void owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in)
233{
234  _owl_fmtext_append_fmtext(f, in, 0, in->textlen);
235
236}
237
238/* Append 'nspaces' number of spaces to the end of 'f' */
239void owl_fmtext_append_spaces(owl_fmtext *f, int nspaces)
240{
241  int i;
242  for (i=0; i<nspaces; i++) {
243    owl_fmtext_append_normal(f, " ");
244  }
245}
246
247/* Return a plain version of the fmtext.  Caller is responsible for
248 * freeing the return
249 */
250char *owl_fmtext_print_plain(owl_fmtext *f)
251{
252  return owl_strip_format_chars(f->textbuff);
253}
254
255void _owl_fmtext_wattrset(WINDOW *w, int attrs) /*noproto*/
256{
257  wattrset(w, A_NORMAL);
258  if (attrs & OWL_FMTEXT_ATTR_BOLD) wattron(w, A_BOLD);
259  if (attrs & OWL_FMTEXT_ATTR_REVERSE) wattron(w, A_REVERSE);
260  if (attrs & OWL_FMTEXT_ATTR_UNDERLINE) wattron(w, A_UNDERLINE);
261}
262
263void _owl_fmtext_update_colorpair(short fg, short bg, short *pair) /*noproto*/
264{
265  if (owl_global_get_hascolors(&g)) {
266    *pair = owl_fmtext_get_colorpair(fg, bg);
267  }
268}
269
270void _owl_fmtext_wcolor_set(WINDOW *w, short pair) /*noproto*/
271{
272  if (owl_global_get_hascolors(&g)) {
273      wcolor_set(w,pair,NULL);
274  }
275}
276
277/* add the formatted text to the curses window 'w'.  The window 'w'
278 * must already be initiatlized with curses
279 */
280void _owl_fmtext_curs_waddstr(owl_fmtext *f, WINDOW *w, int do_search) /*noproto*/
281{
282  /* char *tmpbuff; */
283  /* int position, trans1, trans2, trans3, len, lastsame; */
284  char *s, *p;
285  char attr;
286  short fg, bg, pair;
287 
288  if (w==NULL) {
289    owl_function_debugmsg("Hit a null window in owl_fmtext_curs_waddstr.");
290    return;
291  }
292
293  s = f->textbuff;
294  /* Set default attributes. */
295  attr = f->default_attrs;
296  fg = f->default_fgcolor;
297  bg = f->default_bgcolor;
298  _owl_fmtext_wattrset(w, attr);
299  _owl_fmtext_update_colorpair(fg, bg, &pair);
300  _owl_fmtext_wcolor_set(w, pair);
301
302  /* Find next possible format character. */
303  p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
304  while(p) {
305    if (owl_fmtext_is_format_char(g_utf8_get_char(p))) {
306      /* Deal with all text from last insert to here. */
307      char tmp;
308   
309      tmp = p[0];
310      p[0] = '\0';
311      if (owl_global_is_search_active(&g)) {
312        /* Search is active, so highlight search results. */
313        char tmp2;
314        int start, end;
315        while (owl_regex_compare(owl_global_get_search_re(&g), s, &start, &end) == 0) {
316          /* Found search string, highlight it. */
317
318          tmp2 = s[start];
319          s[start] = '\0';
320          waddstr(w, s);
321          s[start] = tmp2;
322
323          _owl_fmtext_wattrset(w, attr ^ OWL_FMTEXT_ATTR_REVERSE);
324          _owl_fmtext_wcolor_set(w, pair);
325         
326          tmp2 = s[end];
327          s[end] = '\0';
328          waddstr(w, s + start);
329          s[end] = tmp2;
330
331          _owl_fmtext_wattrset(w, attr);
332          _owl_fmtext_wcolor_set(w, pair);
333
334          s += end;
335        }
336      }
337      /* Deal with remaining part of string. */
338      waddstr(w, s);
339      p[0] = tmp;
340
341      /* Deal with new attributes. Initialize to defaults, then
342         process all consecutive formatting characters. */
343      attr = f->default_attrs;
344      fg = f->default_fgcolor;
345      bg = f->default_bgcolor;
346      while (p && owl_fmtext_is_format_char(g_utf8_get_char(p))) {
347        _owl_fmtext_update_attributes(g_utf8_get_char(p), &attr, &fg, &bg);
348        p = g_utf8_next_char(p);
349      }
350      _owl_fmtext_wattrset(w, attr | f->default_attrs);
351      if (fg == OWL_COLOR_DEFAULT) fg = f->default_fgcolor;
352      if (bg == OWL_COLOR_DEFAULT) bg = f->default_bgcolor;
353      _owl_fmtext_update_colorpair(fg, bg, &pair);
354      _owl_fmtext_wcolor_set(w, pair);
355
356      /* Advance to next non-formatting character. */
357      s = p;
358      p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
359    }
360    else {
361      p = strchr(p+1, OWL_FMTEXT_UC_STARTBYTE_UTF8);
362    }
363  }
364  if (s) {
365    waddstr(w, s);
366  }
367}
368
369void owl_fmtext_curs_waddstr(owl_fmtext *f, WINDOW *w)
370{
371  _owl_fmtext_curs_waddstr(f, w, owl_global_is_search_active(&g));
372}
373
374void owl_fmtext_curs_waddstr_without_search(owl_fmtext *f, WINDOW *w)
375{
376  _owl_fmtext_curs_waddstr(f, w, 0);
377}
378
379/* start with line 'aline' (where the first line is 0) and print
380 * 'lines' number of lines into 'out'
381 */
382int owl_fmtext_truncate_lines(owl_fmtext *in, int aline, int lines, owl_fmtext *out)
383{
384  char *ptr1, *ptr2;
385  int i, offset;
386 
387  /* find the starting line */
388  ptr1 = in->textbuff;
389  for (i = 0; i < aline; i++) {
390    ptr1 = strchr(ptr1, '\n');
391    if (!ptr1) return(-1);
392    ptr1++;
393  }
394 
395  /* ptr1 now holds the starting point */
396
397  /* copy the default attributes */
398  out->default_attrs = in->default_attrs;
399  out->default_fgcolor = in->default_fgcolor;
400  out->default_bgcolor = in->default_bgcolor;
401   
402  /* copy in the next 'lines' lines */
403  if (lines < 1) return(-1);
404
405  for (i = 0; i < lines; i++) {
406    offset = ptr1 - in->textbuff;
407    ptr2 = strchr(ptr1, '\n');
408    if (!ptr2) {
409      _owl_fmtext_append_fmtext(out, in, offset, (in->textlen) - 1);
410      return(-1);
411    }
412    _owl_fmtext_append_fmtext(out, in, offset, (ptr2 - ptr1) + offset);
413    ptr1 = ptr2 + 1;
414  }
415  return(0);
416}
417
418/* Truncate the message so that each line begins at column 'acol' and
419 * ends at 'bcol' or sooner.  The first column is number 0.  The new
420 * message is placed in 'out'.  The message is * expected to end in a
421 * new line for now. NOTE: This needs to be modified to deal with
422 * backing up if we find a SPACING COMBINING MARK at the end of a
423 * line. If that happens, we should back up to the last non-mark
424 * character and stop there.
425 */
426void owl_fmtext_truncate_cols(owl_fmtext *in, int acol, int bcol, owl_fmtext *out)
427{
428  char *ptr_s, *ptr_e, *ptr_c, *last;
429  int col, st, padding, chwidth;
430
431  /* copy the default attributes */
432  out->default_attrs = in->default_attrs;
433  out->default_fgcolor = in->default_fgcolor;
434  out->default_bgcolor = in->default_bgcolor;
435
436  last=in->textbuff+in->textlen-1;
437  ptr_s=in->textbuff;
438  while (ptr_s <= last) {
439    ptr_e=strchr(ptr_s, '\n');
440    if (!ptr_e) {
441      /* but this shouldn't happen if we end in a \n */
442      break;
443    }
444   
445    if (ptr_e == ptr_s) {
446      owl_fmtext_append_normal(out, "\n");
447      ++ptr_s;
448      continue;
449    }
450
451    col = 0;
452    st = 0;
453    padding = 0;
454    chwidth = 0;
455    ptr_c = ptr_s;
456    while(ptr_c < ptr_e) {
457      gunichar c = g_utf8_get_char(ptr_c);
458      if (!owl_fmtext_is_format_char(c)) {
459        chwidth = mk_wcwidth(c);
460        if (col + chwidth > bcol) break;
461       
462        if (col >= acol) {
463          if (st == 0) {
464            ptr_s = ptr_c;
465            padding = col - acol;
466            ++st;
467          }
468        }
469        col += chwidth;
470        chwidth = 0;
471      }
472      ptr_c = g_utf8_next_char(ptr_c);
473    }
474    if (st) {
475      /* lead padding */
476      owl_fmtext_append_spaces(out, padding);
477      if (ptr_c == ptr_e) {
478        /* We made it to the newline. */
479        _owl_fmtext_append_fmtext(out, in, ptr_s - in->textbuff, ptr_c - in->textbuff);
480      }
481      else if (chwidth > 1) {
482        /* Last char is wide, truncate. */
483        _owl_fmtext_append_fmtext(out, in, ptr_s - in->textbuff, ptr_c - in->textbuff - 1);
484        owl_fmtext_append_normal(out, "\n");
485      }
486      else {
487        /* Last char fits perfectly, We skip to the next char and back
488         * up a byte to make sure we get it all.
489         */
490        ptr_c = g_utf8_next_char(ptr_c);
491        _owl_fmtext_append_fmtext(out, in, ptr_s - in->textbuff, ptr_c - in->textbuff - 1);
492      }
493    }
494    else {
495      owl_fmtext_append_normal(out, "\n");
496    }
497    ptr_s = g_utf8_next_char(ptr_e);
498  }
499}
500
501/* Return the number of lines in 'f' */
502int owl_fmtext_num_lines(owl_fmtext *f)
503{
504  int lines, i;
505
506  if (f->textlen==0) return(0);
507 
508  lines=0;
509  for (i=0; i<f->textlen; i++) {
510    if (f->textbuff[i]=='\n') lines++;
511  }
512
513  /* if the last char wasn't a \n there's one more line */
514  if (f->textbuff[i-1]!='\n') lines++;
515
516  return(lines);
517}
518
519char *owl_fmtext_get_text(owl_fmtext *f)
520{
521  return(f->textbuff);
522}
523
524/* set the charater at 'index' to be 'char'.  If index is out of
525 * bounds don't do anything. If c or char at index is not ASCII, don't
526 * do anything because it's not UTF-8 safe. */
527void owl_fmtext_set_char(owl_fmtext *f, int index, char ch)
528{
529  if ((index < 0) || (index > f->textlen-1)) return;
530  /* NOT ASCII*/
531  if (f->textbuff[index] & 0x80 || ch & 0x80) return; 
532  f->textbuff[index]=ch;
533}
534
535/* Make a copy of the fmtext 'src' into 'dst' */
536void owl_fmtext_copy(owl_fmtext *dst, owl_fmtext *src)
537{
538  int mallocsize;
539
540  if (src->textlen==0) {
541    mallocsize=5;
542  } else {
543    mallocsize=src->textlen+2;
544  }
545  dst->textlen=src->textlen;
546  dst->bufflen=mallocsize;
547  dst->textbuff=owl_malloc(mallocsize);
548  memcpy(dst->textbuff, src->textbuff, src->textlen+1);
549  dst->default_attrs = src->default_attrs;
550  dst->default_fgcolor = src->default_fgcolor;
551  dst->default_bgcolor = src->default_bgcolor;
552}
553
554/* return 1 if the string is found, 0 if not.  This is a case
555 *  insensitive search.
556 */
557int owl_fmtext_search(owl_fmtext *f, owl_regex *re)
558{
559  if (owl_regex_compare(re, f->textbuff, NULL, NULL) == 0) return(1);
560  return(0);
561}
562
563
564/* Append the text 'text' to 'f' and interpret the zephyr style
565 * formatting syntax to set appropriate attributes.
566 */
567void owl_fmtext_append_ztext(owl_fmtext *f, char *text)
568{
569  int stacksize, curattrs, curcolor;
570  char *ptr, *txtptr, *buff, *tmpptr;
571  int attrstack[32], chrstack[32], colorstack[32];
572
573  curattrs=OWL_FMTEXT_ATTR_NONE;
574  curcolor=OWL_COLOR_DEFAULT;
575  stacksize=0;
576  txtptr=text;
577  while (1) {
578    ptr=strpbrk(txtptr, "@{[<()>]}");
579    if (!ptr) {
580      /* add all the rest of the text and exit */
581      owl_fmtext_append_attr(f, txtptr, curattrs, curcolor, OWL_COLOR_DEFAULT);
582      return;
583    } else if (ptr[0]=='@') {
584      /* add the text up to this point then deal with the stack */
585      buff=owl_malloc(ptr-txtptr+20);
586      strncpy(buff, txtptr, ptr-txtptr);
587      buff[ptr-txtptr]='\0';
588      owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
589      owl_free(buff);
590
591      /* update pointer to point at the @ */
592      txtptr=ptr;
593
594      /* now the stack */
595
596      /* if we've hit our max stack depth, print the @ and move on */
597      if (stacksize==32) {
598        owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
599        txtptr++;
600        continue;
601      }
602
603      /* if it's an @@, print an @ and continue */
604      if (txtptr[1]=='@') {
605        owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
606        txtptr+=2;
607        continue;
608      }
609       
610      /* if there's no opener, print the @ and continue */
611      tmpptr=strpbrk(txtptr, "(<[{ ");
612      if (!tmpptr || tmpptr[0]==' ') {
613        owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
614        txtptr++;
615        continue;
616      }
617
618      /* check what command we've got, push it on the stack, start
619         using it, and continue ... unless it's a color command */
620      buff=owl_malloc(tmpptr-ptr+20);
621      strncpy(buff, ptr, tmpptr-ptr);
622      buff[tmpptr-ptr]='\0';
623      if (!strcasecmp(buff, "@bold")) {
624        attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
625        chrstack[stacksize]=tmpptr[0];
626        colorstack[stacksize]=curcolor;
627        stacksize++;
628        curattrs|=OWL_FMTEXT_ATTR_BOLD;
629        txtptr+=6;
630        owl_free(buff);
631        continue;
632      } else if (!strcasecmp(buff, "@b")) {
633        attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
634        chrstack[stacksize]=tmpptr[0];
635        colorstack[stacksize]=curcolor;
636        stacksize++;
637        curattrs|=OWL_FMTEXT_ATTR_BOLD;
638        txtptr+=3;
639        owl_free(buff);
640        continue;
641      } else if (!strcasecmp(buff, "@i")) {
642        attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
643        chrstack[stacksize]=tmpptr[0];
644        colorstack[stacksize]=curcolor;
645        stacksize++;
646        curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
647        txtptr+=3;
648        owl_free(buff);
649        continue;
650      } else if (!strcasecmp(buff, "@italic")) {
651        attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
652        chrstack[stacksize]=tmpptr[0];
653        colorstack[stacksize]=curcolor;
654        stacksize++;
655        curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
656        txtptr+=8;
657        owl_free(buff);
658        continue;
659      } else if (!strcasecmp(buff, "@")) {
660        attrstack[stacksize]=OWL_FMTEXT_ATTR_NONE;
661        chrstack[stacksize]=tmpptr[0];
662        colorstack[stacksize]=curcolor;
663        stacksize++;
664        txtptr+=2;
665        owl_free(buff);
666        continue;
667
668        /* if it's a color read the color, set the current color and
669           continue */
670      } else if (!strcasecmp(buff, "@color") 
671                 && owl_global_get_hascolors(&g)
672                 && owl_global_is_colorztext(&g)) {
673        owl_free(buff);
674        txtptr+=7;
675        tmpptr=strpbrk(txtptr, "@{[<()>]}");
676        if (tmpptr &&
677            ((txtptr[-1]=='(' && tmpptr[0]==')') ||
678             (txtptr[-1]=='<' && tmpptr[0]=='>') ||
679             (txtptr[-1]=='[' && tmpptr[0]==']') ||
680             (txtptr[-1]=='{' && tmpptr[0]=='}'))) {
681
682          /* grab the color name */
683          buff=owl_malloc(tmpptr-txtptr+20);
684          strncpy(buff, txtptr, tmpptr-txtptr);
685          buff[tmpptr-txtptr]='\0';
686
687          /* set it as the current color */
688          curcolor=owl_util_string_to_color(buff);
689          if (curcolor==-1) curcolor=OWL_COLOR_DEFAULT;
690          owl_free(buff);
691          txtptr=tmpptr+1;
692          continue;
693
694        } else {
695
696        }
697
698      } else {
699        /* if we didn't understand it, we'll print it.  This is different from zwgc
700         * but zwgc seems to be smarter about some screw cases than I am
701         */
702        owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
703        txtptr++;
704        continue;
705      }
706
707    } else if (ptr[0]=='}' || ptr[0]==']' || ptr[0]==')' || ptr[0]=='>') {
708      /* add the text up to this point first */
709      buff=owl_malloc(ptr-txtptr+20);
710      strncpy(buff, txtptr, ptr-txtptr);
711      buff[ptr-txtptr]='\0';
712      owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
713      owl_free(buff);
714
715      /* now deal with the closer */
716      txtptr=ptr;
717
718      /* first, if the stack is empty we must bail (just print and go) */
719      if (stacksize==0) {
720        buff=owl_malloc(5);
721        buff[0]=ptr[0];
722        buff[1]='\0';
723        owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
724        owl_free(buff);
725        txtptr++;
726        continue;
727      }
728
729      /* if the closing char is what's on the stack, turn off the
730         attribue and pop the stack */
731      if ((ptr[0]==')' && chrstack[stacksize-1]=='(') ||
732          (ptr[0]=='>' && chrstack[stacksize-1]=='<') ||
733          (ptr[0]==']' && chrstack[stacksize-1]=='[') ||
734          (ptr[0]=='}' && chrstack[stacksize-1]=='{')) {
735        int i;
736        stacksize--;
737        curattrs=OWL_FMTEXT_ATTR_NONE;
738        curcolor = colorstack[stacksize];
739        for (i=0; i<stacksize; i++) {
740          curattrs|=attrstack[i];
741        }
742        txtptr+=1;
743        continue;
744      } else {
745        /* otherwise print and continue */
746        buff=owl_malloc(5);
747        buff[0]=ptr[0];
748        buff[1]='\0';
749        owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
750        owl_free(buff);
751        txtptr++;
752        continue;
753      }
754    } else {
755      /* we've found an unattached opener, print everything and move on */
756      buff=owl_malloc(ptr-txtptr+20);
757      strncpy(buff, txtptr, ptr-txtptr+1);
758      buff[ptr-txtptr+1]='\0';
759      owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
760      owl_free(buff);
761      txtptr=ptr+1;
762      continue;
763    }
764  }
765}
766
767/* requires that the list values are strings or NULL.
768 * joins the elements together with join_with.
769 * If format_fn is specified, passes it the list element value
770 * and it will return a string which this needs to free. */
771void owl_fmtext_append_list(owl_fmtext *f, owl_list *l, char *join_with, char *(format_fn)(char *))
772{
773  int i, size;
774  char *elem;
775  char *text;
776
777  size = owl_list_get_size(l);
778  for (i=0; i<size; i++) {
779    elem = owl_list_get_element(l,i);
780    if (elem && format_fn) {
781      text = format_fn(elem);
782      if (text) {
783        owl_fmtext_append_normal(f, text);
784        owl_free(text);
785      }
786    } else if (elem) {
787      owl_fmtext_append_normal(f, elem);
788    }
789    if ((i < size-1) && join_with) {
790      owl_fmtext_append_normal(f, join_with);
791    }
792  }
793}
794
795/* Free all memory allocated by the object */
796void owl_fmtext_free(owl_fmtext *f)
797{
798  if (f->textbuff) owl_free(f->textbuff);
799}
800
801/*** Color Pair manager ***/
802void owl_fmtext_init_colorpair_mgr(owl_colorpair_mgr *cpmgr)
803{
804  /* This could be a bitarray if we wanted to save memory. */
805  short i, j;
806  cpmgr->next = 8;
807 
808  /* The test is <= because we allocate COLORS+1 entries. */
809  cpmgr->pairs = owl_malloc((COLORS+1) * sizeof(short*));
810  for(i = 0; i <= COLORS; i++) {
811    cpmgr->pairs[i] = owl_malloc((COLORS+1) * sizeof(short));
812    for(j = 0; j <= COLORS; j++) {
813      cpmgr->pairs[i][j] = -1;
814    }
815  }
816  if (owl_global_get_hascolors(&g)) {
817    for(i = 0; i < 8; i++) {
818      short fg, bg;
819      if (i >= COLORS) continue;
820      pair_content(i, &fg, &bg);
821      cpmgr->pairs[fg+1][bg+1] = i;
822    }
823  }
824}
825
826/* Reset used list */
827void owl_fmtext_reset_colorpairs()
828{
829  if (owl_global_get_hascolors(&g)) {
830    short i, j;
831    owl_colorpair_mgr *cpmgr = owl_global_get_colorpair_mgr(&g);
832    cpmgr->next = 8;
833   
834    /* The test is <= because we allocated COLORS+1 entries. */
835    for(i = 0; i <= COLORS; i++) {
836      for(j = 0; j <= COLORS; j++) {
837        cpmgr->pairs[i][j] = -1;
838      }
839    }
840    for(i = 0; i < 8; i++) {
841      short fg, bg;
842      if (i >= COLORS) continue;
843      pair_content(i, &fg, &bg);
844      cpmgr->pairs[fg+1][bg+1] = i;
845    }
846  }
847}
848
849/* Assign pairs by request */
850short owl_fmtext_get_colorpair(int fg, int bg)
851{
852  owl_colorpair_mgr *cpmgr;
853  short pair, default_bg;
854
855  /* Sanity (Bounds) Check */
856  if (fg > COLORS || fg < OWL_COLOR_DEFAULT) fg = OWL_COLOR_DEFAULT;
857  if (bg > COLORS || bg < OWL_COLOR_DEFAULT) bg = OWL_COLOR_DEFAULT;
858           
859#ifdef HAVE_USE_DEFAULT_COLORS
860  if (fg == OWL_COLOR_DEFAULT) fg = -1;
861  default_bg = OWL_COLOR_DEFAULT;
862#else
863  if (fg == OWL_COLOR_DEFAULT) fg = 0;
864  if (bg == OWL_COLOR_DEFAULT) bg = 0;
865  default_bg = COLOR_BLACK;
866#endif
867
868  /* looking for a pair we already set up for this draw. */
869  cpmgr = owl_global_get_colorpair_mgr(&g);
870  pair = cpmgr->pairs[fg+1][bg+1];
871  if (!(pair != -1 && pair < cpmgr->next)) {
872    /* If we didn't find a pair, search for a free one to assign. */
873    pair = (cpmgr->next < COLOR_PAIRS) ? cpmgr->next : -1;
874    if (pair != -1) {
875      /* We found a free pair, initialize it. */
876      init_pair(pair, fg, bg);
877      cpmgr->pairs[fg+1][bg+1] = pair;
878      cpmgr->next++;
879    }
880    else if (bg != OWL_COLOR_DEFAULT) {
881      /* We still don't have a pair, drop the background color. Too bad. */
882      owl_function_debugmsg("colorpairs: color shortage - dropping background color.");
883      pair = owl_fmtext_get_colorpair(fg, OWL_COLOR_DEFAULT);
884    }
885    else {
886      /* We still don't have a pair, defaults all around. */
887      owl_function_debugmsg("colorpairs: color shortage - dropping foreground and background color.");
888      pair = 0;
889    }
890  }
891  return pair;
892}
Note: See TracBrowser for help on using the repository browser.