/* * recenteditor.c * * A programmers editor * * Author: Dario Rodriguez antartica@whereismybit.com * This program is licensed under the terms of GNU GPL v2.1+ */ #include #include #include #include #include #include #include #include #include /* unix domain socket support */ #include /* unix domain socket support */ #include /* getpwuid() */ #include /* getpwuid() */ #include #include #include #include #include #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 MAXFILENAMESIZE PATH_MAX+1 #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 enum typeprintout_t { printoutView=0, printoutCopy, printoutHelp, printoutManpage, printoutManpageCurlang, } typeprintout_t; typedef struct printout_t { reui_t *ui; redata_t *data; typeprintout_t type; 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 flag_connectedtoserver; char remoteid[MAXFILENAMESIZE]; 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 id[MAXFILENAMESIZE]; char selectedid[MAXFILENAMESIZE]; 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, typeprintout_t typeprintout); int re_delprint(re_t *re, int nprint); int re_rtrim(re_t *re, long curpos, int *trimmed); int re_extractword2buf(re_t *re, redata_t *data, long pos, char *buf, int sizebuf, int *usedbuf); 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 alreadyacceptedfd); 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;isizeprints;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;isizeprints;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;isizeprints;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) && printout->type!=printoutHelp && printout->type!=printoutManpage && printout->type!=printoutManpageCurlang ) { 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) && myy && 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; long curpos; redata_t *funclisting; /* extract word, generate funclisting, search word in funclisting */ funclisting=NULL; if(re_extractword2buf(re, re->data, tmppos, 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(mxui->fontwidth*10) { /* scroll */ int total; total=redata_line_total(re->funclisting); if(myy) { 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 || myy || mxx))) { 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=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;isizeprints;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->curcolorigincol) { 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->curlineoriginline || 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;nspaces0 && 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;iusedprints;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->originlinecurline) { 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(colcurcol) { 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)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;iusedprints;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_F1 && (SDL_GetModState()&(KMOD_CTRL|KMOD_SHIFT|KMOD_ALT))==0) { re_addprint(re,printoutHelp); } else if(event->key.keysym.sym==SDLK_F1 && (SDL_GetModState()&KMOD_CTRL)!=0 && (SDL_GetModState()&(KMOD_SHIFT|KMOD_ALT))==0 ) { re_addprint(re,printoutManpage); } else if(event->key.keysym.sym==SDLK_F1 && (SDL_GetModState()&KMOD_CTRL)!=0 && (SDL_GetModState()&KMOD_SHIFT)!=0 && (SDL_GetModState()&KMOD_ALT)==0) { re_addprint(re,printoutManpageCurlang); } 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_5 && (SDL_GetModState()&KMOD_CTRL)!=0) { char cursorchar[7]; int usedcursorchar; long cursorpos; int matchingpos; char matchingchar; int mline,mcol; usedcursorchar=0; if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)==0 && redata_getutf8char(re->data,cursorpos,cursorchar,sizeof(cursorchar),&usedcursorchar)==0 && usedcursorchar==1 && strchr("[]{}<>()",*cursorchar)!=NULL && (matchingpos=re_getmatchingbracket(re,cursorpos,*cursorchar,&matchingchar))!=-1 && redata_pos2linecol(re->data,matchingpos,&mline,&mcol)!=-1 ) { re->command=COMMAND_GOTOLINE; snprintf(re->commandbuf,sizeof(re->commandbuf),"%i:%i",mline+1,mcol+1); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re_processcommand(re); } 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,printoutView); } 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 && insertposcurline=re->sellinefrom; re->curcol=re->selcolfrom; redata_linecol2pos(re->data,re->curline,re->curcol,&insertpos,&coldone); } redata_undo_groupinit(re->data,NULL); if(coldonecurcol && 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)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(tmpcolcurcol) { 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)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 && doneinccurcol+=(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]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->sellinetosellinefrom) || (re->sellineto==re->sellinefrom && re->selcoltoselcolfrom)) { 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->sellinetosellinefrom) || (re->sellineto==re->sellinefrom && re->selcoltoselcolfrom)) { 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->curlineselstartline || (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 && colselcolto) return(1); return(0); } else if(line==re->sellinefrom) { if(col>=re->selcolfrom) return(1); return(0); } else if(line>re->sellinefrom && linesellineto) { return(1); } else if(line==re->sellineto) { if(colselcolto) 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(linesellinefrom || (line==re->sellinefrom && colselcolfrom)) 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, typeprintout_t typeprintout) { 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;isizeprints;i++) { if(re->prints[i].ui==NULL) break; } if(i>=re->sizeprints) return(-1); /* INTERNAL ERROR */ if(typeprintout==printoutHelp || typeprintout==printoutCopy) { /* setup window */ if((re->prints[i].ui=reui_init(DEFAULTFONTHEIGHT*re->fontheightpercent/100,re->ui))==NULL) return(-1); /* couldn't init window */ /* inmutable printout */ 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++; if(typeprintout==printoutCopy) { /* 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 if(typeprintout==printoutHelp) { static char helptext[]={"\ Recenteditor help\n\ =================\n\ \n\ F1 - Open a printout with this help\n\ Shift+F1 - (TODO) Update hints with functions definitions of all open editors\n\ Control+F1 - Open manpage of word under the cursor in a printout\n\ F2 - Save\n\ \n\ Cursor keys - Move cursor\n\ PageUp/PageDn - Scroll one page up/down\n\ Shift+Cursor - Select\n\ Mousewheel - Scroll\n\ Mouse click - Change cursor position\n\ Click and hold on status bar to open function list\n\ Control+Click - Search function body of function name under the mouse\n\ Tab key - Insert 8 spaces\n\ Control+Tab key - Insert TAB character\n\ Alt+Left/Right - Change indentation of selection\n\ Control+PgUp/PgDn - Go to the beginning/end of the document\n\ Home/End - Go to the start/end of the line\n\ Control+'0' - Set default font size\n\ Shift+Ctrl+'0' - Set default color permutation\n\ Control+'5' - Go to the matching parenthesis or curly brace\n\ Control+C - Copy selection to clipboard. Will deselect selection.\n\ Control+K+Command - Block command, see below\n\ Control+L - Search next (requires a previous search with Control+Q+F)\n\ Control+N - Show/hide line numbers\n\ Control+P - Open printout window with current file or selection\n\ Control+Q+Command - Editing command, see below\n\ Control+S - Save\n\ Control+X - Cut selection into clipboard\n\ Control+V - Paste clipboard into current cursor position\n\ Control+Z - Undo\n\ Control+'+'/'-' - Iterates between font sizes\n\ Shift+Ctrl+'+'/'-' - Iterates between color permutations\n\ \n\ \n\ Editing commands (Control+Q+Command)\n\ ------------------------------------\n\ \n\ Control+Q+L - Go to line number\n\ Control+Q+F - Find\n\ Control+Q+A - Search and replace\n\ \n\ \n\ Block commands (Control+K+Command)\n\ ----------------------------------\n\ \n\ Control+K+Q - Close editor (exit program -- don\'t ask, legacy key combo)\n\ Control+K+H - Show/hide selection\n\ Control+K+B - Set start of selection to cursor position\n\ Control+K+K - Set end of selection to cursor position\n\ Control+K+Y - Delete selection\n\ Control+K+C - Copy selection to current cursor position (doesn\'t use clipboard)\n\ Control+K+V - Move selection to current cursor position (doesn\'t use clipboard)\n\ \n\ \n\ Printout window\n\ ---------------\n\ \n\ Cursor/PageUp/PageDown - Scroll printout\n\ Mousewheel - Scroll printout\n\ "}; reui_title(re->prints[i].ui,"help"); redata_op_add(re->prints[i].data,0,helptext,sizeof(helptext)-1,NULL); re->contentsdirty=1; } } else if(typeprintout==printoutManpage || typeprintout==printoutManpageCurlang) { char searchbuf[1024]; /* magic number: max. size of string to search (truncated if bigger) */ char title[256]; int lenprefix; int usedsearchbuf; long tmppos; int nread; FILE *fp; protolang_t curlang; curlang=redata_prototypes_detectlang(re->data); strncpy(searchbuf,(typeprintout==printoutManpageCurlang && curlang==protolang_c)?"man 2 " :(typeprintout==printoutManpageCurlang && curlang==protolang_tcl)?"man 3tcl " :"man ",sizeof(searchbuf)); searchbuf[sizeof(searchbuf)-1]='\0'; lenprefix=strlen(searchbuf); fp=NULL; if(redata_linecol2pos(re->data, re->curline, re->curcol,&tmppos,NULL)==0 && re_extractword2buf(re, re->data, tmppos, searchbuf+lenprefix, sizeof(searchbuf)-lenprefix, &usedsearchbuf)==0 && strncpy(title,searchbuf,sizeof(title))!=NULL && (title[sizeof(title)-1]='\0')=='\0' && (fp=popen(searchbuf,"r"))!=NULL && (nread=fread(searchbuf,1,sizeof(searchbuf),fp))>0 ) { if((re->prints[i].ui=reui_init(DEFAULTFONTHEIGHT*re->fontheightpercent/100,re->ui))==NULL || (re->prints[i].data=redata_init(redata_highlighter_register,NULL))==NULL) { if(re->prints[i].ui!=NULL) reui_free(re->prints[i].ui),re->prints[i].ui=NULL; return(-1); /* couldn't init data store */ } re->usedprints++; reui_title(re->prints[i].ui,title); redata_op_add(re->prints[i].data,redata_getused(re->prints[i].data),searchbuf,nread,NULL); while((nread=fread(searchbuf,1,sizeof(searchbuf),fp))>0) { redata_op_add(re->prints[i].data,redata_getused(re->prints[i].data),searchbuf,nread,NULL); } pclose(fp),fp=NULL; re->contentsdirty=1; } else { if(fp!=NULL) pclose(fp),fp=NULL; re_delprint(re,i); re->command=COMMAND_WARNING; snprintf(re->commandbuf,sizeof(re->commandbuf),"Couldn't show manpage."); re->commandbuf[sizeof(re->commandbuf)-1]='\0'; re->headerdirty=1; return(0); } } else { char mytitle[256]; /* setup window */ if((re->prints[i].ui=reui_init(DEFAULTFONTHEIGHT*re->fontheightpercent/100,re->ui))==NULL) return(-1); /* couldn't init window */ /* setup title */ snprintf(mytitle,sizeof(mytitle),"view %s",re->filename); mytitle[sizeof(mytitle)-1]='\0'; reui_title(re->prints[i].ui,mytitle); /* printout is 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++; } re->prints[i].type=typeprintout; 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); } int re_extractword2buf(re_t *re, redata_t *data, long pos, char *buf, int sizebuf, int *usedbuf) { char utfchar[5]; int usedutfchar; long startpos,curpos,endpos; int c; if(re==NULL || data==NULL || pos<0) return(-1); /* sanity check failed */ /* search start of word */ for(curpos=startpos=pos;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=pos;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; } } if(redata_getsubstr(re->data,startpos,endpos,buf,sizebuf-1,usedbuf)!=0) return(-1); buf[*usedbuf]='\0'; 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:(posnextoridata,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; char *bgcolor; 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: (printout->type==printoutHelp)?0: (printout->type==printoutManpage)?0: (printout->type==printoutManpageCurlang)?0: 1; 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; } 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 */ bgcolor=(printout!=NULL && (printout->type==printoutHelp || printout->type==printoutManpage || printout->type==printoutManpageCurlang )) ?COLOR_PRINTOUTSTRIPE(re) :COLOR_BACKGROUND(re); reui_fill(ui,x0-linenowidth,y0,w,h,bgcolor); 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 if(printout!=NULL && (printout->type==printoutHelp || printout->type==printoutManpage || printout->type==printoutManpageCurlang)) { ; /* help bgcolor already filled */ } 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)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=(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(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=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+w))?(x0+w-1):x; y=y0+(mline-originline)*ui->fontheight+(ui->fontheight/2); y=(y=(y0+h))?(y0+h-1):y; if(mline=(originline+maxrow)) reui_balloon(ui, 's', x, y0+h-1, 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;ih;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);linenodata,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;ih;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_seclastwheel.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;icomms.sizeclients;i++) { if((client=re->comms.clients[i])==NULL) continue; if(client->fd!=-1) close(client->fd),client->fd=-1; } re->comms.usedclients=0; } /* fill the id */ if(realpath(filename,re->comms.id)==NULL) { /* if the path cannot be resolved, copy the filename */ strncpy(re->comms.id,filename,sizeof(re->comms.id)); re->comms.id[sizeof(re->comms.id)-1]='\0'; } re->comms.id[sizeof(re->comms.id)-1]='\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((errstr="create")==NULL || (oldfd=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="connect")==NULL || connect(oldfd,(struct sockaddr *)&addr,sizeof(addr))==-1 ) { #if 0 fprintf(stderr,"Check for other server result: %s fail.\n",errstr); #endif close(oldfd),oldfd=-1; /* OK: the re is no server as couldn't connect */ } else { 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) { int nclient; comms->socketfilename[0]='\0'; nclient=re_socketnewclient(re,oldfd),oldfd=-1; if(nclient!=-1) { re_socketout(re,nclient,"id %s\n",comms->id); comms->clients[nclient]->flag_connectedtoserver=1; #if 0 fprintf(stderr,"nclient:%i id:\"%s\".\n",nclient,comms->id); fprintf(stderr,"Connected to server.\n"); #endif return(0); } fprintf(stderr,"WARNING: There is a process using the communication socket for the file and couldn't connect to it.\n"); 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 0 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;icomms.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 alreadyacceptedfd) { int i; int fd; commclient_t *client; struct sockaddr addr; socklen_t addrlen; addrlen=sizeof(addr); if(re==NULL) return(-1); /* sanity check failed */ if(alreadyacceptedfd==-1) fd=accept(re->comms.serverfd,&addr,&addrlen); else fd=alreadyacceptedfd; 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;icomms.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++; #if 0 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) return(-1); /* sanity check error */ comms=&(re->comms); FD_ZERO(&readset); FD_ZERO(&writeset); maxfd=0; if(comms->serverfd!=-1) { maxfd=comms->serverfd; FD_SET(comms->serverfd,&readset); } for(i=0;isizeclients;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=(maxfdfd)?client->fd:maxfd; } if((client->usedbufout-client->gotbufout)>0) { FD_SET(client->fd,&writeset); maxfd=(maxfdfd)?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(comms->serverfd!=-1 && FD_ISSET(comms->serverfd,&readset)) re_socketnewclient(re,-1); /* clients */ for(i=0;isizeclients;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 0 fprintf(stderr,"Unregistering client, remote has closed the connection (%i).\n",i); #endif close(client->fd),client->fd=-1; if(client->flag_connectedtoserver) { client->flag_connectedtoserver=0; re_socketinit(re,re->filename); } continue; } avail=(client->sizebufin-client->usedbufin); queued=(queued>avail)?avail:queued; if((nread=read(client->fd,client->bufin+client->usedbufin,queued))>0) { #if 0 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 && nextptr && 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 0 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" */ commclient_t *client; char *ptr; int i; if(re==NULL || nclient<0 || nclient>=re->comms.sizeclients || re->comms.clients[nclient]==NULL || line==NULL) return(-1); /* sanity check failed */ client=re->comms.clients[nclient]; #if 0 fprintf(stderr,"Received from client \"%s\" (%i).\n",line,nclient); #endif if(memcmp(line,"goto ",5)==0) { int oldline; int avail; ptr=line+5; /* change line */ #if 0 fprintf(stderr,"Changing line because of command from client \"%s\" (%i).\n",line,nclient); #endif if(re->comms.selectedid[0]=='\0' || strcmp(re->comms.id,re->comms.selectedid)==0) { 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"); /* forward command to all identified clients except the one who sent teh command */ for(i=0;icomms.sizeclients;i++) { #if 0 if(re->comms.clients[i]!=NULL) fprintf(stderr,"Client[%i] id:\"%s\"\n",i,re->comms.clients[i]->remoteid); #endif if(re->comms.clients[i]==NULL || re->comms.clients[i]->fd==-1 || re->comms.clients[i]->remoteid[0]=='\0') continue; if(re->comms.selectedid[0]=='\0' || strcmp(re->comms.clients[i]->remoteid,re->comms.selectedid)==0) { avail=re->comms.clients[i]->sizebufout-re->comms.clients[i]->usedbufout; if((strlen(line)+1)>avail) { fprintf(stderr,"Couldn't forward command to client because the output buffer is full (avail:%li, req:%li)\n",(long)avail,(long)(strlen(line)+1)); continue; } re_socketout(re, i, "%s\n",line); } } } else if(memcmp(line,"id ",3)==0) { ptr=line+3; /* change id */ #if 0 fprintf(stderr,"Storing client id because of command from client \"%s\" (%i).\n",line,nclient); #endif strncpy(client->remoteid,ptr,sizeof(client->remoteid)); client->remoteid[sizeof(client->remoteid)-1]='\0'; /* send end-of-results */ re_socketout(re, nclient, "\n"); } else if(strcmp(line,"list")==0) { /* list ids */ #if 0 fprintf(stderr,"Listing ids because of command from client \"%s\" (%i).\n",line,nclient); #endif re_socketout(re, nclient, "%s\n",re->comms.id); for(i=0;icomms.sizeclients;i++) { if(re->comms.clients[i]==NULL || re->comms.clients[i]->fd==-1 || re->comms.clients[i]->remoteid[0]=='\0') continue; re_socketout(re, nclient, "%s\n",re->comms.clients[i]->remoteid); } /* send end-of-results */ re_socketout(re, nclient, "\n"); } else if(strcmp(line,"select")==0) { /* reset select id */ #if 0 fprintf(stderr,"Resetting selected id because of command from client \"%s\" (%i).\n",line,nclient); #endif re->comms.selectedid[0]='\0'; /* send end-of-results */ re_socketout(re, nclient, "\n"); } else if(memcmp(line,"select ",7)==0) { ptr=line+7; /* select id */ #if 0 fprintf(stderr,"Changing selected id because of command from client \"%s\" (%i).\n",line,nclient); #endif strncpy(re->comms.selectedid,ptr,sizeof(re->comms.selectedid)); re->comms.selectedid[sizeof(re->comms.selectedid)-1]='\0'; /* send end-of-results */ re_socketout(re, nclient, "\n"); } else if(line[0]=='\0' ) { ; /* ignore the end-of-message (should only receive them on forwarding clients) */ } else if(strcmp(line,"quit")==0) { /* close socket */ #if 0 fprintf(stderr,"Closing socket because of command from client \"%s\" (%i).\n",line,nclient); #endif close(re->comms.clients[nclient]->fd),re->comms.clients[nclient]->fd=-1; return(0); } 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); }