/* * recenteditor.c * * A programmers editor * * 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 <limits.h> #include <signal.h> #include <time.h> #include <sys/time.h> #include <sys/socket.h> /* unix domain socket support */ #include <sys/un.h> /* unix domain socket support */ #include <sys/types.h> /* getpwuid() */ #include <pwd.h> /* getpwuid() */ #include <fcntl.h> #include <sys/select.h> #include <stdarg.h> #include <sys/stat.h> #include "re_data.h" #include "re_plugin_unsaved.h" #include "re_plugin_highlighter.h" #include "re_plugin_prototypes.h" #include "re_ui.h" #include "ext/socklib.h" #define LINEFORCESCROLL 1 #define COLFORCESCROLL 5 #define IDLETIMEOUTSECONDS 10 #define COMMANDBUFSIZE 1024 #define DEFAULTFONTHEIGHT 14 #define SELECTBUFBLOCK 16384 #define PRINTSBLOCKSIZE 32 #define COMMBUFSIZE 16384 #define COMMCLIENTSBLOCKSIZE 32 #define SOCKETFILENAMESIZE 1024 #define LISTENBACKLOG 5 #define VIEWONLYPROGNAME "review" #define COMMAND_WARNING "(!)" #define COMMAND_INFO "(i)" #define COMMAND_GOTOLINE "Go to line:" #define COMMAND_SEARCHFORWARD "Search:" #define COMMAND_REPLACEWHAT "Search for:" #define COMMAND_REPLACEWITH "Replace with:" #define COMMAND_REPLACEHOW "Replace options (Igncase Selected Backwards Entire All Noconfirm):" #define COMMAND_CONFIRMEXIT "There is unsaved data. Please confirm exit with y+ENTER:" #define COMMAND_QUESTION "(?)" #define COMMAND_EXIT "Exit" #if 1 /* "forest" theme */ #define DEFAULT_COLOR_BACKGROUND "\xdf\xdf\xdf\xff" #define DEFAULT_COLOR_PRINTOUTSTRIPE "\xef\xef\xef\xff" #define DEFAULT_COLOR_CURSORBG "\x00\x00\x00\xff" #define DEFAULT_COLOR_CURSORFG "\xff\xff\xff\xff" #define DEFAULT_COLOR_TEXT "\x00\x00\x00\xff" #define DEFAULT_COLOR_MATCHBG "\xff\xff\xff\xaf" #define DEFAULT_COLOR_MATCHFG "\xff\x00\x00\x80" #define DEFAULT_COLOR_STATUSBG "\x14\x3a\xaf\xff" #define DEFAULT_COLOR_STATUSFG "\xe6\xdc\x5d\xff" #define DEFAULT_COLOR_STATUSFGLIGHT "\x6f\x73\xa3\xff" #define DEFAULT_COLOR_QUERYBG "\xad\x92\x5e\xff" #define DEFAULT_COLOR_QUERYFG "\xd0\xef\x4f\xff" #define DEFAULT_COLOR_QUERYBGOLD "\x83\x75\x59\xff" #define DEFAULT_COLOR_QUERYFGOLD "\xb1\xc5\x5e\xff" #define DEFAULT_COLOR_WARNINGBG "\xba\x07\x07\xff" #define DEFAULT_COLOR_WARNINGFG "\xe6\xdc\x5d\xff" #define DEFAULT_COLOR_INFOBG "\x4e\x8a\x4e\xff" #define DEFAULT_COLOR_INFOFG "\xee\xee\x46\xff" #define DEFAULT_COLOR_INFOFGLIGHT "\x84\xa4\x4c\xff" #define DEFAULT_COLOR_SELNORMAL "\xde\xcf\x7f\xff" #define DEFAULT_COLOR_CURLINE "\xf0\xea\xc9\xff" #else /* "alternative" theme */ #define DEFAULT_COLOR_BACKGROUND "\x1a\x1f\x35\xff" #define DEFAULT_COLOR_PRINTOUTSTRIPE "\x0d\x11\x1e\xff" #define DEFAULT_COLOR_CURSORBG "\xff\xff\xff\xff" #define DEFAULT_COLOR_CURSORFG "\x0d\x11\x1e\xff" #define DEFAULT_COLOR_TEXT "\xf2\xf0\xec\xff" #define DEFAULT_COLOR_MATCHBG "\xff\xff\xff\xaf" #define DEFAULT_COLOR_MATCHFG "\xff\x00\x00\x80" #define DEFAULT_COLOR_STATUSBG "\x14\x3a\xaf\xff" #define DEFAULT_COLOR_STATUSFG "\xe6\xdc\x5d\xff" #define DEFAULT_COLOR_STATUSFGLIGHT "\x6f\x73\xa3\xff" #define DEFAULT_COLOR_QUERYBG "\xad\x92\x5e\xff" #define DEFAULT_COLOR_QUERYFG "\xd0\xef\x4f\xff" #define DEFAULT_COLOR_QUERYBGOLD "\x83\x75\x59\xff" #define DEFAULT_COLOR_QUERYFGOLD "\xb1\xc5\x5e\xff" #define DEFAULT_COLOR_WARNINGBG "\xba\x07\x07\xff" #define DEFAULT_COLOR_WARNINGFG "\xe6\xdc\x5d\xff" #define DEFAULT_COLOR_INFOBG "\x4e\x8a\x4e\xff" #define DEFAULT_COLOR_INFOFG "\xee\xee\x46\xff" #define DEFAULT_COLOR_INFOFGLIGHT "\x84\xa4\x4c\xff" #define DEFAULT_COLOR_SELNORMAL "\x4f\x48\x2b\xff" #define DEFAULT_COLOR_CURLINE "\x6e\x64\x3c\xff" #endif #define COLOR_BACKGROUND(re) re->theme.color_background #define COLOR_PRINTOUTSTRIPE(re) re->theme.color_printoutstripe #define COLOR_CURSORBG(re) re->theme.color_cursorbg #define COLOR_CURSORFG(re) re->theme.color_cursorfg #define COLOR_TEXT(re) re->theme.color_text #define COLOR_MATCHBG(re) re->theme.color_matchbg #define COLOR_MATCHFG(re) re->theme.color_matchfg #define COLOR_STATUSBG(re) re->theme.color_statusbg #define COLOR_STATUSFG(re) re->theme.color_statusfg #define COLOR_STATUSFGLIGHT(re) re->theme.color_statusfglight #define COLOR_QUERYBG(re) re->theme.color_querybg #define COLOR_QUERYFG(re) re->theme.color_queryfg #define COLOR_QUERYBGOLD(re) re->theme.color_querybgold #define COLOR_QUERYFGOLD(re) re->theme.color_queryfgold #define COLOR_WARNINGBG(re) re->theme.color_warningbg #define COLOR_WARNINGFG(re) re->theme.color_warningfg #define COLOR_INFOBG(re) re->theme.color_infobg #define COLOR_INFOFG(re) re->theme.color_infofg #define COLOR_INFOFGLIGHT(re) re->theme.color_infofglight #define COLOR_SELNORMAL(re) re->theme.color_selnormal #define COLOR_CURLINE(re) re->theme.color_curline typedef struct printout_t { reui_t *ui; redata_t *data; int originline; int origincol; int showonlyn; int mouseselactive; int dirty; } printout_t; typedef struct theme_t { int ntheme; char color_background[5]; char color_printoutstripe[5]; char color_cursorbg[5]; char color_cursorfg[5]; char color_text[5]; char color_matchbg[5]; char color_matchfg[5]; char color_statusbg[5]; char color_statusfg[5]; char color_statusfglight[5]; char color_querybg[5]; char color_queryfg[5]; char color_querybgold[5]; char color_queryfgold[5]; char color_warningbg[5]; char color_warningfg[5]; char color_infobg[5]; char color_infofg[5]; char color_infofglight[5]; char color_selnormal[5]; char color_curline[5]; } theme_t; typedef struct commclient_t { int fd; int sizebufin; int usedbufin; int gotbufin; char bufin[COMMBUFSIZE]; int sizebufout; int usedbufout; int gotbufout; char bufout[COMMBUFSIZE]; } commclient_t; typedef struct comms_t { int serverfd; char socketfilename[SOCKETFILENAMESIZE]; int sizeclients; int usedclients; commclient_t **clients; } comms_t; typedef struct re_t { redata_t *data; reui_t *ui; theme_t theme; int viewonly; int quirk_duplicates; int flag_newfile; int showlinenumbers; char filename[PATH_MAX]; int x, y, w, h; // contents rect int fontheightpercent; int originline,origincol; int curline,curcol; int maxrow,maxcol; int headerdirty; int contentsdirty; int command_first_key; int selstartactive; int selstartline; int selstartcol; int selactive; int sellinefrom,sellineto; int selcolfrom,selcolto; int mouseselactive; int sellinestart,selcolstart; long sizeselectbuf; long usedselectbuf; char *selectbuf; char *command; char commandbuf[COMMANDBUFSIZE]; int is_oldcommandbuf; char cachelastsearch[COMMANDBUFSIZE]; char cachelastreplacewith[COMMANDBUFSIZE]; question_t *question; int showingwarning; int ignorenkeys; int sizeprints; int usedprints; printout_t *prints; redata_t *funclisting; int originlinefunclisting; int curlinefunclisting; struct timeval lastwheel; comms_t comms; } re_t; volatile int flag_sigint; volatile int flag_sigpipe; static int setsignal(int num, void (*sighandler)(int)); static void sighandler_sigint(int num); static void sighandler_sigpipe(int num); static int mystricmp(const char *s1, const char *s2); re_t *re_init(int viewonly); void re_free(re_t *re); int re_setuidata(re_t *re); int re_setfilename(re_t *re, char *filename); int re_fixorigin(re_t *re); int re_fixorigin_center(re_t *re); int re_textinsert(re_t *re, char *text, int sizetext); int re_processkey_editing(re_t *re, SDL_Event *event); int re_processkey_commandwait(re_t *re, SDL_Event *event); int re_processkey_commanddata(re_t *re, SDL_Event *event); int re_processcommand(re_t *re); int re_moveupdown(re_t *re, int totalinc); int re_moveleftright(re_t *re, int totalinc); int re_changefontsize(re_t *re, int direction); int re_sel_setstart(re_t *re, int line, int col); int re_sel_setend(re_t *re, int line, int col); int re_sel_resize(re_t *re,int oldcol,int oldline,int direction); int re_sel_toggle(re_t *re); int re_sel_lincolisinside(re_t *re, int line, int col); int re_sel_lincolisbefore(re_t *re, int line, int col); int re_sel_lincolisafter(re_t *re, int line, int col); int re_sel_lincolisend(re_t *re, int line, int col); int re_selectbuf_resize(re_t *re,long size); int re_selectbuf_fill(re_t *re,long frompos,long size, int nadditionalspaces); int re_selectbuf_replace(re_t *re,char *newdata); int re_clipget(re_t *re); int re_addprint(re_t *re); int re_delprint(re_t *re, int nprint); int re_rtrim(re_t *re, long curpos, int *trimmed); long re_getmatchingbracket(re_t *re,long posini, char originalchar, char *matchingchar); int re_drawheader_editing(re_t *re); int re_drawheader_command(re_t *re); int re_drawcontents(re_t *re, printout_t *printout); redata_t *re_getfunclisting(re_t *re); int re_funclistingxy2line(re_t *re,int mx,int my); int re_wheelaccel(re_t *re, int rawamount); int re_themeset(re_t *re, int ntheme); int re_socketinit(re_t *re, char *filename); void re_socketfree(re_t *re); int re_socketnewclient(re_t *re); int re_socketstep(re_t *re); int re_socketin(re_t *re, int nclient, char *line); #ifndef __GNUC__ int re_socketout(re_t *re, int nclient, char *format, ...); #else int re_socketout(re_t *re, int nclient, char *format, ...) __attribute__((format(printf,3,4))); #endif int main(int argc, char *argv[]) { re_t *re; int do_exit; SDL_Event event; sselect *ssel; int flag_had_events; time_t lastidle,now; int load_pending; int viewonly; int i,l; if(argc!=2 || strcmp(argv[argc-1],"--help")==0) { fprintf(stderr,"Syntax: %s filename\n",argv[0]); return(1); } viewonly=0; if((i=strlen(argv[0]))>=(l=strlen(VIEWONLYPROGNAME)) && strcmp(argv[0]+i-l,VIEWONLYPROGNAME)==0) viewonly=1; if((re=re_init(viewonly))==NULL) { fprintf(stderr,"ERROR: couldn't init structs.\n"); return(2); } if((ssel=sselect_init())==NULL) { fprintf(stderr,"ERROR: couln't init internal data.\n"); re_free(re),re=NULL; return(2); } if(re_setfilename(re,argv[argc-1])!=0) { fprintf(stderr,"ERROR: filename too long.\n"); re_free(re),re=NULL; return(2); } reui_title(re->ui,re->filename); flag_sigint=flag_sigpipe=0; setsignal(SIGINT,sighandler_sigint); setsignal(SIGPIPE,sighandler_sigpipe); do_exit=0; reui_scr2renderer(re->ui,0,0,re->ui->w,re->ui->h); re_setuidata(re); re->headerdirty=1; re->contentsdirty=1; flag_had_events=0; SDL_StartTextInput(); lastidle=0; load_pending=1; redata_loadquestions_setup(re->data, re->filename); while(do_exit==0 && flag_sigint==0) { /* load file (or ask pre-load questions), if pending */ if(load_pending && re->command==NULL) { question_t *question; if((question=redata_loadquestions_getnext(re->data))!=NULL) { re->command=COMMAND_QUESTION; re->question=question; #if 0 fprintf(stderr,"QUESTION INIT: %s: %s\n",re->question->title,re->question->body); #endif } else { long startpos; char *startptr; int len; if(redata_load(re->data,re->filename,NULL)==-1) re->flag_newfile=1; else re->flag_newfile=0; re->originline=re->origincol=0; re->curline=re->curcol=0; load_pending=0; re->headerdirty=1; re->contentsdirty=1; /* check if last character of file is '\n', if not, add it */ if(redata_getused(re->data)==0 || (redata_line_rawinfo(re->data,redata_getused(re->data)-1,&startpos,&startptr,&len,NULL)!=-1 && len>0 && startptr[len-1]!='\n')) { redata_op_add(re->data,redata_getused(re->data),"\n",1,NULL); re->command=COMMAND_WARNING; snprintf(re->commandbuf,sizeof(re->commandbuf),"Added missing \\n at end of file"); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; redata_needssaving_reset(re->data); } if(re_socketinit(re,re->filename)!=0) { fprintf(stderr,"WARNING: Couldn't init communication socket; there will be no integration with other tools\n"); } } } /* workaround for some nvidia linux drivers, that show old data on partial updates */ if(re->headerdirty || re->contentsdirty || re->ui->rendererdirty) { re->headerdirty=1; re->contentsdirty=1; re->ui->rendererdirty=1; } /* end of fix */ if(re->headerdirty) { if(re->command==NULL) { #if 0 fprintf(stderr,"REDRAW Header (editing)\n"); #endif re_drawheader_editing(re); re->showingwarning=0; } else { #if 0 fprintf(stderr,"REDRAW Header (command)\n"); #endif re_drawheader_command(re); if(strcmp(re->command,COMMAND_WARNING)==0 || strcmp(re->command,COMMAND_INFO)==0) { /* the warnings/info only get shown once, remove it */ re->command=NULL; re->commandbuf[0]='\0'; re->showingwarning=1; } else { re->showingwarning=0; } } } if(re->contentsdirty) { /* set associated printouts as dirty too */ for(i=0;i<re->sizeprints;i++) { if(re->prints[i].ui!=NULL && re->prints[i].data==NULL) re->prints[i].dirty=1; } /* redraw contents */ re_drawcontents(re,NULL); } if(re->ui->rendererdirty) { #if 0 fprintf(stderr,"RENDER\n"); #endif reui_present(re->ui); } for(i=0;i<re->sizeprints;i++) { if(re->prints[i].ui==NULL) continue; if(re->prints[i].dirty) re_drawcontents(re,re->prints+i); if(re->prints[i].ui->rendererdirty) reui_present(re->prints[i].ui); } sselect_wait(ssel,(flag_had_events)?10:100); re_socketstep(re); flag_had_events=(flag_had_events>0)?flag_had_events-1:0; SDL_PumpEvents(); while(SDL_PeepEvents(&event,1,SDL_GETEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT)>0) { Uint32 windowID; flag_had_events=10; /* Process printout events */ if((event.type==SDL_WINDOWEVENT && (windowID=event.window.windowID)!=SDL_GetWindowID(re->ui->win)) || (event.type==SDL_KEYDOWN && (windowID=event.key.windowID)!=SDL_GetWindowID(re->ui->win)) || (event.type==SDL_TEXTEDITING && (windowID=event.edit.windowID)!=SDL_GetWindowID(re->ui->win)) || (event.type==SDL_TEXTINPUT && (windowID=event.text.windowID)!=SDL_GetWindowID(re->ui->win)) || (event.type==SDL_MOUSEWHEEL && (windowID=event.text.windowID)!=SDL_GetWindowID(re->ui->win)) || ((event.type==SDL_MOUSEBUTTONDOWN || event.type==SDL_MOUSEMOTION || event.type==SDL_MOUSEBUTTONUP) && (windowID=event.text.windowID)!=SDL_GetWindowID(re->ui->win)) ) { printout_t *printout; for(i=0;i<re->sizeprints;i++) { if(re->prints[i].ui!=NULL && windowID==SDL_GetWindowID(re->prints[i].ui->win)) { break; } } if(i>=re->sizeprints) continue; /* unknown window */ printout=re->prints+i; /* transfor mousewheel events to keydown events */ switch(event.type) { case SDL_WINDOWEVENT: if(event.window.event==SDL_WINDOWEVENT_SHOWN || event.window.event==SDL_WINDOWEVENT_EXPOSED) { printout->dirty=1; } else if((event.window.event==SDL_WINDOWEVENT_RESIZED || event.window.event==SDL_WINDOWEVENT_SIZE_CHANGED) && (event.window.data1!=printout->ui->w || event.window.data2!=printout->ui->h)) { reui_resize(printout->ui,event.window.data1,event.window.data2); printout->dirty=1; } else if(event.window.event==SDL_WINDOWEVENT_CLOSE) { re_delprint(re,i); } break; case SDL_KEYDOWN: case SDL_MOUSEWHEEL: if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_ESCAPE) { re_delprint(re,i); } if(event.type==SDL_MOUSEWHEEL || (event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_DOWN || event.key.keysym.sym==SDLK_UP || event.key.keysym.sym==SDLK_PAGEDOWN || event.key.keysym.sym==SDLK_PAGEUP))) { int neworiginline; int maxrow; int linetotal; int wheelamount; wheelamount=(event.type==SDL_MOUSEWHEEL)?re_wheelaccel(re,-event.wheel.y):0; maxrow=printout->ui->h/printout->ui->fontheight-1; neworiginline=printout->originline; neworiginline+=(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_UP)?-1: (event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_DOWN)?1: (event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_PAGEUP)?-maxrow: (event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_PAGEDOWN)?maxrow: (event.type==SDL_MOUSEWHEEL)?wheelamount: 0; linetotal=redata_line_total((printout->data!=NULL)?printout->data:re->data); neworiginline=(neworiginline<0)?0:neworiginline; neworiginline=(neworiginline>linetotal)?linetotal:neworiginline; if(neworiginline==printout->originline) break; /* nothing to do, at top */ printout->originline=neworiginline; printout->dirty=1; } else if(event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_DOWN || event.key.keysym.sym==SDLK_UP)) { if(event.key.keysym.sym==SDLK_UP && printout->originline==0) break; /* nothing to do, at top */ printout->originline+=((event.key.keysym.sym==SDLK_UP)?-1:1); printout->dirty=1; } else if(event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_LEFT || event.key.keysym.sym==SDLK_RIGHT)) { if(event.key.keysym.sym==SDLK_LEFT && printout->origincol==0) break; /* nothing to do, at top */ printout->origincol+=((event.key.keysym.sym==SDLK_LEFT)?-1:1)*8; printout->origincol=(printout->origincol<0)?0:printout->origincol; printout->dirty=1; } break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: case SDL_MOUSEMOTION: if(!(event.type==SDL_MOUSEMOTION && re->mouseselactive==0)) { int mx,my; int newposx,newposy; long tmppos; if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT) { printout->mouseselactive=1; } else if( event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT) { printout->mouseselactive=0; } mx=(event.type!=SDL_MOUSEMOTION)?event.button.x:event.motion.x; my=(event.type!=SDL_MOUSEMOTION)?event.button.y:event.motion.y; newposx=(mx-(printout->ui->fontwidth*8+printout->ui->fontwidth/2))/printout->ui->fontwidth; newposx=(newposx<0)?0:newposx; newposy=my/printout->ui->fontheight; if(redata_linecol2pos(re->data, printout->originline+newposy, printout->origincol+newposx,&tmppos,NULL)==0) { re->curline=printout->originline+newposy; re->curcol=printout->origincol+newposx; re->headerdirty=1; re->contentsdirty=1; re_fixorigin_center(re); } } break; } continue; /* only want window events from printouts */ } /* process main window events */ switch(event.type) { case SDL_QUIT: if(redata_needssaving(re->data)) { re->command=COMMAND_CONFIRMEXIT; re->commandbuf[0]='\0'; re->headerdirty=1; } else { do_exit=1; } break; case SDL_KEYUP: if(event.key.keysym.sym==SDLK_RSHIFT || event.key.keysym.sym==SDLK_LSHIFT) { re->selstartactive=0; } break; case SDL_KEYDOWN: case SDL_TEXTINPUT: case SDL_TEXTEDITING: case SDL_MOUSEWHEEL: if(re->viewonly && (event.type==SDL_TEXTINPUT || event.type==SDL_TEXTEDITING)) break; if(re->viewonly && (event.type==SDL_KEYDOWN || event.type==SDL_MOUSEWHEEL)) { if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_ESCAPE) { do_exit=1; } if(event.type==SDL_MOUSEWHEEL || (event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_DOWN || event.key.keysym.sym==SDLK_UP || event.key.keysym.sym==SDLK_PAGEDOWN || event.key.keysym.sym==SDLK_PAGEUP))) { int neworiginline; int linetotal; int wheelamount; wheelamount=(event.type==SDL_MOUSEWHEEL)?re_wheelaccel(re,-event.wheel.y):0; neworiginline=re->originline; neworiginline+=(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_UP)?-1: (event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_DOWN)?1: (event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_PAGEUP)?-re->maxrow: (event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_PAGEDOWN)?re->maxrow: (event.type==SDL_MOUSEWHEEL)?wheelamount: 0; linetotal=redata_line_total(re->data); neworiginline=(neworiginline<0)?0:neworiginline; neworiginline=(neworiginline>linetotal)?linetotal:neworiginline; if(neworiginline==re->originline) break; /* nothing to do, at top */ re->curline+=(neworiginline-re->originline); re->originline=neworiginline; re->headerdirty=1; re->contentsdirty=1; } else if(event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_LEFT || event.key.keysym.sym==SDLK_RIGHT)) { if(event.key.keysym.sym==SDLK_LEFT && re->origincol==0) break; /* nothing to do, at top */ re->origincol+=((event.key.keysym.sym==SDLK_LEFT)?-1:1)*8; re->origincol=(re->origincol<0)?0:re->origincol; re->headerdirty=1; re->contentsdirty=1; } break; } if(!re->viewonly && event.type==SDL_KEYDOWN && (event.key.keysym.sym==SDLK_RSHIFT || event.key.keysym.sym==SDLK_LSHIFT)) { re->selstartactive=1; re->selstartline=re->curline; re->selstartcol=re->curcol; } if(re->command==NULL || strcmp(re->command,COMMAND_WARNING)==0 || strcmp(re->command,COMMAND_INFO)==0) re_processkey_editing(re,&event); else if(re->command[0]=='\0') re_processkey_commandwait(re,&event); else re_processkey_commanddata(re,&event); break; case SDL_WINDOWEVENT: #if 0 fprintf(stderr,"WINDOWEVENT: %s(%li) data1:%li data2:%li\n", (event.window.event==SDL_WINDOWEVENT_NONE)?"NONE": (event.window.event==SDL_WINDOWEVENT_SHOWN)?"SHOWN": (event.window.event==SDL_WINDOWEVENT_HIDDEN)?"HIDDEN": (event.window.event==SDL_WINDOWEVENT_EXPOSED)?"EXPOSED": (event.window.event==SDL_WINDOWEVENT_MOVED)?"MOVED": (event.window.event==SDL_WINDOWEVENT_RESIZED)?"RESIZED": (event.window.event==SDL_WINDOWEVENT_SIZE_CHANGED)?"SIZE_CHANGED": (event.window.event==SDL_WINDOWEVENT_MINIMIZED)?"MINIMIZED": (event.window.event==SDL_WINDOWEVENT_MAXIMIZED)?"MAXIMIZED": (event.window.event==SDL_WINDOWEVENT_RESTORED)?"RESTORED": (event.window.event==SDL_WINDOWEVENT_FOCUS_GAINED)?"FOCUS_GAINED": (event.window.event==SDL_WINDOWEVENT_FOCUS_LOST)?"FOCUS_LOST": (event.window.event==SDL_WINDOWEVENT_CLOSE)?"CLOSE": (event.window.event==SDL_WINDOWEVENT_TAKE_FOCUS)?"TAKE_FOCUS": "UNKNOWN", (long) event.window.event,(long)event.window.data1,(long)event.window.data2); #endif if(event.window.event==SDL_WINDOWEVENT_SHOWN || event.window.event==SDL_WINDOWEVENT_EXPOSED) { if(!re->showingwarning) re->headerdirty=1; re->contentsdirty=1; } else if((event.window.event==SDL_WINDOWEVENT_RESIZED || event.window.event==SDL_WINDOWEVENT_SIZE_CHANGED) && (event.window.data1!=re->ui->w || event.window.data2!=re->ui->h)) { #if 0 fprintf(stderr,"Resizing from %ix%i to %ix%i...\n",re->ui->w,re->ui->h,(int)event.window.data1,(int)event.window.data2); #endif reui_resize(re->ui,event.window.data1,event.window.data2); re_setuidata(re); re->headerdirty=1; re->contentsdirty=1; } break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: case SDL_MOUSEMOTION: { #warning TODO: select with mouse int mx,my; mx=(event.type!=SDL_MOUSEMOTION)?event.button.x:event.motion.x; my=(event.type!=SDL_MOUSEMOTION)?event.button.y:event.motion.y; if(re->mouseselactive==0 && event.type==SDL_MOUSEBUTTONDOWN && !(SDL_GetModState()&KMOD_CTRL) && my<re->y && re->funclisting==NULL) { redata_t *funclisting; if((funclisting=re_getfunclisting(re))==NULL) break; /* mem. insuf. */ if(re->funclisting!=NULL) redata_free(re->funclisting),re->funclisting=NULL; re->funclisting=funclisting; re->originlinefunclisting=0; re->curlinefunclisting=-1; re->contentsdirty=1; } else if(event.type==SDL_MOUSEBUTTONUP && re->funclisting!=NULL) { char linebuf[32],*cmdend; if(redata_line_getstartstrtrimmed(re->funclisting,re->curlinefunclisting,linebuf,sizeof(linebuf)," ")==0) { int oldline; if((cmdend=strchr(linebuf,':'))!=NULL) *cmdend='\0'; re->command=COMMAND_GOTOLINE; strncpy(re->commandbuf,linebuf,sizeof(re->commandbuf)); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; oldline=re->curline; re_processcommand(re); if(oldline!=re->curline) { /* position the cursor near the top of the window */ re->originline=re->curline-3; re->originline=(re->originline<0)?0:re->originline; } } redata_free(re->funclisting),re->funclisting=NULL; re->contentsdirty=1; } else if(event.type==SDL_MOUSEBUTTONUP && (SDL_GetModState()&KMOD_CTRL) && my>=re->y) { /* control+click: search function definition */ int newposx,newposy; long tmppos; newposx=(mx-re->x-(re->showlinenumbers?(re->ui->fontwidth*7+re->ui->fontwidth/2):0))/re->ui->fontwidth; newposx=(newposx<0)?0:newposx; newposy=(my-re->y)/re->ui->fontheight; if(redata_linecol2pos(re->data, re->originline+newposy, re->origincol+newposx,&tmppos,NULL)==0) { char searchbuf[1024]; /* magic number: max. size of string to search (truncated if bigger) */ int usedsearchbuf; char utfchar[5]; int usedutfchar; long startpos,curpos,endpos; int c; redata_t *funclisting; /* search start of word */ for(curpos=startpos=tmppos;redata_getprevutf8char(re->data,curpos,utfchar,sizeof(utfchar),&usedutfchar)==0;startpos=curpos) { curpos-=usedutfchar; if(usedutfchar==1) { c=utfchar[0]; if(!((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_')) break; } } /* search end of word */ for(curpos=endpos=tmppos;redata_getutf8char(re->data,curpos,utfchar,sizeof(utfchar),&usedutfchar)==0;endpos=curpos) { curpos+=usedutfchar; if(usedutfchar==1) { c=utfchar[0]; if(!((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_')) break; } } /* extract word, generate funclisting, search word in funclisting */ funclisting=NULL; if(redata_getsubstr(re->data,startpos,endpos,searchbuf,sizeof(searchbuf),&usedsearchbuf)==0 && (funclisting=re_getfunclisting(re))!=NULL && (curpos=redata_searchforward(funclisting,0,searchbuf,usedsearchbuf))!=-1) { char linebuf[32],*cmdend; int funcline,funccol; if(redata_pos2linecol(funclisting,curpos,&funcline,&funccol)==0 && redata_line_getstartstrtrimmed(funclisting,funcline,linebuf,sizeof(linebuf)," ")==0) { int oldline; if((cmdend=strchr(linebuf,':'))!=NULL) *cmdend='\0'; re->command=COMMAND_GOTOLINE; strncpy(re->commandbuf,linebuf,sizeof(re->commandbuf)); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; oldline=re->curline; re_processcommand(re); if(oldline!=re->curline) { /* position the cursor near the top of the window */ re->originline=re->curline-3; re->originline=(re->originline<0)?0:re->originline; } /* position the cursor at the start of the line */ re->origincol=0; re->curcol=0; /* redraw */ re->contentsdirty=1; } redata_free(re->funclisting),re->funclisting=NULL; } if(funclisting!=NULL) redata_free(funclisting),funclisting=NULL; } } else if(event.type==SDL_MOUSEMOTION && re->funclisting!=NULL) { if(mx<re->ui->fontwidth*10) { /* scroll */ int total; total=redata_line_total(re->funclisting); if(my<re->y) { if(re->originlinefunclisting!=0) { re->originlinefunclisting=0; re->contentsdirty=1; } } else if(my>re->y && re->h>re->y) { int old; old=re->originlinefunclisting; re->originlinefunclisting=total*(my-re->y)/(re->h-re->y); if(old!=re->originlinefunclisting) re->contentsdirty=1; } } else { /* select */ int oldlinefunclisting; oldlinefunclisting=re->curlinefunclisting; re->curlinefunclisting=re_funclistingxy2line(re,mx,my); if(oldlinefunclisting!=re->curlinefunclisting) re->contentsdirty=1; } } else if(!(event.type==SDL_MOUSEMOTION && (re->mouseselactive==0 || my<re->y || mx<re->x))) { int newposx,newposy; long tmppos; if(event.type==SDL_MOUSEBUTTONDOWN && !(SDL_GetModState()&KMOD_CTRL) && event.button.button==SDL_BUTTON_LEFT) { re->mouseselactive=1; } else if( event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT) { re->mouseselactive=0; } newposx=(mx-re->x-(re->showlinenumbers?(re->ui->fontwidth*7+re->ui->fontwidth/2):0))/re->ui->fontwidth; newposx=(newposx<0)?0:newposx; newposy=(my-re->y)/re->ui->fontheight; if(redata_linecol2pos(re->data, re->originline+newposy, re->origincol+newposx,&tmppos,NULL)==0) { re->curline=re->originline+newposy; re->curcol=re->origincol+newposx; re->headerdirty=1; re->contentsdirty=1; } } } break; default: break; } } /* additional processing of global commands */ if(re->command!=NULL && strcmp(re->command,COMMAND_EXIT)==0) do_exit=1; /* timeouts */ now=time(NULL); if((lastidle+IDLETIMEOUTSECONDS)<now || (lastidle-IDLETIMEOUTSECONDS)>now) { lastidle=now; redata_idleproc(re->data,re->filename); } } SDL_StopTextInput(); sselect_free(ssel),ssel=NULL; re_free(re),re=NULL; return(0); } static int setsignal(int num, void (*sighandler)(int)) { struct sigaction sa; sa.sa_handler=sighandler; sigemptyset(&sa.sa_mask); sa.sa_flags=0; return(sigaction(num,&sa,0)); } static void sighandler_sigint(int num) { flag_sigint=1; } static void sighandler_sigpipe(int num) { flag_sigpipe=1; } static int mystricmp(const char *s1, const char *s2) { int c1,c2; for(;*s1!='\0' && *s2!='\0';s1++,s2++) { if(*s1==*s2) continue; c1=*((unsigned char *)s1); c2=*((unsigned char *)s2); c1=(c1>='A' && c1<='Z')?c1-'A'+'a':c1; c2=(c2>='A' && c2<='Z')?c2-'A'+'a':c2; if(c1==c2) continue; return(c1-c2); } return(((int) (*((unsigned char *)s1)))-((int) (*((unsigned char *)s2)))); } re_t * re_init(int viewonly) { re_t *re; SDL_version linked_version; if((re=malloc(sizeof(re_t)))==NULL) return(NULL); /* insuf. mem. */ memset(re,0,sizeof(re_t)); if(viewonly==0) { re->data=redata_init( redata_unsaved_register, redata_highlighter_register, redata_prototypes_register, NULL); } else { /* viewonly */ re->data=redata_init( redata_highlighter_register, NULL); } if(re->data==NULL) { re_free(re),re=NULL; return(NULL); /* insuf. mem. */ } re->fontheightpercent=100; if((re->ui=reui_init(DEFAULTFONTHEIGHT*re->fontheightpercent/100,NULL))==NULL) { re_free(re),re=NULL; return(NULL); /* video init error */ } re->viewonly=(viewonly!=0)?1:0; SDL_GetVersion(&linked_version); re->quirk_duplicates=(linked_version.major==2 && linked_version.minor==0 && linked_version.patch==9)?1:0; re_themeset(re,0); return(re); } void re_free(re_t *re) { int i; if(re==NULL) return; /* all done */ if(re->funclisting!=NULL) redata_free(re->funclisting),re->funclisting=NULL; for(i=0;i<re->sizeprints;i++) { if(re->prints[i].ui!=NULL) re_delprint(re,i); } if(re->prints!=NULL) free(re->prints),re->prints=NULL,re->sizeprints=re->usedprints=0; if(re->ui!=NULL) reui_free(re->ui),re->ui=NULL; if(re->data!=NULL) redata_free(re->data),re->data=NULL; if(re->selectbuf!=NULL) free(re->selectbuf),re->selectbuf=NULL,re->usedselectbuf=re->sizeselectbuf=0; re_socketfree(re); free(re),re=NULL; return; } int re_setuidata(re_t *re) { if(re==NULL) return(-1); re->x=0,re->y=re->ui->fontheight,re->w=re->ui->w,re->h=re->ui->h-re->y; re->maxrow=re->h/re->ui->fontheight-1; re->maxcol=(re->w-(re->showlinenumbers?(re->ui->fontwidth*7+re->ui->fontwidth/2):0))/re->ui->fontwidth-1; re_fixorigin(re); return(0); } int re_setfilename(re_t *re, char *filename) { if(re==NULL || filename==NULL) return(-1); strncpy(re->filename,filename,sizeof(re->filename)); re->filename[sizeof(re->filename)-1]='\0'; if(strcmp(filename,re->filename)!=0) return(-1); /* filename too long */ return(0); } int re_fixorigin(re_t *re) { if(re==NULL) return(-1); if(re->curline<(re->originline+LINEFORCESCROLL)) { re->originline=re->curline-LINEFORCESCROLL; re->originline=(re->originline<0)?0:re->originline; } if(re->curline>(re->originline+re->maxrow-LINEFORCESCROLL)) { re->originline=re->curline+LINEFORCESCROLL-re->maxrow; re->originline=(re->originline<0)?0:(re->originline>re->curline)?re->curline:re->originline; } if(re->curcol<re->origincol) { re->origincol=re->curcol; re->origincol=(re->origincol<0)?0:re->origincol; } if(re->curcol>=(re->origincol+re->maxcol-COLFORCESCROLL)) { int col; col=re->curcol-(re->curcol%COLFORCESCROLL); re->origincol=col+COLFORCESCROLL-re->maxcol; re->origincol=(re->origincol<0)?0:re->origincol; } return(0); } int re_fixorigin_center(re_t *re) { if(re==NULL) return(-1); if(re->curline<re->originline || re->curline>=(re->originline+re->maxrow)) { re->originline=re->curline-(re->maxrow/2); re->originline=(re->originline<0)?0:re->originline; } re->origincol=(re->curcol>(re->maxcol-COLFORCESCROLL))?(re->curcol-(re->maxcol-COLFORCESCROLL)):0; return(0); } int re_textinsert(re_t *re, char *text, int sizetext) { long realend; int at_end; int nspaces; long cursorpos; long selstart,selend; int fixsel; char *ptr,*next; int len; int trimmed; if(re==NULL || text==NULL || sizetext<=0) return(-1); /* sanity check failed */ if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0 || redata_line_realend(re->data,cursorpos,&realend)==-1) { return(-1); /* couldn't get current line info */ } fixsel=0; selstart=selend=0; at_end=(cursorpos==realend)?1:0; for(nspaces=0;nspaces<sizetext && text[nspaces]==' ';nspaces++) ; if(nspaces>0 && nspaces==sizetext && at_end) { /* instead of adding spaces at the end of the line, we move the cursor to the right */ re_moveleftright(re,nspaces); return(0); /* no need to add spaces at the end of the line */ } redata_undo_groupinit(re->data,NULL); if(!(text[0]=='\n' && sizetext==1) && at_end) { int col; if(redata_pos2linecol(re->data,cursorpos,NULL,&col)==-1) return(-1); /* couldn't get line info */ if(redata_op_addn(re->data,cursorpos,' ',re->curcol-col,NULL)!=0) return(-1); /* couldn't add spaces up to current displayed pos */ /* increment cursorpos; spaces are 1 byte, so the number of columns advanced is the number of bytes advanced */ cursorpos+=re->curcol-col; } if(re->selactive && redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&selstart,NULL)==0 && redata_linecol2pos(re->data,re->sellineto,re->selcolto,&selend,NULL)==0) { fixsel=1; } if(redata_op_add(re->data,cursorpos,text,sizetext,NULL)!=0) return(-1); /* couldn't add requested text */ /* fix selection */ if(fixsel) { if(cursorpos<=selstart) redata_pos2linecol(re->data,selstart+sizetext,&(re->sellinefrom),&(re->selcolfrom)); if(cursorpos<=selend) redata_pos2linecol(re->data,selend+sizetext,&(re->sellineto),&(re->selcolto)); } /* fix printouts scope */ if(re->usedprints>0) { int i; int numnl; printout_t *printout; for(numnl=0,ptr=strchr(text,'\n');ptr!=NULL;ptr=strchr(ptr+1,'\n')) numnl++; for(i=0;i<re->usedprints;i++) { printout=re->prints+i; if(printout->data!=NULL) continue; /* has its own copy of the data */ if(printout->originline>re->curline) { printout->originline+=numnl; } else if(printout->showonlyn>0 && re->curline>=printout->originline && re->curline<(printout->originline+printout->showonlyn)) { printout->showonlyn+=numnl; } } } /* fix cursor pos */ for(ptr=text,next=memchr(text,'\n',sizetext);ptr!=NULL;ptr=(next!=NULL)?next+1:NULL,next=(ptr!=NULL)?memchr(ptr,'\n',sizetext-(ptr-text)):NULL) { len=(next!=NULL)?(next-ptr):sizetext-(ptr-text); re->curcol+=redata_generic_utf8len(ptr,len); cursorpos+=len; if(next!=NULL) { cursorpos++; if(re_rtrim(re,cursorpos-1,&trimmed)!=-1) cursorpos-=trimmed; re->curline++; re->curcol=0; } } /* trim last line inserted */ if(redata_getchar(re->data,cursorpos)=='\n' && re_rtrim(re,cursorpos,&trimmed)!=-1) cursorpos-=trimmed; redata_undo_groupcommit(re->data,NULL); re_fixorigin(re); re->headerdirty=1; re->contentsdirty=1; return(0); } int re_processkey_editing(re_t *re, SDL_Event *event) { SDL_Event fakeevent; int wheelamount=0; if(re==NULL || event==NULL) return(-1); /* sanity check failed */ /* convert RETURN KEYDOWN to TEXTINPUT */ if(event->type==SDL_KEYDOWN && event->key.keysym.sym==SDLK_RETURN) { memset(&fakeevent,0,sizeof(SDL_Event)); event=&fakeevent; event->type=SDL_TEXTINPUT; strncpy(event->text.text,"\n",sizeof(event->text.text)); event->text.text[sizeof(event->text.text)-1]='\0'; } else if(event->type==SDL_KEYDOWN && event->key.keysym.sym==SDLK_TAB) { static char spaces[9]={" "}; memset(&fakeevent,0,sizeof(SDL_Event)); event=&fakeevent; event->type=SDL_TEXTINPUT; /* If control is pressed, insert a real tab, otherwise, insert spaces until next tabstop */ if((SDL_GetModState()&KMOD_CTRL)!=0) { strncpy(event->text.text,"\t",sizeof(event->text.text)); event->text.text[sizeof(event->text.text)-1]='\0'; } else { strncpy(event->text.text,spaces+(re->curcol%8),sizeof(event->text.text)); event->text.text[sizeof(event->text.text)-1]='\0'; } } else if(event->type==SDL_KEYDOWN && event->key.keysym.sym==SDLK_DELETE) { #if 0 fprintf(stderr,"SDL_KEYDOWN: DELETE\n"); #endif long realend; int at_end; long cursorpos; if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0 || cursorpos==redata_getused(re->data)) { return(0); /* already at end, nothing to delete */ } if(redata_line_realend(re->data,cursorpos,&realend)==-1) return(-1); /* couldn't get current line info */ at_end=(cursorpos==realend)?1:0; if(at_end && (cursorpos+1)==redata_getused(re->data)) return(-1); /* we shouldn't erase the final '\n' */ memset(&fakeevent,0,sizeof(SDL_Event)); event=&fakeevent; event->type=SDL_KEYDOWN; event->key.keysym.sym=SDLK_BACKSPACE; redata_undo_groupinit(re->data,NULL); if(at_end) { int col; if(redata_pos2linecol(re->data,cursorpos,NULL,&col)==-1) return(-1); /* couldn't get line info */ if(redata_op_addn(re->data,cursorpos,' ',re->curcol-col,NULL)!=0) return(-1); /* couldn't add spaces up to current displayed pos */ re->curline++; re->curcol=0; } else { re_moveleftright(re,1); } #if 0 fprintf(stderr,"SDL_KEYDOWN: fake setup ok\n"); #endif } else if(event->type==SDL_MOUSEWHEEL && event->wheel.y!=0) { wheelamount=re_wheelaccel(re,-event->wheel.y); memset(&fakeevent,0,sizeof(SDL_Event)); event=&fakeevent; event->type=SDL_KEYDOWN; event->key.keysym.sym=(wheelamount>0)?SDLK_DOWN:SDLK_UP; } /* special case: text editing event */ if(event->type==SDL_TEXTINPUT) { if(re->ignorenkeys>0) { re->ignorenkeys--; return(0); /* this is an already processed key, ignore it */ } #if 0 fprintf(stderr,"SDL_TEXTINPUT:\"%s\"\n",event->text.text); #endif re_textinsert(re,event->text.text,strlen(event->text.text)); } #if 0 else if(event->type==SDL_TEXTEDITING) { /* NOTE: on window enter events we cam also receive this event (!) */ #warning TODO: CJK support fprintf(stderr,"SDL_TEXTEDITING: composition:\"%s\" start:%i len:%i\n",event->edit.text,event->edit.start,event->edit.length); } #endif /* default case: keydown event */ if(event->type!=SDL_KEYDOWN) return(0); /* Ignore other possible events */ #if 0 fprintf(stderr,"SDL_KEYDOWN: sym:%i\n",event->key.keysym.sym); #endif if(event->key.keysym.sym==SDLK_ESCAPE) { re->headerdirty=1; } else if((SDL_GetModState()&KMOD_ALT)!=0 && re->selactive && (event->key.keysym.sym==SDLK_LEFT || event->key.keysym.sym==SDLK_RIGHT)) { int l; int is_del; int tolinefix; long pos,pos2; is_del=(event->key.keysym.sym==SDLK_LEFT)?1:0; redata_undo_groupinit(re->data,NULL); tolinefix=(re->selcolto==0)?1:0; for(l=re->sellinefrom;l<=(re->sellineto-tolinefix);l++) { if(redata_linecol2pos(re->data,l,0,&pos,NULL)!=0) continue; if(is_del==1 && redata_linecol2pos(re->data,l,1,&pos2,NULL)!=0) continue; if(!is_del) redata_op_add(re->data,pos," ",1,NULL); else if(pos!=pos2) redata_op_del(re->data,pos,pos2-pos,NULL); } redata_undo_groupcommit(re->data,NULL); re->headerdirty=1; re->contentsdirty=1; } else if(event->key.keysym.sym==SDLK_DOWN || event->key.keysym.sym==SDLK_UP) { int move_res; int oldcol=re->curcol,oldline=re->curline; if(((SDL_GetModState()&KMOD_CTRL)!=0 || wheelamount!=0) && event->key.keysym.sym==SDLK_UP && re->originline==0) return(0); /* nothing to do, already at top */ move_res=re_moveupdown(re,(wheelamount!=0)?wheelamount:(event->key.keysym.sym==SDLK_UP)?-1:1); if(move_res==0 && (SDL_GetModState()&KMOD_SHIFT)!=0 && (re->curcol!=oldcol || re->curline!=oldline)) { re_sel_resize(re,oldcol,oldline,(event->key.keysym.sym==SDLK_UP)?-1:1); } if(move_res==0 && ((SDL_GetModState()&KMOD_CTRL)!=0 || wheelamount!=0)) { if(event->key.keysym.sym==SDLK_UP && re->originline>0) { re->originline-=(wheelamount==0)?1:((wheelamount<0)?-wheelamount:wheelamount); re->originline=(re->originline<0)?0:re->originline; re->contentsdirty=1; } else if(event->key.keysym.sym==SDLK_DOWN && re->originline<re->curline) { re->originline+=(wheelamount==0)?1:((wheelamount<0)?-wheelamount:wheelamount); re->originline=(re->originline>re->curline)?re->curline:re->originline; re->contentsdirty=1; } } } else if((event->key.keysym.sym==SDLK_LEFT || event->key.keysym.sym==SDLK_RIGHT)) { int oldcol=re->curcol,oldline=re->curline; if(re_moveleftright(re,(event->key.keysym.sym==SDLK_LEFT)?-1:1)==0 && (SDL_GetModState()&KMOD_SHIFT)!=0 && (re->curcol!=oldcol || re->curline!=oldline)) re_sel_resize(re,oldcol,oldline,(event->key.keysym.sym==SDLK_LEFT)?-1:1); } else if((SDL_GetModState()&KMOD_CTRL)!=0 && (event->key.keysym.sym==SDLK_PAGEDOWN || event->key.keysym.sym==SDLK_PAGEUP)) { long cursorpos; int oldcol=re->curcol,oldline=re->curline; cursorpos=(event->key.keysym.sym==SDLK_PAGEDOWN)?(redata_getused(re->data)-1):0; cursorpos=(cursorpos<0)?0:cursorpos; redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol)); re_fixorigin_center(re); if((SDL_GetModState()&KMOD_SHIFT)!=0 && (re->curcol!=oldcol || re->curline!=oldline)) re_sel_resize(re,oldcol,oldline,(event->key.keysym.sym==SDLK_PAGEUP)?-1:1); re->headerdirty=1; re->contentsdirty=1; } else if(event->key.keysym.sym==SDLK_PAGEDOWN || event->key.keysym.sym==SDLK_PAGEUP) { int oldcol=re->curcol,oldline=re->curline; re_moveupdown(re,(event->key.keysym.sym==SDLK_PAGEUP)?-(re->maxrow):re->maxrow); if((SDL_GetModState()&KMOD_SHIFT)!=0 && (re->curcol!=oldcol || re->curline!=oldline)) re_sel_resize(re,oldcol,oldline,(event->key.keysym.sym==SDLK_PAGEUP)?-1:1); } else if(event->key.keysym.sym==SDLK_HOME || event->key.keysym.sym==SDLK_END) { long newpos; long cursorpos; int oldcol=re->curcol,oldline=re->curline; if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0) return(0); /* error obtaining position */ if((event->key.keysym.sym==SDLK_HOME && redata_line_realstart(re->data,cursorpos,&newpos)==-1) || (event->key.keysym.sym==SDLK_END && redata_line_realend(re->data,cursorpos,&newpos)==-1)) { return(-1); /* couldn't get destination pos data */ } cursorpos=newpos; if(redata_pos2linecol(re->data,cursorpos,NULL,&(re->curcol))==-1) return(-1); /* couldn't get col of current pos */; if((SDL_GetModState()&KMOD_SHIFT)!=0 && (re->curcol!=oldcol || re->curline!=oldline)) re_sel_resize(re,oldcol,oldline,(event->key.keysym.sym==SDLK_HOME)?-1:1); re_fixorigin(re); re->headerdirty=1; re->contentsdirty=1; } else if(event->key.keysym.sym==SDLK_BACKSPACE && (re->curline>0 || re->curcol>0)) { int line,col; long startpos,delpos,realstart,start; long dellen; char *ptr; int len; int trimmed; int doneinc; long cursorpos; long selstart,selend; int fixsel; if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0) #if 0 fprintf(stderr,"SDL_KEYDOWN: BACKSPACE%s\n",(event==&fakeevent)?" (fake)":""); #endif return(0); /* error obtaining position */ if(redata_pos2linecol(re->data,cursorpos,&line,&col)==-1) return(-1); /* couldn't get current line data */ if(col<re->curcol) { re_moveleftright(re,-1); return(0); /* were hovering to the right of last char, only had to move left */ } if(event!=&fakeevent) redata_undo_groupinit(re->data,NULL); /* delete the last character */ fixsel=0; selstart=selend=0; if(re->selactive && redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&selstart,NULL)==0 && redata_linecol2pos(re->data,re->sellineto,re->selcolto,&selend,NULL)==0) { fixsel=1; } if(col>0) { if(redata_line_realstart(re->data,cursorpos,&realstart)==-1) return(-1); /* couldn't get line info */ /* get the start of the part to delete */ doneinc=0; for(ptr=NULL,len=0,start=0,delpos=cursorpos;doneinc!=1;) { if((delpos-1)<realstart) break; delpos--; if(ptr==NULL || (start+len)<=delpos || delpos<start) { if(redata_line_rawinfo(re->data,delpos,&start,&ptr,&len,NULL)==-1) return(-1); /* couldn't get line data */ } if(redata_generic_utf8isstartbyte(ptr[delpos-start])) doneinc++; } /* delete */ dellen=cursorpos-delpos; redata_op_del(re->data,delpos,dellen,NULL); cursorpos=delpos; } else { /* to make the code trivial, we're being lazy on '\n' deletion: we call ...rawinfo() for each byte in the multibyte utf-8 sequence */ for(delpos=cursorpos-1 ;delpos>0 && redata_line_rawinfo(re->data,delpos,&startpos,&ptr,&len,NULL)==0;) { if(redata_generic_utf8isstartbyte(ptr[delpos-startpos])) break; } dellen=cursorpos-delpos; redata_op_del(re->data,delpos,dellen,NULL); cursorpos=delpos; /* fix printouts scope */ if(re->usedprints>0) { int i; printout_t *printout; for(i=0;i<re->usedprints;i++) { printout=re->prints+i; if(printout->data!=NULL) continue; /* has its own copy of the data */ if(printout->originline>re->curline) { printout->originline--; } else if(printout->showonlyn>1 && re->curline>=printout->originline && re->curline<(printout->originline+printout->showonlyn)) { printout->showonlyn--; } } } } redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol)); if(fixsel) { if(delpos<=selstart) { /* if we deleted the start of selection, make the start of selection the cursorpos */ if(selstart>=delpos && selstart<(delpos+dellen)) redata_pos2linecol(re->data,cursorpos,&(re->sellinefrom),&(re->selcolfrom)); else redata_pos2linecol(re->data,selstart-dellen,&(re->sellinefrom),&(re->selcolfrom)); } if(delpos<=selend) { /* if we deleted the end of selection, make the start of selection the cursorpos */ if(selend>=delpos && selend<(delpos+dellen)) redata_pos2linecol(re->data,cursorpos,&(re->sellineto),&(re->selcolto)); else redata_pos2linecol(re->data,selend-dellen,&(re->sellineto),&(re->selcolto)); } } /* if cursor at end of line and there are trailing spaces at the end, delete them too */ if(re_rtrim(re,cursorpos,&trimmed)) cursorpos-=trimmed; /* end of deletion */ redata_undo_groupcommit(re->data,NULL); re_fixorigin(re); re->headerdirty=1; re->contentsdirty=1; } else if(event->key.keysym.sym==SDLK_q && (SDL_GetModState()&KMOD_CTRL)!=0) { re->command=""; re->command_first_key='q'; re->headerdirty=1; } else if(event->key.keysym.sym==SDLK_F2 || (event->key.keysym.sym==SDLK_s && (SDL_GetModState()&KMOD_CTRL)!=0)) { char *errormsg=NULL; if(redata_save(re->data,re->filename,&errormsg)!=-1) errormsg="File saved"; re->command=COMMAND_INFO; snprintf(re->commandbuf,sizeof(re->commandbuf),errormsg); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; } else if(event->key.keysym.sym==SDLK_k && (SDL_GetModState()&KMOD_CTRL)!=0) { re->command=""; re->command_first_key='k'; re->headerdirty=1; } else if(event->key.keysym.sym==SDLK_z && (SDL_GetModState()&KMOD_CTRL)!=0) { long newpos; if(redata_op_undo(re->data,&newpos)) return(-1); redata_pos2linecol(re->data,newpos,&(re->curline),&(re->curcol)); re_fixorigin(re); re->headerdirty=1; re->contentsdirty=1; } else if(event->key.keysym.sym==SDLK_l && (SDL_GetModState()&KMOD_CTRL)!=0 && re->cachelastsearch[0]!='\0') { long newpos; long cursorpos; if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0) return(-1); /* error obtaining position */ /* search next (forward) */ if((newpos=redata_searchforward(re->data,cursorpos+1,re->cachelastsearch,strlen(re->cachelastsearch)))==-1) { re->command=COMMAND_WARNING; snprintf(re->commandbuf,sizeof(re->commandbuf),"String not found"); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; return(-1); } cursorpos=newpos; redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol)); re_fixorigin_center(re); re->headerdirty=1; re->contentsdirty=1; } else if(event->key.keysym.sym==SDLK_PLUS && (SDL_GetModState()&KMOD_CTRL)!=0) { /* NOTE: Control+'+'/'-' iterates between the font sizes (Control+'0' resets to the default font size) */ /* NOTE: Control+shift+'+'/'-' iterates between the color permutations of the current theme in rgb, 6 in total (Control+shift+'0' resets to the default permutation) */ if((SDL_GetModState()&KMOD_SHIFT)==0) re_changefontsize(re, 1); else re_themeset(re, re->theme.ntheme+1); re->ignorenkeys++; } else if(event->key.keysym.sym==SDLK_MINUS && (SDL_GetModState()&KMOD_CTRL)!=0) { if((SDL_GetModState()&KMOD_SHIFT)==0) re_changefontsize(re, -1); else re_themeset(re, re->theme.ntheme-1); re->ignorenkeys++; } else if(event->key.keysym.sym==SDLK_0 && (SDL_GetModState()&KMOD_CTRL)!=0) { if((SDL_GetModState()&KMOD_SHIFT)==0) re_changefontsize(re, 0); else re_themeset(re, 0); if(re->quirk_duplicates) re->ignorenkeys++; } else if((event->key.keysym.sym==SDLK_c || event->key.keysym.sym==SDLK_x) && (SDL_GetModState()&KMOD_CTRL)!=0) { long frompos,topos; int coldone; int is_cut=(event->key.keysym.sym==SDLK_x)?1:0; /* Mac-HIG/OpenLook-style copy (ctrl+c) / cut (ctrl+x) */ if(re->selactive && redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&frompos,NULL)==0 && redata_linecol2pos(re->data,re->sellineto,re->selcolto,&topos,&coldone)==0) { re_selectbuf_fill(re,frompos,topos-frompos,re->selcolto-coldone); SDL_SetClipboardText(re->selectbuf); if(is_cut) { int trimmed; redata_undo_groupinit(re->data,NULL); redata_op_del(re->data,frompos,topos-frompos,NULL); redata_pos2linecol(re->data,frompos,&(re->curline),&(re->curcol)); if(redata_getchar(re->data,frompos)=='\n' && re_rtrim(re,frompos,&trimmed)!=-1) frompos-=trimmed; redata_undo_groupcommit(re->data,NULL); re_fixorigin(re); re->headerdirty=1; } re_sel_toggle(re); re->contentsdirty=1; } if(re->quirk_duplicates) re->ignorenkeys++; } else if(event->key.keysym.sym==SDLK_v && (SDL_GetModState()&KMOD_CTRL)!=0) { long cursorpos; /* Mac-HIG/OpenLook-style paste (ctrl+v)*/ if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0) return(-1); /* error obtaining position */ if(re->selactive) re_sel_toggle(re); if(SDL_HasClipboardText()) { char *ptr; re_selectbuf_replace(re,(ptr=SDL_GetClipboardText())); SDL_free(ptr),ptr=NULL; } if(SDL_HasClipboardText()) { char *ptr; re_selectbuf_replace(re,(ptr=SDL_GetClipboardText())); SDL_free(ptr),ptr=NULL; } if(re->usedselectbuf>0) re_textinsert(re, re->selectbuf,re->usedselectbuf); if(re->quirk_duplicates) re->ignorenkeys++; } else if(/*re->selactive &&*/ event->key.keysym.sym==SDLK_p && (SDL_GetModState()&KMOD_CTRL)!=0) { re_addprint(re); } else if(event->key.keysym.sym==SDLK_n && (SDL_GetModState()&KMOD_CTRL)!=0) { re->showlinenumbers=1-re->showlinenumbers; re_setuidata(re); re->contentsdirty=1; } return(0); } int re_processkey_commandwait(re_t *re, SDL_Event *event) { if(re==NULL || event==NULL) return(-1); /* sanity check failed */ /* default case: keydown event */ if(event->type!=SDL_KEYDOWN) return(0); /* Ignore other possible events */ if(re->command_first_key=='q' && event->key.keysym.sym==SDLK_l) { re->command=COMMAND_GOTOLINE; re->commandbuf[0]='\0'; re->is_oldcommandbuf=0; re->headerdirty=1; } else if(re->command_first_key=='q' && event->key.keysym.sym==SDLK_f) { re->command=COMMAND_SEARCHFORWARD; strncpy(re->commandbuf,re->cachelastsearch,sizeof(re->commandbuf)); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->is_oldcommandbuf=1; re->headerdirty=1; } else if(re->command_first_key=='q' && event->key.keysym.sym==SDLK_a) { re->command=COMMAND_REPLACEWHAT; strncpy(re->commandbuf,re->cachelastsearch,sizeof(re->commandbuf)); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->is_oldcommandbuf=1; re->headerdirty=1; } else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_q) { if(redata_needssaving(re->data)) { re->command=COMMAND_CONFIRMEXIT; re->commandbuf[0]='\0'; } else { re->command=COMMAND_EXIT; re->commandbuf[0]='\0'; } re->is_oldcommandbuf=0; re->headerdirty=1; } else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_h) { re_sel_toggle(re); re->command=NULL; re->headerdirty=1; } else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_b) { re_sel_setstart(re,re->curline,re->curcol); re->command=NULL; re->headerdirty=1; } else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_k) { re_sel_setend(re,re->curline,re->curcol); re->command=NULL; re->headerdirty=1; } else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_y) { long frompos,topos; int coldone; long cursorpos; /* wordstar-style blockdel (ctrl+k+y) */ if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0) return(-1); /* error obtaining position */ if(re->selactive && redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&frompos,NULL)==0 && redata_linecol2pos(re->data,re->sellineto,re->selcolto,&topos,&coldone)==0) { re_selectbuf_fill(re,frompos,topos-frompos,re->selcolto-coldone); redata_undo_groupinit(re->data,NULL); redata_op_del(re->data,frompos,topos-frompos,NULL); redata_undo_groupcommit(re->data,NULL); if(cursorpos>frompos) { cursorpos=frompos; redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol)); re_fixorigin(re); } re->headerdirty=1; re->contentsdirty=1; } if(re->selactive) re_sel_toggle(re); re->command=NULL; re->headerdirty=1; } else if(re->command_first_key=='k' && (event->key.keysym.sym==SDLK_c || event->key.keysym.sym==SDLK_v)) { long frompos,topos,insertpos; int tocoldone,coldone; /* wordstar-style blockcopy (ctrl+k+c) or move (ctrl+k+v)*/ int is_move=(event->key.keysym.sym==SDLK_v)?1:0; if(re->selactive && redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&frompos,NULL)==0 && redata_linecol2pos(re->data,re->sellineto,re->selcolto,&topos,&tocoldone)==0 && redata_linecol2pos(re->data,re->curline,re->curcol,&insertpos,&coldone)==0 && re_selectbuf_fill(re,frompos,topos-frompos,re->selcolto-tocoldone)==0 && redata_preallocate(re->data,redata_getused(re->data)+(re->curcol-coldone)+(is_move?0:re->usedselectbuf))==0) { if(is_move && insertpos>=frompos && insertpos<topos) { re->curline=re->sellinefrom; re->curcol=re->selcolfrom; redata_linecol2pos(re->data,re->curline,re->curcol,&insertpos,&coldone); } redata_undo_groupinit(re->data,NULL); if(coldone<re->curcol && redata_op_addn(re->data,insertpos,' ',re->curcol-coldone,NULL)==0) { insertpos+=re->curcol-coldone; } if(is_move) { long cursorpos; redata_op_del(re->data,frompos,re->usedselectbuf,NULL); redata_op_add(re->data,insertpos-((insertpos>frompos)?re->usedselectbuf:0),re->selectbuf,re->usedselectbuf,NULL); cursorpos=insertpos-((insertpos>frompos)?re->usedselectbuf:0)+re->usedselectbuf; redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol)); redata_pos2linecol(re->data,cursorpos-re->usedselectbuf,&(re->sellinefrom),&(re->selcolfrom)); re->sellineto=re->curline; re->selcolto=re->curcol; } else { long cursorpos; redata_op_add(re->data,insertpos,re->selectbuf,re->usedselectbuf,NULL); cursorpos=insertpos+re->usedselectbuf; redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol)); redata_pos2linecol(re->data,insertpos,&(re->sellinefrom),&(re->selcolfrom)); re->sellineto=re->curline; re->selcolto=re->curcol; } redata_undo_groupcommit(re->data,NULL); re_fixorigin(re); re->headerdirty=1; re->contentsdirty=1; } re->command=NULL; re->headerdirty=1; } else { re->command=NULL; re->headerdirty=1; } if(!(SDL_GetModState()&KMOD_CTRL)) { if(re->quirk_duplicates) re->ignorenkeys=1; /* control was not pressed, we will receive again this key as SDL_TEXTINPUT */ } return(0); } int re_processkey_commanddata(re_t *re, SDL_Event *event) { SDL_Event fakeevent; if(re==NULL || event==NULL) return(-1); /* sanity check failed */ /* special case: text editing event */ if(event->type==SDL_KEYDOWN && event->key.keysym.sym==SDLK_TAB && (SDL_GetModState()&KMOD_CTRL)!=0) { /* If control+tab is pressed, insert a real tab (to be able to search for them using Control+F) */ memset(&fakeevent,0,sizeof(SDL_Event)); event=&fakeevent; event->type=SDL_TEXTINPUT; strncpy(event->text.text,"\t",sizeof(event->text.text)); event->text.text[sizeof(event->text.text)-1]='\0'; } if(event->type==SDL_TEXTINPUT) { int len; if(re->ignorenkeys>0) { re->ignorenkeys--; return(0); /* this is an already processed key, ignore it */ } if(re->is_oldcommandbuf) { re->commandbuf[0]='\0'; re->is_oldcommandbuf=0; } len=strlen(event->text.text); if((strlen(re->commandbuf)+len+1)>=sizeof(re->commandbuf)) return(-1); /* No space for text */ memcpy(re->commandbuf+strlen(re->commandbuf),event->text.text,len+1); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; } /* default case: keydown event */ if(event->type!=SDL_KEYDOWN) return(0); /* Ignore other possible events */ if(event->key.keysym.sym==SDLK_ESCAPE) { re->command=NULL; re->commandbuf[0]='\0'; re->headerdirty=1; #warning XXX: TODO: re->question: Make cursor change option, make numbers change option } else if(event->key.keysym.sym==SDLK_BACKSPACE && re->commandbuf[0]!='\0') { int nchar; char *ptr; if(re->is_oldcommandbuf) { re->commandbuf[0]='\0'; re->is_oldcommandbuf=0; re->headerdirty=1; return(0); } if((nchar=redata_generic_utf8len(re->commandbuf,strlen(re->commandbuf)))<=0) return(-1); /* error parsing commandbuf */ if((ptr=redata_generic_utf8col(re->commandbuf,strlen(re->commandbuf),nchar-1))==NULL) return(-1); /* error positioning in commandbuf */ *ptr='\0'; re->headerdirty=1; } else if(event->key.keysym.sym==SDLK_RIGHT || event->key.keysym.sym==SDLK_END) { if(re->is_oldcommandbuf) { re->is_oldcommandbuf=0; re->headerdirty=1; return(0); } } else if(event->key.keysym.sym==SDLK_RETURN) { re_processcommand(re); } return(0); } int re_processcommand(re_t *re) { if(re==NULL || re->command==NULL) return(-1); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; if(strcmp(re->command,COMMAND_CONFIRMEXIT)==0) { if(strcmp(re->commandbuf,"y")==0) { re->command=COMMAND_EXIT; re->commandbuf[0]='\0'; re->headerdirty=1; return(0); } re->headerdirty=1; } else if(strcmp(re->command,COMMAND_GOTOLINE)==0) { int line,col; long pos; col=re->curcol; if(re->commandbuf[0]=='+') { line=re->curline+atoi(re->commandbuf+1); } else if(re->commandbuf[0]=='-') { line=re->curline-atoi(re->commandbuf+1); } else { char *ptr; line=atoi(re->commandbuf)-1; for(ptr=re->commandbuf;*ptr>='0' && *ptr<='9';ptr++) ; if(*ptr==':') col=atoi(ptr+1)-1,col=(col<0)?0:col; } line=(line<0)?0:line; if(redata_linecol2pos(re->data,line,col,&pos,NULL)==-1) { re->command=COMMAND_WARNING; snprintf(re->commandbuf,sizeof(re->commandbuf),"Unknown line"); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; return(-1); } re->curline=line; re->curcol=col; /* position the line in the center of viewport */ re->originline=line-(re->maxrow/2); re->originline=(re->originline<0)?0:re->originline; re->headerdirty=1; re->contentsdirty=1; } else if(strcmp(re->command,COMMAND_SEARCHFORWARD)==0) { long newpos; long cursorpos; if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0) return(-1); /* error obtaining position */ if((newpos=redata_searchforward(re->data,cursorpos,re->commandbuf,strlen(re->commandbuf)))==-1) { re->command=COMMAND_WARNING; snprintf(re->commandbuf,sizeof(re->commandbuf),"String not found"); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; return(-1); } strncpy(re->cachelastsearch,re->commandbuf,sizeof(re->cachelastsearch)); re->cachelastsearch[sizeof(re->cachelastsearch)-1]='\0'; cursorpos=newpos; redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol)); re_fixorigin_center(re); re->headerdirty=1; re->contentsdirty=1; } else if(strcmp(re->command,COMMAND_REPLACEWHAT)==0) { re->command=COMMAND_REPLACEWITH; strncpy(re->cachelastsearch,re->commandbuf,sizeof(re->cachelastsearch)); re->cachelastsearch[sizeof(re->cachelastsearch)-1]='\0'; strncpy(re->commandbuf,re->cachelastreplacewith,sizeof(re->commandbuf)); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->is_oldcommandbuf=1; re->headerdirty=1; return(0); } else if(strcmp(re->command,COMMAND_REPLACEWITH)==0) { re->command=COMMAND_REPLACEHOW; strncpy(re->cachelastreplacewith,re->commandbuf,sizeof(re->cachelastreplacewith)); re->cachelastreplacewith[sizeof(re->cachelastreplacewith)-1]='\0'; re->commandbuf[0]='\0'; re->is_oldcommandbuf=0; re->headerdirty=1; return(0); } else if(strcmp(re->command,COMMAND_REPLACEHOW)==0) { int is_all; int slen,rlen; long oldpos,newpos,endpos; long total; is_all=(mystricmp(re->commandbuf,"ean")==0)?1:(mystricmp(re->commandbuf,"san")==0)?0:-1; if(is_all==-1) { re->command=COMMAND_WARNING; snprintf(re->commandbuf,sizeof(re->commandbuf),"Sorry, unimplemented. For now use only EAN or SAN options."); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; return(-1); } if(is_all==0 && !re->selactive) { re->command=COMMAND_WARNING; snprintf(re->commandbuf,sizeof(re->commandbuf),"No active selection, nothing changed."); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; return(-1); } oldpos=0; endpos=redata_getused(re->data); if(!is_all && (redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&oldpos,NULL)!=0 || redata_linecol2pos(re->data,re->sellineto,re->selcolto,&endpos,NULL)!=0)) { re->command=COMMAND_WARNING; snprintf(re->commandbuf,sizeof(re->commandbuf),"Invalid selection."); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; return(-1); } slen=strlen(re->cachelastsearch); rlen=strlen(re->cachelastreplacewith); redata_undo_groupinit(re->data,NULL); for(total=0 ;(oldpos+slen)<endpos && (newpos=redata_searchforward(re->data,oldpos,re->cachelastsearch,slen))!=-1 && (newpos+slen)<=endpos ;total++,oldpos=newpos+rlen) { redata_op_del(re->data,newpos,slen,NULL); redata_op_add(re->data,newpos,re->cachelastreplacewith,rlen,NULL); endpos=endpos-slen+rlen; } redata_undo_groupcommit(re->data,NULL); redata_pos2linecol(re->data,oldpos,&(re->curline),&(re->curcol)); re_fixorigin_center(re); re->headerdirty=1; re->contentsdirty=1; re->command=COMMAND_INFO; snprintf(re->commandbuf,sizeof(re->commandbuf),"%li subst",total); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; return(0); } else if(strcmp(re->command,COMMAND_QUESTION)==0) { /* validate reply */ if(!(atoi(re->commandbuf)>0 && atoi(re->commandbuf)<=re->question->nopts)) { /* invalid reply: reset buf */ re->commandbuf[0]=0; re->headerdirty=1; return(-1); } /* send reply */ redata_loadquestions_reply(re->data,re->question,atoi(re->commandbuf)-1); re->headerdirty=1; } re->command=NULL; re->commandbuf[0]='\0'; return(0); } int re_moveupdown(re_t *re, int totalinc) { long newpos,newpos2; int inc,doneinc; long realstart; int needs_inccol; long cursorpos; if(re==NULL) return(-1); /* sanity check failed */ if(totalinc==0) return(0); /* nothing to do */ inc=(totalinc<0)?-1:1; if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0) return(-1); /* error obtaining position */ newpos=cursorpos; /* get rid of compiler warning (will be overwitten in the loop as totalinc never is 0) */ needs_inccol=0; for(doneinc=0;doneinc!=totalinc;doneinc+=inc) { #if 0 fprintf(stderr,"MOVING from cursorpos:%li line:%i col:%i\n",cursorpos,re->curline,re->curcol); #endif if((inc==-1 && redata_line_prevrealstart(re->data,cursorpos,&realstart)==-1) || (inc==1 && redata_line_nextrealstart(re->data,cursorpos,&realstart)==-1)) { break; /* couldn't get current line data, we are at start/end */ } cursorpos=realstart; needs_inccol=1; re->curline+=inc; #if 0 fprintf(stderr,"MOVING to cursorpos:%li line:%i col:%i\n",cursorpos,re->curline,re->curcol); #endif } if(!needs_inccol) return(-1); if(redata_line_inccol(re->data,cursorpos,re->curcol,&newpos2,NULL)==-1) { /* error advancing cursor, "emergency" repositioning */ #if 0 fprintf(stderr,"SETTING COLUMN ERROR\n"); #endif re->curcol=0; newpos2=newpos; } #if 0 fprintf(stderr,"COLUMN from cursorpos:%li line:%i col:%i\n",cursorpos,re->curline,re->curcol); #endif cursorpos=newpos2; #if 0 fprintf(stderr,"COLUMN to cursorpos:%li line:%i col:%i\n",cursorpos,re->curline,re->curcol); #endif re_fixorigin(re); re->contentsdirty=1; re->headerdirty=1; return(0); } int re_moveleftright(re_t *re, int totalinc) { long newpos,realstart; int inc,doneinc; char *ptr; int len; long start; int tmpcol,oldcol; long cursorpos; if(re==NULL) return(-1); /* sanity check failed */ if(totalinc==0) return(0); /* nothing to do */ if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0) return(-1); /* error obtaining position */ oldcol=re->curcol; if(totalinc<0 && (re->curcol+totalinc)<=0) { /* we'll land on the start of the line -- do it trivially */ if(redata_line_realstart(re->data,cursorpos,&realstart)==-1) return(-1); /* couldn't get current pos */ re->curcol=0; cursorpos=realstart; } else { /* move a char at a time */ if(redata_line_realstart(re->data,cursorpos,&realstart)==-1) return(-1); /* couldn't get current pos */ inc=(totalinc<0)?-1:1; doneinc=0; if(redata_pos2linecol(re->data,cursorpos,NULL,&tmpcol)==-1) return(-1); /* couldn't get current pos */ /* special case: we're just over the '\n' going right, we have to move 1 to enable the generic case to work */ if(tmpcol==re->curcol && inc>0) { if(redata_line_rawinfo(re->data,cursorpos,&start,&ptr,&len,NULL)==-1) return(-1); /* couldn't get line data */ if(ptr[cursorpos-start]=='\n') { tmpcol++; totalinc--; re->curcol++; } } /* generic case: cursor after the \n ("floating") */ if(tmpcol<re->curcol) { int avail; if(inc>0) { doneinc=totalinc; } else { avail=re->curcol-tmpcol; doneinc=(avail>=totalinc)?totalinc:avail; } } /* generic case: move one char at a time */ for(ptr=NULL,len=0,start=0,newpos=cursorpos;doneinc!=totalinc;) { if((newpos+inc)<realstart) break; newpos+=inc; if(ptr==NULL || (start+len)<=newpos || newpos<start) { if(redata_line_rawinfo(re->data,newpos,&start,&ptr,&len,NULL)==-1) return(-1); /* couldn't get line data */ } if(redata_generic_utf8isstartbyte(ptr[newpos-start])) doneinc+=inc; if(ptr[newpos-start]=='\n') break; } cursorpos=newpos; re->curcol+=doneinc; if(inc>0 && doneinc<totalinc && ptr!=NULL && ptr[newpos-start]=='\n') re->curcol+=(totalinc-doneinc); } if(re->curcol!=oldcol) { re->contentsdirty=1; re->headerdirty=1; if(re->curcol<(re->origincol+COLFORCESCROLL) && re->origincol>0) { re->origincol=(re->curcol-COLFORCESCROLL)/COLFORCESCROLL; re->origincol=re->origincol*COLFORCESCROLL; re->origincol=(re->origincol<0)?0:re->origincol; } if(re->curcol>(re->origincol+re->maxcol-COLFORCESCROLL)) { re->origincol=re->curcol+COLFORCESCROLL-re->maxcol-(re->curcol%COLFORCESCROLL); re->origincol=(re->origincol<0)?0:re->origincol; } } return(0); } int re_changefontsize(re_t *re, int direction) { int validpercent[]={50,67,90,100,110,120,133,150,170,200,240,300}; int newpercent; int i; if(re==NULL) return(-1); /* sanity check failed */ if(direction<0) { newpercent=validpercent[0]; for(i=0;i<(sizeof(validpercent)/sizeof(validpercent[0]));i++) { if(validpercent[i]<re->fontheightpercent) newpercent=validpercent[i]; else break; } } else if(direction>0) { newpercent=validpercent[(sizeof(validpercent)/sizeof(validpercent[0]))-1]; for(i=(sizeof(validpercent)/sizeof(validpercent[0]))-1;i>=0;i--) { if(validpercent[i]>re->fontheightpercent) newpercent=validpercent[i]; else break; } } else { newpercent=100; } if(reui_setfontheight(re->ui,DEFAULTFONTHEIGHT*newpercent/100)==-1) return(-1); /* couldn't setup new font size */ re->fontheightpercent=newpercent; re_setuidata(re); re_fixorigin(re); re->contentsdirty=1; re->headerdirty=1; return(0); } int re_sel_setstart(re_t *re, int line, int col) { if(re==NULL || line<0 || col<0) return(-1); re->sellinefrom=line; re->selcolfrom=col; if(re->selactive==0 || (re->sellineto<re->sellinefrom) || (re->sellineto==re->sellinefrom && re->selcolto<re->selcolfrom)) { re->sellineto=re->sellinefrom; re->selcolto=re->selcolfrom; } re->selactive=1; re->contentsdirty=1; return(0); } int re_sel_setend(re_t *re, int line, int col) { if(re==NULL || line<0 || col<0) return(-1); re->sellineto=line; re->selcolto=col; if(re->selactive==0 || (re->sellineto<re->sellinefrom) || (re->sellineto==re->sellinefrom && re->selcolto<re->selcolfrom)) { re->sellinefrom=re->sellineto; re->selcolfrom=re->selcolto; } re->selactive=1; re->contentsdirty=1; return(0); } int re_sel_resize(re_t *re,int oldcol,int oldline,int direction) { if(re==NULL || oldcol<0 || oldline<0 || (direction!=-1 && direction!=1)) return(-1); if(re->selstartactive==0) { /* we don't know the start position, manage with the nifo we have */ if(direction==-1) { if(re->selactive==0) { re_sel_setstart(re,re->curline,re->curcol); re_sel_setend(re,oldline,oldcol); } else if(re_sel_lincolisafter(re,re->curline,re->curcol) || re_sel_lincolisinside(re,re->curline,re->curcol)) { re_sel_setend(re,re->curline,re->curcol); } else { re_sel_setstart(re,re->curline,re->curcol); } } else { if(re->selactive==0 || re_sel_lincolisbefore(re,oldline,oldcol)) { re_sel_setstart(re,oldline,oldcol); re_sel_setend(re,re->curline,re->curcol); } else if(re_sel_lincolisinside(re,oldline,oldcol)) { re_sel_setstart(re,re->curline,re->curcol); } else { re_sel_setend(re,re->curline,re->curcol); } } } else { /* we know the start position, try to select accordingly */ if(re->curline<re->selstartline || (re->curline==re->selstartline && re->curcol<=re->selstartcol)) { re_sel_setstart(re,re->curline,re->curcol); re_sel_setend(re,re->selstartline,re->selstartcol); } else { re_sel_setstart(re,re->selstartline,re->selstartcol); re_sel_setend(re,re->curline,re->curcol); } } if(re->sellinefrom==re->sellineto && re->selcolfrom==re->selcolto) re->selactive=0; return(0); } int re_sel_toggle(re_t *re) { if(re==NULL) return(-1); re->selactive=1-re->selactive; re->contentsdirty=1; return(0); } int re_sel_lincolisinside(re_t *re, int line, int col) { if(re==NULL || line<0 || col<0) return(0); if(line==re->sellinefrom && line==re->sellineto) { if(col>=re->selcolfrom && col<re->selcolto) return(1); return(0); } else if(line==re->sellinefrom) { if(col>=re->selcolfrom) return(1); return(0); } else if(line>re->sellinefrom && line<re->sellineto) { return(1); } else if(line==re->sellineto) { if(col<re->selcolto) return(1); return(0); } return(0); } int re_sel_lincolisbefore(re_t *re, int line, int col) { if(re==NULL || line<0 || col<0) return(0); if(line<re->sellinefrom || (line==re->sellinefrom && col<re->selcolfrom)) return(1); return(0); } int re_sel_lincolisafter(re_t *re, int line, int col) { if(re==NULL || line<0 || col<0) return(0); if(line>re->sellineto || (line==re->sellineto && col>=re->selcolto)) return(1); return(0); } int re_sel_lincolisend(re_t *re, int line, int col) { if(re==NULL || line<0 || col<0) return(0); if(line==re->sellineto && col==re->selcolto) return(1); return(0); } int re_selectbuf_resize(re_t *re,long size) { long newsize; char *newptr; if(re==NULL || size<0) return(-1); /* sanity check failed */ if(size==0) return(0); /* nothing to do */ newsize=(size+SELECTBUFBLOCK-1)/SELECTBUFBLOCK; newsize*=SELECTBUFBLOCK; if((newptr=realloc(re->selectbuf,newsize))==NULL) return(-1); re->selectbuf=newptr; re->sizeselectbuf=newsize; return(0); } int re_selectbuf_fill(re_t *re,long frompos,long size, int nadditionalspaces) { if(re==NULL || size<0 || nadditionalspaces<0 || (frompos+size)>redata_getused(re->data)) return(-1); /* sanity check failed */ re->usedselectbuf=0; if((size+nadditionalspaces+1)>re->sizeselectbuf && re_selectbuf_resize(re,size+nadditionalspaces+1)!=0) { return(-1); /* insuf. mem. */ } if(redata_getdata(re->data,frompos,size,re->selectbuf)!=0) { re->usedselectbuf=0; return(-1); /* internal error */ } re->usedselectbuf=size; if(nadditionalspaces>0) { memset(re->selectbuf+re->usedselectbuf,' ',nadditionalspaces); re->usedselectbuf+=nadditionalspaces; } re->selectbuf[re->usedselectbuf]='\0'; return(0); } int re_selectbuf_replace(re_t *re,char *newdata) { long size; if(re==NULL || newdata==NULL) return(-1); size=strlen(newdata); if((size+1)>re->sizeselectbuf && re_selectbuf_resize(re,size+1)!=0) { return(-1); /* insuf. mem. */ } memcpy(re->selectbuf,newdata,size); re->usedselectbuf=size; re->selectbuf[re->usedselectbuf]='\0'; return(0); } int re_clipget(re_t *re) { #warning TODO return(-1); } int re_addprint(re_t *re) { int i; if(re==NULL) return(-1); /* ensure space for the new printout */ if(re->usedprints==re->sizeprints) { printout_t *newprints; if((newprints=realloc(re->prints,sizeof(printout_t)*(re->sizeprints+PRINTSBLOCKSIZE)))==NULL) return(-1); /* insuf. mem. */ re->prints=newprints; memset(re->prints+re->sizeprints,0,sizeof(printout_t)*PRINTSBLOCKSIZE); re->sizeprints+=PRINTSBLOCKSIZE; } for(i=0;i<re->sizeprints;i++) { if(re->prints[i].ui==NULL) break; } if(i>=re->sizeprints) return(-1); /* INTERNAL ERROR */ /* setup window */ if((re->prints[i].ui=reui_init(DEFAULTFONTHEIGHT*re->fontheightpercent/100,re->ui))==NULL) return(-1); /* couldn't init window */ #if 0 /* option A: printouts are immutable */ if((re->prints[i].data=redata_init(redata_highlighter_register,NULL))==NULL) { reui_free(re->prints[i].ui),re->prints[i].ui=NULL; return(-1); /* couldn't init data store */ } re->usedprints++; /* copy contents (and set clipboard contents and unselect)*/ if(re->selactive) { long frompos,topos; int coldone; if(redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&frompos,NULL)==0 && redata_linecol2pos(re->data,re->sellineto,re->selcolto,&topos,&coldone)==0) { re_selectbuf_fill(re,frompos,topos-frompos,re->selcolto-coldone); redata_op_add(re->prints[i].data,0,re->selectbuf,strlen(re->selectbuf),NULL); SDL_SetClipboardText(re->selectbuf); re_sel_toggle(re); re->contentsdirty=1; } } else { re_selectbuf_fill(re,0,redata_getused(re->data),0); redata_op_add(re->prints[i].data,0,re->selectbuf,strlen(re->selectbuf),NULL); SDL_SetClipboardText(re->selectbuf); re->contentsdirty=1; } #else /* option B: printouts are a window into a fixed place of the file */ re->prints[i].data=NULL; re->prints[i].originline=(re->selactive)?re->sellinefrom:0; re->prints[i].origincol=0; re->prints[i].showonlyn=0 /* (re->selactive)?re->sellineto-re->sellinefrom+1:0 */ ; re->usedprints++; #endif re->prints[i].dirty=1; return(0); } int re_delprint(re_t *re, int nprint) { if(re==NULL || nprint<0 || nprint>re->sizeprints || re->prints[nprint].ui==NULL) return(-1); reui_free(re->prints[nprint].ui),re->prints[nprint].ui=NULL; if(re->prints[nprint].data!=NULL) redata_free(re->prints[nprint].data),re->prints[nprint].data=NULL; re->usedprints--; return(0); } int re_rtrim(re_t *re, long curpos, int *trimmed) { long startpos; char *start; int len; int n; if(re==NULL || curpos<0 || curpos>redata_getused(re->data)) return(-1); if(trimmed!=NULL) *trimmed=0; if(redata_line_rawinfo(re->data,curpos,&startpos,&start,&len,NULL)==0 && len>1 && start[len-1]=='\n' && start[len-2]==' ' && curpos==(startpos+len-1)) { /* count the number of spaces at end of line */ n=0; while((len-1-n-1)>=0 && start[len-1-n-1]==' ') n++; /* delete the counted number of spaces at end of line */ redata_op_del(re->data,startpos+len-1-n,n,NULL); if(trimmed!=NULL) *trimmed=n; } return(0); } long re_getmatchingbracket(re_t *re,long posini, char originalchar, char *matchingchar) { char *ori,*dest; char *pairs="[]{}<>()"; int is_backwards; int colorindex,newcolor; long realstart; int line; long pos; long posnextori,posnextdest; long posnext; int counter; if(re==NULL) return(-1); /* sanity check failed */ if((ori=strchr(pairs,originalchar))==NULL) return(-1); /* unknown char */ is_backwards=(ori-pairs)%2; dest=ori+((is_backwards)?-1:1); if(redata_pos2linecol(re->data,posini,&line,NULL)==-1 || redata_line_realstart(re->data,posini,&realstart)==-1) { return(-1); /* couldn't get line number or startpos */ } colorindex=redata_highlighter_getcolorindex(re->data,line,posini-realstart); pos=posini; counter=1; while(1) { /* get the next pos */ if(!is_backwards) { posnextori=redata_searchforward(re->data,pos+1,ori,1); posnextdest=redata_searchforward(re->data,pos+1,dest,1); posnext=(posnextori==-1)?posnextdest:(posnextdest==-1)?posnextori:(posnextori<posnextdest)?posnextori:posnextdest; } else { posnextori=redata_searchbackwards(re->data,pos-1,ori,1); posnextdest=redata_searchbackwards(re->data,pos-1,dest,1); posnext=(posnextori==-1)?posnextdest:(posnextdest==-1)?posnextori:(posnextori>posnextdest)?posnextori:posnextdest; } if(posnext==-1) break; /* search ended and couln't get the counter to zero */ /* check that the color index is ok */ if(redata_pos2linecol(re->data,posnext,&line,NULL)==-1 || redata_line_realstart(re->data,posnext,&realstart)==-1) { return(-1); /* couldn't get line number or startpos */ } newcolor=redata_highlighter_getcolorindex(re->data,line,posnext-realstart); if(colorindex!=newcolor) { pos=posnext; continue; /* it doesn't have the same color */ } /* do the math */ if(posnext==posnextori) counter++; if(posnext==posnextdest) counter--; pos=posnext; if(counter==0) { if(matchingchar!=NULL) *matchingchar=*dest; return(posnext); /* found matching bracket */ } } return(-1); } int re_drawheader_editing(re_t *re) { long cursorpos; char linebuf[32],colbuf[32],posbuf[32],sizebuf[32]; char *spaceslinebuf,*spacescolbuf,*spacesposbuf,*spacessizebuf; char spaces[128]; int lenfilename; char *filename; char *spacesfilename; if(re==NULL) return(-1); if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0) return(0); /* error obtaining position */ reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,redata_needssaving(re->data)?COLOR_STATUSBG(re):COLOR_INFOBG(re)); /* for the user, lines start at 0 (internally they start at 0) */ memset(spaces,' ',sizeof(spaces)); spaces[sizeof(spaces)-1]='\0'; snprintf(linebuf,sizeof(linebuf),"%i",re->curline+1),linebuf[sizeof(linebuf)-1]='\0'; spaceslinebuf=spaces+sizeof(spaces)-1-strlen(linebuf); snprintf(colbuf,sizeof(colbuf),"%i",re->curcol+1),colbuf[sizeof(colbuf)-1]='\0'; spacescolbuf=spaces+sizeof(spaces)-1-strlen(colbuf); snprintf(posbuf,sizeof(posbuf),"%li",cursorpos),posbuf[sizeof(posbuf)-1]='\0'; spacesposbuf=spaces+sizeof(spaces)-1-strlen(posbuf); snprintf(sizebuf,sizeof(sizebuf),"%li",redata_getused(re->data)),sizebuf[sizeof(sizebuf)-1]='\0'; spacessizebuf=spaces+sizeof(spaces)-1-strlen(sizebuf); lenfilename=strlen(re->filename); filename=((lenfilename)>(sizeof(spaces)-1))?(re->filename+lenfilename-sizeof(spaces)-1):re->filename; spacesfilename=spaces+sizeof(spaces)-1-strlen(filename); reui_printf(re->ui,0,0,redata_needssaving(re->data)?COLOR_STATUSFGLIGHT(re):COLOR_INFOFGLIGHT(re) ,"File:%s%s Line:%s Col:%s Pos:%s Size:%s" ,spacesfilename,redata_needssaving(re->data)?"*":" ",spaceslinebuf,spacescolbuf,spacesposbuf,spacessizebuf); reui_printf(re->ui,0,0,redata_needssaving(re->data)?COLOR_STATUSFG(re):COLOR_INFOFG(re) ," %s%s %s %s %s %s" ,filename," ",linebuf,colbuf,posbuf,sizebuf); re->headerdirty=0; re->ui->rendererdirty=1; return(0); } int re_drawheader_command(re_t *re) { if(re==NULL) return(-1); if(re->command==NULL) return(-1); if(re->command[0]=='\0') { reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG(re)); reui_printf(re->ui,0,0,COLOR_QUERYFG(re),"Command:"); } else if(strcmp(re->command,COMMAND_WARNING)==0 || strcmp(re->command,COMMAND_CONFIRMEXIT)==0) { reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_WARNINGBG(re)); reui_printf(re->ui,0,0,COLOR_WARNINGFG(re),"%s %s",re->command,re->commandbuf); } else if(strcmp(re->command,COMMAND_INFO)==0) { reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_INFOBG(re)); reui_printf(re->ui,0,0,COLOR_INFOFG(re),"%s %s",re->command,re->commandbuf); } else if(strcmp(re->command,COMMAND_QUESTION)==0) { question_t *q; reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG(re)); #warning XXX TODO: suppont arbitrary number of options, highlight current option q=re->question; re->commandbuf[sizeof(re->commandbuf)-1]='\0'; reui_printf(re->ui,0,0,COLOR_QUERYFG(re),"%s %s: %s %s%s%s%s%s%s%s%s: %s" ,re->command ,(q->titleshort!=NULL)?q->titleshort:q->title ,(q->bodyshort!=NULL)?q->bodyshort:q->body ,(q->nopts>0)?"1.":"",(q->nopts>0)?(q->optsshort!=NULL)?q->optsshort[0]:q->opts[0]:"" ,(q->nopts>1)?" 2.":"",(q->nopts>1)?(q->optsshort!=NULL)?q->optsshort[1]:q->opts[1]:"" ,(q->nopts>2)?" 3.":"",(q->nopts>2)?(q->optsshort!=NULL)?q->optsshort[2]:q->opts[2]:"" ,(q->nopts>3)?" 4.":"",(q->nopts>4)?(q->optsshort!=NULL)?q->optsshort[3]:q->opts[3]:"" ,re->commandbuf); } else { int commandlen; int commandbuflen; reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG(re)); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; commandlen=redata_generic_utf8len(re->command,strlen(re->command)); reui_printf(re->ui,0,0,COLOR_QUERYFG(re),"%s",re->command); commandbuflen=redata_generic_utf8len(re->commandbuf,strlen(re->commandbuf)); if(!(re->is_oldcommandbuf) || re->commandbuf[0]=='\0') { reui_printf(re->ui,re->ui->fontwidth*(commandlen+1),0,COLOR_QUERYFG(re),"%s",re->commandbuf); /* draw something that to indicate the end of the commandbuf (useful if commandbuf ends in spaces) */ reui_fill(re->ui,re->ui->fontwidth*(commandlen+1+commandbuflen)+1,re->ui->fontheight/2,1,re->ui->fontheight/2,COLOR_QUERYFG(re)); } else { reui_fillrounded(re->ui,re->ui->fontwidth*(commandlen+1)-re->ui->fontwidth/2,0,re->ui->fontwidth*(commandbuflen+1)+1,re->ui->fontheight,COLOR_QUERYBGOLD(re)); reui_printf(re->ui,re->ui->fontwidth*(commandlen+1),0,COLOR_QUERYFGOLD(re),"%s",re->commandbuf); } } re->headerdirty=0; re->ui->rendererdirty=1; return(0); } int re_drawcontents(re_t *re, printout_t *printout) { reui_t *ui; redata_t *data; int x0,y0,w,h; int originline,origincol; int curline,curcol; int maxcol,maxrow; long pos,newpos; int y,row,tmprow; long cursorpos; int is_continuation; printout_t fakeprintout; hcolor_t *colors,fakecolor; int ncolors; long realstart,realend; linecolor_t *linecolors,fakelinecolors; int nlinecolors; int drawn_cursor; int matchingpos; char matchingchar; int mline,mcol; int showonlyn; int flaglineno; int linenowidth; int linenosize; if(re==NULL || (printout!=NULL && printout->ui==NULL)) return(-1); /* init vars and clear screen */ ui=re->ui; data=re->data; x0=re->x; y0=re->y; w=re->w; h=re->h; originline=re->originline; origincol=re->origincol; curline=re->curline; curcol=re->curcol; maxcol=re->maxcol; maxrow=re->maxrow; showonlyn=0; flaglineno=(printout==NULL)?re->showlinenumbers:0; linenosize=linenowidth=0; if(printout==NULL && re->viewonly==1) { memset(&fakeprintout,0,sizeof(printout_t)); fakeprintout.ui=re->ui; fakeprintout.data=re->data; fakeprintout.originline=re->originline; fakeprintout.origincol=re->origincol; printout=&fakeprintout; } else if(printout!=NULL) { ui=printout->ui; data=(printout->data==NULL)?re->data:printout->data; x0=0; y0=0; w=ui->w; h=ui->h; originline=curline=printout->originline; origincol=curcol=printout->origincol; maxcol=w/ui->fontwidth-1; maxrow=h/ui->fontheight-1; showonlyn=printout->showonlyn; flaglineno=1; } if(flaglineno) { linenosize=6; if(printout==NULL) linenowidth=linenosize*ui->fontwidth+ui->fontwidth*3/2; else linenowidth=linenosize*ui->fontwidth+ui->fontwidth*2; x0+=linenowidth; } if(redata_linecol2pos(data,curline,curcol,&cursorpos,NULL)!=0) return(0); /* error obtaining position */ reui_fill(ui,x0-linenowidth,y0,w,h,COLOR_BACKGROUND(re)); row=curline-originline; pos=cursorpos; /* get position/row/col of character at top left of screen */ while(row>0 && pos>0) { if(redata_line_rawinfo(data,pos,&newpos,NULL,NULL,&is_continuation)==-1) return(-1); pos=(newpos>0)?newpos-1:0; if(!is_continuation) row--; } /* highlight current line (in printouts, highlight alternating lines) */ if(printout==NULL) { reui_fill(ui,x0-linenowidth,y0+(curline-originline)*ui->fontheight,w,ui->fontheight+1,COLOR_PRINTOUTSTRIPE(re)); } else { for(y=y0+((printout->data==NULL)?1:(printout->originline%2))*ui->fontheight;y<(y0+h);y+=ui->fontheight*2) reui_fill(ui,x0-linenowidth,y,w,ui->fontheight+1,COLOR_PRINTOUTSTRIPE(re)); } /* highlight the selection */ if(printout==NULL && re->selactive) { const char *selcolor; tmprow=row; for(y=y0;y<(y0+h);y+=ui->fontheight,row++) { selcolor=(row==(curline-originline))?COLOR_CURLINE(re):COLOR_SELNORMAL(re); if((originline+row)==re->sellinefrom && (originline+row)==re->sellineto) { reui_fill(ui,x0+(re->selcolfrom-origincol)*ui->fontwidth,y0+row*ui->fontheight,(re->selcolto-re->selcolfrom)*ui->fontwidth,ui->fontheight+1,selcolor); } else if((originline+row)==re->sellinefrom) { int x1; x1=x0+(re->selcolfrom-origincol)*ui->fontwidth; if(x1<(x0+w)) reui_fill(ui,x1,y0+row*ui->fontheight,w-x1,ui->fontheight+1,selcolor); } else if((originline+row)>re->sellinefrom && (originline+row)<re->sellineto) { reui_fill(ui,x0,y0+row*ui->fontheight,w,ui->fontheight+1,selcolor); } else if((originline+row)==re->sellineto) { int x2; x2=x0+(re->selcolto-origincol)*ui->fontwidth; if(x2>(x0)) reui_fill(ui,x0,y0+row*ui->fontheight,x2-x0,ui->fontheight+1,selcolor); } } row=tmprow; } /* draw the lines */ if((colors=redata_highlighter_getcolors(data,&ncolors))==NULL) { colors=&fakecolor; ncolors=1; memcpy(fakecolor.rgba,COLOR_TEXT(re),5); } drawn_cursor=0; matchingpos=-1; matchingchar='\0'; for(y=y0;y<(y0+h);y+=ui->fontheight,row++) { int in_error; int availcol,tmpcol,len; char *lastcolor; /* definition of vars for tracking linecolor usage */ int curlinecolor; /* current linecolor */ int usedlenlinecolor; /* number of bytes of current linecolor already drawn */ if(showonlyn>0 && ui->fontheight>0 && (showonlyn)<((y-y0)/ui->fontheight)) break; /* this printout is configured to only show up to here */ /* get start/end of line */ if(redata_line_realstart(data,pos,&realstart)==-1 || redata_line_realend(data,pos,&realend)==-1) break; /* couldn't get real start/end */ /* setup colors */ if(colors==(&fakecolor) || (linecolors=redata_highlighter_getline(data,originline+row,&nlinecolors))==NULL) { linecolors=&fakelinecolors; fakelinecolors.len=1; fakelinecolors.color=0; } curlinecolor=0; usedlenlinecolor=0; lastcolor=COLOR_TEXT(re); in_error=0; if(flaglineno && origincol==0) { int i,n; char buf[2]={0}; for(i=linenosize,n=originline+row+1;i>=0;i--,n/=10) { buf[0]=(n>0 || i==linenosize)?(n%10)+'0':' '; reui_write(ui,x0-linenowidth+(i-1)*ui->fontwidth,y,"\xb6\xb6\xb6\xff",buf,1); } } /* draw each part of this line */ for(tmpcol=0,pos=realstart,availcol=0;tmpcol<(origincol+maxcol) && pos<=realend;pos=newpos+len,tmpcol+=availcol) { int has_nl; char *ptr,*aux; int incompletestart,incompleteend,endreq; int used,usedcol; /* number of bytes/columns used of redata chunk (those are already drawn) */ if(redata_line_rawinfo(data,pos,&newpos,&ptr,&len,&is_continuation)==-1) { in_error=1; break; /* couldn't get this line part info */ } has_nl=((len>0 && ptr[len-1]=='\n')?1:0); availcol=redata_generic_utf8lenincomplete(ptr,len-has_nl,&incompletestart,&incompleteend,&endreq); /* display the line */ used=incompletestart; usedcol=0; /* while the avail text is larger than the linecolor len */ while(curlinecolor<nlinecolors && (len-has_nl-incompleteend-used)>=(linecolors[curlinecolor].len-usedlenlinecolor)) { lastcolor=colors[linecolors[curlinecolor].color].rgba; while((aux=memchr(ptr+used,'\t',linecolors[curlinecolor].len-usedlenlinecolor))!=NULL) { /* there is a tab, we want it highlighted with red bg */ int thislen=aux-(ptr+used); reui_write(ui,x0+(tmpcol-origincol+usedcol)*ui->fontwidth,y,lastcolor,ptr+used,thislen); usedcol+=redata_generic_utf8len(ptr+used,thislen); used+=thislen; usedlenlinecolor+=thislen; reui_fill(ui,x0+(tmpcol-origincol+usedcol)*ui->fontwidth,y,ui->fontwidth,ui->fontheight+1,"\xba\x07\x07\xff"); usedcol++; used++; usedlenlinecolor++; } reui_write(ui,x0+(tmpcol-origincol+usedcol)*ui->fontwidth,y,lastcolor,ptr+used,linecolors[curlinecolor].len-usedlenlinecolor); usedcol+=redata_generic_utf8len(ptr+used,linecolors[curlinecolor].len-usedlenlinecolor); used+=linecolors[curlinecolor].len-usedlenlinecolor; curlinecolor++; usedlenlinecolor=0; } /* for the last bytes of avail text, after writing them we save how many bytes we have processed of that linecolor to be able to continue later */ if((len-has_nl-incompleteend-used)>0) { if(curlinecolor<nlinecolors && (linecolors[curlinecolor].len-usedlenlinecolor)>(len-has_nl-incompleteend-used)) { lastcolor=colors[linecolors[curlinecolor].color].rgba; usedlenlinecolor+=(len-has_nl-incompleteend-used); if(usedlenlinecolor==linecolors[curlinecolor].len) { curlinecolor++; usedlenlinecolor=0; } } reui_write(ui,x0+(tmpcol-origincol+usedcol)*ui->fontwidth,y,lastcolor,ptr+used,(len-has_nl-incompleteend-used)); usedcol+=redata_generic_utf8len(ptr+used,(len-has_nl-incompleteend-used)); used+=(len-has_nl-incompleteend-used); } /* if the last utf-8 char is broken into several blocks */ if(incompleteend>0) { char broken[7]; int usedbroken; usedbroken=0; redata_getutf8char(re->data,newpos+len-has_nl-incompleteend,broken,sizeof(broken),&usedbroken); /* write the just-recomposer character in the correct color */ if(curlinecolor<nlinecolors && (linecolors[curlinecolor].len-usedlenlinecolor)>=1) { lastcolor=colors[linecolors[curlinecolor].color].rgba; usedlenlinecolor+=1; if(usedlenlinecolor==linecolors[curlinecolor].len) { curlinecolor++; usedlenlinecolor=0; } } reui_write(ui,x0+(tmpcol-origincol+usedcol)*ui->fontwidth,y,lastcolor,broken,usedbroken); usedcol++; availcol++; } /* draw cursor if applicable */ if(printout==NULL && row==(curline-originline) && curcol>=tmpcol && curcol<(tmpcol+availcol)) { char cursorchar[7]; int usedcursorchar; /* draw cursor */ reui_fill(ui,x0+ui->fontwidth*(curcol-origincol),y,ui->fontwidth,ui->fontheight+1,COLOR_CURSORBG(re)); drawn_cursor=1; usedcursorchar=0; redata_getutf8char(re->data,cursorpos,cursorchar,sizeof(cursorchar),&usedcursorchar); /* tab chars are drawn as an almost filled block, other chars are written as-is */ if(usedcursorchar==1 && *cursorchar=='\t') reui_fill(ui,x0+ui->fontwidth*(curcol-origincol)+1,y+1,ui->fontwidth-2,ui->fontheight+1-2,COLOR_CURSORFG(re)); else reui_write(ui,x0+ui->fontwidth*(curcol-origincol),y,COLOR_CURSORFG(re),cursorchar,usedcursorchar); /* get matching braces/parens/anglebracket/curlybraces for highlighting */ if(usedcursorchar==1 && strchr("[]{}<>()",*cursorchar)!=NULL) matchingpos=re_getmatchingbracket(re,cursorpos,*cursorchar,&matchingchar); } } if(printout==NULL && row==(curline-originline) && !drawn_cursor) reui_fill(ui,x0+ui->fontwidth*(curcol-origincol),y,ui->fontwidth,ui->fontheight+1,COLOR_CURSORBG(re)); if(in_error) break; pos=realend+1; } /* highlight matching parens/brace/... if applicable */ if(printout==NULL && matchingpos!=-1 && redata_pos2linecol(data,matchingpos,&mline,&mcol)!=-1) { int x,y; char *fg=COLOR_MATCHFG(re); char *bg=COLOR_MATCHBG(re); x=x0+(mcol-origincol)*ui->fontwidth+(ui->fontwidth/2); x=(x<x0)?x0:(x>=(x0+w))?(x0+w-1):x; y=y0+(mline-originline)*ui->fontheight+(ui->fontheight/2); y=(y<y0)?y0:(y>=(y0+h))?(y0+h-1):y; if(mline<originline) reui_balloon(ui, 'n', x, y0, fg, bg, &matchingchar,1); else if(mline>=(originline+maxrow)) reui_balloon(ui, 's', x, y0+h-1, fg, bg, &matchingchar,1); else if(mcol<origincol) reui_balloon(ui, 'w', x0, y, fg, bg, &matchingchar,1); else if(mcol>=(origincol+maxcol)) reui_balloon(ui, 'e', x0+w-1,y, fg, bg, &matchingchar,1); else reui_balloon(ui, '\0', x,y, fg, bg, &matchingchar,1); } if(printout==NULL) { const char *hint; hint=redata_prototypes_get(data, cursorpos); if(hint!=NULL) { if((curline-originline)>=3) reui_balloon(ui, '\0', x0+w/2, y0+ui->fontheight*3/2, "\x80\x80\x80\xff", "\xff\xff\xff\xcf",(char *) hint,strlen(hint)); else reui_balloon(ui, '\0', x0+w/2, y0+ui->fontheight*((curline-originline)*2+5)/2, "\x80\x80\x80\xff", "\xff\xff\xff\xcf",(char *) hint,strlen(hint)); } } /* show func listing if requested */ if(printout==NULL && re->funclisting!=NULL) { int total,i,ypos; char linebuf[1024]; char *fg; reui_fillblended(ui,x0-linenowidth,y0,w,re->h,"\x44\x33\x22\xef"); for(total=redata_line_total(re->funclisting),i=re->originlinefunclisting,ypos=y0;i<total && ypos<re->h;i++,ypos+=(ui->fontheight/((linebuf[0]=='\0')?2:1))) { fg="\xff\xff\xff\xff"; if(i==re->curlinefunclisting) { reui_fill(ui,x0-linenowidth+ui->fontwidth*3,ypos,w-ui->fontwidth*6,ui->fontheight,COLOR_BACKGROUND(re)); fg="\x00\x00\x00\xff"; } if(redata_line_getstartstr(re->funclisting,i,linebuf,sizeof(linebuf))!=0) break; reui_write(ui,x0-linenowidth+ui->fontwidth*3,ypos,fg,linebuf,strlen(linebuf)); } } /* all done */ if(printout==NULL || printout==&fakeprintout) { re->contentsdirty=0; ui->rendererdirty=1; } else { printout->dirty=0; printout->ui->rendererdirty=1; } return(0); } redata_t * re_getfunclisting(re_t *re) { int lineno,total; char startbuf[3]; char endbuf[3]; char nextendbuf[3]; int flag_havenextendbuf; int flag_isstart; int flag_semicolon; int flag_curlybrace; int flag_nextcurlybrace; int flag_previsstart; char linebuf[1024]; char annotatedlinebuf[1024]; redata_t *newdata; if(re==NULL) return(NULL); /* sanity check error */; if((newdata=redata_init(NULL))==NULL) return(NULL); /* couldn't init new buffer */ flag_havenextendbuf=0; for(lineno=0,total=redata_line_total(re->data);lineno<total;lineno++) { if(redata_line_getstartstr(re->data,lineno,startbuf,sizeof(startbuf))!=0) break; /* internal error */ if(flag_havenextendbuf==1) { strncpy(endbuf,nextendbuf,sizeof(endbuf)); endbuf[sizeof(endbuf)-1]='\0'; } else if(redata_line_getendstrtrimmed(re->data,lineno,endbuf,sizeof(endbuf)," \t")!=0) { break; /* internal error */ } flag_havenextendbuf=flag_nextcurlybrace=0; if(redata_line_getendstrtrimmed(re->data,lineno+1,nextendbuf,sizeof(nextendbuf)," \t")==0) { flag_havenextendbuf=1; flag_nextcurlybrace=(*nextendbuf!='\0' && nextendbuf[strlen(nextendbuf)-1]=='{')?1:0; } flag_isstart=(*startbuf!='\0' && strchr("# \t{/",*startbuf)==NULL)?1:0; flag_semicolon=(*endbuf!='\0' && endbuf[strlen(endbuf)-1]==';')?1:0; flag_curlybrace=(*endbuf!='\0' && endbuf[strlen(endbuf)-1]=='{')?1:0; if(flag_isstart && !flag_semicolon && (flag_curlybrace || (flag_havenextendbuf && flag_nextcurlybrace))) { if(flag_previsstart) { snprintf(annotatedlinebuf,sizeof(annotatedlinebuf),"%6i: %s\n",lineno+1-1,linebuf); annotatedlinebuf[sizeof(annotatedlinebuf)-1]='\0'; redata_op_add(newdata,redata_getused(newdata),annotatedlinebuf,strlen(annotatedlinebuf),NULL); flag_previsstart=0; } redata_line_getstartstr(re->data,lineno,linebuf,sizeof(linebuf)); snprintf(annotatedlinebuf,sizeof(annotatedlinebuf),"%6i: %s\n\n",lineno+1,linebuf); annotatedlinebuf[sizeof(annotatedlinebuf)-1]='\0'; redata_op_add(newdata,redata_getused(newdata),annotatedlinebuf,strlen(annotatedlinebuf),NULL); } else if(flag_isstart) { redata_line_getstartstr(re->data,lineno,linebuf,sizeof(linebuf)); flag_previsstart=1; } else { flag_previsstart=0; } } return(newdata); } int re_funclistingxy2line(re_t *re,int mx,int my) { int total,i,ypos; char linebuf[2]; int h; if(re==NULL || mx<0 || my<0) return(-1); for(total=redata_line_total(re->funclisting),i=re->originlinefunclisting,ypos=re->y;i<total && ypos<re->h;i++,ypos+=h) { if(redata_line_getstartstr(re->funclisting,i,linebuf,sizeof(linebuf))!=0) break; h=(re->ui->fontheight/((linebuf[0]=='\0')?2:1)); if(my>=ypos && my<(ypos+h)) { if(linebuf[0]=='\0') return(-1); return(i); } } return(-1); } int re_wheelaccel(re_t *re, int rawamount) { int wheelamount; struct timeval old; struct timezone dummy; int oldmsec,newmsec,d; if(re==NULL || rawamount==0) return(rawamount); memcpy(&old,&(re->lastwheel),sizeof(old)); if(gettimeofday(&(re->lastwheel),&dummy)!=0 || old.tv_sec<(re->lastwheel.tv_sec-1) || old.tv_sec>re->lastwheel.tv_sec) return(rawamount); oldmsec=old.tv_usec/1000; newmsec=re->lastwheel.tv_usec/1000+((old.tv_sec<re->lastwheel.tv_sec)?1000:0); d=newmsec-oldmsec; wheelamount=rawamount; if(d<70) wheelamount*=10; return(wheelamount); } void util_applypermutation(int *template3,char *colors,int invert) { unsigned char c[3]; if(template3==NULL || colors==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) { c[0]=((unsigned char *)colors)[0]; c[1]=((unsigned char *)colors)[1]; c[2]=((unsigned char *)colors)[2]; } else { c[0]=255-((unsigned char *)colors)[0]; c[1]=255-((unsigned char *)colors)[1]; c[2]=255-((unsigned char *)colors)[2]; } ((unsigned char *)colors)[0]=c[template3[0]]; ((unsigned char *)colors)[1]=c[template3[1]]; ((unsigned char *)colors)[2]=c[template3[2]]; return; } int re_themeset(re_t *re, int ntheme) { int permutations[6][3]={{0,1,2},{0,2,1},{1,0,2},{1,2,0},{2,1,0},{2,0,1}}; int *permutationtemplate; int invert; if(re==NULL) return(-1); /* num. themes: 3*2*1*2=12 (permutations of 3 colors plus inverted colors)*/ ntheme=(ntheme<0)?12-((-ntheme)%12):ntheme; ntheme%=12; re->theme.ntheme=ntheme; /* compute invert and the number of permutation */ invert=(ntheme>=6)?1:0; ntheme%=6; permutationtemplate=permutations[ntheme]; /* reset colors and permutate them */ strncpy(re->theme.color_background,DEFAULT_COLOR_BACKGROUND,sizeof(re->theme.color_background)),re->theme.color_background[sizeof(re->theme.color_background)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_background,invert); strncpy(re->theme.color_printoutstripe,DEFAULT_COLOR_PRINTOUTSTRIPE,sizeof(re->theme.color_printoutstripe)),re->theme.color_printoutstripe[sizeof(re->theme.color_printoutstripe)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_printoutstripe,invert); strncpy(re->theme.color_cursorbg,DEFAULT_COLOR_CURSORBG,sizeof(re->theme.color_cursorbg)),re->theme.color_cursorbg[sizeof(re->theme.color_cursorbg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_cursorbg,invert); strncpy(re->theme.color_cursorfg,DEFAULT_COLOR_CURSORFG,sizeof(re->theme.color_cursorfg)),re->theme.color_cursorfg[sizeof(re->theme.color_cursorfg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_cursorfg,invert); strncpy(re->theme.color_text,DEFAULT_COLOR_TEXT,sizeof(re->theme.color_text)),re->theme.color_text[sizeof(re->theme.color_text)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_text,invert); strncpy(re->theme.color_matchbg,DEFAULT_COLOR_MATCHBG,sizeof(re->theme.color_matchbg)),re->theme.color_matchbg[sizeof(re->theme.color_matchbg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_matchbg,invert); strncpy(re->theme.color_matchfg,DEFAULT_COLOR_MATCHFG,sizeof(re->theme.color_matchfg)),re->theme.color_matchfg[sizeof(re->theme.color_matchfg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_matchfg,invert); strncpy(re->theme.color_statusbg,DEFAULT_COLOR_STATUSBG,sizeof(re->theme.color_statusbg)),re->theme.color_statusbg[sizeof(re->theme.color_statusbg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_statusbg,invert); strncpy(re->theme.color_statusfg,DEFAULT_COLOR_STATUSFG,sizeof(re->theme.color_statusfg)),re->theme.color_statusfg[sizeof(re->theme.color_statusfg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_statusfg,invert); strncpy(re->theme.color_statusfglight,DEFAULT_COLOR_STATUSFGLIGHT,sizeof(re->theme.color_statusfglight)),re->theme.color_statusfglight[sizeof(re->theme.color_statusfglight)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_statusfglight,invert); strncpy(re->theme.color_querybg,DEFAULT_COLOR_QUERYBG,sizeof(re->theme.color_querybg)),re->theme.color_querybg[sizeof(re->theme.color_querybg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_querybg,invert); strncpy(re->theme.color_queryfg,DEFAULT_COLOR_QUERYFG,sizeof(re->theme.color_queryfg)),re->theme.color_queryfg[sizeof(re->theme.color_queryfg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_queryfg,invert); strncpy(re->theme.color_querybgold,DEFAULT_COLOR_QUERYBGOLD,sizeof(re->theme.color_querybgold)),re->theme.color_querybgold[sizeof(re->theme.color_querybgold)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_querybgold,invert); strncpy(re->theme.color_queryfgold,DEFAULT_COLOR_QUERYFGOLD,sizeof(re->theme.color_queryfgold)),re->theme.color_queryfgold[sizeof(re->theme.color_queryfgold)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_queryfgold,invert); strncpy(re->theme.color_warningbg,DEFAULT_COLOR_WARNINGBG,sizeof(re->theme.color_warningbg)),re->theme.color_warningbg[sizeof(re->theme.color_warningbg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_warningbg,invert); strncpy(re->theme.color_warningfg,DEFAULT_COLOR_WARNINGFG,sizeof(re->theme.color_warningfg)),re->theme.color_warningfg[sizeof(re->theme.color_warningfg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_warningfg,invert); strncpy(re->theme.color_infobg,DEFAULT_COLOR_INFOBG,sizeof(re->theme.color_infobg)),re->theme.color_infobg[sizeof(re->theme.color_infobg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_infobg,invert); strncpy(re->theme.color_infofg,DEFAULT_COLOR_INFOFG,sizeof(re->theme.color_infofg)),re->theme.color_infofg[sizeof(re->theme.color_infofg)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_infofg,invert); strncpy(re->theme.color_infofglight,DEFAULT_COLOR_INFOFGLIGHT,sizeof(re->theme.color_infofglight)),re->theme.color_infofglight[sizeof(re->theme.color_infofglight)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_infofglight,invert); strncpy(re->theme.color_selnormal,DEFAULT_COLOR_SELNORMAL,sizeof(re->theme.color_selnormal)),re->theme.color_selnormal[sizeof(re->theme.color_selnormal)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_selnormal,invert); strncpy(re->theme.color_curline,DEFAULT_COLOR_CURLINE,sizeof(re->theme.color_curline)),re->theme.color_curline[sizeof(re->theme.color_curline)-1]='\0',util_applypermutation(permutationtemplate,re->theme.color_curline,invert); /* set theme in highlighter */ redata_highlighter_settheme(re->data,ntheme,invert); /* force redraw */ re->contentsdirty=1; re->headerdirty=1; return(0); } int re_socketinit(re_t *re,char *filename) { int i; comms_t *comms; commclient_t *client; struct sockaddr_un addr; char *ptr; struct passwd *passwd; char *username; int oldfd; char *errstr; if(re==NULL) return(-1); comms=&(re->comms); /* close all current comms */ if(comms->serverfd!=-1) { close(comms->serverfd),comms->serverfd=-1; if(comms->socketfilename[0]!='\0') unlink(comms->socketfilename),comms->socketfilename[0]='\0'; } if(re->comms.clients!=NULL) { for(i=0;i<re->comms.sizeclients;i++) { if((client=re->comms.clients[i])==NULL) continue; if(client->fd!=-1) close(client->fd),client->fd=-1; } re->comms.usedclients=0; } /* prepare the new filepath for the socket */ if((passwd=getpwuid(getuid()))==NULL || (username=passwd->pw_name)==NULL) { fprintf(stderr,"WARNING: Couldn't get username\n"); return(-1); } ptr=strrchr(filename,'/'); ptr=(ptr!=NULL)?ptr+1:filename; snprintf(comms->socketfilename,sizeof(comms->socketfilename),"/tmp/.re_%s",username); comms->socketfilename[sizeof(comms->socketfilename)-1]='\0'; mkdir(comms->socketfilename,0700); snprintf(comms->socketfilename,sizeof(comms->socketfilename),"/tmp/.re_%s/%s",username,ptr); comms->socketfilename[sizeof(comms->socketfilename)-1]='\0'; if(strlen(comms->socketfilename)>=(sizeof(addr.sun_path))) { comms->socketfilename[0]='\0'; fprintf(stderr,"WARNING: Socket path filename too long\n"); return(-1); } /* detect if there is a stale socket */ if((oldfd=open(comms->socketfilename, O_WRONLY|O_APPEND))!=-1) { fd_set writeset; struct timeval tv; FD_ZERO(&writeset); FD_SET(oldfd,&writeset); tv.tv_sec=tv.tv_usec=0; if(select(oldfd+1,NULL,&writeset,NULL,&tv)>0) { comms->socketfilename[0]='\0'; fprintf(stderr,"WARNING: There is a process using the communication socket for the file\n"); #warning TODO: if the unix domain socket is in use, connect to it and warn that I''m also able to service that filename, so (1) it forwards the petitions, and (2) be waware of when the other process quits to retake control of the unix domain socket return(-1); } close(oldfd),oldfd=-1; } unlink(comms->socketfilename); /* create and bind socket */ if((errstr="create")==NULL || (comms->serverfd=socket(AF_UNIX,SOCK_STREAM,0))==-1 || (errstr="assing")==NULL /* this one never fails, just for completion */ || (memset(&addr,0,sizeof(struct sockaddr_un)))==NULL || (addr.sun_family=AF_UNIX)!=AF_UNIX || strncpy(addr.sun_path,comms->socketfilename,sizeof(addr.sun_path))==NULL || (errstr="bind")==NULL || bind(comms->serverfd,(struct sockaddr *)&addr,sizeof(addr))==-1 || (errstr="listen")==NULL || listen(comms->serverfd,LISTENBACKLOG)==-1 ) { comms->socketfilename[0]='\0'; if(comms->serverfd!=-1) close(comms->serverfd),comms->serverfd=-1; fprintf(stderr,"WARNING: Couldn't %s unix domain socket\n",errstr); return(-1); } #if 1 fprintf(stderr,"Server registered.\n"); #endif return(0); } void re_socketfree(re_t *re) { int i; commclient_t *client; if(re==NULL) return; /* nothing to do */ if(re->comms.serverfd!=-1) close(re->comms.serverfd),re->comms.serverfd=-1; if(re->comms.socketfilename[0]!='\0') unlink(re->comms.socketfilename),re->comms.socketfilename[0]='\0'; if(re->comms.clients!=NULL) { for(i=0;i<re->comms.sizeclients;i++) { if((client=re->comms.clients[i])==NULL) continue; if(client->fd!=-1) close(client->fd),client->fd=-1; free(client),client=NULL; re->comms.clients[i]=NULL; } re->comms.usedclients=re->comms.sizeclients=0; free(re->comms.clients),re->comms.clients=NULL; } return; } int re_socketnewclient(re_t *re) { int i; int fd; commclient_t *client; struct sockaddr addr; socklen_t addrlen; addrlen=sizeof(addr); if(re==NULL) return(-1); /* sanity check failed */ fd=accept(re->comms.serverfd,&addr,&addrlen); if(re->comms.usedclients==re->comms.sizeclients) { commclient_t **newclients; if((newclients=realloc(re->comms.clients,sizeof(commclient_t *)*(re->comms.sizeclients+COMMCLIENTSBLOCKSIZE)))==NULL) { close(fd),fd=-1; return(-1); /* insuf. mem. */ } re->comms.clients=newclients; memset(re->comms.clients+re->comms.sizeclients,0,sizeof(commclient_t *)*COMMCLIENTSBLOCKSIZE); re->comms.sizeclients+=COMMCLIENTSBLOCKSIZE; } for(i=0;i<re->comms.sizeclients;i++) { if(re->comms.clients[i]==NULL || re->comms.clients[i]->fd==-1) break; } if(i>=re->comms.sizeclients) { close(fd),fd=-1; re->comms.usedclients=re->comms.sizeclients; return(-1); /* INTERNAL ERROR */ } client=re->comms.clients[i]; if(client==NULL) { if((client=malloc(sizeof(commclient_t)))==NULL) { close(fd),fd=-1; return(-1); /* INTERNAL ERROR */ } re->comms.clients[i]=client; memset(client,0,sizeof(commclient_t)); client->fd=-1; client->sizebufin=sizeof(client->bufin); client->sizebufout=sizeof(client->bufout); } client->fd=fd; client->usedbufin=client->gotbufin=0; client->usedbufout=client->gotbufout=0; re->comms.usedclients++; re_socketout(re, i, "OK\n"); #if 1 fprintf(stderr,"New client registered (%i).\n",i); #endif return(i); } int re_socketstep(re_t *re) { int maxfd; fd_set readset,writeset; struct timeval tv; int i; comms_t *comms; commclient_t *client; int avail,queued; int nread,nwritten; char *ptr,*next,*end; if(re==NULL || re->comms.serverfd==-1) return(-1); /* sanity check error */ comms=&(re->comms); FD_ZERO(&readset); FD_ZERO(&writeset); maxfd=comms->serverfd; FD_SET(comms->serverfd,&readset); for(i=0;i<comms->sizeclients;i++) { if((client=comms->clients[i])==NULL || client->fd==-1) continue; if(client->gotbufin>0) { memmove(client->bufin,client->bufin+client->gotbufin,client->usedbufin-client->gotbufin); client->usedbufin-=client->gotbufin; client->gotbufin=0; } if((client->sizebufin-client->usedbufin)>0) { FD_SET(client->fd,&readset); maxfd=(maxfd<client->fd)?client->fd:maxfd; } if((client->usedbufout-client->gotbufout)>0) { FD_SET(client->fd,&writeset); maxfd=(maxfd<client->fd)?client->fd:maxfd; } } tv.tv_sec=tv.tv_usec=0; if(select(maxfd+1,&readset,&writeset,NULL,&tv)<=0) return(0); /* nothing to do */ /* server */ if(FD_ISSET(comms->serverfd,&readset)) re_socketnewclient(re); /* clients */ for(i=0;i<comms->sizeclients;i++) { if((client=comms->clients[i])==NULL || client->fd==-1) continue; if(FD_ISSET(client->fd,&readset)) { if((queued=sock_queued(client->fd))<=0) { /* remote has closed the connection */ #if 1 fprintf(stderr,"Unregistering client, remote has closed the connection (%i).\n",i); #endif close(client->fd),client->fd=-1; continue; } avail=(client->sizebufin-client->usedbufin); queued=(queued>avail)?avail:queued; if((nread=read(client->fd,client->bufin+client->usedbufin,queued))>0) { #if 1 fprintf(stderr,"Read from client %li bytes (%i).\n",(long)nread,i); #endif client->usedbufin+=nread; for(ptr=client->bufin+client->gotbufin ,end=client->bufin+client->usedbufin ,next=memchr(ptr,'\n',end-ptr) ,next=(next==NULL)?end:next ;client->fd!=-1 && next<end ;ptr=(next!=end)?next+1:end ,next=memchr(ptr,'\n',end-ptr) ,next=(next==NULL)?end:next ) { *next='\0'; if(next>ptr && next[-1]=='\r') next[-1]='\0'; re_socketin(re,i,ptr); } client->gotbufin=ptr-client->bufin; if(client->fd==-1) continue; /* in case the socket has been closed inside re_socketin() */ } } if(FD_ISSET(client->fd,&writeset)) { queued=client->usedbufout-client->gotbufout; if((nwritten=write(client->fd,client->bufout+client->gotbufout,client->usedbufout-client->gotbufout))>0) client->gotbufout+=nwritten; #if 1 fprintf(stderr,"Write to client %li bytes (%i).\n",(long)nwritten,i); #endif } } return(0); } int re_socketin(re_t *re, int nclient, char *line) { /* note that the '\n' delimiter has been already removed */ /* Commands can be sent from the command line using socat as in: */ /* "socat - UNIX-CONNECT:/tmp/.re_${USER}/filename.ext" */ char *ptr; #if 1 fprintf(stderr,"Received from client \"%s\" (%i).\n",line,nclient); #endif if(memcmp(line,"goto ",5)==0) { int oldline; ptr=line+5; /* change line */ re->command=COMMAND_GOTOLINE; strncpy(re->commandbuf,ptr,sizeof(re->commandbuf)); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; oldline=re->curline; re_processcommand(re); if(oldline!=re->curline) { /* position the cursor near the top of the window */ re->originline=re->curline-3; re->originline=(re->originline<0)?0:re->originline; } /* send end-of-results */ re_socketout(re, nclient, "\n"); } else { #if 1 fprintf(stderr,"Ignoring unknown command from client \"%s\" (%i).\n",line,nclient); #endif } return(0); } int re_socketout(re_t *re, int nclient, char *format, ...) { /* note that the caller has to output the '\n' delimiter to mark the end of the line */ commclient_t *client; va_list mylist; int avail; int res; if(re==NULL || nclient<0 || nclient>=re->comms.sizeclients || re->comms.clients[nclient]==NULL || re->comms.clients[nclient]->fd==-1) return(-1); /* sanity check error */ client=re->comms.clients[nclient]; memmove(client->bufout,client->bufout+client->gotbufout,client->usedbufout-client->gotbufout); client->usedbufout-=client->gotbufout; client->gotbufout=0; avail=client->sizebufout-client->usedbufout; va_start(mylist,format); res=vsnprintf(client->bufout+client->usedbufout,avail,format,mylist); va_end(mylist); if(res<0 || res>=avail) return(-1); /* doesn't fit or another error */ client->usedbufout+=res; return(0); }