/* ---------------------------------------------------------------- */ /* BEGIN COPYRIGHT AND LICENSE NOTICE */ /* ---------------------------------------------------------------- */ /* ** Copyright (c) 2007 by Richard Harter. ** ** Permission is hereby granted, free of charge, to any person ** obtaining a copy of this software and associated documentation ** files (the "Software"), to deal in the Software without ** restriction, including without limitation the rights to use, ** copy, modify, merge, publish, distribute, sublicense, and/or ** sell copies of the Software, and to permit persons to whom the ** Software is furnished to do so, subject to the following ** conditions: ** ** The above copyright notices and this permission notice shall be ** included in all copies or substantial portions of the ** Software. ** ** Derivative works shall include a notice that the software is a ** modified version of the copyrighted software. ** ** There is no guarantee that this software is useful for anything ** or that it is any way correct or of value. The author disclaims ** any responsibility for the consequences of using this software. ** */ /* ----------------------------------------------------------------- */ /* END COPYRIGHT AND LICENSE NOTICE */ /* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */ /* */ /* REVISION HISTORY */ /* */ /* 21 NOV 2007: Initial release by RH */ /* 24 May 2008: Fixed bug in check_flags */ /* */ /* ----------------------------------------------------------------- */ #include "getfline.h" #include #define LINESIZE 64 enum resize_results {failed, resized, longline}; struct private { FILE * fptr; unsigned int flags; size_t maxlen; size_t bufsize; char * buffer; enum gfl_flags fl_clean; enum gfl_flags fl_eofok; enum gfl_flags fl_cut; enum gfl_flags fl_trunc; enum gfl_flags fl_omit; enum gfl_flags fl_exit; enum gfl_flags fl_log; enum gfl_flags fl_nomax; }; static char * close_buffer(struct gfl_cb *cb, char * buffer, size_t count); static int check_flags(struct gfl_cb *cb); static char * cleanup(struct gfl_cb *cb, enum gfl_errors err); static enum resize_results bigger_buffer(struct gfl_cb * cb); char * getfline(FILE *fptr, struct gfl_cb * cb) { struct private * pvt; int intcc; size_t count = 0; size_t countlim = 0; size_t bufsize = 0; size_t oldsize = 0; char * buffer; char * oldbuf; /* Part A - no control block */ if (!cb) { if (!fptr) return NULL; buffer = malloc(LINESIZE); if (!buffer) return NULL; bufsize = LINESIZE; for (count=0;;) { for (;count < bufsize; count++) { intcc = fgetc(fptr); if (intcc == '\n') { buffer[count] = '\0'; return buffer; } else if (intcc == EOF) { if (count == 0) { free(buffer); return NULL; } else { buffer[count] = '\0'; return buffer; } } buffer[count] = intcc; } oldsize = bufsize; bufsize += bufsize/2; if (bufsize < oldsize) { free(buffer); return NULL; } oldbuf = buffer; buffer = realloc(buffer,bufsize); if (!buffer) { free (oldbuf); return NULL; } } } /* Part B - control block - check initialization */ if (!fptr) return cleanup(cb,gfl_stream); if (!cb->pvt) { if (check_flags(cb)) return cleanup(cb,gfl_flags); if (cb->maxlen == 0) { if (!(cb->flags & gfl_nomax)) { return cleanup(cb,gfl_maxlen); } } pvt = malloc(sizeof(struct private)); if (!pvt) return cleanup(cb,gfl_storage); cb->pvt = pvt; pvt->fptr = fptr; pvt->flags = cb->flags; pvt->buffer = 0; pvt->bufsize = 0; pvt->maxlen = cb->maxlen; pvt->fl_clean = cb->flags & gfl_clean; pvt->fl_eofok = cb->flags & gfl_eofok; pvt->fl_cut = cb->flags & gfl_cut ; pvt->fl_trunc = cb->flags & gfl_trunc; pvt->fl_omit = cb->flags & gfl_omit ; pvt->fl_exit = cb->flags & gfl_exit ; pvt->fl_log = cb->flags & gfl_log ; pvt->fl_nomax = cb->flags & gfl_nomax; } else { pvt = cb->pvt; if (fptr != pvt->fptr) return cleanup(cb,gfl_stream); if (pvt->flags != cb->flags) { if (check_flags(cb)) return cleanup(cb,gfl_flags); } else pvt->flags = cb->flags; if ((cb->maxlen == 0) && (!pvt->fl_nomax)) { return cleanup(cb,gfl_nomax); } } buffer = pvt->buffer; if (!buffer) { if (pvt->fl_nomax) bufsize = LINESIZE; else bufsize = (cb->maxlenmaxlen: LINESIZE); buffer = malloc(bufsize+2); if (!buffer) cleanup(cb,gfl_storage); pvt->buffer = buffer; pvt->bufsize = bufsize; } else bufsize = pvt->bufsize; /* Initialization complete - main read loop */ for (count=0;;) { if (pvt->fl_nomax) countlim = bufsize; else countlim = (bufsize >= pvt->maxlen)? pvt->maxlen : bufsize; for (;count < countlim; count++) { intcc = fgetc(fptr); if (intcc == '\n') return close_buffer(cb,buffer,count); else if (intcc == EOF ) { if (count == 0) { free(buffer); free(pvt); return NULL; } if (!pvt->fl_eofok ) return cleanup(cb,gfl_badeof); else return close_buffer(cb,buffer,count); } buffer[count] = intcc; } /* Need more space, get more hair */ switch (bigger_buffer(cb)) { case resized: { buffer = pvt->buffer; bufsize = pvt->bufsize; break; } case failed: { return cleanup(cb,gfl_storage); } case longline: { if (pvt->fl_trunc) { for (;;) { intcc = fgetc(fptr); if ((intcc == EOF) || (intcc == '\n')) { return close_buffer(cb,buffer,count); } } } else if (pvt->fl_cut) { return close_buffer(cb,buffer,count); } else if (pvt->fl_omit) { for (;;) { intcc = fgetc(fptr); if ((intcc == EOF) || (intcc == '\n')) break; } count = 0; break; } else { return cleanup(cb,gfl_long); } } } /* End of switch */ } /* End of read loop */ } /* Private function close_buffer ** ** This function terminates the current buffer, enters ** the string length, takes care of clean mode if relevant, ** and then returns the buffer; getfline is expected to ** return the what close_buffer returns. */ static char * close_buffer(struct gfl_cb *cb, char * buffer, size_t count) { struct private * pvt; pvt = cb->pvt; buffer[count] = '\0'; cb->length = count; if (pvt->fl_clean) { pvt->buffer = 0; pvt->bufsize = 0; } return buffer; } /* Private function check_flags ** ** This function checks the flags word in the control block ** to make sure that it is legitimate. It first checks that ** there aren't any stray bits. It then checks that at most ** one of the three flags, gfl_cut, gfl_trunc, and gfl_omit ** are set. A return of false (0) indicates no errors; a ** return of true (1) indicates that there is an error. */ static int check_flags(struct gfl_cb *cb) { unsigned int flags; flags = cb->flags; if (flags & (~0xff)) return 1; switch (flags & (gfl_cut | gfl_trunc | gfl_omit)) { case 0: case gfl_cut: case gfl_trunc: case gfl_omit: return 0; default: return 1; } printf("past switch\n"); return 0; } /* Private function cleanup ** ** This function handles the clean up if an error ist detected ** while reading a file. It will write a message to the error ** if the gfl_log flag is set, and will call exit if the gfl_exit ** flag is set. It also clears all storage allocated by getfline. ** Finally it returns a NULL pointer that is expected to be ** returned by getfline. */ static char * cleanup(struct gfl_cb * cb, enum gfl_errors err) { struct private * pvt = 0; int write_log = 0; int call_exit = 0; cb->errno = err; if ((pvt = cb->pvt)) { write_log = pvt->fl_log; call_exit = pvt->fl_exit; free(pvt->buffer); free(pvt); } else { write_log = cb->flags & gfl_log; call_exit = cb->flags & gfl_exit; } if (write_log) fprintf(stderr,"Error %ud in getfline\n",err); if (call_exit) exit(EXIT_FAILURE); return NULL; } /* Private function bigger_buffer ** ** Function bigger_buffer increases the buffer size. ** It uses the "size += size/2" formula and does the ** following checks: If the nomax flag is not set ** then we first check to see if bufsize is .ge. maxlen. ** If it is, then we report the line as being long. ** If it is less than maxlen, then we use the formula ** to increase bufsize. If bufsize decreases (indicating ** wraparound) or bufsize .ge. maxlen we set bufsize to ** maxlen. ** ** If the nomax flag is set we use the formula. If bufsize ** decreases we report failed. The main routine is supposed ** to do the cleanup. ** ** Once we have a valid new size we attempt to resize it. If ** the resizing fails we report failed; otherwise we report ** resized. */ static enum resize_results bigger_buffer(struct gfl_cb * cb) { struct private * pvt; size_t oldsize; char * newbuf; pvt = cb->pvt; oldsize = pvt->bufsize; if (pvt->fl_nomax) { pvt->bufsize += pvt->bufsize/2; if (pvt->bufsize <= oldsize) return failed; } else { if (oldsize >= pvt->maxlen) return longline; pvt->bufsize += pvt->bufsize/2; if ((pvt->bufsize <= oldsize) ||(pvt->bufsize >= pvt->maxlen)) { pvt->bufsize = pvt->maxlen; } } newbuf = realloc(pvt->buffer,(pvt->bufsize)+2); if (!newbuf) return failed; else { pvt->buffer = newbuf; return resized; } }