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

Diff of /lbbs/src/lml.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 1.48 by sysadm, Mon Nov 17 12:47:41 2025 UTC Revision 1.56 by sysadm, Wed Jan 21 07:54:50 2026 UTC
# Line 3  Line 3 
3   * lml   * lml
4   *   - LML render   *   - LML render
5   *   *
6   * Copyright (C) 2004-2025  Leaflet <leaflet@leafok.com>   * Copyright (C) 2004-2026  Leaflet <leaflet@leafok.com>
7   */   */
8    
9  #ifdef HAVE_CONFIG_H  #ifdef HAVE_CONFIG_H
# Line 14  Line 14 
14  #include "lml.h"  #include "lml.h"
15  #include "log.h"  #include "log.h"
16  #include "str_process.h"  #include "str_process.h"
17    #include "trie_dict.h"
18  #include <ctype.h>  #include <ctype.h>
19  #include <stdio.h>  #include <stdio.h>
20  #include <string.h>  #include <string.h>
# Line 21  Line 22 
22    
23  enum _lml_constant_t  enum _lml_constant_t
24  {  {
25            LML_TAG_NAME_BUF_LEN = 21,
26          LML_TAG_PARAM_BUF_LEN = 256,          LML_TAG_PARAM_BUF_LEN = 256,
27          LML_TAG_OUTPUT_BUF_LEN = 1024,          LML_TAG_OUTPUT_BUF_LEN = 1024,
28          LML_TAG_QUOTE_MAX_LEVEL = 10,          LML_TAG_QUOTE_MAX_LEVEL = 10,
# Line 162  const LML_TAG_DEF lml_tag_def[] = { Line 164  const LML_TAG_DEF lml_tag_def[] = {
164    
165  static const int lml_tag_count = sizeof(lml_tag_def) / sizeof(LML_TAG_DEF);  static const int lml_tag_count = sizeof(lml_tag_def) / sizeof(LML_TAG_DEF);
166  static int lml_tag_name_len[sizeof(lml_tag_def) / sizeof(LML_TAG_DEF)];  static int lml_tag_name_len[sizeof(lml_tag_def) / sizeof(LML_TAG_DEF)];
167    static TRIE_NODE *p_lml_tag_dict;
168  static int lml_ready = 0;  static int lml_ready = 0;
169    
170  inline static void lml_init(void)  int lml_init(void)
171  {  {
172            char tag_name_buf[LML_TAG_NAME_BUF_LEN];
173          int i;          int i;
174            int j;
175    
176          if (!lml_ready)          if (lml_ready)
177            {
178                    lml_cleanup();
179            }
180    
181            p_lml_tag_dict = trie_dict_create();
182            if (p_lml_tag_dict == NULL)
183          {          {
184                  for (i = 0; i < lml_tag_count; i++)                  log_error("trie_dict_create(lml_tag_dict) error");
185                    return -1;
186            }
187    
188            for (i = 0; i < lml_tag_count; i++)
189            {
190                    for (j = 0; j < sizeof(tag_name_buf) - 1 && lml_tag_def[i].tag_name[j] != '\0'; j++)
191                  {                  {
192                          lml_tag_name_len[i] = (int)strlen(lml_tag_def[i].tag_name);                          tag_name_buf[j] = (char)tolower(lml_tag_def[i].tag_name[j]);
193                  }                  }
194                    tag_name_buf[j] = '\0';
195                    lml_tag_name_len[i] = j;
196    
197                  lml_ready = 1;                  if (trie_dict_set(p_lml_tag_dict, tag_name_buf, (int64_t)i) != 1)
198                    {
199                            log_error("trie_dict_set(lml_tag_dict, %s, %d) error", tag_name_buf, i);
200                            lml_cleanup();
201                            return -1;
202                    }
203          }          }
204    
205            lml_ready = 1;
206    
207            return 0;
208  }  }
209    
210  #define CHECK_AND_APPEND_OUTPUT(out_buf, out_buf_len, out_buf_offset, tag_out, tag_out_len, line_width)                             \  void lml_cleanup(void)
211          {                                                                                                                               \  {
212                  if ((out_buf_offset) + (tag_out_len) >= (out_buf_len))                                                                      \          if (p_lml_tag_dict != NULL)
213                  {                                                                                                                           \          {
214                          log_error("Buffer is not longer enough for output string %d >= %d\n", (out_buf_offset) + (tag_out_len), (out_buf_len)); \                  trie_dict_destroy(p_lml_tag_dict);
215                          out_buf[out_buf_offset] = '\0';                                                                                         \                  p_lml_tag_dict = NULL;
216                          return (out_buf_offset);                                                                                                \          }
217                  }                                                                                                                           \  
218                  memcpy((out_buf) + (out_buf_offset), (tag_out), (size_t)(tag_out_len));                                                     \          lml_ready = 0;
219                  *((out_buf) + (out_buf_offset) + (size_t)(tag_out_len)) = '\0';                                                             \  }
220                  (line_width) += str_length((out_buf) + (out_buf_offset), 1);                                                                \  
221                  (out_buf_offset) += (tag_out_len);                                                                                          \  #define CHECK_AND_APPEND_OUTPUT(out_buf, out_buf_len, out_buf_offset, tag_out, tag_out_len, line_width)                           \
222            if ((tag_out_len) > 0)                                                                                                        \
223            {                                                                                                                             \
224                    if ((out_buf_offset) + (tag_out_len) >= (out_buf_len))                                                                    \
225                    {                                                                                                                         \
226                            log_error("Buffer is not longer enough for output string %d >= %d", (out_buf_offset) + (tag_out_len), (out_buf_len)); \
227                            out_buf[out_buf_offset] = '\0';                                                                                       \
228                            return (out_buf_offset);                                                                                              \
229                    }                                                                                                                         \
230                    memcpy((out_buf) + (out_buf_offset), (tag_out), (size_t)(tag_out_len));                                                   \
231                    *((out_buf) + (out_buf_offset) + (size_t)(tag_out_len)) = '\0';                                                           \
232                    (line_width) += str_length((out_buf) + (out_buf_offset), 1);                                                              \
233                    (out_buf_offset) += (tag_out_len);                                                                                        \
234          }          }
235    
236  int lml_render(const char *str_in, char *str_out, int buf_len, int width, int quote_mode)  int lml_render(const char *str_in, char *str_out, int buf_len, int width, int quote_mode)
# Line 199  int lml_render(const char *str_in, char Line 239  int lml_render(const char *str_in, char
239          clock_t clock_end;          clock_t clock_end;
240    
241          char c;          char c;
242            char tag_name_buf[LML_TAG_NAME_BUF_LEN];
243          char tag_param_buf[LML_TAG_PARAM_BUF_LEN];          char tag_param_buf[LML_TAG_PARAM_BUF_LEN];
244          char tag_output_buf[LML_TAG_OUTPUT_BUF_LEN];          char tag_output_buf[LML_TAG_OUTPUT_BUF_LEN];
245          int i;          int i;
246          int j = 0;          int j = 0;
247          int k;          int k;
248            int last_i = -1;
249            int64_t tag_index;
250          int tag_start_pos = -1;          int tag_start_pos = -1;
251          int tag_name_pos = -1;          int tag_name_pos = -1;
252          int tag_end_pos = -1;          int tag_end_pos = -1;
# Line 211  int lml_render(const char *str_in, char Line 254  int lml_render(const char *str_in, char
254          int tag_output_len;          int tag_output_len;
255          int new_line = 1;          int new_line = 1;
256          int fb_quote_level = 0;          int fb_quote_level = 0;
         int tag_name_found;  
257          int line_width = 0;          int line_width = 0;
258          char tab_spaces[TAB_SIZE + 1];          char tab_spaces[TAB_SIZE + 1];
259          int tab_width = 0;          int tab_width = 0;
260    
261          clock_begin = clock();          clock_begin = clock();
262    
263          lml_init();  #ifdef _DEBUG
264            size_t str_in_len = strlen(str_in);
265    #endif
266    
267            if (!lml_ready)
268            {
269                    log_error("LML module is not initialized");
270                    return -1;
271            }
272    
273          lml_tag_disabled = 0;          lml_tag_disabled = 0;
274          lml_tag_quote_level = 0;          lml_tag_quote_level = 0;
# Line 230  int lml_render(const char *str_in, char Line 280  int lml_render(const char *str_in, char
280    
281          for (i = 0; str_in[i] != '\0'; i++)          for (i = 0; str_in[i] != '\0'; i++)
282          {          {
283    #ifdef _DEBUG
284                    if (i >= str_in_len)
285                    {
286                            log_error("Bug: i(%d) >= str_in_len(%d)", i, str_in_len);
287                            break;
288                    }
289    #endif
290    
291                  if (!lml_tag_disabled && new_line)                  if (!lml_tag_disabled && new_line)
292                  {                  {
293                          while (str_in[i] == ':' && str_in[i + 1] == ' ') // FB2000 quote leading str                          while (str_in[i] == ':' && str_in[i + 1] == ' ') // FB2000 quote leading str
# Line 256  int lml_render(const char *str_in, char Line 314  int lml_render(const char *str_in, char
314                          CHECK_AND_APPEND_OUTPUT(str_out, buf_len, j, tag_output_buf, tag_output_len, line_width);                          CHECK_AND_APPEND_OUTPUT(str_out, buf_len, j, tag_output_buf, tag_output_len, line_width);
315    
316                          new_line = 0;                          new_line = 0;
317                            i--; // redo at current i
318                            continue;
319                  }                  }
320    
321                  if (lml_tag_disabled && new_line)                  if (lml_tag_disabled && new_line)
# Line 349  int lml_render(const char *str_in, char Line 409  int lml_render(const char *str_in, char
409                          CHECK_AND_APPEND_OUTPUT(str_out, buf_len, j, tab_spaces, tab_width, line_width);                          CHECK_AND_APPEND_OUTPUT(str_out, buf_len, j, tab_spaces, tab_width, line_width);
410                          continue;                          continue;
411                  }                  }
412                    else if (!quote_mode && str_in[i] == '\033')
413                    {
414                            continue; // Skip control characters while not in quote mode
415                    }
416    
417                  if (!lml_tag_disabled && str_in[i] == '[')                  if (!lml_tag_disabled && str_in[i] == '[')
418                  {                  {
# Line 383  int lml_render(const char *str_in, char Line 447  int lml_render(const char *str_in, char
447                                  tag_name_pos++;                                  tag_name_pos++;
448                          }                          }
449    
450                          for (tag_name_found = 0, k = 0; k < lml_tag_count; k++)                          for (k = 0; k < sizeof(tag_name_buf) - 1 && tag_name_pos + k < tag_end_pos; k++)
451                            {
452                                    if (str_in[tag_name_pos + k] == ' ' || str_in[tag_name_pos + k] == ']')
453                                    {
454                                            break;
455                                    }
456                                    tag_name_buf[k] = (char)tolower(str_in[tag_name_pos + k]);
457                            }
458                            tag_name_buf[k] = '\0';
459    
460                            k = -1;
461                            if (tag_name_buf[0] != '\0')
462                          {                          {
463                                  if (strncasecmp(lml_tag_def[k].tag_name, str_in + tag_name_pos, (size_t)lml_tag_name_len[k]) == 0)                                  tag_index = -1;
464                                    if (trie_dict_get(p_lml_tag_dict, tag_name_buf, (int64_t *)&tag_index) < 0)
465                                  {                                  {
466                                          tag_param_pos = -1;                                          log_error("trie_dict_get(lml_tag_dict, %s) error", tag_name_buf);
467                                          switch (str_in[tag_name_pos + lml_tag_name_len[k]])                                  }
468                                    else
469                                    {
470                                            k = (int)tag_index;
471                                    }
472                            }
473    
474                            if (k != -1)
475                            {
476                                    tag_param_pos = -1;
477                                    switch (str_in[tag_name_pos + lml_tag_name_len[k]])
478                                    {
479                                    case ' ':
480                                            tag_param_pos = tag_name_pos + lml_tag_name_len[k] + 1;
481                                            while (str_in[tag_param_pos] == ' ')
482                                          {                                          {
483                                          case ' ':                                                  tag_param_pos++;
484                                                  tag_name_found = 1;                                          }
485                                                  tag_param_pos = tag_name_pos + lml_tag_name_len[k] + 1;                                          strncpy(tag_param_buf, str_in + tag_param_pos, (size_t)MIN(tag_end_pos - tag_param_pos, LML_TAG_PARAM_BUF_LEN));
486                                                  while (str_in[tag_param_pos] == ' ')                                          tag_param_buf[MIN(tag_end_pos - tag_param_pos, LML_TAG_PARAM_BUF_LEN)] = '\0';
487                                    case ']':
488                                            if (tag_param_pos == -1 && lml_tag_def[k].tag_output != NULL && lml_tag_def[k].default_param != NULL) // Apply default param if not defined
489                                            {
490                                                    strncpy(tag_param_buf, lml_tag_def[k].default_param, LML_TAG_PARAM_BUF_LEN - 1);
491                                                    tag_param_buf[LML_TAG_PARAM_BUF_LEN - 1] = '\0';
492                                            }
493                                            tag_output_len = 0;
494                                            if (!quote_mode)
495                                            {
496                                                    if (lml_tag_def[k].tag_output != NULL)
497                                                    {
498                                                            tag_output_len = snprintf(tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, lml_tag_def[k].tag_output, tag_param_buf);
499                                                    }
500                                                    else if (lml_tag_def[k].tag_filter_cb != NULL)
501                                                    {
502                                                            tag_output_len = lml_tag_def[k].tag_filter_cb(
503                                                                    lml_tag_def[k].tag_name, tag_param_buf, tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, 0);
504                                                    }
505                                            }
506                                            else // if (quote_mode)
507                                            {
508                                                    if (lml_tag_def[k].quote_mode_output != NULL)
509                                                  {                                                  {
510                                                          tag_param_pos++;                                                          tag_output_len = snprintf(tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, lml_tag_def[k].quote_mode_output, tag_param_buf);
511                                                  }                                                  }
512                                                  strncpy(tag_param_buf, str_in + tag_param_pos, (size_t)MIN(tag_end_pos - tag_param_pos, LML_TAG_PARAM_BUF_LEN));                                                  else if (lml_tag_def[k].tag_filter_cb != NULL)
                                                 tag_param_buf[MIN(tag_end_pos - tag_param_pos, LML_TAG_PARAM_BUF_LEN)] = '\0';  
                                         case ']':  
                                                 tag_name_found = 1;  
                                                 if (tag_param_pos == -1 && lml_tag_def[k].tag_output != NULL && lml_tag_def[k].default_param != NULL) // Apply default param if not defined  
513                                                  {                                                  {
514                                                          strncpy(tag_param_buf, lml_tag_def[k].default_param, LML_TAG_PARAM_BUF_LEN - 1);                                                          tag_output_len = lml_tag_def[k].tag_filter_cb(
515                                                          tag_param_buf[LML_TAG_PARAM_BUF_LEN - 1] = '\0';                                                                  lml_tag_def[k].tag_name, tag_param_buf, tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, 1);
516                                                  }                                                  }
517                                                  tag_output_len = 0;                                          }
518                                                  if (!quote_mode)  
519                                            if (line_width + tag_output_len > width)
520                                            {
521                                                    if (i > last_i)
522                                                  {                                                  {
523                                                          if (lml_tag_def[k].tag_output != NULL)                                                          last_i = i;
524                                                          {                                                          CHECK_AND_APPEND_OUTPUT(str_out, buf_len, j, "\n", 1, line_width);
525                                                                  tag_output_len = snprintf(tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, lml_tag_def[k].tag_output, tag_param_buf);                                                          new_line = 1;
526                                                          }                                                          line_width = 0;
527                                                          else if (lml_tag_def[k].tag_filter_cb != NULL)                                                          i--; // redo at current i
528                                                          {                                                          continue;
                                                                 tag_output_len = lml_tag_def[k].tag_filter_cb(  
                                                                         lml_tag_def[k].tag_name, tag_param_buf, tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, 0);  
                                                         }  
529                                                  }                                                  }
530                                                  else // if (quote_mode)                                                  else
531                                                  {                                                  {
532                                                          if (lml_tag_def[k].quote_mode_output != NULL)                                                          continue; // Output current tag in plain text if line width exceeded
                                                         {  
                                                                 tag_output_len = snprintf(tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, lml_tag_def[k].quote_mode_output, tag_param_buf);  
                                                         }  
                                                         else if (lml_tag_def[k].tag_filter_cb != NULL)  
                                                         {  
                                                                 tag_output_len = lml_tag_def[k].tag_filter_cb(  
                                                                         lml_tag_def[k].tag_name, tag_param_buf, tag_output_buf, LML_TAG_OUTPUT_BUF_LEN, 1);  
                                                         }  
533                                                  }                                                  }
                                                 CHECK_AND_APPEND_OUTPUT(str_out, buf_len, j, tag_output_buf, tag_output_len, line_width);  
                                                 break;  
                                         default: // tag_name not match  
                                                 continue;  
534                                          }                                          }
535    
536                                            CHECK_AND_APPEND_OUTPUT(str_out, buf_len, j, tag_output_buf, tag_output_len, line_width);
537                                          break;                                          break;
538                                    default:
539                                            log_error("Bug: tag name not match");
540                                  }                                  }
541                          }                          }
542                            else // tag_name not found
                         if (!tag_name_found)  
543                          {                          {
544                                  if (line_width + 1 > width)                                  if (line_width + 1 > width)
545                                  {                                  {


Legend:
Removed lines/characters  
Changed lines/characters
  Added lines/characters

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