... | ... |
@@ -15,6 +15,14 @@ |
15 | 15 |
#include <signal.h> |
16 | 16 |
#include <time.h> |
17 | 17 |
#include <sys/time.h> |
18 |
+#include <sys/socket.h> /* unix domain socket support */ |
|
19 |
+#include <sys/un.h> /* unix domain socket support */ |
|
20 |
+#include <sys/types.h> /* getpwuid() */ |
|
21 |
+#include <pwd.h> /* getpwuid() */ |
|
22 |
+#include <fcntl.h> |
|
23 |
+#include <sys/select.h> |
|
24 |
+#include <stdarg.h> |
|
25 |
+#include <sys/stat.h> |
|
18 | 26 |
|
19 | 27 |
#include "re_data.h" |
20 | 28 |
#include "re_plugin_unsaved.h" |
... | ... |
@@ -31,7 +39,11 @@ |
31 | 39 |
#define COMMANDBUFSIZE 1024 |
32 | 40 |
#define DEFAULTFONTHEIGHT 14 |
33 | 41 |
#define SELECTBUFBLOCK 16384 |
34 |
-#define SIZEBLOCKPRINTS 32 |
|
42 |
+#define PRINTSBLOCKSIZE 32 |
|
43 |
+#define COMMBUFSIZE 16384 |
|
44 |
+#define COMMCLIENTSBLOCKSIZE 32 |
|
45 |
+#define SOCKETFILENAMESIZE 1024 |
|
46 |
+#define LISTENBACKLOG 5 |
|
35 | 47 |
|
36 | 48 |
#define VIEWONLYPROGNAME "review" |
37 | 49 |
|
... | ... |
@@ -167,6 +179,26 @@ typedef struct theme_t { |
167 | 179 |
char color_curline[5]; |
168 | 180 |
} theme_t; |
169 | 181 |
|
182 |
+typedef struct commclient_t { |
|
183 |
+ int fd; |
|
184 |
+ int sizebufin; |
|
185 |
+ int usedbufin; |
|
186 |
+ int gotbufin; |
|
187 |
+ char bufin[COMMBUFSIZE]; |
|
188 |
+ int sizebufout; |
|
189 |
+ int usedbufout; |
|
190 |
+ int gotbufout; |
|
191 |
+ char bufout[COMMBUFSIZE]; |
|
192 |
+} commclient_t; |
|
193 |
+ |
|
194 |
+typedef struct comms_t { |
|
195 |
+ int serverfd; |
|
196 |
+ char socketfilename[SOCKETFILENAMESIZE]; |
|
197 |
+ int sizeclients; |
|
198 |
+ int usedclients; |
|
199 |
+ commclient_t **clients; |
|
200 |
+} comms_t; |
|
201 |
+ |
|
170 | 202 |
typedef struct re_t { |
171 | 203 |
redata_t *data; |
172 | 204 |
reui_t *ui; |
... | ... |
@@ -210,6 +242,7 @@ typedef struct re_t { |
210 | 242 |
int originlinefunclisting; |
211 | 243 |
int curlinefunclisting; |
212 | 244 |
struct timeval lastwheel; |
245 |
+ comms_t comms; |
|
213 | 246 |
} re_t; |
214 | 247 |
|
215 | 248 |
volatile int flag_sigint; |
... | ... |
@@ -259,7 +292,16 @@ redata_t *re_getfunclisting(re_t *re); |
259 | 292 |
int re_funclistingxy2line(re_t *re,int mx,int my); |
260 | 293 |
int re_wheelaccel(re_t *re, int rawamount); |
261 | 294 |
int re_themeset(re_t *re, int ntheme); |
262 |
- |
|
295 |
+int re_socketinit(re_t *re, char *filename); |
|
296 |
+void re_socketfree(re_t *re); |
|
297 |
+int re_socketnewclient(re_t *re); |
|
298 |
+int re_socketstep(re_t *re); |
|
299 |
+int re_socketin(re_t *re, int nclient, char *line); |
|
300 |
+#ifndef __GNUC__ |
|
301 |
+int re_socketout(re_t *re, int nclient, char *format, ...); |
|
302 |
+#else |
|
303 |
+ int re_socketout(re_t *re, int nclient, char *format, ...) __attribute__((format(printf,3,4))); |
|
304 |
+#endif |
|
263 | 305 |
|
264 | 306 |
int |
265 | 307 |
main(int argc, char *argv[]) |
... | ... |
@@ -341,6 +383,9 @@ fprintf(stderr,"QUESTION INIT: %s: %s\n",re->question->title,re->question->body) |
341 | 383 |
re->commandbuf[sizeof(re->commandbuf)-1]='\0'; |
342 | 384 |
redata_needssaving_reset(re->data); |
343 | 385 |
} |
386 |
+ if(re_socketinit(re,re->filename)!=0) { |
|
387 |
+ fprintf(stderr,"WARNING: Couldn't init communication socket; there will be no integration with other tools\n"); |
|
388 |
+ } |
|
344 | 389 |
} |
345 | 390 |
} |
346 | 391 |
/* workaround for some nvidia linux drivers, that show old data on partial updates */ |
... | ... |
@@ -396,6 +441,7 @@ fprintf(stderr,"RENDER\n"); |
396 | 441 |
reui_present(re->prints[i].ui); |
397 | 442 |
} |
398 | 443 |
sselect_wait(ssel,(flag_had_events)?10:100); |
444 |
+ re_socketstep(re); |
|
399 | 445 |
flag_had_events=(flag_had_events>0)?flag_had_events-1:0; |
400 | 446 |
SDL_PumpEvents(); |
401 | 447 |
while(SDL_PeepEvents(&event,1,SDL_GETEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT)>0) { |
... | ... |
@@ -874,6 +920,7 @@ re_free(re_t *re) |
874 | 920 |
redata_free(re->data),re->data=NULL; |
875 | 921 |
if(re->selectbuf!=NULL) |
876 | 922 |
free(re->selectbuf),re->selectbuf=NULL,re->usedselectbuf=re->sizeselectbuf=0; |
923 |
+ re_socketfree(re); |
|
877 | 924 |
free(re),re=NULL; |
878 | 925 |
return; |
879 | 926 |
} |
... | ... |
@@ -2172,11 +2219,11 @@ re_addprint(re_t *re) |
2172 | 2219 |
/* ensure space for the new printout */ |
2173 | 2220 |
if(re->usedprints==re->sizeprints) { |
2174 | 2221 |
printout_t *newprints; |
2175 |
- if((newprints=realloc(re->prints,sizeof(printout_t)*(re->sizeprints+SIZEBLOCKPRINTS)))==NULL) |
|
2222 |
+ if((newprints=realloc(re->prints,sizeof(printout_t)*(re->sizeprints+PRINTSBLOCKSIZE)))==NULL) |
|
2176 | 2223 |
return(-1); /* insuf. mem. */ |
2177 | 2224 |
re->prints=newprints; |
2178 |
- memset(re->prints+re->sizeprints,0,sizeof(printout_t)*SIZEBLOCKPRINTS); |
|
2179 |
- re->sizeprints+=SIZEBLOCKPRINTS; |
|
2225 |
+ memset(re->prints+re->sizeprints,0,sizeof(printout_t)*PRINTSBLOCKSIZE); |
|
2226 |
+ re->sizeprints+=PRINTSBLOCKSIZE; |
|
2180 | 2227 |
} |
2181 | 2228 |
for(i=0;i<re->sizeprints;i++) { |
2182 | 2229 |
if(re->prints[i].ui==NULL) |
... | ... |
@@ -2897,3 +2944,297 @@ re_themeset(re_t *re, int ntheme) |
2897 | 2944 |
return(0); |
2898 | 2945 |
} |
2899 | 2946 |
|
2947 |
+int |
|
2948 |
+re_socketinit(re_t *re,char *filename) |
|
2949 |
+{ |
|
2950 |
+ int i; |
|
2951 |
+ comms_t *comms; |
|
2952 |
+ commclient_t *client; |
|
2953 |
+ struct sockaddr_un addr; |
|
2954 |
+ char *ptr; |
|
2955 |
+ struct passwd *passwd; |
|
2956 |
+ char *username; |
|
2957 |
+ int oldfd; |
|
2958 |
+ char *errstr; |
|
2959 |
+ if(re==NULL) |
|
2960 |
+ return(-1); |
|
2961 |
+ comms=&(re->comms); |
|
2962 |
+ /* close all current comms */ |
|
2963 |
+ if(comms->serverfd!=-1) { |
|
2964 |
+ close(comms->serverfd),comms->serverfd=-1; |
|
2965 |
+ if(comms->socketfilename[0]!='\0') |
|
2966 |
+ unlink(comms->socketfilename),comms->socketfilename[0]='\0'; |
|
2967 |
+ } |
|
2968 |
+ if(re->comms.clients!=NULL) { |
|
2969 |
+ for(i=0;i<re->comms.sizeclients;i++) { |
|
2970 |
+ if((client=re->comms.clients[i])==NULL) |
|
2971 |
+ continue; |
|
2972 |
+ if(client->fd!=-1) |
|
2973 |
+ close(client->fd),client->fd=-1; |
|
2974 |
+ } |
|
2975 |
+ re->comms.usedclients=0; |
|
2976 |
+ } |
|
2977 |
+ /* prepare the new filepath for the socket */ |
|
2978 |
+ if((passwd=getpwuid(getuid()))==NULL || (username=passwd->pw_name)==NULL) { |
|
2979 |
+ fprintf(stderr,"WARNING: Couldn't get username\n"); |
|
2980 |
+ return(-1); |
|
2981 |
+ } |
|
2982 |
+ ptr=strrchr(filename,'/'); |
|
2983 |
+ ptr=(ptr!=NULL)?ptr+1:filename; |
|
2984 |
+ snprintf(comms->socketfilename,sizeof(comms->socketfilename),"/tmp/.re_%s",username); |
|
2985 |
+ comms->socketfilename[sizeof(comms->socketfilename)-1]='\0'; |
|
2986 |
+ mkdir(comms->socketfilename,0700); |
|
2987 |
+ snprintf(comms->socketfilename,sizeof(comms->socketfilename),"/tmp/.re_%s/%s",username,ptr); |
|
2988 |
+ comms->socketfilename[sizeof(comms->socketfilename)-1]='\0'; |
|
2989 |
+ if(strlen(comms->socketfilename)>=(sizeof(addr.sun_path))) { |
|
2990 |
+ comms->socketfilename[0]='\0'; |
|
2991 |
+ fprintf(stderr,"WARNING: Socket path filename too long\n"); |
|
2992 |
+ return(-1); |
|
2993 |
+ } |
|
2994 |
+ /* detect if there is a stale socket */ |
|
2995 |
+ if((oldfd=open(comms->socketfilename, O_WRONLY|O_APPEND))!=-1) { |
|
2996 |
+ fd_set writeset; |
|
2997 |
+ struct timeval tv; |
|
2998 |
+ FD_ZERO(&writeset); |
|
2999 |
+ FD_SET(oldfd,&writeset); |
|
3000 |
+ tv.tv_sec=tv.tv_usec=0; |
|
3001 |
+ if(select(oldfd+1,NULL,&writeset,NULL,&tv)>0) { |
|
3002 |
+ comms->socketfilename[0]='\0'; |
|
3003 |
+ fprintf(stderr,"WARNING: There is a process using the communication socket for the file\n"); |
|
3004 |
+ return(-1); |
|
3005 |
+ } |
|
3006 |
+ close(oldfd),oldfd=-1; |
|
3007 |
+ } |
|
3008 |
+ unlink(comms->socketfilename); |
|
3009 |
+ /* create and bind socket */ |
|
3010 |
+ if((errstr="create")==NULL |
|
3011 |
+ || (comms->serverfd=socket(AF_UNIX,SOCK_STREAM,0))==-1 |
|
3012 |
+ || (errstr="assing")==NULL /* this one never fails, just for completion */ |
|
3013 |
+ || (memset(&addr,0,sizeof(struct sockaddr_un)))==NULL |
|
3014 |
+ || (addr.sun_family=AF_UNIX)!=AF_UNIX |
|
3015 |
+ || strncpy(addr.sun_path,comms->socketfilename,sizeof(addr.sun_path))==NULL |
|
3016 |
+ || (errstr="bind")==NULL |
|
3017 |
+ || bind(comms->serverfd,(struct sockaddr *)&addr,sizeof(addr))==-1 |
|
3018 |
+ || (errstr="listen")==NULL |
|
3019 |
+ || listen(comms->serverfd,LISTENBACKLOG)==-1 |
|
3020 |
+ ) { |
|
3021 |
+ comms->socketfilename[0]='\0'; |
|
3022 |
+ if(comms->serverfd!=-1) |
|
3023 |
+ close(comms->serverfd),comms->serverfd=-1; |
|
3024 |
+ fprintf(stderr,"WARNING: Couldn't %s unix domain socket\n",errstr); |
|
3025 |
+ return(-1); |
|
3026 |
+ } |
|
3027 |
+#if 1 |
|
3028 |
+fprintf(stderr,"Server registered.\n"); |
|
3029 |
+#endif |
|
3030 |
+ return(0); |
|
3031 |
+} |
|
3032 |
+ |
|
3033 |
+void |
|
3034 |
+re_socketfree(re_t *re) |
|
3035 |
+{ |
|
3036 |
+ int i; |
|
3037 |
+ commclient_t *client; |
|
3038 |
+ if(re==NULL) |
|
3039 |
+ return; /* nothing to do */ |
|
3040 |
+ if(re->comms.serverfd!=-1) |
|
3041 |
+ close(re->comms.serverfd),re->comms.serverfd=-1; |
|
3042 |
+ if(re->comms.socketfilename[0]!='\0') |
|
3043 |
+ unlink(re->comms.socketfilename),re->comms.socketfilename[0]='\0'; |
|
3044 |
+ if(re->comms.clients!=NULL) { |
|
3045 |
+ for(i=0;i<re->comms.sizeclients;i++) { |
|
3046 |
+ if((client=re->comms.clients[i])==NULL) |
|
3047 |
+ continue; |
|
3048 |
+ if(client->fd!=-1) |
|
3049 |
+ close(client->fd),client->fd=-1; |
|
3050 |
+ free(client),client=NULL; |
|
3051 |
+ re->comms.clients[i]=NULL; |
|
3052 |
+ } |
|
3053 |
+ re->comms.usedclients=re->comms.sizeclients=0; |
|
3054 |
+ free(re->comms.clients),re->comms.clients=NULL; |
|
3055 |
+ } |
|
3056 |
+ return; |
|
3057 |
+} |
|
3058 |
+ |
|
3059 |
+int |
|
3060 |
+re_socketnewclient(re_t *re) |
|
3061 |
+{ |
|
3062 |
+ int i; |
|
3063 |
+ int fd; |
|
3064 |
+ commclient_t *client; |
|
3065 |
+ struct sockaddr addr; |
|
3066 |
+ socklen_t addrlen; |
|
3067 |
+ addrlen=sizeof(addr); |
|
3068 |
+ if(re==NULL) |
|
3069 |
+ return(-1); /* sanity check failed */ |
|
3070 |
+ fd=accept(re->comms.serverfd,&addr,&addrlen); |
|
3071 |
+ if(re->comms.usedclients==re->comms.sizeclients) { |
|
3072 |
+ commclient_t **newclients; |
|
3073 |
+ if((newclients=realloc(re->comms.clients,sizeof(commclient_t *)*(re->comms.sizeclients+COMMCLIENTSBLOCKSIZE)))==NULL) { |
|
3074 |
+ close(fd),fd=-1; |
|
3075 |
+ return(-1); /* insuf. mem. */ |
|
3076 |
+ } |
|
3077 |
+ re->comms.clients=newclients; |
|
3078 |
+ memset(re->comms.clients+re->comms.sizeclients,0,sizeof(commclient_t *)*COMMCLIENTSBLOCKSIZE); |
|
3079 |
+ re->comms.sizeclients+=COMMCLIENTSBLOCKSIZE; |
|
3080 |
+ } |
|
3081 |
+ for(i=0;i<re->comms.sizeclients;i++) { |
|
3082 |
+ if(re->comms.clients[i]==NULL || re->comms.clients[i]->fd==-1) |
|
3083 |
+ break; |
|
3084 |
+ } |
|
3085 |
+ if(i>=re->comms.sizeclients) { |
|
3086 |
+ close(fd),fd=-1; |
|
3087 |
+ re->comms.usedclients=re->comms.sizeclients; |
|
3088 |
+ return(-1); /* INTERNAL ERROR */ |
|
3089 |
+ } |
|
3090 |
+ client=re->comms.clients[i]; |
|
3091 |
+ if(client==NULL) { |
|
3092 |
+ if((client=malloc(sizeof(commclient_t)))==NULL) { |
|
3093 |
+ close(fd),fd=-1; |
|
3094 |
+ return(-1); /* INTERNAL ERROR */ |
|
3095 |
+ } |
|
3096 |
+ re->comms.clients[i]=client; |
|
3097 |
+ memset(client,0,sizeof(commclient_t)); |
|
3098 |
+ client->fd=-1; |
|
3099 |
+ client->sizebufin=sizeof(client->bufin); |
|
3100 |
+ client->sizebufout=sizeof(client->bufout); |
|
3101 |
+ } |
|
3102 |
+ client->fd=fd; |
|
3103 |
+ client->usedbufin=client->gotbufin=0; |
|
3104 |
+ client->usedbufout=client->gotbufout=0; |
|
3105 |
+ re->comms.usedclients++; |
|
3106 |
+ re_socketout(re, i, "OK\n"); |
|
3107 |
+#if 1 |
|
3108 |
+fprintf(stderr,"New client registered (%i).\n",i); |
|
3109 |
+#endif |
|
3110 |
+ return(i); |
|
3111 |
+} |
|
3112 |
+ |
|
3113 |
+int re_socketstep(re_t *re) |
|
3114 |
+{ |
|
3115 |
+ int maxfd; |
|
3116 |
+ fd_set readset,writeset; |
|
3117 |
+ struct timeval tv; |
|
3118 |
+ int i; |
|
3119 |
+ comms_t *comms; |
|
3120 |
+ commclient_t *client; |
|
3121 |
+ int avail,queued; |
|
3122 |
+ int nread,nwritten; |
|
3123 |
+ char *ptr,*next,*end; |
|
3124 |
+ if(re==NULL || re->comms.serverfd==-1) |
|
3125 |
+ return(-1); /* sanity check error */ |
|
3126 |
+ comms=&(re->comms); |
|
3127 |
+ FD_ZERO(&readset); |
|
3128 |
+ FD_ZERO(&writeset); |
|
3129 |
+ maxfd=comms->serverfd; |
|
3130 |
+ FD_SET(comms->serverfd,&readset); |
|
3131 |
+ for(i=0;i<comms->sizeclients;i++) { |
|
3132 |
+ if((client=comms->clients[i])==NULL || client->fd==-1) |
|
3133 |
+ continue; |
|
3134 |
+ if(client->gotbufin>0) { |
|
3135 |
+ memmove(client->bufin,client->bufin+client->gotbufin,client->usedbufin-client->gotbufin); |
|
3136 |
+ client->usedbufin-=client->gotbufin; |
|
3137 |
+ client->gotbufin=0; |
|
3138 |
+ } |
|
3139 |
+ if((client->sizebufin-client->usedbufin)>0) { |
|
3140 |
+ FD_SET(client->fd,&readset); |
|
3141 |
+ maxfd=(maxfd<client->fd)?client->fd:maxfd; |
|
3142 |
+ } |
|
3143 |
+ if((client->usedbufout-client->gotbufout)>0) { |
|
3144 |
+ FD_SET(client->fd,&writeset); |
|
3145 |
+ maxfd=(maxfd<client->fd)?client->fd:maxfd; |
|
3146 |
+ } |
|
3147 |
+ } |
|
3148 |
+ tv.tv_sec=tv.tv_usec=0; |
|
3149 |
+ if(select(maxfd+1,&readset,&writeset,NULL,&tv)<=0) |
|
3150 |
+ return(0); /* nothing to do */ |
|
3151 |
+ /* server */ |
|
3152 |
+ if(FD_ISSET(comms->serverfd,&readset)) |
|
3153 |
+ re_socketnewclient(re); |
|
3154 |
+ /* clients */ |
|
3155 |
+ for(i=0;i<comms->sizeclients;i++) { |
|
3156 |
+ if((client=comms->clients[i])==NULL || client->fd==-1) |
|
3157 |
+ continue; |
|
3158 |
+ if(FD_ISSET(client->fd,&readset)) { |
|
3159 |
+ if((queued=sock_queued(client->fd))<=0) { |
|
3160 |
+ /* remote has closed the connection */ |
|
3161 |
+#if 1 |
|
3162 |
+fprintf(stderr,"Unregistering client, remote has closed the connection (%i).\n",i); |
|
3163 |
+#endif |
|
3164 |
+ close(client->fd),client->fd=-1; |
|
3165 |
+ continue; |
|
3166 |
+ } |
|
3167 |
+ avail=(client->sizebufin-client->usedbufin); |
|
3168 |
+ queued=(queued>avail)?avail:queued; |
|
3169 |
+ if((nread=read(client->fd,client->bufin+client->usedbufin,queued))>0) { |
|
3170 |
+#if 1 |
|
3171 |
+fprintf(stderr,"Read from client %li bytes (%i).\n",(long)nread,i); |
|
3172 |
+#endif |
|
3173 |
+ client->usedbufin+=nread; |
|
3174 |
+ for(ptr=client->bufin+client->gotbufin |
|
3175 |
+ ,end=client->bufin+client->usedbufin |
|
3176 |
+ ,next=memchr(ptr,'\n',end-ptr) |
|
3177 |
+ ,next=(next==NULL)?end:next |
|
3178 |
+ ;client->fd!=-1 && next<end |
|
3179 |
+ ;ptr=(next!=end)?next+1:end |
|
3180 |
+ ,next=memchr(ptr,'\n',end-ptr) |
|
3181 |
+ ,next=(next==NULL)?end:next |
|
3182 |
+ ) { |
|
3183 |
+ *next='\0'; |
|
3184 |
+ if(next>ptr && next[-1]=='\r') |
|
3185 |
+ next[-1]='\0'; |
|
3186 |
+ re_socketin(re,i,ptr); |
|
3187 |
+ } |
|
3188 |
+ client->gotbufin=ptr-client->bufin; |
|
3189 |
+ if(client->fd==-1) |
|
3190 |
+ continue; /* in case the socket has been closed inside re_socketin() */ |
|
3191 |
+ } |
|
3192 |
+ } |
|
3193 |
+ if(FD_ISSET(client->fd,&writeset)) { |
|
3194 |
+ queued=client->usedbufout-client->gotbufout; |
|
3195 |
+ if((nwritten=write(client->fd,client->bufout+client->gotbufout,client->usedbufout-client->gotbufout))>0) |
|
3196 |
+ client->gotbufout+=nwritten; |
|
3197 |
+#if 1 |
|
3198 |
+fprintf(stderr,"Write to client %li bytes (%i).\n",(long)nwritten,i); |
|
3199 |
+#endif |
|
3200 |
+ } |
|
3201 |
+ } |
|
3202 |
+ return(0); |
|
3203 |
+} |
|
3204 |
+ |
|
3205 |
+int |
|
3206 |
+re_socketin(re_t *re, int nclient, char *line) |
|
3207 |
+{ |
|
3208 |
+ /* note that the '\n' delimiter has been already removed */ |
|
3209 |
+#warning TODO |
|
3210 |
+#warning Test with "socat - UNIX-CONNECT:/tmp/.re_${USER}/filename.ext" |
|
3211 |
+#if 1 |
|
3212 |
+ |
|
3213 |
+fprintf(stderr,"Received from client \"%s\" (%i).\n",line,nclient); |
|
3214 |
+#endif |
|
3215 |
+ return(0); |
|
3216 |
+} |
|
3217 |
+ |
|
3218 |
+int |
|
3219 |
+re_socketout(re_t *re, int nclient, char *format, ...) |
|
3220 |
+{ |
|
3221 |
+ /* note that the caller has to output the '\n' delimiter to mark the end of the line */ |
|
3222 |
+ commclient_t *client; |
|
3223 |
+ va_list mylist; |
|
3224 |
+ int avail; |
|
3225 |
+ int res; |
|
3226 |
+ if(re==NULL || nclient<0 || nclient>=re->comms.sizeclients || re->comms.clients[nclient]==NULL || re->comms.clients[nclient]->fd==-1) |
|
3227 |
+ return(-1); /* sanity check error */ |
|
3228 |
+ client=re->comms.clients[nclient]; |
|
3229 |
+ memmove(client->bufout,client->bufout+client->gotbufout,client->usedbufout-client->gotbufout); |
|
3230 |
+ client->usedbufout-=client->gotbufout; |
|
3231 |
+ client->gotbufout=0; |
|
3232 |
+ avail=client->sizebufout-client->usedbufout; |
|
3233 |
+ va_start(mylist,format); |
|
3234 |
+ res=vsnprintf(client->bufout+client->usedbufout,avail,format,mylist); |
|
3235 |
+ va_end(mylist); |
|
3236 |
+ if(res<0 || res>=avail) |
|
3237 |
+ return(-1); /* doesn't fit or another error */ |
|
3238 |
+ client->usedbufout+=res; |
|
3239 |
+ return(0); |
|
3240 |
+} |