home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / c-filt.c < prev    next >
C/C++ Source or Header  |  1998-09-22  |  13KB  |  562 lines

  1. /*
  2.  * Program: A simple comment and keyword attributer for vile
  3.  * Author : Jukka Keto, jketo@cs.joensuu.fi
  4.  * Date   : 30.12.1994
  5.  * Modifications:  kevin buettner and paul fox  2/95
  6.  *         string literal ("Literal") support --  ben stoltz
  7.  *
  8.  * $Header: /usr/build/vile/vile/RCS/c-filt.c,v 1.12 1998/09/22 10:50:30 Gary.Ross Exp $
  9.  *
  10.  * Features:
  11.  *     - Reads the keyword file ".vile.keywords" from the home directory.
  12.  *      Keyword file consists lines "keyword:attribute" where
  13.  *      keyword is any alphanumeric string [#a-zA-Z0-9_] followed
  14.  *      by colon ":" and attribute character; "I" for italic,
  15.  *      "U" for underline, "B" for bold, "R" for reverse or
  16.  *      "C#" for color (where # is a single hexadecimal digit representing
  17.  *      one of 16 colors).
  18.  *    - Attributes the file read from stdin using vile attribute sequences
  19.  *      and outputs the file to stdout with keywords and comments
  20.  *      attributed.
  21.  *    - Normal C-Comments are handled by the pseudo-keyword "Comments".
  22.  *    - "String literals" are handled by the pseudo-keyword "Literals".
  23.  *    - #if, #include, etc. are handled by the pseudo-keyword "Cpp".
  24.  *    - Here is a macro one might use to invoke the colorizer:
  25.  *        30 store-macro
  26.  *            write-message "[Attaching C/C++ attributes...]"
  27.  *            set-variable %savcol $curcol
  28.  *            set-variable %savline $curline
  29.  *            set-variable %modified $modified
  30.  *            goto-beginning-of-file
  31.  *            filter-til end-of-file "c-filt"
  32.  *            goto-beginning-of-file
  33.  *            attribute-cntl_a-sequences-til end-of-file
  34.  *            ~if ¬ %modified
  35.  *                unmark-buffer
  36.  *            ~endif
  37.  *            %savline goto-line
  38.  *            %savcol goto-column
  39.  *            write-message "[Attaching C/C++ attributes...done ]"
  40.  *        ~endm
  41.  *        bind-key execute-macro-30 ^X-q
  42.  *
  43.  * example .vile.keywords files:
  44.  * (first, for color use)
  45.  *    Comments:C1
  46.  *    Literal:C1
  47.  *    Cpp:C2
  48.  *    if:C3
  49.  *    else:C3
  50.  *    for:C3
  51.  *    return:C3
  52.  *    while:C3
  53.  *    switch:C3
  54.  *    case:C3
  55.  *    do:C3
  56.  *    goto:C3
  57.  *    break:C3
  58.  *
  59.  * (for non-color use)
  60.  *    Comments:U
  61.  *    Literal:U
  62.  *    Cpp:I
  63.  *    if:B
  64.  *    else:B
  65.  *    for:B
  66.  *    return:B
  67.  *    while:B
  68.  *    switch:B
  69.  *    case:B
  70.  *    do:B
  71.  *    goto:B
  72.  *    break:B
  73.  *
  74.  * Note:
  75.  *    - I use this to get the coloring effects in XVile, or when
  76.  *      using a terminal emulator which supports color.  some, for
  77.  *      instance, allow the mapping of bold and italic attributes to
  78.  *      color.
  79.  *
  80.  * Known Bugs (other features):
  81.  *    - The keyword lists should be ordered for optimal operation.
  82.  *
  83.  * Win32 Notes:
  84.  *    1) Keywords are read from either $HOME\vile.keywords or
  85.  *       .\vile.keywords .
  86.  *
  87.  *    2) The console and GUI versions of vile both support full use of
  88.  *       16 colors.  The default color mapping (palette) is as follows:
  89.  *
  90.  *       C0:black       C1:red            C2:green        C3:brown
  91.  *       C4:blue        C5:magenta        C6:cyan         C7:lightgray
  92.  *       C8:gray        C9:brightred      CA:brightgreen  CB:yellow
  93.  *       CC:brightblue  CD:brightmagenta  CE:brightcyan   CF:white
  94.  *
  95.  *    3) Note also that the user may specify the editor's foreground and
  96.  *       background colors (:se fcolor, :se bcolor) as well as a
  97.  *       foreground color for search matches (:se visual-matches).
  98.  *
  99.  *    Pulling 1-3 together, here is an example vile.rc file that
  100.  *    sets the foreground color to white, background color to (dark) blue,
  101.  *    and visual matches color to bright red:
  102.  *
  103.  *    vile.rc
  104.  *    =======
  105.       set bcolor=blue
  106.       set fcolor=white
  107.       set visual-matches=brightred
  108.  
  109.  *    And here is an example vile.keywords file that colors comments in
  110.  *    yellow, C keywords in brightcyan, preprocesor directives in
  111.  *    brightmagenta, and string constants in brightgreen.
  112.  *
  113.  *    vile.keywords
  114.  *    =============
  115.       Comments:CB
  116.       Cpp:CD
  117.       Literal:CA
  118.       if:CE
  119.       else:CE
  120.       for:CE
  121.       return:CE
  122.       while:CE
  123.       switch:CE
  124.       case:CE
  125.       do:CE
  126.       goto:CE
  127.       break:CE
  128.  */
  129.  
  130. #ifdef HAVE_CONFIG_H
  131. #include "config.h"
  132. #else
  133. /* assume ANSI C */
  134. # define HAVE_STDLIB_H 1
  135. # define HAVE_STRING_H 1
  136. #endif
  137.  
  138. #include <sys/types.h>        /* sometimes needed to get size_t */
  139.  
  140. #if HAVE_STDLIB_H
  141. #include <stdlib.h>
  142. #else
  143. # if !defined(HAVE_CONFIG_H) || MISSING_EXTERN_MALLOC
  144. extern    char *    malloc    ( size_t len );
  145. # endif
  146. #endif
  147.  
  148. #ifdef HAVE_UNISTD_H
  149. #include <unistd.h>
  150. #endif
  151.  
  152. #include <stdio.h>
  153.  
  154. #ifdef HAVE_STRING_H
  155. #include <string.h>
  156. #endif
  157.  
  158. #if MISSING_EXTERN__FILBUF
  159. extern    int    _filbuf    ( FILE *fp );
  160. #endif
  161.  
  162. #if MISSING_EXTERN__FLSBUF
  163. extern    int    _flsbuf    ( int len, FILE *fp );
  164. #endif
  165.  
  166. #if MISSING_EXTERN_FCLOSE
  167. extern    int    fclose    ( FILE *fp );
  168. #endif
  169.  
  170. #if MISSING_EXTERN_FPRINTF
  171. extern    int    fprintf    ( FILE *fp, const char *fmt, ... );
  172. #endif
  173.  
  174. #if MISSING_EXTERN_FPUTS
  175. extern    int    fputs    ( const char *s, FILE *fp );
  176. #endif
  177.  
  178. #if MISSING_EXTERN_PRINTF
  179. extern    int    printf    ( const char *fmt, ... );
  180. #endif
  181.  
  182. #if MISSING_EXTERN_SSCANF
  183. extern    int    sscanf    ( const char *src, const char *fmt, ... );
  184. #endif
  185.  
  186. #if OPT_LOCALE
  187. #include <locale.h>
  188. #endif
  189. #include <ctype.h>
  190.  
  191. #define MAX_KEYWORD_LENGTH 80
  192. #define HASH_LENGTH 256
  193. #define MAX_LINELENGTH 256
  194. #define MAX_ATTR_LENGTH 6
  195.  
  196. #ifdef _WIN32
  197. static char *keyword_file="vile.keywords";
  198. #define PATHSEP '\\'
  199. #else
  200. # if __GO32__
  201. #define PATHSEP '\\'
  202. static char *keyword_file="vile.key";
  203. # else
  204. #define PATHSEP '/'
  205. static char *keyword_file=".vile.keywords";
  206. # endif
  207. #endif
  208.  
  209. #define isBlank(c)  ((c) == ' ' || (c) == '\t')
  210.  
  211. typedef struct _keyword KEYWORD;
  212.  
  213. struct _keyword {
  214.     char kw[MAX_KEYWORD_LENGTH+1];
  215.     char attribute[MAX_ATTR_LENGTH+1];
  216.     int  length;
  217.     KEYWORD *next;
  218. };
  219.  
  220. static KEYWORD *hashtable[HASH_LENGTH];
  221. static KEYWORD identifier;
  222. static char comment_attr[MAX_ATTR_LENGTH+1] = "C1"; /* color 1 */
  223. static char literal_attr[MAX_ATTR_LENGTH+1] = "C2"; /* color 1 */
  224. static char cpp_attr[MAX_ATTR_LENGTH+1]     = "C3"; /* color 3 */
  225.  
  226. static void
  227. inithash(void)
  228. {
  229.     int i;
  230.     for (i=0;i<HASH_LENGTH;i++) hashtable[i] = NULL;
  231. }
  232.  
  233. static void
  234. removelist(KEYWORD *k)
  235. {
  236.     if (k != NULL) {
  237.     if (k->next != NULL) removelist(k->next);
  238.     free((char *)k);
  239.     }
  240. }
  241.  
  242. static void
  243. closehash(void)
  244. {
  245.     int i;
  246.     for (i=0;i<HASH_LENGTH;i++) {
  247.     removelist(hashtable[i]);
  248.     hashtable[i] = NULL; /* For unseen future i do this */
  249.     }
  250. }
  251.  
  252. static int
  253. hash_function(char *id)
  254. {
  255.     /*
  256.      * Build more elaborate hashing scheme. If you want one.
  257.      */
  258.     return ( (int) *id );
  259. }
  260.  
  261. static void
  262. insert_keyword(
  263.     char *ident,
  264.     char *attribute)
  265. {
  266.     KEYWORD *first;
  267.     KEYWORD *nxt;
  268.     int Index;
  269.     if (!strcmp(ident,"Comments")) {
  270.     (void)strcpy(comment_attr,attribute);
  271.     return;
  272.     }
  273.     if (!strcmp(ident,"Literal")) {
  274.     strcpy(literal_attr,attribute);
  275.     return;
  276.     }
  277.     if (!strcmp(ident,"Cpp")) {
  278.       strcpy(cpp_attr,attribute);
  279.       return;
  280.     }
  281.     nxt = first = NULL;
  282.     Index = hash_function(ident);
  283.     first = hashtable[Index];
  284.     if ((nxt = (KEYWORD *)malloc(sizeof(struct _keyword))) != NULL) {
  285.     (void)strcpy(nxt->kw,ident);
  286.     nxt->length = strlen(nxt->kw);
  287.     (void)strcpy(nxt->attribute,attribute);
  288.     nxt->next = first;
  289.     hashtable[Index] = nxt;
  290. #ifdef DEBUG
  291.     fprintf(stderr,"insert_keyword: new %li, new->kw %s, new->length %i, new->attribute %c, new->next %li\n", new,
  292.                         nxt->kw, nxt->length, nxt->attribute,nxt->next);
  293. #endif
  294.     }
  295. }
  296.  
  297.  
  298. static void
  299. match_identifier(void)
  300. {
  301.     KEYWORD *hash_id;
  302.     int Index, match = 0;
  303.     Index = hash_function(identifier.kw);
  304.     hash_id = hashtable[Index];
  305.     while (hash_id != NULL) {
  306.     if (hash_id->length == identifier.length) { /* Possible match */
  307.         if (strcmp(hash_id->kw,identifier.kw) == 0) {
  308.         match = 1;
  309.         break;
  310.         }
  311.     } else if (identifier.kw[0] == '#' && hash_id->kw[0] == '#') {
  312.         char *s = &identifier.kw[1];
  313.         while (isBlank(*s))
  314.             s++;
  315.  
  316.         if (strcmp(&hash_id->kw[1],s) == 0) {
  317.         match = 1;
  318.         break;
  319.         }
  320.     }
  321.     hash_id = hash_id->next;
  322.     }
  323.     if (match)
  324.     printf("\001%i%s:%s",identifier.length, hash_id->attribute,
  325.                    identifier.kw);
  326.     else
  327.         printf("%s",identifier.kw);
  328. }
  329.  
  330.  
  331. static char *
  332. extract_identifier(char *s)
  333. {
  334.     register char *kwp = identifier.kw;
  335.     identifier.kw[0] = '\0';
  336.     identifier.length = 0;
  337.     if (*s == '#') {
  338.     do {
  339.         identifier.length += 1;
  340.         *kwp++ = *s++;
  341.     } while (isBlank(*s) &&
  342.         (identifier.length < MAX_KEYWORD_LENGTH));
  343.     }
  344.     while ((isalpha(*s) || *s == '_' || isdigit(*s)) &&
  345.            identifier.length < MAX_KEYWORD_LENGTH) {
  346.     identifier.length += 1;
  347.     *kwp++ = *s++;
  348.     }
  349.     *kwp = '\0';
  350.     return(s);
  351. }
  352.  
  353. static FILE *
  354. open_keywords(char *filename)
  355. {
  356.     char *home = getenv("HOME");
  357.     char fullname[1024];
  358.  
  359.     if (home == 0)
  360.         home = "";
  361.     sprintf(fullname, "%s%c%s", home, PATHSEP, filename);
  362.     return fopen(fullname, "r");
  363. }
  364.  
  365. static void
  366. read_keywords(char *filename)
  367. {
  368.     char ident[MAX_KEYWORD_LENGTH+1];
  369.     char line[MAX_LINELENGTH+1];
  370.     char attribute[MAX_ATTR_LENGTH+1];
  371.     int  items;
  372.     FILE *kwfile;
  373.  
  374.     if ((kwfile = open_keywords(filename)) != NULL) {
  375.     fgets(line,MAX_LINELENGTH,kwfile);
  376.     items = sscanf(line,"%[#a-zA-Z0-9_]:%[IURC0-9A-F]",ident,attribute);
  377.     while (! feof(kwfile) ) {
  378. #ifdef DEBUG
  379.         fprintf(stderr,"read_keywords: Items %i, kw = %s, attr = %s\n",items,ident,attribute);
  380. #endif
  381.         if (items == 2)
  382.         insert_keyword(ident,attribute);
  383.         fgets(line,MAX_LINELENGTH,kwfile);
  384.         items = sscanf(line,"%[#a-zA-Z0-9_]:%[IURC0-9A-F]",ident,attribute);
  385.     }
  386.     fclose(kwfile);
  387.     }
  388. }
  389.  
  390. static int
  391. has_endofcomment(char *s)
  392. {
  393.     int i=0;
  394.     while (*s) {
  395.     if (*s == '*' && *(s+1) == '/') {
  396.         return(i+2);
  397.     }
  398.     i += 1;
  399.     s += 1;
  400.     }
  401.     return(0);
  402. }
  403.  
  404. static int
  405. has_endofliteral(char *s)    /* points to '"' */
  406. {
  407.     int i=0;
  408.     while (*s) {
  409.     if (*s == '\"')
  410.         return (i);
  411.     if (s[0] == '\\' && (s[1] == '\"' || s[1] == '\\')) {
  412.         ++i;
  413.         ++s;
  414.     }
  415.     ++i;
  416.     ++s;
  417.     }
  418.     return(0);
  419. }
  420.  
  421. static char *
  422. skip_white(char *s)
  423. {
  424.     while(*s && isBlank(*s))
  425.         putchar(*s++);
  426.     return s;
  427. }
  428.  
  429. static int
  430. firstnonblank(char *tst, char *cmp)
  431. {
  432.     while (*cmp && isBlank(*cmp))
  433.     cmp++;
  434.     return (tst == cmp);
  435. }
  436.  
  437. static char *
  438. write_literal(char *s, int *literal)
  439. {
  440.     int c_length = has_endofliteral(s);
  441.     if (c_length == 0)
  442.     c_length = strlen(s);
  443.     else
  444.     *literal = 0;
  445.     printf("\001%i%s:%.*s", c_length, literal_attr, c_length, s);
  446.     s += c_length;
  447.     if (!*literal)
  448.         putchar(*s++);
  449.     return s;
  450. }
  451.  
  452. int
  453. main(int argc, char **argv)
  454. {
  455.     char line[MAX_LINELENGTH+1];
  456.     char *s;
  457.     int comment,c_length,literal;
  458.  
  459. #if OPT_LOCALE
  460.     setlocale(LC_CTYPE, "");
  461. #endif
  462.  
  463.     comment = 0;
  464.     literal = 0;
  465.     inithash();
  466.  
  467.     if (argc > 1) {
  468.     int n;
  469.     for (n = 1; n < argc; n++)
  470.         read_keywords(argv[n]);
  471.     } else {
  472.     read_keywords(keyword_file);
  473.     }
  474.  
  475.     while (fgets(line,MAX_LINELENGTH,stdin) != NULL) {
  476.     s = line;
  477.     if (literal)
  478.         s = write_literal(s, &literal);
  479.     s = skip_white(s);
  480.     while (*s) {
  481.         if (!comment && *s == '/' && *(s+1) == '*') {
  482.         c_length = has_endofcomment(s+2);
  483.         if (c_length == 0) { /* Comment continues to the next line */
  484.             c_length = strlen(s);
  485.             comment += 1;
  486.         } else {
  487.             c_length += 2;
  488.         }
  489.         printf("\001%i%s:%.*s",c_length,comment_attr,c_length,s);
  490.         s = s + c_length ;
  491.         }
  492.         if (!comment && *s == '/' && *(s+1) == '/') { /* C++ comments */
  493.             c_length = strlen(s);
  494.         printf("\001%i%s:%.*s",c_length,comment_attr,c_length,s);
  495.           break;
  496.         }
  497.         if (!comment && *s == '#' && firstnonblank(s, line) ) {
  498.         char *ss = s+1;
  499.         int isinclude;
  500.         while (isspace(*ss))
  501.             ss++;
  502.         isinclude = !strncmp(ss, "include", 7);
  503.         while (isalpha(*ss))
  504.             ss++;
  505.         if (isinclude) {  /* eat filename as well */
  506.             while (isspace(*ss))
  507.             ss++;
  508.             while (!isspace(*ss))
  509.             ss++;
  510.         }
  511.         c_length = ss - s;
  512.         printf("\001%i%s:%.*s",c_length,cpp_attr,c_length,s);
  513.         s = s + c_length;
  514.         }
  515.         if (comment && *s) {
  516.         if ((c_length = has_endofcomment(s)) > 0) {
  517.             printf("\001%i%s:%.*s",c_length,comment_attr,c_length,s);
  518.             s = s + c_length ;
  519.             comment -= 1;
  520.             if (comment < 0) comment = 0;
  521.         } else { /* Whole line belongs to comment */
  522.             c_length = strlen(s);
  523.             printf("\001%i%s:%.*s",c_length,comment_attr,c_length,s);
  524.             s = s + c_length;
  525.         }
  526.         } else if (*s) {
  527.             if (*s == '\\' && *(s+1) == '\"') {/* Skip literal single character */
  528.             putchar(*s++);
  529.             putchar(*s++);
  530.         } else if (!literal && *s == '\"' && s[1] == '\"') {
  531.             putchar(*s++);
  532.             putchar(*s++);
  533.         } else if (!literal && *s == '\'' && s[1] == '"' && s[2] == '\'') {
  534.             putchar(*s++);
  535.             putchar(*s++);
  536.             putchar(*s++);
  537.         }
  538.         if (*s == '\"')  {
  539.             literal = literal == 0 ? 1 : 0;
  540.             putchar(*s++);
  541.             if (literal) {
  542.             s = write_literal(s, &literal);
  543.             }
  544.         }
  545.  
  546.         if (*s) {
  547.             if ( (isalpha(*s) || *s == '_' || *s == '#')
  548.                 && ! literal) {
  549.                 s = extract_identifier(s);
  550.                 match_identifier();
  551.             } else {
  552.                 putchar(*s++);
  553.             }
  554.         }
  555.         }
  556.     }
  557.     }
  558.     closehash();
  559.  
  560.     exit(0);
  561. }
  562.