src/highlighter.c

Mon, 24 Apr 2023 21:01:41 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 24 Apr 2023 21:01:41 +0200
changeset 67
5da2cb5aea6b
parent 66
1b12cf799fee
parent 61
47a5fc33590a
permissions
-rw-r--r--

merge upstream changes

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2016 Mike Becker. All rights reserved.
     5  *
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  *
    28  */
    30 #include "highlighter.h"
    32 #include <stdlib.h>
    33 #include <stdio.h>
    34 #include <string.h>
    35 #include <ctype.h>
    37 #include <cx/string.h>
    39 static void put_htmlescaped(CxBuffer *dest, char c) {
    40     if (c == '>') {
    41         cxBufferPutString(dest, "&gt;");
    42     } else if (c == '<') {
    43         cxBufferPutString(dest, "&lt;");
    44     } else if (c) {
    45         cxBufferPut(dest, c);
    46     }
    47 }
    49 static void put_htmlescapedstr(CxBuffer *dest, cxstring s) {
    50     for (int i = 0 ; i < s.length ; i++) {
    51         put_htmlescaped(dest, s.ptr[i]);
    52     }
    53 }
    55 static int check_keyword(cxstring word, const char** keywords) {
    56     for (int i = 0 ; keywords[i] ; i++) {
    57         if (cx_strcmp(word, cx_str(keywords[i])) == 0) {
    58             return 1;
    59         }
    60     }
    61     return 0;
    62 }
    64 static int check_capsonly(cxstring word) {
    65     for (size_t i = 0 ; i < word.length ; i++) {
    66         if (!isupper(word.ptr[i]) && !isdigit(word.ptr[i])
    67                 && word.ptr[i] != '_') {
    68             return 0;
    69         }
    70     }
    71     return 1;
    72 }
    74 /* Plaintext Highlighter */
    76 void c2html_plain_highlighter(char const *src, CxBuffer *dest,
    77         c2html_highlighter_data *hd) {
    78     while (*src && *src != '\n') {
    79         if (*src != '\r') {
    80             put_htmlescaped(dest, *src);
    81         }
    82         src++;
    83     }
    84     cxBufferPut(dest, '\n');
    85 }
    87 /* C Highlighter */
    89 static const char* ckeywords[] = {
    90     "auto", "break", "case", "char", "const", "continue", "default", "do",
    91     "double", "else", "enum", "extern", "float", "for", "goto", "if", "int",
    92     "long", "register", "return", "short", "signed", "sizeof", "static",
    93     "struct", "switch", "typedef", "union", "unsigned", "void", "volatile",
    94     "while", NULL
    95 };
    97 void c2html_c_highlighter(char const *src, CxBuffer *dest,
    98         c2html_highlighter_data *hd) {
    99     /* reset buffers without clearing them */
   100     hd->primary_buffer.size = hd->primary_buffer.pos = 0;
   101     hd->secondary_buffer.size = hd->secondary_buffer.pos = 0;
   103     /* alias the buffers for better handling */
   104     CxBuffer *wbuf = &hd->primary_buffer;
   105     CxBuffer *ifilebuf = &hd->secondary_buffer;
   107     /* local information */
   108     size_t sp = (size_t)-1;
   109     int isstring = 0, iscomment = 0, isinclude = 0, parseinclude = 0;
   110     char quote = '\0';
   111     int isescaping = 0;
   113     /* continue a multi line comment highlighting */
   114     if (hd->multiline_comment) {
   115         iscomment = 1;
   116         cxBufferPutString(dest, "<span class=\"c2html-comment\">");
   117     }
   119     char c;
   120     do {
   121         c = src[++sp];
   122         if (c == '\r') continue;
   124         /* comments */
   125         if (!isstring && c == '/') {
   126             if (hd->multiline_comment && sp > 0 && src[sp-1] == '*') {
   127                 iscomment = 0;
   128                 hd->multiline_comment = 0;
   129                 cxBufferPutString(dest, "/</span>");
   130                 continue;
   131             } else if (!iscomment && (src[sp+1] == '/' || src[sp+1] == '*')) {
   132                 iscomment = 1;
   133                 hd->multiline_comment = (src[sp+1] == '*');
   134                 cxBufferPutString(dest, "<span class=\"c2html-comment\">");
   135             }
   136         }
   138         if (iscomment) {
   139             if (c == '\n') {
   140                 cxBufferPutString(dest, "</span>\n");
   141             } else {
   142                 put_htmlescaped(dest, c);
   143             }
   144         } else if (isinclude) {
   145             if (c == '<') {
   146                 cxBufferPutString(dest,
   147                         "<span class=\"c2html-stdinclude\">&lt;");
   148             } else if (c == '\"') {
   149                 if (parseinclude) {
   150                     cxBufferPutString(dest, "\">");
   151                     cxBufferWrite(ifilebuf->space, 1, ifilebuf->size, dest);
   152                     cxBufferPutString(dest, "\"</a>");
   153                     parseinclude = 0;
   154                 } else {
   155                     cxBufferPutString(dest,
   156                             "<a class=\"c2html-userinclude\" href=\"");
   157                     cxBufferPut(ifilebuf, '\"');
   158                     parseinclude = 1;
   159                 }
   160             } else if (c == '>') {
   161                 cxBufferPutString(dest,  "&gt;</span>");
   162             } else {
   163                 if (parseinclude) {
   164                     cxBufferPut(ifilebuf, c);
   165                 }
   166                 put_htmlescaped(dest, c);
   167             }
   168         } else {
   169             /* strings */
   170             if (!isescaping && (c == '\'' || c == '\"')) {
   171                 if (isstring) {
   172                     put_htmlescaped(dest, c);
   173                     if (c == quote) {
   174                         isstring = 0;
   175                         cxBufferPutString(dest, "</span>");
   176                     } else {
   177                         put_htmlescaped(dest, c);
   178                     }
   179                 } else {
   180                     isstring = 1;
   181                     quote = c;
   182                     cxBufferPutString(dest, "<span class=\"c2html-string\">");
   183                     put_htmlescaped(dest, c);
   184                 }
   185             } else {
   186                 if (isstring) {
   187                     put_htmlescaped(dest, c);
   188                 } else if (isalnum(c) ||  c == '_' || c == '#') {
   189                     /* buffer the current word */
   190                     cxBufferPut(wbuf, c);
   191                 } else {
   192                     /* write buffered word, if any */
   193                     if (wbuf->size > 0) {
   194                         cxstring word = cx_strn(wbuf->space, wbuf->size);
   195                         int closespan = 1;
   196                         cxstring typesuffix = CX_STR("_t");
   197                         if (check_keyword(word, ckeywords)) {
   198                             cxBufferPutString(dest,
   199                                     "<span class=\"c2html-keyword\">");
   200                         } else if (cx_strsuffix(word, typesuffix)) {
   201                             cxBufferPutString(dest,
   202                                 "<span class=\"c2html-type\">");
   203                         } else if (word.ptr[0] == '#') {
   204                             isinclude = !cx_strcmp(word, CX_STR("#include"));
   205                             cxBufferPutString(dest,
   206                                 "<span class=\"c2html-directive\">");
   207                         } else if (check_capsonly(word)) {
   208                             cxBufferPutString(dest,
   209                                 "<span class=\"c2html-macroconst\">");
   210                         } else {
   211                             closespan = 0;
   212                         }
   213                         put_htmlescapedstr(dest, word);
   214                         if (closespan) {
   215                             cxBufferPutString(dest, "</span>");
   216                         }
   217                     }
   218                     wbuf->pos = wbuf->size = 0; /* reset word buffer */
   220                     /* write current character */
   221                     put_htmlescaped(dest, c);
   222                 }
   223             }
   225             isescaping = !isescaping & (c == '\\');
   226         }
   227     } while (c && c != '\n');
   228 }
   230 /* Java Highlighter */
   232 static const char* jkeywords[] = {
   233     "abstract", "continue", "for", "new", "switch", "assert", "default", "goto",
   234     "package", "synchronized", "boolean", "do", "if", "private", "this",
   235     "break", "double", "implements", "protected", "throw", "byte", "else",
   236     "import", "public", "throws", "case", "enum", "instanceof", "return",
   237     "transient", "catch", "extends", "int", "short", "try", "char", "final",
   238     "interface", "static", "void", "class", "finally", "long", "strictfp",
   239     "volatile", "const", "float", "native", "super", "while", NULL
   240 };
   242 void c2html_java_highlighter(char const *src, CxBuffer *dest,
   243         c2html_highlighter_data *hd) {
   244     /* reset buffers without clearing them */
   245     hd->primary_buffer.size = hd->primary_buffer.pos = 0;
   246     hd->secondary_buffer.size = hd->secondary_buffer.pos = 0;
   248     /* alias the buffers for better handling */
   249     CxBuffer *wbuf = &hd->primary_buffer;
   251     /* local information */
   252     size_t sp = (size_t)-1;
   253     int isstring = 0, iscomment = 0, isimport = 0;
   254     char quote = '\0';
   255     int isescaping = 0;
   257     if (hd->multiline_comment) {
   258         iscomment = 1;
   259         cxBufferPutString(dest, "<span class=\"c2html-comment\">");
   260     }
   262     char c;
   263     do {
   264         c = src[++sp];
   265         if (c == '\r') continue;
   267         /* comments */
   268         if (!isstring && c == '/') {
   269             if (hd->multiline_comment && sp > 0 && src[sp-1] == '*') {
   270                 iscomment = 0;
   271                 hd->multiline_comment = 0;
   272                 cxBufferPutString(dest, "/</span>");
   273                 continue;
   274             } else if (!iscomment && (src[sp+1] == '/' || src[sp+1] == '*')) {
   275                 iscomment = 1;
   276                 hd->multiline_comment = (src[sp+1] == '*');
   277                 cxBufferPutString(dest, "<span class=\"c2html-comment\">");
   278             }
   279         }
   281         if (iscomment) {
   282             if (c == '\n') {
   283                 cxBufferPutString(dest, "</span>\n");
   284             } else {
   285                 put_htmlescaped(dest, c);
   286             }
   287         } else if (isimport) {
   288             /* TODO: local imports */
   289         } else {
   290             /* strings */
   291             if (!isescaping && (c == '\'' || c == '\"')) {
   292                 if (isstring) {
   293                     put_htmlescaped(dest, c);
   294                     if (c == quote) {
   295                         isstring = 0;
   296                         cxBufferPutString(dest, "</span>");
   297                     } else {
   298                         put_htmlescaped(dest, c);
   299                     }
   300                 } else {
   301                     isstring = 1;
   302                     quote = c;
   303                     cxBufferPutString(dest,
   304                         "<span class=\"c2html-string\">");
   305                     put_htmlescaped(dest, c);
   306                 }
   307             } else {
   308                 if (isstring) {
   309                     put_htmlescaped(dest, c);
   310                 } else if (isalnum(c) || c == '_' || c == '@') {
   311                     /* buffer the current word */
   312                     cxBufferPut(wbuf, c);
   313                 } else {
   314                     /* write buffered word, if any */
   315                     if (wbuf->size > 0) {
   316                         cxstring word = cx_strn(wbuf->space, wbuf->size);
   317                         int closespan = 1;
   318                         if (check_keyword(word, jkeywords)) {
   319                             cxBufferPutString(dest,
   320                                 "<span class=\"c2html-keyword\">");
   321                         } else if (isupper(word.ptr[0])) {
   322                             cxBufferPutString(dest,
   323                                 "<span class=\"c2html-type\">");
   324                         } else if (word.ptr[0] == '@') {
   325                             cxBufferPutString(dest,
   326                                 "<span class=\"c2html-directive\">");
   327                         } else if (check_capsonly(word)) {
   328                             cxBufferPutString(dest,
   329                                 "<span class=\"c2html-macroconst\">");
   330                         } else {
   331                             closespan = 0;
   332                         }
   333                         put_htmlescapedstr(dest, word);
   335                         if (closespan) {
   336                             cxBufferPutString(dest, "</span>");
   337                         }
   338                     }
   339                     wbuf->pos = wbuf->size = 0; /* reset buffer */
   341                     /* write current character */
   342                     put_htmlescaped(dest, c);
   343                 }
   344             }
   346             isescaping = !isescaping & (c == '\\');
   347         }
   348     } while (c && c != '\n');
   349 }

mercurial