From a8769624058e999fb28c792daf315ec8a0f4b560 Mon Sep 17 00:00:00 2001 From: Arkshine Date: Sun, 17 Aug 2014 12:00:01 +0200 Subject: [PATCH] Compiler: Use in-memory buffers for reading files, and handle newlines better. Imported from https://github.com/alliedmodders/sourcemod/pull/63. --- compiler/libpc300/libpawnc.c | 157 ++++++++++++++++++++++++++++++++--- compiler/libpc300/sc1.c | 8 +- compiler/libpc300/sc2.c | 6 +- 3 files changed, 152 insertions(+), 19 deletions(-) diff --git a/compiler/libpc300/libpawnc.c b/compiler/libpc300/libpawnc.c index 9133b83f..9bd98846 100755 --- a/compiler/libpc300/libpawnc.c +++ b/compiler/libpc300/libpawnc.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include "sc.h" @@ -119,6 +120,14 @@ static char *prefix[3]={ "error", "fatal error", "warning" }; return 0; } +typedef struct src_file_s { + FILE *fp; // Set if writing. + char *buffer; // IO buffer. + char *pos; // IO position. + char *end; // End of buffer. + size_t maxlength; // Maximum length of the writable buffer. +} src_file_t; + /* pc_opensrc() * Opens a source file (or include file) for reading. The "file" does not have * to be a physical file, one might compile from memory. @@ -133,7 +142,45 @@ static char *prefix[3]={ "error", "fatal error", "warning" }; */ void *pc_opensrc(char *filename) { - return fopen(filename,"rt"); + FILE *fp = NULL; + long length; + src_file_t *src = NULL; + +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined DARWIN + struct stat fileInfo; + if (stat(filename, &fileInfo) != 0) { + return NULL; + } + + if (S_ISDIR(fileInfo.st_mode)) { + return NULL; + } +#endif + + if ((fp = fopen(filename, "rb")) == NULL) + return NULL; + if (fseek(fp, 0, SEEK_END) == -1) + goto err; + if ((length = ftell(fp)) == -1) + goto err; + if (fseek(fp, 0, SEEK_SET) == -1) + goto err; + + if ((src = (src_file_t *)calloc(1, sizeof(src_file_t))) == NULL) + goto err; + if ((src->buffer = (char *)calloc(length, sizeof(char))) == NULL) + goto err; + if (fread(src->buffer, length, 1, fp) != 1) + goto err; + + src->pos = src->buffer; + src->end = src->buffer + length; + return src; + +err: + pc_closesrc(src); + fclose(fp); + return NULL; } /* pc_createsrc() @@ -150,7 +197,23 @@ void *pc_opensrc(char *filename) */ void *pc_createsrc(char *filename) { - return fopen(filename,"wt"); + src_file_t *src = (src_file_t *)calloc(1, sizeof(src_file_t)); + if (!src) + return NULL; + if ((src->fp = fopen(filename, "wt")) == NULL) { + pc_closesrc(src); + return NULL; + } + + src->maxlength = 1024; + if ((src->buffer = (char *)calloc(1, src->maxlength)) == NULL) { + pc_closesrc(src); + return NULL; + } + + src->pos = src->buffer; + src->end = src->buffer; + return src; } /* pc_closesrc() @@ -159,8 +222,15 @@ void *pc_createsrc(char *filename) */ void pc_closesrc(void *handle) { - assert(handle!=NULL); - fclose((FILE*)handle); + src_file_t *src = (src_file_t *)handle; + if (!src) + return; + if (src->fp) { + fwrite(src->buffer, src->pos - src->buffer, 1, src->fp); + fclose(src->fp); + } + free(src->buffer); + free(src); } /* pc_resetsrc() @@ -169,8 +239,12 @@ void pc_closesrc(void *handle) */ void pc_resetsrc(void *handle,void *position) { - assert(handle!=NULL); - fsetpos((FILE*)handle,(fpos_t *)position); + src_file_t *src = (src_file_t *)handle; + ptrdiff_t pos = (ptrdiff_t)position; + + assert(!src->fp); + assert(pos >= 0 && src->buffer + pos <= src->end); + src->pos = src->buffer + pos; } /* pc_readsrc() @@ -179,7 +253,35 @@ void pc_resetsrc(void *handle,void *position) */ char *pc_readsrc(void *handle,unsigned char *target,int maxchars) { - return fgets((char*)target,maxchars,(FILE*)handle); + src_file_t *src = (src_file_t *)handle; + char *outptr = (char *)target; + char *outend = outptr + maxchars; + + assert(!src->fp); + + if (src->pos == src->end) + return NULL; + + while (outptr < outend && src->pos < src->end) { + char c = *src->pos++; + *outptr++ = c; + + if (c == '\n') + break; + if (c == '\r') { + // Handle CRLF. + if (src->pos < src->end && *src->pos == '\n') { + src->pos++; + if (outptr < outend) + *outptr++ = '\n'; + } + break; + } + } + + // Caller passes in a buffer of size >= maxchars+1. + *outptr = '\0'; + return (char *)target; } /* pc_writesrc() @@ -188,20 +290,51 @@ char *pc_readsrc(void *handle,unsigned char *target,int maxchars) */ int pc_writesrc(void *handle,unsigned char *source) { - return fputs((char*)source,(FILE*)handle) >= 0; + char *str = (char *)source; + size_t len = strlen(str); + src_file_t *src = (src_file_t *)handle; + + assert(src->fp && src->maxlength); + + if (src->pos + len > src->end) { + char *newbuf; + size_t newmax = src->maxlength; + size_t newlen = (src->pos - src->buffer) + len; + while (newmax < newlen) { + // Grow by 1.5X + newmax += newmax + newmax / 2; + if (newmax < src->maxlength) + abort(); + } + + newbuf = (char *)realloc(src->buffer, newmax); + if (!newbuf) + abort(); + src->pos = newbuf + (src->pos - src->buffer); + src->end = newbuf + newmax; + src->buffer = newbuf; + src->maxlength = newmax; + } + + strcpy(src->pos, str); + src->pos += len; + return 0; } void *pc_getpossrc(void *handle) { - static fpos_t lastpos; /* may need to have a LIFO stack of such positions */ + src_file_t *src = (src_file_t *)handle; - fgetpos((FILE*)handle,&lastpos); - return &lastpos; + assert(!src->fp); + return (void *)(ptrdiff_t)(src->pos - src->buffer); } int pc_eofsrc(void *handle) { - return feof((FILE*)handle); + src_file_t *src = (src_file_t *)handle; + + assert(!src->fp); + return src->pos == src->end; } /* should return a pointer, which is used as a "magic cookie" to all I/O diff --git a/compiler/libpc300/sc1.c b/compiler/libpc300/sc1.c index c6eff2e8..8fa709fe 100755 --- a/compiler/libpc300/sc1.c +++ b/compiler/libpc300/sc1.c @@ -508,7 +508,7 @@ int pc_compile(int argc, char *argv[]) if (get_sourcefile(1)!=NULL) { /* there are at least two or more source files */ char *tname,*sname; - FILE *ftmp,*fsrc; + void *ftmp,*fsrc; int fidx; #if defined __WIN32__ || defined _WIN32 tname=_tempnam(NULL,"pawn"); @@ -524,10 +524,10 @@ int pc_compile(int argc, char *argv[]) close(mkstemp(buffer)); tname=buffer; #endif - ftmp=(FILE*)pc_createsrc(tname); + ftmp=(void*)pc_createsrc(tname); for (fidx=0; (sname=get_sourcefile(fidx))!=NULL; fidx++) { unsigned char tstring[128]; - fsrc=(FILE*)pc_opensrc(sname); + fsrc=(void*)pc_opensrc(sname); if (fsrc==NULL) error(100,sname); pc_writesrc(ftmp,(unsigned char*)"#file "); @@ -545,7 +545,7 @@ int pc_compile(int argc, char *argv[]) } else { strcpy(inpfname,get_sourcefile(0)); } /* if */ - inpf_org=(FILE*)pc_opensrc(inpfname); + inpf_org=(void*)pc_opensrc(inpfname); if (inpf_org==NULL) error(100,inpfname); freading=TRUE; diff --git a/compiler/libpc300/sc2.c b/compiler/libpc300/sc2.c index e3bde15b..7e715645 100755 --- a/compiler/libpc300/sc2.c +++ b/compiler/libpc300/sc2.c @@ -120,18 +120,18 @@ SC_FUNC void clearstk(void) SC_FUNC int plungequalifiedfile(char *name) { static char *extensions[] = { ".inc", ".p", ".pawn" }; - FILE *fp; + void *fp; char *ext; int ext_idx; ext_idx=0; do { - fp=(FILE*)pc_opensrc(name); + fp=(void*)pc_opensrc(name); ext=strchr(name,'\0'); /* save position */ if (fp==NULL) { /* try to append an extension */ strcpy(ext,extensions[ext_idx]); - fp=(FILE*)pc_opensrc(name); + fp=(void*)pc_opensrc(name); if (fp==NULL) *ext='\0'; /* on failure, restore filename */ } /* if */