/*
* re_plugin_highlighter.c
*
* A programmers editor
*
* re_data plugin to support the syntax highlighter.
*
* Author: Dario Rodriguez antartica@whereismybit.com
* This program is licensed under the terms of GNU GPL v2.1+
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "re_plugin_highlighter.h"
#define PLUGINNAME "highlighter"
#define HLINETBLOCK (16*1024)
#define BUFBLOCK (64*1024)
#define HIGHLIGHTERGROWSIZE (256*1024)
#define DEFAULTCOLOR "\x80\x80\x80\xff"
typedef enum colorsenum_t {
color_normal=0,
color_directive,
color_directivekeyword,
color_directivestring,
color_directiveinclude,
color_directiveincludestring,
color_keyword,
color_string,
color_multilinecomment,
color_linecomment,
color_number,
color_operator,
color_symbol,
color_endenum,
} colorsenum_t;
static int redata_highlighter_add_or_unadd(redata_t *redata, redata_plugin_t *slot, undo_t *undo, int is_unadd);
static int redata_highlighter_postload(redata_t *redata, redata_plugin_t *slot,char *filename);
static int hl_invalidatefrom(redata_t *redata, highlighter_t *hl, int nline);
hcolor_t *hl_initcolors(int *ncolors, /* int color, char *colordef, */ ...);
static highlighter_t *hl_getplugin(redata_t *redata);
static int hl_doline(redata_t *redata, highlighter_t *hl, int nline);
int hl_C_getkeywords(char ***keywords,int *nkeywords, int *maxlen);
int hl_C_getdirectives(char ***directives,int *ndirectives, int *maxlen);
int hl_searchlist(char **wordlist, int wordlistlen, char *word, int wordlen, int *res);
linecolor_t *hl_addtolinecolor(int *opaque, highlighter_t *hl,linecolor_t *linecolor,int posoff, int color);
int
redata_highlighter_register(redata_t *redata, redata_plugin_t *slot)
{
hcolor_t *colors;
int ncolors;
highlighter_t *hl;
if(redata==NULL || slot==NULL)
return(-1);
#if 1
/* "forest" theme */
colors=hl_initcolors(&ncolors,
color_keyword,"\x38\x17\x1e\xff",
color_directive,"\x38\x4b\x00\xff",
color_directivekeyword,"\x63\x7d\x16\xff",
color_directivestring,"\x07\x20\x3b\xff",
color_directiveinclude,"\x16\x63\x7d\xff",
color_directiveincludestring,"\x6\x13\x2d\xff",
color_normal,"\x9a\x67\x43\xff",
color_string,"\x9d\x15\x00\xff",
color_multilinecomment,"\x4f\x40\x57\xff",
color_linecomment,"\xc6\x8c\xa4\xff",
color_number,"\x3b\x10\x35\xff",
color_operator,"\xaa\x8c\xcd\xff",
color_symbol,"\x69\x2a\x44\xff",
color_endenum,DEFAULTCOLOR,
-1);
#else
/* "alternative" theme (dark) */
colors=hl_initcolors(&ncolors,
color_keyword,"\xcc\x99\xcc\xff",//
color_directive,"\x38\x4b\x00\xff",
color_directivekeyword,"\x63\x7d\x16\xff",
color_directivestring,"\x07\x20\x3b\xff",
color_directiveinclude,"\x16\x63\x7d\xff",
color_directiveincludestring,"\x6\x13\x2d\xff",
color_normal,"\xf2\xf0\xec\xff",//
color_string,"\x99\xcc\x99\xff",//
color_multilinecomment,"\x74\x73\x69\xff",//
color_linecomment,"\x74\x73\x69\xff",//
color_number,"\xf9\x91\x57\xff",//
color_operator,"\x66\xcc\xcc\xff",//
color_symbol,"\x91\xa7\xff\xff",//
color_endenum,"\xf2\xf0\xec\xff",//
-1);
#endif
if(colors==NULL || (hl=malloc(sizeof(highlighter_t)))==NULL) {
if(colors!=NULL)
free(colors),colors=NULL;
return(-1); /* insufficient memory */
}
memset(hl,0,sizeof(highlighter_t));
hl->sizebuf=hl->usedbuf=0;
hl->sizecolors=ncolors;
hl->colors=colors;
strncpy(slot->name,PLUGINNAME,sizeof(slot->name));
slot->name[sizeof(slot->name)-1]='\0';
slot->unregister=redata_highlighter_unregister;
slot->postload=redata_highlighter_postload;
slot->add_or_unadd=redata_highlighter_add_or_unadd;
slot->userptr=hl;
return(0);
}
int
redata_highlighter_unregister(redata_t *redata, redata_plugin_t *slot, char *filename)
{
highlighter_t *hl=(highlighter_t *) ((slot!=NULL)?(slot->userptr):NULL);
if(redata==NULL || slot==NULL || hl==NULL)
return(-1);
if(hl->buf!=NULL)
free(hl->buf),hl->buf=NULL;
hl->sizebuf=hl->usedbuf=0;
if(hl->colors!=NULL)
free(hl->colors),hl->colors=NULL;
hl->sizecolors=0;
if(hl->lines!=NULL)
free(hl->lines),hl->lines=NULL;
hl->sizelines=hl->usedlines=0;
if(hl->keywordbuf!=NULL)
free(hl->keywordbuf),hl->keywordbuf=NULL;
hl->sizekeywordbuf=hl->usedkeywordbuf=0;
if(hl->directivebuf!=NULL)
free(hl->directivebuf),hl->directivebuf=NULL;
hl->sizedirectivebuf=hl->useddirectivebuf=0;
if(slot->userptr!=NULL)
free(slot->userptr),slot->userptr=NULL;
return(0);
}
hcolor_t *
redata_highlighter_getcolors(redata_t *redata, int *ncolors)
{
highlighter_t *hl;
if(redata==NULL || (hl=hl_getplugin(redata))==NULL)
return(NULL); /* sanity check failed or plugin not found */
if(ncolors!=NULL)
*ncolors=hl->sizecolors;
return(hl->colors);
}
linecolor_t *
redata_highlighter_getline(redata_t *redata, int line, int *nlinecolors)
{
highlighter_t *hl;
if(redata==NULL || line<0 || (hl=hl_getplugin(redata))==NULL)
return(NULL); /* sanity check failed or plugin not found */
if(!hl->flag_doneall)
hl_doline(redata,hl,line);
if(line<0 || line>=hl->usedlines)
return(NULL);
if(nlinecolors!=NULL)
*nlinecolors=(hl->lines[line].len)/sizeof(linecolor_t);
return((linecolor_t *) (hl->buf+hl->lines[line].off));
}
int
redata_highlighter_getcolorindex(redata_t *redata, int line, int nthbyte)
{
int i,n;
int nlinecolors;
linecolor_t *linecolors;
if((linecolors=redata_highlighter_getline(redata,line,&nlinecolors))==NULL)
return(-1);
for(i=0,n=0;n<nlinecolors;i+=linecolors[n].len,n++) {
if(nthbyte<(i+linecolors[n].len))
return(linecolors[n].color);
}
return(-1);
}
static void
redata_highlighter_util_applytheme(int *template3,hcolor_t *color,int invert)
{
if(template3==NULL || color==NULL || template3[0]<0 || template3[0]>2 || template3[1]<0 || template3[1]>2 || template3[2]<0 || template3[2]>2)
return; /* sanity check error */
if(invert==0) {
((unsigned char *)color->rgba)[0]=((unsigned char *)color->origrgba)[template3[0]];
((unsigned char *)color->rgba)[1]=((unsigned char *)color->origrgba)[template3[1]];
((unsigned char *)color->rgba)[2]=((unsigned char *)color->origrgba)[template3[2]];
} else {
((unsigned char *)color->rgba)[0]=255-((unsigned char *)color->origrgba)[template3[0]];
((unsigned char *)color->rgba)[1]=255-((unsigned char *)color->origrgba)[template3[1]];
((unsigned char *)color->rgba)[2]=255-((unsigned char *)color->origrgba)[template3[2]];
}
return;
}
int
redata_highlighter_settheme(redata_t *redata, int ntheme, int invert)
{
int permutations[6][3]={{0,1,2},{0,2,1},{1,0,2},{1,2,0},{2,1,0},{2,0,1}};
int *permutationtemplate;
hcolor_t *colors;
int ncolors,i;
ntheme=(ntheme<0)?0:ntheme;
ntheme%=6;
permutationtemplate=permutations[ntheme];
if((colors=redata_highlighter_getcolors(redata, &ncolors))==NULL)
return(-1);
for(i=0;i<ncolors;i++)
redata_highlighter_util_applytheme(permutationtemplate,colors+i,invert);
return(0);
}
static int
redata_highlighter_add_or_unadd(redata_t *redata, redata_plugin_t *slot, undo_t *undo, int is_unadd)
{
long pos;
int nline;
highlighter_t *hl=(highlighter_t *) ((slot!=NULL)?(slot->userptr):NULL);
if(redata==NULL || slot==NULL || hl==NULL || undo==NULL || slot->active==0)
return(-1); /* sanity check failed */
if(hl->usedlines==0) {
return(0); /* nothing to do */
}
/* get the first pos of the operation */
pos=undo->posorig;
if((!is_unadd && undo->type=='D') || (is_unadd && undo->type=='A'))
pos-=undo->len;
if(undo->type=='M' && undo->posdest<pos)
pos=undo->posdest;
/* get line of pos */
for(nline=0;nline<hl->usedlines;nline++) {
if(hl->lines[nline].pos>pos) {
nline--;
break;
}
}
/* special case: check if pos is inside last line */
if(nline>=hl->usedlines)
nline=hl->usedlines-1;
/* invalidate from this line on */
nline=(nline<0)?0:nline;
hl_invalidatefrom(redata,hl,nline);
return(0);
}
static int
redata_highlighter_postload(redata_t *redata, redata_plugin_t *slot,char *filename)
{
#warning XXX TODO: get the language from the filename
return(-1);
}
static int
hl_invalidatefrom(redata_t *redata, highlighter_t *hl, int nline)
{
if(redata==NULL || hl==NULL || nline<0)
return(-1); /* sanity check failed */
if(nline>=hl->usedlines)
return(0); /* nothing to do */
if(nline==0) {
hl->usedbuf=0;
hl->usedlines=0;
}
hl->usedbuf=hl->lines[nline].off;
hl->usedlines=nline;
hl->flag_doneall=0;
return(0);
}
hcolor_t *
hl_initcolors(int *ncolors, /* int color, char *colordef, */ ...)
{
int maxcolor;
int color;
char *colordef;
int round;
hcolor_t *hcolors;
va_list paramlist;
int i;
static char defcolor[]={DEFAULTCOLOR};
if(ncolors==NULL)
return(NULL); /* sanity check failed */
*ncolors=0;
hcolors=NULL;
for(round=0;round<2;round++) {
maxcolor=-1;
va_start(paramlist,ncolors);
while((color=va_arg(paramlist,int))>=0) {
colordef=va_arg(paramlist,char *);
maxcolor=(maxcolor<color)?color:maxcolor;
if(round==1) {
strncpy(hcolors[color].origrgba,colordef,sizeof(hcolors[color].origrgba));
hcolors[color].origrgba[sizeof(hcolors[color].origrgba)-1]='\0';
strncpy(hcolors[color].rgba,colordef,sizeof(hcolors[color].rgba));
hcolors[color].rgba[sizeof(hcolors[color].rgba)-1]='\0';
}
}
va_end(paramlist);
if(maxcolor<0)
return(NULL); /* no colors were defined */
if(round==0) {
if((hcolors=malloc(sizeof(hcolor_t)*(maxcolor+1)))==NULL)
return(NULL); /* insufficient memory */
memset(hcolors,0,sizeof(hcolor_t)*(maxcolor+1));
for(i=0;i<=maxcolor;i++)
memcpy(hcolors[i].rgba,defcolor,sizeof(defcolor));
}
}
*ncolors=maxcolor+1;
return(hcolors);
}
static highlighter_t *
hl_getplugin(redata_t *redata)
{
highlighter_t *hl;
int i;
if(redata==NULL)
return(NULL); /* sanity check failed */
for(i=0;i<redata->sizeplugins;i++) {
if(strcmp(redata->plugins[i].name,PLUGINNAME)==0)
break;
}
if(i>=redata->sizeplugins || redata->plugins[i].active==0)
return(NULL); /* plugin not found or nor active */
hl=(highlighter_t *) (redata->plugins[i].userptr);
if(hl==NULL)
return(NULL); /* internal error */
return(hl);
}
static int
hl_doline(redata_t *redata, highlighter_t *hl, int nline)
{
int i;
long realpos;
long pos;
char *ptr;
int len;
hline_t *line;
long redataused;
int has_nl;
int has_next;
char **keywords;
int nkeywords;
char **directives;
int ndirectives;
int maxlenkeywords,maxlendirectives;
linecolor_t *linecolor;
int prev_char;
int prev_char_mask;
int cant_define;
int cant_directivekeyword;
int is_directiveinclude;
int opaque;
int posoffset;
enum {
mode_whatever=0,
mode_in_multilinecomment,
mode_in_linecomment,
mode_in_directive,
mode_in_string,
mode_in_singlestring,
mode_in_directivestring,
} mode,mlcprevmode;
if(redata==NULL || hl==NULL || nline<0)
return(-1); /* sanoty check failed */
if(hl->usedlines>nline)
return(0); /* nothing to do */
if(hl_C_getkeywords(&keywords,&nkeywords,&maxlenkeywords)==-1)
return(-1); /* couldn't get keyword list */
if(hl_C_getdirectives(&directives,&ndirectives,&maxlendirectives)==-1)
return(-1); /* couldn't get directive list */
/* make sure keywordbuf is large enough */
if(maxlenkeywords>hl->sizekeywordbuf) {
char *newkeywordbuf;
int newsize;
newsize=maxlenkeywords;
if((newkeywordbuf=realloc(hl->keywordbuf,newsize))==NULL)
return(-1); /* insufficient memory */
memset(newkeywordbuf,0,newsize);
hl->keywordbuf=newkeywordbuf;
hl->sizekeywordbuf=newsize;
hl->usedkeywordbuf=0;
}
/* make sure directivebuf is large enough */
if(maxlendirectives>hl->sizedirectivebuf) {
char *newdirectivebuf;
int newsize;
newsize=maxlendirectives;
if((newdirectivebuf=realloc(hl->directivebuf,newsize))==NULL)
return(-1); /* insufficient memory */
memset(newdirectivebuf,0,newsize);
hl->directivebuf=newdirectivebuf;
hl->sizedirectivebuf=newsize;
hl->useddirectivebuf=0;
}
/* make sure we have enough hline_t structs */
if(hl->sizelines<=nline) {
hline_t *newlines;
int newsize=(nline+1+HLINETBLOCK)/HLINETBLOCK;
newsize*=HLINETBLOCK;
if((newlines=realloc(hl->lines,newsize*sizeof(hline_t)))==NULL)
return(-1); /* insufficient memory */
hl->lines=newlines;
memset(hl->lines+hl->sizelines,0,(newsize-hl->sizelines)*sizeof(hline_t));
hl->sizelines=newsize;
}
/* make sure we have previous lines highlighted */
for(i=hl->usedlines;i<nline;i++) {
if(hl_doline(redata,hl,i)==-1)
return(-1); /* error highlighting line */
}
hl_invalidatefrom(redata,hl,nline);
line=hl->lines+nline;
if(redata_linecol2pos(redata,nline,0,&realpos,NULL)==-1)
return(-1); /* couldn't get line pos */
/* NOTE: here comes the highlighter */
line->pos=realpos;
line->off=(nline==0)?0:hl->lines[nline-1].off+hl->lines[nline-1].len;
line->len=0;
line->endingmode=mode_whatever;
redataused=redata_getused(redata);
mode=(nline>0)?line[-1].endingmode:mode_whatever;
cant_define=0;
cant_directivekeyword=0;
is_directiveinclude=0;
opaque=0;
linecolor=hl_addtolinecolor(&opaque,hl,NULL,color_normal,0);
posoffset=0;
prev_char='\0';
prev_char_mask=0xff;
hl->usedkeywordbuf=0;
hl->useddirectivebuf=0;
mlcprevmode=mode_whatever;
do {
if(redata_line_rawinfo(redata,realpos+posoffset,&pos,&ptr,&len,NULL)==-1)
return(-1); /* couldn't get line data */
has_nl=((len>0 && ptr[len-1]=='\n')?1:0);
has_next=(len==0)?1:(ptr[len-1]=='\n')?0:1;
/* special case: line with only a newline */
if(posoffset==0 && has_nl==1 && len==1) {
/* delete the existing linecolor and break */
line->len=0;
break;
}
/* iterate */
for(i=0;i<(len-has_nl);prev_char=(ptr[i]&prev_char_mask),prev_char_mask=0xff,i++) {
/* special case: keyword ends in a change of mode */
if(mode!=mode_whatever && hl->usedkeywordbuf>0) {
if(hl_searchlist(keywords,nkeywords,hl->keywordbuf,hl->usedkeywordbuf,NULL)==0) {
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->keywordbufstart,color_keyword);
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->keywordbufstart+hl->usedkeywordbuf-1,color_keyword);
}
hl->usedkeywordbuf=0;
}
/* end of special case */
if(mode==mode_whatever) {
if(prev_char=='\\' || ptr[i]=='\\') {
/* escape char mark or escaped char */
continue;
}
if(prev_char=='/' && ptr[i]=='/') {
mode=mode_in_linecomment;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i-1,color_linecomment);
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_linecomment);
continue;
}
if(prev_char=='/' && ptr[i]=='*') {
mlcprevmode=mode;
mode=mode_in_multilinecomment;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i-1,color_multilinecomment);
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_multilinecomment);
continue;
}
if(ptr[i]=='\"') {
mode=mode_in_string;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_string);
continue;
}
if(ptr[i]=='\'') {
mode=mode_in_singlestring;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_string);
continue;
}
if(!cant_define && ptr[i]=='#') {
mode=mode_in_directive;
is_directiveinclude=0;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_directive);
continue;
}
/* keyword detection */
if(hl->usedkeywordbuf==0 && ((ptr[i]>='a' && ptr[i]<='z') || (ptr[i]>='A' && ptr[i]<='Z') || ptr[i]=='_')) {
cant_define=1;
hl->keywordbuf[hl->usedkeywordbuf++]=ptr[i];
hl->keywordbufstart=posoffset+i;
} else if(hl->usedkeywordbuf>0 && ((ptr[i]>='0' && ptr[i]<='9') || (ptr[i]>='a' && ptr[i]<='z') || (ptr[i]>='A' && ptr[i]<='Z') || ptr[i]=='_')) {
if(hl->usedkeywordbuf<hl->sizekeywordbuf) {
hl->keywordbuf[hl->usedkeywordbuf++]=ptr[i];
} else {
hl->keywordbuf[0]='\0'; /* too long */
}
}
if(hl->usedkeywordbuf>0 && !((ptr[i]>='0' && ptr[i]<='9') || (ptr[i]>='a' && ptr[i]<='z') || (ptr[i]>='A' && ptr[i]<='Z') || ptr[i]=='_')) {
if(hl_searchlist(keywords,nkeywords,hl->keywordbuf,hl->usedkeywordbuf,NULL)==0) {
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->keywordbufstart,color_keyword);
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->keywordbufstart+hl->usedkeywordbuf-1,color_keyword);
}
hl->usedkeywordbuf=0;
}
/* keyword detection */
if(strchr("0123456789",ptr[i])!=NULL) {
cant_define=1;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_number);
continue;
}
if(strchr("<>=+-%*/!|&^",ptr[i])!=NULL) {
cant_define=1;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_operator);
continue;
}
if(strchr("(){}[],;",ptr[i])!=NULL) {
cant_define=1;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_symbol);
continue;
}
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_normal);
continue;
}
if(mode==mode_in_multilinecomment) {
if(prev_char=='\\' || ptr[i]=='\\') {
continue;
}
if(prev_char=='*' && ptr[i]=='/') {
prev_char_mask=0x00;
mode=mlcprevmode;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_multilinecomment);
continue;
}
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_multilinecomment);
continue;
}
if(mode==mode_in_directive) {
if(prev_char=='\\' || ptr[i]=='\\') {
continue;
}
if(prev_char=='/' && ptr[i]=='/') {
mode=mode_in_linecomment;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i-1,color_linecomment);
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_linecomment);
continue;
}
if(prev_char=='/' && ptr[i]=='*') {
mlcprevmode=mode;
mode=mode_in_multilinecomment;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i-1,color_multilinecomment);
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_multilinecomment);
continue;
}
/* directive detection */
if(!cant_directivekeyword && hl->useddirectivebuf==0 && ((ptr[i]>='a' && ptr[i]<='z') || (ptr[i]>='A' && ptr[i]<='Z') || ptr[i]=='_')) {
hl->directivebuf[hl->useddirectivebuf++]=ptr[i];
hl->directivebufstart=posoffset+i;
} else if(hl->useddirectivebuf>0 && ((ptr[i]>='0' && ptr[i]<='9') || (ptr[i]>='a' && ptr[i]<='z') || (ptr[i]>='A' && ptr[i]<='Z') || ptr[i]=='_')) {
if(hl->useddirectivebuf<hl->sizedirectivebuf) {
hl->directivebuf[hl->useddirectivebuf++]=ptr[i];
} else {
hl->directivebuf[0]='\0'; /* too long */
cant_directivekeyword=1;
}
}
if(hl->useddirectivebuf>0 && !((ptr[i]>='0' && ptr[i]<='9') || (ptr[i]>='a' && ptr[i]<='z') || (ptr[i]>='A' && ptr[i]<='Z') || ptr[i]=='_')) {
int nfound;
if(hl_searchlist(directives,ndirectives,hl->directivebuf,hl->useddirectivebuf,&nfound)==0) {
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->directivebufstart,color_directivekeyword);
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->directivebufstart+hl->useddirectivebuf-1,color_directivekeyword);
if(strcmp(directives[nfound],"include")==0)
is_directiveinclude=1;
}
hl->useddirectivebuf=0;
cant_directivekeyword=1;
}
if(ptr[i]!=' ' && ptr[i]!='\t')
cant_directivekeyword=1;
/* directive detection */
if(ptr[i]=='\"') {
mode=mode_in_directivestring;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,(is_directiveinclude==0)?color_directivestring:color_directiveincludestring);
continue;
}
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,(is_directiveinclude==0)?color_directive:color_directiveinclude);
continue;
}
if(mode==mode_in_directivestring) {
if(prev_char=='\\' || ptr[i]=='\\') {
continue;
}
if(ptr[i]=='\"') {
mode=mode_in_directive;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,(is_directiveinclude==0)?color_directivestring:color_directiveincludestring);
continue;
}
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,(is_directiveinclude==0)?color_directivestring:color_directiveincludestring);
continue;
}
if(mode==mode_in_string || mode_in_singlestring) {
if(prev_char=='\\' || ptr[i]=='\\') {
continue;
}
if((mode==mode_in_string && ptr[i]=='\"')
|| (mode==mode_in_singlestring && ptr[i]=='\'')) {
mode=mode_whatever;
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_string);
continue;
}
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_string);
continue;
}
if(mode==mode_in_linecomment) {
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,posoffset+i,color_linecomment);
continue;
}
}
posoffset+=len;
} while(has_next!=0 && (realpos+posoffset)<redataused);
/* special case: keyword ends at end-of-line */
if(mode==mode_whatever && hl->usedkeywordbuf>0 && hl_searchlist(keywords,nkeywords,hl->keywordbuf,hl->usedkeywordbuf,NULL)==0) {
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->keywordbufstart,color_keyword);
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->keywordbufstart+hl->usedkeywordbuf-1,color_keyword);
}
/* end of special case */
/* special case: deirectivekeyword ends at end-of-line */
if(mode==mode_in_directive && hl->useddirectivebuf>0 && hl_searchlist(directives,ndirectives,hl->directivebuf,hl->useddirectivebuf,NULL)==0) {
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->directivebufstart,color_directivekeyword);
linecolor=hl_addtolinecolor(&opaque,hl,linecolor,hl->directivebufstart+hl->useddirectivebuf-1,color_directivekeyword);
}
/* end of special case */
if(linecolor==NULL)
return(-1);
if((prev_char=='\\' && mode!=mode_in_linecomment) || mode==mode_in_multilinecomment)
line->endingmode=mode;
hl->usedlines++;
return(0);
}
int
hl_C_getkeywords(char ***keywords,int *nkeywords, int *maxlen)
{
static int init=0;
static int staticnkeywords=0;
static int staticmaxlen=0;
static const char *C_keywords[]={
"auto",
"break",
"case",
"char",
"const",
"continue",
"default",
"do",
"double",
"else",
"enum",
"extern",
"float",
"for",
"goto",
"if",
"inline",
"int",
"long",
"register",
"restrict",
"return",
"short",
"signed",
"sizeof",
"static",
"struct",
"switch",
"typedef",
"union",
"unsigned",
"void",
"volatile",
"while",
"_Alignas",
"_Alignof",
"_Atomic",
"_Bool",
"_Complex",
"_Generic",
"_Imaginary",
"_Noreturn",
"_Static_assert",
"_Thread_local",
NULL
};
if(keywords==NULL || nkeywords==NULL)
return(-1);
if(init==0) {
int k,l,maxl;
for(k=0,maxl=0;C_keywords[k]!=NULL;k++) {
l=strlen(C_keywords[k]);
maxl=(l>maxl)?l:maxl;
}
staticnkeywords=k;
staticmaxlen=maxl;
init=1;
}
*keywords=(char **)C_keywords;
*nkeywords=staticnkeywords;
if(maxlen!=NULL)
*maxlen=staticmaxlen;
return(0);
}
int
hl_C_getdirectives(char ***directives,int *ndirectives, int *maxlen)
{
static int init=0;
static int staticndirectives=0;
static int staticmaxlen=0;
static const char *C_directives[]={
"define",
"undef",
"include",
"if",
"ifdef",
"ifndef",
"else",
"elif",
"endif",
"line",
"error",
"pragma",
NULL
};
if(directives==NULL || ndirectives==NULL)
return(-1);
if(init==0) {
int k,l,maxl;
for(k=0,maxl=0;C_directives[k]!=NULL;k++) {
l=strlen(C_directives[k]);
maxl=(l>maxl)?l:maxl;
}
staticndirectives=k;
staticmaxlen=maxl;
init=1;
}
*directives=(char **)C_directives;
*ndirectives=staticndirectives;
if(maxlen!=NULL)
*maxlen=staticmaxlen;
return(0);
}
int
hl_searchlist(char **wordlist, int wordlistlen, char *word, int wordlen, int *res)
{
int k;
if(wordlist==NULL || wordlistlen<0 || word==NULL || wordlen<0)
return(-1); /* sanity check failed */
for(k=0;k<wordlistlen;k++) {
if(memcmp(wordlist[k],word,wordlen)==0 && (wordlist[k])[wordlen]=='\0') {
if(res!=NULL)
*res=k;
return(0); /* word found */
}
}
return(-1); /* word not found */
}
linecolor_t *
hl_addtolinecolor(int *opaque, highlighter_t *hl,linecolor_t *linecolor,int posoff,int color)
{
hline_t *line;
if(opaque==NULL || *opaque<0 || hl==NULL || (posoff!=0 && linecolor==NULL) || color<0 || posoff<0)
return(NULL); /* sanity check failed */
/* make sure there is a space for a linecolor_t in buf */
if((hl->usedbuf+sizeof(linecolor_t))>=hl->sizebuf) {
char *newbuf;
int newsize=(hl->sizebuf+sizeof(linecolor_t)+BUFBLOCK-1)/BUFBLOCK;
newsize*=BUFBLOCK;
if((newbuf=realloc(hl->buf,newsize))==NULL)
return(NULL); /* insufficient memory */
hl->buf=newbuf;
memset(hl->buf+hl->sizebuf,0,(newsize-hl->sizebuf));
hl->sizebuf=newsize;
}
line=hl->lines+hl->usedlines;
/* posoff==0 means "do init" */
if(posoff==0) {
line->off=(hl->usedlines==0)?0:hl->lines[hl->usedlines-1].off+hl->lines[hl->usedlines-1].len;
line->len=sizeof(linecolor_t);
linecolor=(linecolor_t *) (hl->buf+line->off);
linecolor->len=1;
linecolor->color=color;
*opaque=1;
hl->usedbuf=line->off+line->len;
return(linecolor);
}
/* if posoff was already done, truncate */
if(posoff<*opaque) {
int delta,maxdelta;
int l;
maxdelta=line->len;
line->len=0;
for(delta=0,l=0;delta<maxdelta;l+=linecolor->len,delta+=sizeof(linecolor_t)) {
linecolor=(linecolor_t *) (hl->buf+line->off+delta);
line->len=delta;
if((l+linecolor->len)>=posoff) {
line->len+=sizeof(linecolor_t);
linecolor->len=posoff-l;
*opaque=posoff;
break;
}
}
hl->usedbuf=line->off+line->len;
}
linecolor=(linecolor_t *) (hl->buf+line->off+line->len);
/* if we have not changed color, expand */
if(linecolor[-1].color==color) {
int added=(posoff-*opaque)+1;
linecolor[-1].len+=added;
*opaque+=added;
return(linecolor-1);
}
/* add new linecolor */
linecolor->len=1;
linecolor->color=color;
*opaque+=1;
line->len+=sizeof(linecolor_t);
hl->usedbuf=line->off+line->len;
return(linecolor);
}