/[LeafOK_CVS]/lbbs/src/lml.c
ViewVC logotype

Annotation of /lbbs/src/lml.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.14 - (hide annotations)
Wed Jul 16 05:24:08 2025 UTC (8 months ago) by sysadm
Branch: MAIN
Changes since 1.13: +5 -4 lines
Content type: text/x-csrc
Fix errors reported by gcc -Wformat-security

1 sysadm 1.1 /***************************************************************************
2     lml.h - description
3     -------------------
4     Copyright : (C) 2004-2025 by Leaflet
5     Email : leaflet@leafok.com
6     ***************************************************************************/
7    
8     /***************************************************************************
9     * *
10     * This program is free software; you can redistribute it and/or modify *
11     * it under the terms of the GNU General Public License as published by *
12     * the Free Software Foundation; either version 3 of the License, or *
13     * (at your option) any later version. *
14     * *
15     ***************************************************************************/
16    
17 sysadm 1.12 #include "common.h"
18 sysadm 1.1 #include "lml.h"
19     #include "log.h"
20     #include <stdio.h>
21     #include <string.h>
22     #include <sys/param.h>
23    
24 sysadm 1.2 #define LML_TAG_PARAM_BUF_LEN 256
25     #define LML_TAG_OUTPUT_BUF_LEN 1024
26 sysadm 1.1
27 sysadm 1.3 typedef int (*lml_tag_filter_cb)(const char *tag_name, const char *tag_param_buf, char *tag_output_buf, size_t tag_output_buf_len);
28    
29     static int lml_tag_color_filter(const char *tag_name, const char *tag_param_buf, char *tag_output_buf, size_t tag_output_buf_len)
30     {
31     if (strcasecmp(tag_name, "color") == 0)
32     {
33     if (strcasecmp(tag_param_buf, "red") == 0)
34     {
35     return snprintf(tag_output_buf, tag_output_buf_len, "\033[1;31m");
36     }
37     else if (strcasecmp(tag_param_buf, "green") == 0)
38     {
39     return snprintf(tag_output_buf, tag_output_buf_len, "\033[1;32m");
40     }
41     else if (strcasecmp(tag_param_buf, "yellow") == 0)
42     {
43     return snprintf(tag_output_buf, tag_output_buf_len, "\033[1;33m");
44     }
45     else if (strcasecmp(tag_param_buf, "blue") == 0)
46     {
47     return snprintf(tag_output_buf, tag_output_buf_len, "\033[1;34m");
48     }
49     else if (strcasecmp(tag_param_buf, "magenta") == 0)
50     {
51     return snprintf(tag_output_buf, tag_output_buf_len, "\033[1;35m");
52     }
53     else if (strcasecmp(tag_param_buf, "cyan") == 0)
54     {
55     return snprintf(tag_output_buf, tag_output_buf_len, "\033[1;36m");
56     }
57     else if (strcasecmp(tag_param_buf, "black") == 0)
58     {
59     return snprintf(tag_output_buf, tag_output_buf_len, "\033[1;30;47m");
60     }
61     }
62     return 0;
63     }
64 sysadm 1.1
65 sysadm 1.4 #define LML_TAG_QUOTE_MAX_LEVEL 10
66     #define LML_TAG_QUOTE_LEVEL_LOOP 3
67    
68     static const char *lml_tag_quote_color[] = {
69 sysadm 1.5 "\033[33m", // yellow
70     "\033[32m", // green
71     "\033[35m", // magenta
72 sysadm 1.4 };
73    
74     static int lml_tag_quote_level = 0;
75    
76     static int lml_tag_quote_filter(const char *tag_name, const char *tag_param_buf, char *tag_output_buf, size_t tag_output_buf_len)
77     {
78     if (strcasecmp(tag_name, "quote") == 0)
79     {
80     if (lml_tag_quote_level <= LML_TAG_QUOTE_MAX_LEVEL)
81     {
82     lml_tag_quote_level++;
83     }
84 sysadm 1.14 return snprintf(tag_output_buf, tag_output_buf_len, "%s",
85     lml_tag_quote_color[lml_tag_quote_level % LML_TAG_QUOTE_LEVEL_LOOP]);
86 sysadm 1.4 }
87     else if (strcasecmp(tag_name, "/quote") == 0)
88     {
89     if (lml_tag_quote_level > 0)
90     {
91     lml_tag_quote_level--;
92     }
93 sysadm 1.14 return snprintf(tag_output_buf, tag_output_buf_len, "%s",
94 sysadm 1.5 (lml_tag_quote_level > 0 ? lml_tag_quote_color[lml_tag_quote_level % LML_TAG_QUOTE_LEVEL_LOOP] : "\033[m"));
95 sysadm 1.4 }
96    
97     return 0;
98     }
99    
100 sysadm 1.2 const static char *LML_tag_def[][3] = {
101     {"left", "[", ""},
102     {"right", "]", NULL},
103 sysadm 1.6 {"bold", "\033[1m", ""}, // does not work in Fterm
104 sysadm 1.5 {"/bold", "\033[22m", NULL},
105 sysadm 1.3 {"b", "\033[1m", ""},
106 sysadm 1.5 {"/b", "\033[22m", NULL},
107 sysadm 1.7 {"italic", "\033[5m", ""}, // use blink instead
108 sysadm 1.6 {"/italic", "\033[m", NULL}, // \033[25m does not work in Fterm
109 sysadm 1.5 {"i", "\033[5m", ""},
110 sysadm 1.6 {"/i", "\033[m", NULL},
111 sysadm 1.2 {"underline", "\033[4m", ""},
112 sysadm 1.6 {"/underline", "\033[m", NULL}, // \033[24m does not work in Fterm
113 sysadm 1.2 {"u", "\033[4m", ""},
114 sysadm 1.6 {"/u", "\033[m", NULL},
115 sysadm 1.3 {"color", NULL, (const char *)lml_tag_color_filter},
116     {"/color", "\033[m", NULL},
117 sysadm 1.4 {"quote", NULL, (const char *)lml_tag_quote_filter},
118     {"/quote", NULL, (const char *)lml_tag_quote_filter},
119 sysadm 1.2 {"url", "", ""},
120 sysadm 1.13 {"/url", "(链接: %s)", NULL},
121 sysadm 1.2 {"link", "", ""},
122 sysadm 1.13 {"/link", "(链接: %s)", NULL},
123 sysadm 1.2 {"email", "", ""},
124     {"/email", "(Email: %s)", NULL},
125     {"user", "", ""},
126 sysadm 1.13 {"/user", "(用户: %s)", NULL},
127 sysadm 1.2 {"article", "", ""},
128 sysadm 1.13 {"/article", "(文章: %s)", NULL},
129     {"image", "(图片: %s)", ""},
130 sysadm 1.2 {"flash", "(Flash: %s)", ""},
131 sysadm 1.3 {"bwf", "\033[1;31m****\033[m", ""},
132 sysadm 1.1 };
133    
134 sysadm 1.4 #define LML_TAG_COUNT 31
135 sysadm 1.3
136 sysadm 1.1 static int LML_tag_name_len[LML_TAG_COUNT];
137     static int LML_init = 0;
138    
139     inline static void lml_init(void)
140     {
141     int i;
142    
143     if (!LML_init)
144     {
145     for (i = 0; i < LML_TAG_COUNT; i++)
146     {
147 sysadm 1.2 LML_tag_name_len[i] = (int)strlen(LML_tag_def[i][0]);
148 sysadm 1.1 }
149    
150     LML_init = 1;
151     }
152     }
153    
154     int lml_plain(const char *str_in, char *str_out, int buf_len)
155     {
156 sysadm 1.13 char c;
157 sysadm 1.2 char tag_param_buf[LML_TAG_PARAM_BUF_LEN];
158 sysadm 1.1 char tag_output_buf[LML_TAG_OUTPUT_BUF_LEN];
159     int i;
160     int j = 0;
161     int k;
162     int tag_start_pos = -1;
163     int tag_end_pos = -1;
164 sysadm 1.2 int tag_param_pos = -1;
165 sysadm 1.1 int tag_output_len;
166 sysadm 1.5 int new_line = 1;
167     int fb_quote_level = 0;
168 sysadm 1.1
169     lml_init();
170    
171 sysadm 1.4 lml_tag_quote_level = 0;
172    
173 sysadm 1.1 for (i = 0; str_in[i] != '\0'; i++)
174     {
175 sysadm 1.5 if (new_line)
176     {
177     if (fb_quote_level > 0)
178     {
179     lml_tag_quote_level -= fb_quote_level;
180    
181 sysadm 1.14 tag_output_len = snprintf(tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, "%s",
182 sysadm 1.5 (lml_tag_quote_level > 0 ? lml_tag_quote_color[lml_tag_quote_level % LML_TAG_QUOTE_LEVEL_LOOP] : "\033[m"));
183 sysadm 1.8 if (j + tag_output_len >= buf_len)
184 sysadm 1.5 {
185 sysadm 1.8 log_error("Buffer is not longer enough for output string %d >= %d\n", j + tag_output_len, buf_len);
186 sysadm 1.5 str_out[j] = '\0';
187     return j;
188     }
189     memcpy(str_out + j, tag_output_buf, (size_t)tag_output_len);
190     j += tag_output_len;
191    
192     fb_quote_level = 0;
193     }
194    
195     while (str_in[i + fb_quote_level * 2] == ':' && str_in[i + fb_quote_level * 2 + 1] == ' ') // FB2000 quote leading str
196     {
197     fb_quote_level++;
198     }
199    
200     if (fb_quote_level > 0)
201     {
202     lml_tag_quote_level += fb_quote_level;
203    
204 sysadm 1.14 tag_output_len = snprintf(tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, "%s",
205 sysadm 1.5 lml_tag_quote_color[(lml_tag_quote_level) % LML_TAG_QUOTE_LEVEL_LOOP]);
206 sysadm 1.8 if (j + tag_output_len >= buf_len)
207 sysadm 1.5 {
208 sysadm 1.8 log_error("Buffer is not longer enough for output string %d >= %d\n", j + tag_output_len, buf_len);
209 sysadm 1.5 str_out[j] = '\0';
210     return j;
211     }
212     memcpy(str_out + j, tag_output_buf, (size_t)tag_output_len);
213     j += tag_output_len;
214     }
215    
216     new_line = 0;
217     }
218    
219 sysadm 1.8 if (str_in[i] == '\033' && str_in[i + 1] == '[') // Escape sequence -- copy directly
220     {
221     for (k = i + 2; str_in[k] != '\0' && str_in[k] != 'm'; k++)
222     ;
223    
224     if (str_in[k] == 'm') // Valid
225     {
226     if (j + (k - i + 1) >= buf_len)
227     {
228     log_error("Buffer is not longer enough for output string %d >= %d\n", j + (k - i + 1), buf_len);
229     str_out[j] = '\0';
230     return j;
231     }
232     memcpy(str_out + j, str_in + i, (size_t)(k - i + 1));
233 sysadm 1.9 j += (k - i + 1);
234 sysadm 1.8 i = k;
235     continue;
236     }
237     else // reach end of string
238     {
239     break;
240     }
241     }
242    
243 sysadm 1.5 if (str_in[i] == '\n')
244     {
245     tag_start_pos = -1; // jump out of tag at end of line
246     new_line = 1;
247     }
248     else if (str_in[i] == '\r')
249     {
250     continue; // ignore '\r'
251     }
252    
253 sysadm 1.1 if (str_in[i] == '[')
254     {
255     tag_start_pos = i + 1;
256     }
257     else if (str_in[i] == ']')
258     {
259     if (tag_start_pos >= 0)
260     {
261     tag_end_pos = i;
262    
263     // Skip space characters
264     while (str_in[tag_start_pos] == ' ')
265     {
266     tag_start_pos++;
267     }
268    
269     for (k = 0; k < LML_TAG_COUNT; k++)
270     {
271 sysadm 1.2 if (strncasecmp(LML_tag_def[k][0], str_in + tag_start_pos, (size_t)LML_tag_name_len[k]) == 0)
272 sysadm 1.1 {
273 sysadm 1.2 tag_param_pos = -1;
274 sysadm 1.1 switch (str_in[tag_start_pos + LML_tag_name_len[k]])
275     {
276     case ' ':
277     tag_param_pos = tag_start_pos + LML_tag_name_len[k] + 1;
278     while (str_in[tag_param_pos] == ' ')
279     {
280     tag_param_pos++;
281     }
282 sysadm 1.2 strncpy(tag_param_buf, str_in + tag_param_pos, (size_t)MIN(tag_end_pos - tag_param_pos, LML_TAG_PARAM_BUF_LEN));
283     tag_param_buf[MIN(tag_end_pos - tag_param_pos, LML_TAG_PARAM_BUF_LEN)] = '\0';
284     case ']':
285 sysadm 1.3 if (tag_param_pos == -1 && LML_tag_def[k][1] != NULL && LML_tag_def[k][2] != NULL) // Apply default param if not defined
286 sysadm 1.1 {
287 sysadm 1.2 strncpy(tag_param_buf, LML_tag_def[k][2], LML_TAG_PARAM_BUF_LEN - 1);
288     tag_param_buf[LML_TAG_PARAM_BUF_LEN - 1] = '\0';
289 sysadm 1.1 }
290 sysadm 1.3 if (LML_tag_def[k][1] != NULL)
291     {
292     tag_output_len = snprintf(tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, LML_tag_def[k][1], tag_param_buf);
293     }
294     else
295     {
296     tag_output_len = ((lml_tag_filter_cb)LML_tag_def[k][2])(
297     LML_tag_def[k][0], tag_param_buf, tag_output_buf, LML_TAG_OUTPUT_BUF_LEN);
298     }
299 sysadm 1.8 if (j + tag_output_len >= buf_len)
300 sysadm 1.1 {
301 sysadm 1.8 log_error("Buffer is not longer enough for output string %d >= %d\n", j + tag_output_len, buf_len);
302 sysadm 1.1 str_out[j] = '\0';
303     return j;
304     }
305     memcpy(str_out + j, tag_output_buf, (size_t)tag_output_len);
306     j += tag_output_len;
307     break;
308     default: // tag_name not match
309     continue;
310     }
311     break;
312     }
313     }
314    
315     tag_start_pos = -1;
316     }
317     }
318     else if (tag_start_pos == -1) // not in LML tag
319     {
320 sysadm 1.13 if (str_in[i] & 0b10000000) // head of multi-byte character
321 sysadm 1.1 {
322 sysadm 1.13 if (j + 4 >= buf_len) // Assuming UTF-8 CJK characters use 4 bytes, though most of them actually use 3 bytes
323 sysadm 1.1 {
324 sysadm 1.13 log_error("Buffer is not longer enough for output string %ld >= %d\n", j + 4, buf_len);
325 sysadm 1.1 str_out[j] = '\0';
326     return j;
327     }
328 sysadm 1.13
329     c = (str_in[i] & 0b01110000) << 1;
330     while (c & 0b10000000)
331 sysadm 1.1 {
332 sysadm 1.13 str_out[j++] = str_in[i++];
333     if (str_in[i] == '\0')
334     {
335     str_out[j] = '\0';
336     return j;
337     }
338     c = (c & 0b01111111) << 1;
339 sysadm 1.1 }
340     }
341    
342 sysadm 1.8 if (j + 1 >= buf_len)
343 sysadm 1.1 {
344 sysadm 1.8 log_error("Buffer is not longer enough for output string %ld >= %d\n", j + 1, buf_len);
345 sysadm 1.1 str_out[j] = '\0';
346     return j;
347     }
348     str_out[j++] = str_in[i];
349     }
350     else // in LML tag
351     {
352     // Do nothing
353     }
354     }
355    
356 sysadm 1.5 if (lml_tag_quote_level > 0)
357     {
358     tag_output_len = snprintf(tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, "\033[m");
359 sysadm 1.8 if (j + tag_output_len >= buf_len)
360 sysadm 1.5 {
361 sysadm 1.8 log_error("Buffer is not longer enough for output string %d >= %d\n", j + tag_output_len, buf_len);
362 sysadm 1.5 str_out[j] = '\0';
363     return j;
364     }
365     memcpy(str_out + j, tag_output_buf, (size_t)tag_output_len);
366     j += tag_output_len;
367     }
368    
369 sysadm 1.1 str_out[j] = '\0';
370    
371     return j;
372 sysadm 1.5 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1