/*
* re_plugin_unsaved.c
*
* A programmers editor
*
* re_data plugin to support the unsaved changes file.
* (for recovery when the program closes unexpectedly)
*
* Author: Dario Rodriguez dario@softhome.net
* This program is licensed under the terms of GNU GPL v2.1+
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "re_plugin_unsaved.h"
#define UNSAVEDPREFIX "."
#define UNSAVEDPOSTFIX ".reu"
#define UNSAVEDHEADER "reunsaved00,"
#define UNSAVEDGROWSIZE (256*1024)
int redata_unsaved_add(redata_t *redata, redata_plugin_t *slot, undo_t *undo);
int redata_unsaved_unadd(redata_t *redata, redata_plugin_t *slot, undo_t *undo);
static int redata_unsaved_loadquestion(redata_t *redata, redata_plugin_t *slot,char *filename);
static int redata_unsaved_postload(redata_t *redata, redata_plugin_t *slot,char *filename);
static int redata_unsaved_check_gen(redata_t *redata, redata_plugin_t *slot, char *filename);
static char *unsaved_genname(char *filename, char *buf, int bufsize);
static char *ptr_getlong(char *ptr,char *endptr,long *data);
static char *ptr_getchar(char *ptr,char *endptr,char *data);
static char *ptr_searchendchar(char *ptr, char *endptr, char endchar, char **endcharpos);
static char sep_select(char *buf, int bufsize, char **pos);
int
redata_unsaved_register(redata_t *redata, redata_plugin_t *slot)
{
unsaved_t *unsaved;
if(redata==NULL || slot==NULL)
return(-1);
if((unsaved=malloc(sizeof(unsaved_t)))==NULL)
return(-1);
memset(unsaved,0,sizeof(unsaved_t));
unsaved->unsavedfd=-1;
unsaved->sizebuf=unsaved->usedbuf=0;
strncpy(slot->name,"unsaved",sizeof(slot->name));
slot->name[sizeof(slot->name)-1]='\0';
slot->unregister=redata_unsaved_unregister;
slot->loadquestion=redata_unsaved_loadquestion;
slot->wipe=redata_unsaved_wipe;
slot->postload=redata_unsaved_postload;
slot->postsave=redata_unsaved_trunc;
slot->add_or_unadd=redata_unsaved_add_or_unadd;
slot->commit=redata_unsaved_commit;
slot->userptr=unsaved;
return(0);
}
int
redata_unsaved_unregister(redata_t *redata, redata_plugin_t *slot, char *filename)
{
char unsname[PATH_MAX];
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
if(redata==NULL || slot==NULL || unsaved==NULL)
return(-1);
if(unsaved->buf!=NULL)
free(unsaved->buf),unsaved->buf=NULL;
unsaved->sizebuf=unsaved->usedbuf=0;
if(unsaved->unsavedfd!=-1) {
close(unsaved->unsavedfd),unsaved->unsavedfd=-1;
if(filename!=NULL && unsaved_genname(filename,unsname,sizeof(unsname))!=NULL)
unlink(unsname);
}
if(slot->userptr!=NULL)
free(slot->userptr),slot->userptr=NULL;
return(0);
}
int
redata_unsaved_exists(redata_t *redata, redata_plugin_t *slot, char *filename)
{
char unsname[PATH_MAX+1];
int fd;
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
if(redata==NULL || slot==NULL || unsaved==NULL || filename==NULL || filename[0]=='\0')
return(-1); /* sanity check failed */
if((unsaved_genname(filename,unsname,sizeof(unsname)))==NULL)
return(-1); /* malformed filename */
if((fd=open(unsname,O_RDONLY))==-1)
return(-1);
close(fd),fd=-1;
return(0);
}
int
redata_unsaved_wipe(redata_t *redata, redata_plugin_t *slot, char *filename)
{
char unsname[PATH_MAX];
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
if(redata==NULL || slot==NULL || unsaved==NULL || filename==NULL || filename[0]=='\0')
return(-1);
if(unsaved->unsavedfd!=-1) {
close(unsaved->unsavedfd),unsaved->unsavedfd=-1;
if(unsaved_genname(filename,unsname,sizeof(unsname))!=NULL)
unlink(unsname);
}
unsaved->usedbuf=0;
return(0);
}
static int
redata_unsaved_check_gen(redata_t *redata, redata_plugin_t *slot, char *filename)
{
char unsname[PATH_MAX+1];
int fd,nread;
static char header[]={UNSAVEDHEADER};
char filehash[129],undohash[129],buf[16];
char fileheader[]={UNSAVEDHEADER};
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
if(redata==NULL || slot==NULL || unsaved==NULL || filename==NULL || filename[0]=='\0')
return(-1); /* sanity check failed */
if((unsaved_genname(filename,unsname,sizeof(unsname)))==NULL)
return(-1); /* malformed filename */
if(redata_filehash(redata,filename,filehash)!=0)
return(-1);
if((fd=open(unsname,O_RDONLY))==-1)
return(-1);
memset(fileheader,0,sizeof(fileheader));
if((nread=read(fd,fileheader,sizeof(fileheader)-1))==-1
|| nread!=(sizeof(fileheader)-1) || memcmp(fileheader,header,sizeof(fileheader))!=0) {
close(fd),fd=-1;
return(-1); /* corrupted header */
}
if((nread=read(fd,undohash,128))==-1 || nread!=128 || memcmp(undohash,filehash,128)!=0) {
close(fd),fd=-1;
return(-1); /* wrong hash */
}
if((nread=read(fd,buf,1))==-1 || nread!=1 || *buf!=',') {
close(fd),fd=-1;
return(-1); /* wrong hash separator */
}
return(fd);
}
int
redata_unsaved_check(redata_t *redata, redata_plugin_t *slot, char *filename)
{
int fd;
if((fd=redata_unsaved_check_gen(redata,slot,filename))==-1)
return(-1); /* check failed */
close(fd),fd=-1;
return(0);
}
int
redata_unsaved_unlink(redata_t *redata, redata_plugin_t *slot, char *filename)
{
char unsname[PATH_MAX+1];
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
if(redata==NULL || slot==NULL || unsaved==NULL || filename==NULL || filename[0]=='\0')
return(-1); /* sanity check failed */
if(redata_unsaved_exists(redata,slot,filename)==-1)
return(0); /* file not found, nothing to unlink */
if((unsaved_genname(filename,unsname,sizeof(unsname)))==NULL)
return(-1); /* malformed filename */
unlink(unsname);
return(0);
}
int
redata_unsaved_trunc(redata_t *redata, redata_plugin_t *slot, char *oldfilename, char *newfilename)
{
char unsname[PATH_MAX+1];
static char header[]={UNSAVEDHEADER};
int n;
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
if(redata==NULL || slot==NULL || unsaved==NULL || newfilename==NULL || newfilename[0]=='\0')
return(-1); /* sanity check failed */
if(oldfilename!=NULL)
redata_unsaved_unlink(redata,slot,oldfilename);
if((unsaved_genname(newfilename,unsname,sizeof(unsname)))==NULL)
return(-1); /* malformed filename */
if(unsaved->unsavedfd!=-1)
close(unsaved->unsavedfd),unsaved->unsavedfd=-1;
redata_hash(redata,unsaved->initialhash);
unsaved->usedbuf=0;
if((unsaved->unsavedfd=open(unsname,O_WRONLY|O_TRUNC|O_CREAT,0644))==-1)
return(-1); /* couldn't open file for writing */
if((n=write(unsaved->unsavedfd,header,sizeof(header)-1))==-1
|| n!=(sizeof(header)-1)
|| (n=write(unsaved->unsavedfd,unsaved->initialhash,128))==-1 || n!=128
|| (n=write(unsaved->unsavedfd,",",1))==-1 || n!=1) {
close(unsaved->unsavedfd),unsaved->unsavedfd=-1;
unlink(unsname);
return(-1); /* couldn't write header/hash */
}
return(0);
}
int
redata_unsaved_truncload(redata_t *redata, redata_plugin_t *slot, char *filename)
{
#if 0
fprintf(stderr,"UNSAVED: TRUNCLOAD (ignore)\n");
#endif
return(redata_unsaved_trunc(redata, slot, NULL, filename));
}
int
redata_unsaved_loadappend(redata_t *redata, redata_plugin_t *slot, char *filename)
{
int fd;
struct stat statbuf;
static char header[]={UNSAVEDHEADER};
long headerhashsize;
char *newptr;
long nread,lim;
char *ptr,*endptr,*aux,*bufptr;
char actioncode;
long pos,pos2;
char endcode;
int flag_multipart;
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
#if 0
fprintf(stderr,"UNSAVED: LOADAPPEND (recover)\n");
#endif
if(redata==NULL || slot==NULL || unsaved==NULL || filename==NULL)
return(-1);
#if 0
fprintf(stderr,"UNSAVED: LOADAPPEND pre-check\n");
#endif
if((fd=redata_unsaved_check_gen(redata,slot,filename))==-1)
return(-1); /* check failed */
#if 0
fprintf(stderr,"UNSAVED: LOADAPPEND post-check\n");
#endif
if(fstat(fd,&statbuf)!=0 || !S_ISREG(statbuf.st_mode)) {
close(fd),fd=-1;
return(-1); /* couldn't query size or not regular file */
}
redata_hash(redata,unsaved->initialhash);
headerhashsize=(sizeof(header)-1)+128+1;
#if 0
fprintf(stderr,"UNSAVED: LOADAPPEND headerhashsize: %li\n",headerhashsize);
#endif
/* load unsaved to memory */
if(unsaved->sizebuf<(statbuf.st_size-headerhashsize)) {
if((newptr=realloc(unsaved->buf,(statbuf.st_size-headerhashsize)))==NULL) {
close(fd),fd=-1;
return(-1); /* insuf. mem. */
}
unsaved->buf=newptr;
unsaved->sizebuf=(statbuf.st_size-headerhashsize);
}
unsaved->usedbuf=0;
lim=(statbuf.st_size-headerhashsize);
for(nread=0;unsaved->usedbuf<lim;unsaved->usedbuf+=nread,nread=0) {
if((nread=read(fd,unsaved->buf+unsaved->usedbuf,lim-unsaved->usedbuf))<=0) {
unsaved->usedbuf=0;
close(fd),fd=-1;
return(-1); /* short read */
}
}
close(fd),fd=-1;
/* process unsaved data */
slot->active=0;
endptr=unsaved->buf+unsaved->usedbuf;
for(ptr=unsaved->buf;ptr<endptr;) {
#ifdef DEBUG_PLUGIN_UNSAVED
{
int m;
fprintf(stderr,"%05X: ",ptr-unsaved->buf);
for(m=0;m<16 && (ptr+m)<endptr;m++)
fprintf(stderr,"%02X%s ",((unsigned char *)ptr)[m],(m==7)?" ":"");
fprintf(stderr," | ");
for(m=0;m<16 && (ptr+m)<endptr;m++)
fprintf(stderr,"%c%s",((((unsigned char *)ptr)[m])<20)?'.':((((unsigned char *)ptr)[m])>126)?'.':((unsigned char *)ptr)[m],(m==7)?" ":"");
fprintf(stderr,"\n");
}
#endif
if((ptr=ptr_getchar(ptr,endptr,&actioncode))==NULL)
return(-1); /* no space for action char */
/* multipart example: A10+$aj$%$% >> insert "aj$" into pos 10 */
if(actioncode=='A') {
ptr=ptr_getlong(ptr,endptr,&pos);
do {
flag_multipart=0;
ptr=ptr_getchar(ptr,endptr,&endcode);
if(ptr!=NULL && endcode=='+') {
flag_multipart=1;
ptr=ptr_getchar(ptr,endptr,&endcode);
}
bufptr=ptr;
ptr=ptr_searchendchar(ptr,endptr,endcode,&aux);
if(ptr==NULL || pos<0 || pos>redata_getused(redata))
return(-1); /* malformed register */
redata_op_add(redata,pos,bufptr,aux-bufptr,NULL);
pos+=aux-bufptr;
} while(flag_multipart);
} else if(actioncode=='D') {
ptr=ptr_getlong(ptr,endptr,&pos);
do {
flag_multipart=0;
ptr=ptr_getchar(ptr,endptr,&endcode);
if(ptr!=NULL && endcode=='+') {
flag_multipart=1;
ptr=ptr_getchar(ptr,endptr,&endcode);
}
bufptr=ptr;
ptr=ptr_searchendchar(ptr,endptr,endcode,&aux);
if(ptr==NULL || pos<0 || (pos+(aux-bufptr))>redata_getused(redata))
return(-1); /* malformed register */
if(redata_data_compare(redata,pos,bufptr,aux-bufptr)!=0)
return(-1); /* corrupted data */
redata_op_del(redata,pos,aux-bufptr,NULL);
} while(flag_multipart);
} else if(actioncode=='M') {
ptr=ptr_getlong(ptr,endptr,&pos);
ptr+=(ptr!=NULL && ptr<endptr)?1:0;
ptr=ptr_getlong(ptr,endptr,&pos2);
do {
flag_multipart=0;
ptr=ptr_getchar(ptr,endptr,&endcode);
if(ptr!=NULL && endcode=='+') {
flag_multipart=1;
ptr=ptr_getchar(ptr,endptr,&endcode);
}
bufptr=ptr;
ptr=ptr_searchendchar(ptr,endptr,endcode,&aux);
if(ptr==NULL
|| pos<0 || (pos+(aux-bufptr))>redata_getused(redata)
|| pos2<0 || pos2>redata_getused(redata)
|| ((aux-bufptr)>0 && pos2>=pos && pos2<(pos+(aux-bufptr)))
) {
return(-1); /* malformed register */
}
if(redata_data_compare(redata,pos,bufptr,aux-bufptr)!=0)
return(-1); /* corrupted data */
redata_op_move(redata,pos,(aux-bufptr),pos2,NULL);
pos=(pos<pos2)?pos:(pos+(aux-bufptr));
pos2=pos2+(aux-bufptr);
} while(flag_multipart);
} else {
return(-1); /* corrupted undobuf */
}
}
slot->active=1;
return(0);
}
int
redata_unsaved_add_or_unadd(redata_t *redata, redata_plugin_t *slot, undo_t *undo, int is_unadd)
{
return((is_unadd==0)?
redata_unsaved_add(redata, slot, undo):
redata_unsaved_unadd(redata, slot, undo));
}
int
redata_unsaved_add(redata_t *redata, redata_plugin_t *slot, undo_t *undo)
{
char sep;
char *sepend,*ptr,*endptr;
char *buf;
int k;
int maxsize,newsize;
char posbuf[128];
undostack_t *stack;
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
stack=redata_getstack(redata,undo);
if(redata==NULL || slot==NULL || unsaved==NULL || undo==NULL || stack==NULL || slot->active==0)
return(-1); /* sanity check failed */
/* syntax (see loadappend): A<pos><+?><sep><text><sep>[<+?><sep><text><sep>[...]] */
if(undo->type!='A' && undo->type!='D' && undo->type!='M')
return(-1); /* unrecognized undo type */
for(k=0,maxsize=0,buf=NULL;k<2;k++,maxsize=0) {
ptr=stack->buf+undo->off;
endptr=ptr+undo->len;
if(k!=0)
buf[maxsize]=undo->type;
maxsize++;
snprintf(posbuf,sizeof(posbuf),"%li",undo->posorig);
posbuf[sizeof(posbuf)-1]='\0';
if(k!=0)
strcpy(buf+maxsize,posbuf);
maxsize+=strlen(posbuf);
if(undo->type=='M') {
snprintf(posbuf,sizeof(posbuf),",%li",undo->posdest);
posbuf[sizeof(posbuf)-1]='\0';
if(k!=0)
strcpy(buf+maxsize,posbuf);
maxsize+=strlen(posbuf);
}
while(ptr<endptr) {
sep=sep_select(ptr,endptr-ptr,&sepend);
if(sepend!=endptr) {
if(k!=0)
buf[maxsize]='+';
maxsize++;
}
if(k!=0)
buf[maxsize]=sep;
maxsize++;
if(k!=0)
memcpy(buf+maxsize,ptr,sepend-ptr);
maxsize+=sepend-ptr;
if(k!=0)
buf[maxsize]=sep;
maxsize++;
ptr=sepend;
}
if(k==0) {
/* get mem */
if((unsaved->sizebuf-unsaved->usedbuf)<maxsize) {
newsize=(unsaved->sizebuf+maxsize+UNSAVEDGROWSIZE-1)/UNSAVEDGROWSIZE;
newsize*=UNSAVEDGROWSIZE;
if((buf=realloc(unsaved->buf,newsize))==NULL)
return(-1); /* insuf. mem. */
unsaved->buf=buf;
unsaved->sizebuf=newsize;
#if 0
fprintf(stderr,"UNSAVED: ADD realloc: %li\n",(long)newsize);
#endif
}
buf=unsaved->buf+unsaved->usedbuf;
} else
unsaved->usedbuf+=maxsize;
}
return(0);
}
int
redata_unsaved_unadd(redata_t *redata, redata_plugin_t *slot, undo_t *undo)
{
/* adds to unsaved the inverse operation to the one specified in the undo */
char sep;
char *sepend,*ptr,*endptr;
char *buf;
int k;
int maxsize,newsize;
char posbuf[128];
undostack_t *stack;
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
stack=redata_getstack(redata,undo);
if(redata==NULL || slot==NULL || unsaved==NULL || undo==NULL || stack==NULL || slot->active==0)
return(-1); /* sanity check failed */
/* syntax (see loadappend): A<pos><+?><sep><text><sep>[<+?><sep><text><sep>[...]] */
if(undo->type!='A' && undo->type!='D' && undo->type!='M')
return(-1); /* unrecognized undo type */
for(k=0,maxsize=0,buf=NULL;k<2;k++,maxsize=0) {
ptr=stack->buf+undo->off;
endptr=ptr+undo->len;
if(k!=0)
buf[maxsize]=(undo->type=='A')?'D':(undo->type=='D')?'A':undo->type;
maxsize++;
if(undo->type=='A' || undo->type=='D')
snprintf(posbuf,sizeof(posbuf),"%li",undo->posorig);
else
snprintf(posbuf,sizeof(posbuf),"%li",(undo->posorig<undo->posdest)?undo->posdest-undo->len:undo->posdest);
posbuf[sizeof(posbuf)-1]='\0';
if(k!=0)
strcpy(buf+maxsize,posbuf);
maxsize+=strlen(posbuf);
if(undo->type=='M') {
snprintf(posbuf,sizeof(posbuf),",%li",(undo->posorig<undo->posdest)?undo->posorig:undo->posorig+undo->len);
posbuf[sizeof(posbuf)-1]='\0';
if(k!=0)
strcpy(buf+maxsize,posbuf);
maxsize+=strlen(posbuf);
}
while(ptr<endptr) {
sep=sep_select(ptr,endptr-ptr,&sepend);
if(sepend!=endptr) {
if(k!=0)
buf[maxsize]='+';
maxsize++;
}
if(k!=0)
buf[maxsize]=sep;
maxsize++;
if(k!=0)
memcpy(buf+maxsize,ptr,sepend-ptr);
maxsize+=sepend-ptr;
if(k!=0)
buf[maxsize]=sep;
maxsize++;
ptr=sepend;
}
if(k==0) {
/* get mem */
if((unsaved->sizebuf-unsaved->usedbuf)<maxsize) {
newsize=(unsaved->sizebuf+maxsize+UNSAVEDGROWSIZE-1)/UNSAVEDGROWSIZE;
newsize*=UNSAVEDGROWSIZE;
if((buf=realloc(unsaved->buf,newsize))==NULL)
return(-1); /* insuf. mem. */
unsaved->buf=buf;
unsaved->sizebuf=newsize;
#if 0
fprintf(stderr,"UNSAVED: UNADD realloc: %li\n",(long)newsize);
#endif
}
buf=unsaved->buf+unsaved->usedbuf;
}
}
return(0);
}
int
redata_unsaved_commit(redata_t *redata, redata_plugin_t *slot,char *filename)
{
int n,nwritten;
char unsname[PATH_MAX+1];
unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
if(redata==NULL || slot==NULL || unsaved==NULL || unsaved->unsavedfd==-1)
return(-1);
#if 0
if(unsaved->usedbuf>0)
fprintf(stderr,"UNSAVED_COMMITTED\n");
#endif
for(nwritten=0;nwritten<unsaved->usedbuf;nwritten+=n) {
if((n=write(unsaved->unsavedfd,unsaved->buf+nwritten,unsaved->usedbuf-nwritten))<0) {
close(unsaved->unsavedfd),unsaved->unsavedfd=-1;
if((unsaved_genname(filename,unsname,sizeof(unsname)))!=NULL)
unlink(unsname); /* a corrupted unsaved is of no use, delete it */
slot->active=0;
return(-1); /* error writing */
}
}
unsaved->usedbuf=0;
return(0);
}
static int
redata_unsaved_loadquestion(redata_t *redata, redata_plugin_t *slot,char *filename)
{
static char mytitle[]={"Unsaved data from a previous session detected"};
static char mybody[]={"The file you're trying to load has some unsaved data from a previous session. I can recover the data. What do you want to do?"};
static char *myopts[]={"Recover unsaved data (safest option)","Don't use old unsaved data, delete old unsaved data (if you're sure what you're doing)"};
static char mytitleshort[]={"Unsaved data found"};
static char mybodyshort[]={"Should try to recover the data?"};
static char *myoptsshort[]={"Recover","Ignore it"};
question_t *q;
if(redata==NULL || slot==NULL || filename==NULL)
return(-1); /* sanity check failed */
q=&(slot->question);
q->active=0;
if(redata_unsaved_check(redata,slot,filename)==-1)
return(0); /* no unsaved data on disk */
q->active=1;
q->title=mytitle;
q->titleshort=mytitleshort;
q->body=mybody;
q->bodyshort=mybodyshort;
q->opts=myopts;
q->optsshort=myoptsshort;
q->nopts=2;
q->defaultoption=1;
q->selectedoption=-1;
return(0);
}
static int
redata_unsaved_postload(redata_t *redata, redata_plugin_t *slot,char *filename)
{
#warning XXX TODO: add errordesc parameter, so that the user can see what happened.
int selectedoption;
if(redata==NULL || slot==NULL || filename==NULL)
return(-1); /* sanity check failed */
/* questionreply is from loadquestion, specifically the selected element in the array "opts" */
selectedoption=slot->question.selectedoption;
if(slot->question.active==0 || selectedoption==1 || selectedoption==-1) /* delete unsaved data */
return(redata_unsaved_truncload(redata, slot, filename));
else if(selectedoption==0) /* recover */
return(redata_unsaved_loadappend(redata, slot, filename));
return(-1);
}
static char *
unsaved_genname(char *filename, char *buf, int bufsize)
{
static char pre[]={UNSAVEDPREFIX};
static char post[]={UNSAVEDPOSTFIX};
return(redata_generic_genname(filename,pre,post,buf,bufsize));
}
static char *
ptr_getlong(char *ptr,char *endptr,long *data)
{
long l,s;
if(ptr==NULL || endptr==NULL || ptr>endptr)
return(NULL);
s=1;
if(ptr<endptr && *ptr=='-') {
s=-1;
ptr++;
}
for(l=0;ptr<endptr && *ptr>='0' && *ptr<='9';ptr++) {
l*=10;
l+=(*ptr-'0');
}
l*=s;
if(data!=NULL)
*data=l;
return(ptr);
}
static char *
ptr_getchar(char *ptr,char *endptr,char *data)
{
if(ptr==NULL || endptr==NULL || ptr>endptr)
return(NULL);
if(data!=NULL)
*data=*ptr;
ptr++;
return(ptr);
}
static char *
ptr_searchendchar(char *ptr, char *endptr, char endchar, char **endcharpos)
{
char *aux;
if(ptr==NULL || endptr==NULL || ptr>endptr)
return(NULL);
if((aux=memchr(ptr,endchar,endptr-ptr))==NULL)
return(NULL);
if(endcharpos!=NULL)
*endcharpos=aux;
return(aux+1);
}
static char
sep_select(char *buf, int bufsize, char **pos)
{
static char seps[]={"$%@!|&/='\"^*;:,-_"};
char *ptr,*bestptr;
int i,besti;
bestptr=buf;
if(pos!=NULL)
*pos=NULL;
for(i=0,besti=0;i<sizeof(seps);i++) {
if((ptr=memchr(buf,seps[i],bufsize))==NULL) {
if(pos!=NULL)
*pos=buf+bufsize;
return(seps[i]);
}
if(ptr>bestptr) {
besti=0;
bestptr=ptr;
}
}
if(pos!=NULL)
*pos=bestptr;
return(seps[besti]);
}