source: fmtext.c @ fa00c5c

owl
Last change on this file since fa00c5c was fa00c5c, checked in by James M. Kretchmar <kretch@mit.edu>, 15 years ago
Correct license.
  • Property mode set to 100644
File size: 16.8 KB
Line 
1/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar
2 *
3 * This file is part of Owl.
4 *
5 * Owl is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * Owl is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Owl.  If not, see <http://www.gnu.org/licenses/>.
17 *
18 * ---------------------------------------------------------------
19 *
20 * As of Owl version 2.1.12 there are patches contributed by
21 * developers of the branched BarnOwl project, Copyright (c)
22 * 2006-2009 The BarnOwl Developers. All rights reserved.
23 */
24
25#include "owl.h"
26#include <stdlib.h>
27#include <string.h>
28
29static const char fileIdent[] = "$Id$";
30
31/* initialize an fmtext with no data */
32void owl_fmtext_init_null(owl_fmtext *f)
33{
34  f->textlen=0;
35  f->textbuff=owl_strdup("");
36  f->fmbuff=owl_malloc(5);
37  f->colorbuff=owl_malloc(5);
38  f->fmbuff[0]=OWL_FMTEXT_ATTR_NONE;
39  f->colorbuff[0]=OWL_COLOR_DEFAULT;
40}
41
42/* Internal function.  Set the attribute 'attr' from index 'first' to
43 * index 'last'
44 */
45void _owl_fmtext_set_attr(owl_fmtext *f, int attr, int first, int last)
46{
47  int i;
48  for (i=first; i<=last; i++) {
49    f->fmbuff[i]=(unsigned char) attr;
50  }
51}
52
53/* Internal function.  Add the attribute 'attr' to the existing
54 * attributes from index 'first' to index 'last'
55 */
56void _owl_fmtext_add_attr(owl_fmtext *f, int attr, int first, int last)
57{
58  int i;
59  for (i=first; i<=last; i++) {
60    f->fmbuff[i]|=(unsigned char) attr;
61  }
62}
63
64/* Internal function.  Set the color to be 'color' from index 'first'
65 * to index 'last
66 */
67void _owl_fmtext_set_color(owl_fmtext *f, int color, int first, int last)
68{
69  int i;
70  for (i=first; i<=last; i++) {
71    f->colorbuff[i]=(unsigned char) color;
72  }
73}
74
75/* append text to the end of 'f' with attribute 'attr' and color
76 * 'color'
77 */
78void owl_fmtext_append_attr(owl_fmtext *f, char *text, int attr, int color)
79{
80  int newlen;
81
82  newlen=strlen(f->textbuff)+strlen(text);
83  f->textbuff=owl_realloc(f->textbuff, newlen+2);
84  f->fmbuff=owl_realloc(f->fmbuff, newlen+2);
85  f->colorbuff=owl_realloc(f->colorbuff, newlen+2);
86
87  strcat(f->textbuff, text);
88  _owl_fmtext_set_attr(f, attr, f->textlen, newlen);
89  _owl_fmtext_set_color(f, color, f->textlen, newlen);
90  f->textlen=newlen;
91}
92
93/* Append normal, uncolored text 'text' to 'f' */
94void owl_fmtext_append_normal(owl_fmtext *f, char *text)
95{
96  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT);
97}
98
99/* Append normal text 'text' to 'f' with color 'color' */
100void owl_fmtext_append_normal_color(owl_fmtext *f, char *text, int color)
101{
102  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, color);
103}
104
105/* Append bold text 'text' to 'f' */
106void owl_fmtext_append_bold(owl_fmtext *f, char *text)
107{
108  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT);
109}
110
111/* Append reverse video text 'text' to 'f' */
112void owl_fmtext_append_reverse(owl_fmtext *f, char *text)
113{
114  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE, OWL_COLOR_DEFAULT);
115}
116
117/* Append reversed and bold, uncolored text 'text' to 'f' */
118void owl_fmtext_append_reversebold(owl_fmtext *f, char *text)
119{
120  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE | OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT);
121}
122
123/* Add the attribute 'attr' to all text in 'f' */
124void owl_fmtext_addattr(owl_fmtext *f, int attr)
125{
126  /* add the attribute to all text */
127  int i, j;
128
129  j=f->textlen;
130  for (i=0; i<j; i++) {
131    f->fmbuff[i] |= attr;
132  }
133}
134
135/* Anywhere the color is NOT ALREDY SET, set the color to 'color'.
136 * Other colors are left unchanged
137 */
138void owl_fmtext_colorize(owl_fmtext *f, int color)
139{
140  /* everywhere the color is OWL_COLOR_DEFAULT, change it to be 'color' */
141  int i, j;
142
143  j=f->textlen;
144  for(i=0; i<j; i++) {
145    if (f->colorbuff[i]==OWL_COLOR_DEFAULT) f->colorbuff[i] = color;
146  }
147}
148
149/* Internal function.  Append text from 'in' between index 'start' and
150 * 'stop' to the end of 'f'
151 */
152void _owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in, int start, int stop)
153{
154  int newlen, i;
155
156  newlen=strlen(f->textbuff)+(stop-start+1);
157  f->textbuff=owl_realloc(f->textbuff, newlen+1);
158  f->fmbuff=owl_realloc(f->fmbuff, newlen+1);
159  f->colorbuff=owl_realloc(f->colorbuff, newlen+1);
160
161  strncat(f->textbuff, in->textbuff+start, stop-start+1);
162  f->textbuff[newlen]='\0';
163  for (i=start; i<=stop; i++) {
164    f->fmbuff[f->textlen+(i-start)]=in->fmbuff[i];
165    f->colorbuff[f->textlen+(i-start)]=in->colorbuff[i];
166  }
167  f->textlen=newlen;
168}
169
170/* append fmtext 'in' to 'f' */
171void owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in)
172{
173  _owl_fmtext_append_fmtext(f, in, 0, in->textlen);
174
175}
176
177/* Append 'nspaces' number of spaces to the end of 'f' */
178void owl_fmtext_append_spaces(owl_fmtext *f, int nspaces)
179{
180  int i;
181  for (i=0; i<nspaces; i++) {
182    owl_fmtext_append_normal(f, " ");
183  }
184}
185
186/* Return a plain version of the fmtext.  Caller is responsible for
187 * freeing the return
188 */
189char *owl_fmtext_print_plain(owl_fmtext *f)
190{
191  return(owl_strdup(f->textbuff));
192}
193
194/* add the formatted text to the curses window 'w'.  The window 'w'
195 * must already be initiatlized with curses
196 */
197void owl_fmtext_curs_waddstr(owl_fmtext *f, WINDOW *w)
198{
199  char *tmpbuff;
200  int position, trans1, trans2, len, lastsame;
201
202  if (w==NULL) {
203    owl_function_debugmsg("Hit a null window in owl_fmtext_curs_waddstr.");
204    return;
205  }
206
207  tmpbuff=owl_malloc(f->textlen+10);
208
209  position=0;
210  len=f->textlen;
211  while (position<=len) {
212    /* find the last char with the current format and color */
213    trans1=owl_util_find_trans(f->fmbuff+position, len-position);
214    trans2=owl_util_find_trans(f->colorbuff+position, len-position);
215
216    if (trans1<trans2) {
217      lastsame=position+trans1;
218    } else {
219      lastsame=position+trans2;
220    }
221
222    /* set the format */
223    wattrset(w, A_NORMAL);
224    if (f->fmbuff[position] & OWL_FMTEXT_ATTR_BOLD) {
225      wattron(w, A_BOLD);
226    }
227    if (f->fmbuff[position] & OWL_FMTEXT_ATTR_REVERSE) {
228      wattron(w, A_REVERSE);
229    }
230    if (f->fmbuff[position] & OWL_FMTEXT_ATTR_UNDERLINE) {
231      wattron(w, A_UNDERLINE);
232    }
233
234    /* set the color */
235    /* warning, this is sort of a hack */
236    if (owl_global_get_hascolors(&g)) {
237      if (f->colorbuff[position]!=OWL_COLOR_DEFAULT) {
238        wattron(w, COLOR_PAIR(f->colorbuff[position]));
239      }
240    }
241
242    /* add the text */
243    strncpy(tmpbuff, f->textbuff + position, lastsame-position+1);
244    tmpbuff[lastsame-position+1]='\0';
245    waddstr(w, tmpbuff);
246
247    position=lastsame+1;
248  }
249  owl_free(tmpbuff);
250}
251
252
253/* start with line 'aline' (where the first line is 0) and print
254 * 'lines' number of lines into 'out'
255 */
256int owl_fmtext_truncate_lines(owl_fmtext *in, int aline, int lines, owl_fmtext *out)
257{
258  char *ptr1, *ptr2;
259  int i, offset;
260 
261  /* find the starting line */
262  ptr1=in->textbuff;
263  if (aline!=0) {
264    for (i=0; i<aline; i++) {
265      ptr1=strchr(ptr1, '\n');
266      if (!ptr1) return(-1);
267      ptr1++;
268    }
269  }
270  /* ptr1 now holds the starting point */
271
272  /* copy in the next 'lines' lines */
273  if (lines<1) return(-1);
274
275  for (i=0; i<lines; i++) {
276    offset=ptr1-in->textbuff;
277    ptr2=strchr(ptr1, '\n');
278    if (!ptr2) {
279      _owl_fmtext_append_fmtext(out, in, offset, (in->textlen)-1);
280      return(-1);
281    }
282    _owl_fmtext_append_fmtext(out, in, offset, (ptr2-ptr1)+offset);
283    ptr1=ptr2+1;
284  }
285  return(0);
286}
287
288/* Truncate the message so that each line begins at column 'acol' and
289 * ends at 'bcol' or sooner.  The first column is number 0.  The new
290 * message is placed in 'out'.  The message is * expected to end in a
291 * new line for now
292 */
293void owl_fmtext_truncate_cols(owl_fmtext *in, int acol, int bcol, owl_fmtext *out)
294{
295  char *ptr1, *ptr2, *last;
296  int len, offset;
297
298  last=in->textbuff+in->textlen-1;
299  ptr1=in->textbuff;
300  while (ptr1<=last) {
301    ptr2=strchr(ptr1, '\n');
302    if (!ptr2) {
303      /* but this shouldn't happen if we end in a \n */
304      break;
305    }
306   
307    if (ptr2==ptr1) {
308      owl_fmtext_append_normal(out, "\n");
309      ptr1++;
310      continue;
311    }
312
313    /* we need to check that we won't run over here */
314    len=bcol-acol;
315    if (len > (ptr2-(ptr1+acol))) {
316      /* the whole line fits with room to spare, don't take a full 'len' */
317      len=ptr2-(ptr1+acol);
318    }
319    if (len>last-ptr1) {
320      /* the whole rest of the text fits with room to spare, adjust for it */
321      len-=(last-ptr1);
322    }
323    if (len<=0) {
324      /* saftey check */
325      owl_fmtext_append_normal(out, "\n");
326      ptr1=ptr2+1;
327      continue;
328    }
329
330    offset=ptr1-in->textbuff;
331    _owl_fmtext_append_fmtext(out, in, offset+acol, offset+acol+len);
332
333    ptr1=ptr2+1;
334  }
335}
336
337/* Return the number of lines in 'f' */
338int owl_fmtext_num_lines(owl_fmtext *f)
339{
340  int lines, i;
341
342  if (f->textlen==0) return(0);
343 
344  lines=0;
345  for (i=0; i<f->textlen; i++) {
346    if (f->textbuff[i]=='\n') lines++;
347  }
348
349  /* if the last char wasn't a \n there's one more line */
350  if (f->textbuff[i-1]!='\n') lines++;
351
352  return(lines);
353}
354
355char *owl_fmtext_get_text(owl_fmtext *f)
356{
357  return(f->textbuff);
358}
359
360/* set the charater at 'index' to be 'char'.  If index is out of
361 * bounds don't do anything */
362void owl_fmtext_set_char(owl_fmtext *f, int index, int ch)
363{
364  if ((index < 0) || (index > f->textlen-1)) return;
365  f->textbuff[index]=ch;
366}
367
368/* Make a copy of the fmtext 'src' into 'dst' */
369void owl_fmtext_copy(owl_fmtext *dst, owl_fmtext *src)
370{
371  int mallocsize;
372
373  if (src->textlen==0) {
374    mallocsize=5;
375  } else {
376    mallocsize=src->textlen+2;
377  }
378  dst->textlen=src->textlen;
379  dst->textbuff=owl_malloc(mallocsize);
380  dst->fmbuff=owl_malloc(mallocsize);
381  dst->colorbuff=owl_malloc(mallocsize);
382  memcpy(dst->textbuff, src->textbuff, src->textlen+1);
383  memcpy(dst->fmbuff, src->fmbuff, src->textlen);
384  memcpy(dst->colorbuff, src->colorbuff, src->textlen);
385}
386
387/* highlight all instances of "string".  Return the number of
388 * instances found.  This is a case insensitive search.
389 */
390int owl_fmtext_search_and_highlight(owl_fmtext *f, char *string)
391{
392
393  int found, len;
394  char *ptr1, *ptr2;
395
396  len=strlen(string);
397  found=0;
398  ptr1=f->textbuff;
399  while (ptr1-f->textbuff <= f->textlen) {
400    ptr2=stristr(ptr1, string);
401    if (!ptr2) return(found);
402
403    found++;
404    _owl_fmtext_add_attr(f, OWL_FMTEXT_ATTR_REVERSE,
405                         ptr2 - f->textbuff,
406                         ptr2 - f->textbuff + len - 1);
407
408    ptr1=ptr2+len;
409  }
410  return(found);
411}
412
413/* return 1 if the string is found, 0 if not.  This is a case
414 *  insensitive search.
415 */
416int owl_fmtext_search(owl_fmtext *f, char *string)
417{
418
419  if (stristr(f->textbuff, string)) return(1);
420  return(0);
421}
422
423
424/* Append the text 'text' to 'f' and interpret the zephyr style
425 * formatting syntax to set appropriate attributes.
426 */
427void owl_fmtext_append_ztext(owl_fmtext *f, char *text)
428{
429  int stacksize, curattrs, curcolor;
430  char *ptr, *txtptr, *buff, *tmpptr;
431  int attrstack[32], chrstack[32];
432
433  curattrs=OWL_FMTEXT_ATTR_NONE;
434  curcolor=OWL_COLOR_DEFAULT;
435  stacksize=0;
436  txtptr=text;
437  while (1) {
438    ptr=strpbrk(txtptr, "@{[<()>]}");
439    if (!ptr) {
440      /* add all the rest of the text and exit */
441      owl_fmtext_append_attr(f, txtptr, curattrs, curcolor);
442      return;
443    } else if (ptr[0]=='@') {
444      /* add the text up to this point then deal with the stack */
445      buff=owl_malloc(ptr-txtptr+20);
446      strncpy(buff, txtptr, ptr-txtptr);
447      buff[ptr-txtptr]='\0';
448      owl_fmtext_append_attr(f, buff, curattrs, curcolor);
449      owl_free(buff);
450
451      /* update pointer to point at the @ */
452      txtptr=ptr;
453
454      /* now the stack */
455
456      /* if we've hit our max stack depth, print the @ and move on */
457      if (stacksize==32) {
458        owl_fmtext_append_attr(f, "@", curattrs, curcolor);
459        txtptr++;
460        continue;
461      }
462
463      /* if it's an @@, print an @ and continue */
464      if (txtptr[1]=='@') {
465        owl_fmtext_append_attr(f, "@", curattrs, curcolor);
466        txtptr+=2;
467        continue;
468      }
469       
470      /* if there's no opener, print the @ and continue */
471      tmpptr=strpbrk(txtptr, "(<[{ ");
472      if (!tmpptr || tmpptr[0]==' ') {
473        owl_fmtext_append_attr(f, "@", curattrs, curcolor);
474        txtptr++;
475        continue;
476      }
477
478      /* check what command we've got, push it on the stack, start
479         using it, and continue ... unless it's a color command */
480      buff=owl_malloc(tmpptr-ptr+20);
481      strncpy(buff, ptr, tmpptr-ptr);
482      buff[tmpptr-ptr]='\0';
483      if (!strcasecmp(buff, "@bold")) {
484        attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
485        chrstack[stacksize]=tmpptr[0];
486        stacksize++;
487        curattrs|=OWL_FMTEXT_ATTR_BOLD;
488        txtptr+=6;
489        owl_free(buff);
490        continue;
491      } else if (!strcasecmp(buff, "@b")) {
492        attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
493        chrstack[stacksize]=tmpptr[0];
494        stacksize++;
495        curattrs|=OWL_FMTEXT_ATTR_BOLD;
496        txtptr+=3;
497        owl_free(buff);
498        continue;
499      } else if (!strcasecmp(buff, "@i")) {
500        attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
501        chrstack[stacksize]=tmpptr[0];
502        stacksize++;
503        curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
504        txtptr+=3;
505        owl_free(buff);
506        continue;
507      } else if (!strcasecmp(buff, "@italic")) {
508        attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
509        chrstack[stacksize]=tmpptr[0];
510        stacksize++;
511        curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
512        txtptr+=8;
513        owl_free(buff);
514        continue;
515
516        /* if it's a color read the color, set the current color and
517           continue */
518      } else if (!strcasecmp(buff, "@color") 
519                 && owl_global_get_hascolors(&g)
520                 && owl_global_is_colorztext(&g)) {
521        owl_free(buff);
522        txtptr+=7;
523        tmpptr=strpbrk(txtptr, "@{[<()>]}");
524        if (tmpptr &&
525            ((txtptr[-1]=='(' && tmpptr[0]==')') ||
526             (txtptr[-1]=='<' && tmpptr[0]=='>') ||
527             (txtptr[-1]=='[' && tmpptr[0]==']') ||
528             (txtptr[-1]=='{' && tmpptr[0]=='}'))) {
529
530          /* grab the color name */
531          buff=owl_malloc(tmpptr-txtptr+20);
532          strncpy(buff, txtptr, tmpptr-txtptr);
533          buff[tmpptr-txtptr]='\0';
534
535          /* set it as the current color */
536          curcolor=owl_util_string_to_color(buff);
537          if (curcolor==-1) curcolor=OWL_COLOR_DEFAULT;
538          owl_free(buff);
539          txtptr=tmpptr+1;
540          continue;
541
542        } else {
543
544        }
545
546      } else {
547        /* if we didn't understand it, we'll print it.  This is different from zwgc
548         * but zwgc seems to be smarter about some screw cases than I am
549         */
550        owl_fmtext_append_attr(f, "@", curattrs, curcolor);
551        txtptr++;
552        continue;
553      }
554
555    } else if (ptr[0]=='}' || ptr[0]==']' || ptr[0]==')' || ptr[0]=='>') {
556      /* add the text up to this point first */
557      buff=owl_malloc(ptr-txtptr+20);
558      strncpy(buff, txtptr, ptr-txtptr);
559      buff[ptr-txtptr]='\0';
560      owl_fmtext_append_attr(f, buff, curattrs, curcolor);
561      owl_free(buff);
562
563      /* now deal with the closer */
564      txtptr=ptr;
565
566      /* first, if the stack is empty we must bail (just print and go) */
567      if (stacksize==0) {
568        buff=owl_malloc(5);
569        buff[0]=ptr[0];
570        buff[1]='\0';
571        owl_fmtext_append_attr(f, buff, curattrs, curcolor);
572        owl_free(buff);
573        txtptr++;
574        continue;
575      }
576
577      /* if the closing char is what's on the stack, turn off the
578         attribue and pop the stack */
579      if ((ptr[0]==')' && chrstack[stacksize-1]=='(') ||
580          (ptr[0]=='>' && chrstack[stacksize-1]=='<') ||
581          (ptr[0]==']' && chrstack[stacksize-1]=='[') ||
582          (ptr[0]=='}' && chrstack[stacksize-1]=='{')) {
583        int i;
584        stacksize--;
585        curattrs=OWL_FMTEXT_ATTR_NONE;
586        for (i=0; i<stacksize; i++) {
587          curattrs|=attrstack[i];
588        }
589        txtptr+=1;
590        continue;
591      } else {
592        /* otherwise print and continue */
593        buff=owl_malloc(5);
594        buff[0]=ptr[0];
595        buff[1]='\0';
596        owl_fmtext_append_attr(f, buff, curattrs, curcolor);
597        owl_free(buff);
598        txtptr++;
599        continue;
600      }
601    } else {
602      /* we've found an unattached opener, print everything and move on */
603      buff=owl_malloc(ptr-txtptr+20);
604      strncpy(buff, txtptr, ptr-txtptr+1);
605      buff[ptr-txtptr+1]='\0';
606      owl_fmtext_append_attr(f, buff, curattrs, curcolor);
607      owl_free(buff);
608      txtptr=ptr+1;
609      continue;
610    }
611  }
612}
613
614/* requires that the list values are strings or NULL.
615 * joins the elements together with join_with.
616 * If format_fn is specified, passes it the list element value
617 * and it will return a string which this needs to free. */
618void owl_fmtext_append_list(owl_fmtext *f, owl_list *l, char *join_with, char *(format_fn)(void*))
619{
620  int i, size;
621  void *elem;
622  char *text;
623
624  size = owl_list_get_size(l);
625  for (i=0; i<size; i++) {
626    elem = (char*)owl_list_get_element(l,i);
627    if (elem && format_fn) {
628      text = format_fn(elem);
629      if (text) {
630        owl_fmtext_append_normal(f, text);
631        owl_free(text);
632      }
633    } else if (elem) {
634      owl_fmtext_append_normal(f, elem);
635    }
636    if ((i < size-1) && join_with) {
637      owl_fmtext_append_normal(f, join_with);
638    }
639  }
640}
641
642/* Free all memory allocated by the object */
643void owl_fmtext_free(owl_fmtext *f)
644{
645  if (f->textbuff) owl_free(f->textbuff);
646  if (f->fmbuff) owl_free(f->fmbuff);
647  if (f->colorbuff) owl_free(f->colorbuff);
648}
649
Note: See TracBrowser for help on using the repository browser.