1 /* $Id: mreadline.c 2515 2008-03-02 23:20:52Z kuhlmann $ */
2 
3 /*****************************************************
4  * Copyright (C) 1998 Sergey Shkonda (serg@bcs.zp.ua)
5  * This file may be distributed under version 2 of the GPL licence.
6  * Originally placed in the public domain by Sergey Shkonda Nov 27, 1998
7  *****************************************************/
8 
9 #include "climm.h"
10 
11 #ifdef USE_MREADLINE
12 
13 #include "mreadline.h"
14 #include "util_ui.h"
15 #include "util_rl.h"
16 #include "util.h"
17 #include "cmd_user.h"
18 #include "util_tabs.h"
19 #include "conv.h"
20 #include "contact.h"
21 #include "preferences.h"
22 
23 #include <stdarg.h>
24 #include <ctype.h>
25 #include <signal.h>
26 #include <assert.h>
27 
28 static int CharCount = 0;       /* number of characters printed on line. */
29 static int IndentCount = 0;
30 
31 #define LOGOS 10
32 static const char *logos[LOGOS] = { NULL };
33 static UBYTE logoc = 0;
34 static UBYTE first = 0;
35 
rl_logo(const char * logo)36 void rl_logo (const char *logo)
37 {
38     if (logoc != LOGOS)
39         logos[logoc++] = logo;
40     first = 2;
41 }
42 
M_getlogo(void)43 static const char *M_getlogo (void)
44 {
45     UBYTE i;
46     const char *logo;
47 
48     if (!logoc)
49         return first ? "         " : "";
50     logo = logos[0];
51     logoc--;
52     for (i = 0; i < logoc; i++)
53         logos[i] = logos[i + 1];
54     return ConvTo (logo, prG->enc_loc)->txt;
55 }
56 
rl_logo_clear()57 void rl_logo_clear ()
58 {
59     first = 1;
60     puts ("");
61     while (logoc)
62         puts (M_getlogo ());
63     rl_print ("\r");
64     first = 0;
65 }
66 
67 #define chardiff(aa,bb)  (ENC(enc_loc) == ENC_UTF8 ? s_strnlen ((bb), (aa) - (bb)) : (aa) - (bb))
68 
69 #define USECOLORINVCHAR if (!colinvchar) colinvchar = ((prG->flags & FLAG_COLOR) ? ContactPrefStr (NULL, CO_COLORINVCHAR) : "")
70 
71 /*
72  * Print a string to the output, interpreting color and indenting codes.
73  */
rl_print(const char * org)74 void rl_print (const char *org)
75 {
76     const char *test, *save, *temp, *str, *para;
77     char *fstr;
78     const char *colnone = NULL, *colinvchar = NULL, *col;
79     UBYTE isline = 0, ismsg = 0;
80     int i;
81     static str_s colbuf = { NULL, 0, 0 };
82     int sw = rl_columns - IndentCount;
83 
84     fstr = strdup (ConvTo (org, prG->enc_loc)->txt);
85     str = fstr;
86     switch (ENC(enc_loc))
87     {
88         case ENC_UTF8:   para = "\xc2\xb6"; break;
89         case ENC_LATIN1:
90         case ENC_LATIN9: para = "\xb6";  break;
91         default:         para = "P";  break;
92     }
93     col = colnone = (prG->flags & FLAG_COLOR) ? ContactPrefStr (NULL, CO_COLORNONE) : "";
94 
95     if (first)
96     {
97         sw -= 9;
98         if (first == 2)
99         {
100             if (!CharCount)
101                 printf ("%s", M_getlogo ());
102             first = 1;
103         }
104     }
105 
106     ReadLinePromptHide ();
107 
108 #ifdef ENABLE_TCL
109     if (prG->tclout)
110     {
111         prG->tclout (fstr);
112         free (fstr);
113         return;
114     }
115 #endif
116 
117     for (; *str; str++)
118     {
119         for (test = save = str; *test; test++)
120         {
121             if (!(*test & 0xe0) || (*test == 127)) /* special character reached - emit text till last saved position */
122             {
123                 if (save != str)
124                     test = save;
125                 break;
126             }
127             else if (strchr ("-.,_:;!?/ ", *test)) /* punctuation found - save position after it */
128             {
129                 temp = test + 1;
130                 if (chardiff (temp, str) <= sw - CharCount)
131                     save = temp;
132                 else
133                 {
134                     if (save != str)
135                         test = save;
136                     else
137                         test = temp;
138                     break;
139                 }
140             }
141         }
142         if (test != str)           /* Print out (block of) word(s) from str to test*/
143         {
144             while (chardiff (test, str) > sw) /* word is longer than line, print till end of line */
145             {
146                 printf ("%.*s%*s", (int) c_offset (str, sw - CharCount), str, IndentCount, "");
147                 str += c_offset (str, sw - CharCount);
148                 if (isline)
149                 {
150                     USECOLORINVCHAR;
151                     printf ("%s...%s", colinvchar, col);
152                     CharCount = 0;
153                     free (fstr);
154                     return;
155                 }
156                 CharCount = 0;
157             }
158             if (chardiff (test, str) > sw - CharCount) /* remainder doesn't fit anymore => linebreak */
159             {
160                 if (isline)
161                 {
162                     USECOLORINVCHAR;
163                     printf ("%s...%s", colinvchar, col);
164                     CharCount = 0;
165                     free (fstr);
166                     return;
167                 }
168                 printf ("\n%s%*s%s", M_getlogo (), IndentCount, "", col);
169                 CharCount = 0;
170             }
171             printf ("%.*s", (int)(test - str), str);
172             CharCount += chardiff (test, str);
173             str = test;
174         }
175         if (*str != 0x7f && (*str <= 0 || *str >= ' '))
176         {
177             str--;
178             continue;
179         }
180         if (isline && (*str == '\n' || *str == '\r'))
181         {
182             if (str[1])
183             {
184                 USECOLORINVCHAR;
185                 printf ("%s%s..", colinvchar, para);
186             }
187             printf ("%s", col);
188             CharCount = 0;
189             free (fstr);
190             return;
191         }
192         if (*str == '\n' || *str == '\r' || (*str == '\t' && !isline))
193         {
194             if (!str[1] && ismsg)
195             {
196                 printf ("%s\n", colnone);
197                 CharCount = 0;
198                 IndentCount = 0;
199                 free (fstr);
200                 return;
201             }
202         }
203         else if (ismsg || isline)
204         {
205             USECOLORINVCHAR;
206             printf ("%s%c%s", colinvchar, *str - 1 + 'A', col);
207             CharCount++;
208             continue;
209         }
210         switch (*str)           /* Take care of specials */
211         {
212             case '\b':
213                 CharCount--;
214                 printf ("\b");
215                 break;
216             case '\r':
217                 if (!ismsg)
218                 {
219                     putchar ('\r');
220                     if (str[1] != '\n' && IndentCount)
221                     {
222                         printf ("\x1b[%dD", IndentCount);
223                     }
224                     CharCount = 0;
225                     break;
226                 }
227             case '\n':
228                 if (ismsg && (str[1] == (*str ^ '\r' ^ '\n')))
229                     str++;
230                 printf ("\n%s%*s%s", M_getlogo (), IndentCount, "", col);
231                 CharCount = 0;
232                 break;
233             case '\t':
234                 i = TAB_STOP - (CharCount % TAB_STOP);
235                 if (CharCount + i > sw)
236                 {
237                     printf ("\n%s%*s", M_getlogo (), IndentCount, "");
238                     CharCount = 0;
239                 }
240                 else
241                 {
242                     printf ("%*s", i, "");
243                     CharCount += i;
244                 }
245                 break;
246             case '\a':
247                 if (prG->sound == SFLAG_EVENT && prG->event_cmd && *prG->event_cmd)
248                     EventExec (NULL, prG->event_cmd, ev_beep, 0, ims_online, NULL);
249                 else if (prG->sound == SFLAG_BEEP)
250                     printf ("\a");
251                 break;
252             case '\x1b':
253                 if (isline)
254                     break;
255                 switch (*++test)
256                 {
257                     case '<':
258                         ismsg = 1;
259                         switch (prG->flags & (FLAG_LIBR_BR | FLAG_LIBR_INT))
260                         {
261                             case FLAG_LIBR_BR:
262                                 printf ("\n%s", M_getlogo ());
263                                 CharCount = 0;
264                                 break;
265                             case FLAG_LIBR_INT:
266                                 IndentCount = CharCount;
267                                 sw -= IndentCount;
268                                 CharCount = 0;
269                                 break;
270                             case FLAG_LIBR_BR | FLAG_LIBR_INT:
271                                 save = strstr (str, "\x1bv");
272                                 if (save && chardiff (save, str) - 2 > sw - CharCount)
273                                 {
274                                     printf ("\n%s", M_getlogo ());
275                                     CharCount = 0;
276                                     break;
277                                 }
278                                 break;
279                         }
280                         str++;
281                         break;
282                     case 'v':
283                         IndentCount += CharCount;
284                         sw -= IndentCount;
285                         CharCount = 0;
286                         str++;
287                         break;
288                     case '^':
289                         CharCount += IndentCount;
290                         sw += IndentCount;
291                         IndentCount = 0;
292                         str++;
293                         break;
294                     case '.':
295                         isline = 1;
296                         sw -= 3;
297                         if (sw <= CharCount)
298                         {
299                             printf ("%s", col);
300                             CharCount = IndentCount = 0;
301                             free (fstr);
302                             return;
303                         }
304                         str++;
305                         break;
306                     default:
307                         save = strchr (test, 'm');
308                         if (save)
309                         {
310                             while (!strncmp (save, "m" ESC "[", 3) && strchr (save + 1, 'm'))
311                                 save = strchr (save + 1, 'm');
312                             if (prG->flags & FLAG_COLOR)
313                             {
314                                 s_init (&colbuf, "", (int)(save - str) + 1);
315                                 s_catf (&colbuf, "%.*s", (int)(save - str) + 1, str);
316                                 printf ("%s", col = colbuf.txt);
317                             }
318                             else
319                                 col = "";
320                             str = save;
321                         }
322                         break;
323                 }
324                 break;
325             default:
326                 USECOLORINVCHAR;
327                 printf ("%s%c%s", colinvchar, *str - 1 + 'A', col);
328 
329         }
330     }
331     free (fstr);
332 }
333 
334 /*
335  * Print a formatted string to the output, interpreting color and indenting
336  * codes.
337  * Note: does not use the same static buffer as s_sprintf().
338  */
rl_printf(const char * str,...)339 void rl_printf (const char *str, ...)
340 {
341     va_list args;
342     char buf[8 * 1024];
343 
344     va_start (args, str);
345     vsnprintf (buf, sizeof (buf), str, args);
346     rl_print (buf);
347     va_end (args);
348 }
349 
350 /*
351  * Returns current horizontal position
352  */
rl_pos()353 int rl_pos ()
354 {
355     return CharCount;
356 }
357 
rl_log_for(const char * nick,const char * col)358 void rl_log_for (const char *nick, const char *col)
359 {
360     rl_printf ("%s %s", s_now, ReadLinePrintCont (nick, col));
361 }
362 
363 
364 #endif /* USE_MREADLINE */
365