4 changed files with 772 additions and 4 deletions
-
698src/xspice/cmpp/file_buffer.c
-
58src/xspice/cmpp/file_buffer.h
-
10src/xspice/icm/dlmain.h
-
10visualc/xspice/cmpp/cmpp.vcxproj
@ -0,0 +1,698 @@ |
|||||
|
#include <errno.h> |
||||
|
#include <limits.h> |
||||
|
#include <stdbool.h> |
||||
|
#include <stddef.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#include "file_buffer.h" |
||||
|
#include "cmpp.h" |
||||
|
|
||||
|
/* Default buffer size */ |
||||
|
#define N_BYTE_FILEBUF_INIT_DFLT 16384 |
||||
|
|
||||
|
|
||||
|
static int fb_fill(FILEBUF *p_fb); |
||||
|
static int fbget_quoted_unescaped_string(FILEBUF *p_fb, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj); |
||||
|
static size_t fb_make_space_at_end(FILEBUF *p_fb); |
||||
|
static int fbget_quoted_string(FILEBUF *p_fb, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj); |
||||
|
static int fbget_quoted_escaped_string(FILEBUF *p_fb, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj); |
||||
|
static int fbget_unquoted_string(FILEBUF *p_fb, |
||||
|
unsigned int n_type_wanted, FBTYPE *p_type_wanted, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj); |
||||
|
static int fb_return_obj(FILEBUF *p_fb, |
||||
|
unsigned int n_type_wanted, FBTYPE *p_type_wanted, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj); |
||||
|
static int fb_return_string(FILEBUF *p_fb, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj); |
||||
|
static int fb_skip_to_eol(FILEBUF *p_fb); |
||||
|
static int fb_skip_whitespace(FILEBUF *p_fb); |
||||
|
|
||||
|
|
||||
|
/* This function initializes a file-buffer read access to the named file. |
||||
|
* |
||||
|
* Parameters |
||||
|
* filename: Name of file to be opened for reading |
||||
|
* n_byte_buf_init: Intial buffer size. May be 0 for default size |
||||
|
* |
||||
|
* Return values |
||||
|
* NULL: Error occurred. The value of errno will provide more details |
||||
|
* Otherwise an initialized structure |
||||
|
*/ |
||||
|
FILEBUF *fbopen(const char *filename, size_t n_byte_buf_init) |
||||
|
{ |
||||
|
int xrc = 0; |
||||
|
FILEBUF *p_fb = (FILEBUF *) NULL; |
||||
|
|
||||
|
/* Set default buffer size if requested */ |
||||
|
if (n_byte_buf_init == 0) { |
||||
|
n_byte_buf_init = N_BYTE_FILEBUF_INIT_DFLT; |
||||
|
} |
||||
|
|
||||
|
/* Allocate structure to return */ |
||||
|
if ((p_fb = (FILEBUF *) malloc(sizeof *p_fb)) == (FILEBUF *) NULL) { |
||||
|
xrc = -1; |
||||
|
goto EXITPOINT; |
||||
|
} |
||||
|
|
||||
|
p_fb->is_eof = false; |
||||
|
p_fb->f_skip_to_eol = false; |
||||
|
|
||||
|
/* Init resources for error recovery */ |
||||
|
p_fb->fp = (FILE *) NULL; |
||||
|
p_fb->p_buf = (char *) NULL; |
||||
|
|
||||
|
/* Allocate data buffer */ |
||||
|
if ((p_fb->p_buf = (char *) malloc(n_byte_buf_init)) == (char *) NULL) { |
||||
|
xrc = -1; |
||||
|
goto EXITPOINT; |
||||
|
} |
||||
|
p_fb->n_byte_buf_alloc = n_byte_buf_init; |
||||
|
p_fb->p_data_end = p_fb->p_data_cur = p_fb->p_buf; |
||||
|
p_fb->p_buf_end = p_fb->p_buf + n_byte_buf_init; |
||||
|
|
||||
|
/* p_fb->p_obj_undefined since no object yet */ |
||||
|
|
||||
|
/* Open file. It is opened in binary mode because the scanning will |
||||
|
* handle all EOL chars, so any translations by the OS are almost |
||||
|
* pure overhead. Also, not converting ensures that if fread returns |
||||
|
* fewer than the requested number of chars, that read was to the |
||||
|
* end of the file (if not an error). Otherwise an additional read |
||||
|
* getting a size of 0 would be required. */ |
||||
|
if ((p_fb->fp = fopen(filename, "rb")) == (FILE *) NULL) { |
||||
|
xrc = -1; |
||||
|
goto EXITPOINT; |
||||
|
} |
||||
|
|
||||
|
EXITPOINT: |
||||
|
/* Free resources on error */ |
||||
|
if (xrc != 0) { |
||||
|
if (p_fb != (FILEBUF *) NULL) { |
||||
|
const int errno_save = errno; /* save errno in case fbclose() |
||||
|
* changes it */ |
||||
|
(void) fbclose(p_fb); |
||||
|
errno = errno_save; |
||||
|
p_fb = (FILEBUF *) NULL; |
||||
|
} |
||||
|
} /* end of case of error */ |
||||
|
|
||||
|
return p_fb; |
||||
|
} /* end of function fbopen */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* This function frees resources used by a FILEBUF. |
||||
|
* |
||||
|
* Parameter |
||||
|
* p_fb: The address of the FILEBUF to free. This argument may be NULL. |
||||
|
* |
||||
|
* Return values |
||||
|
* 0: OK |
||||
|
* EOF: Error closing file. Details can be found using errno. |
||||
|
*/ |
||||
|
int fbclose(FILEBUF *p_fb) |
||||
|
{ |
||||
|
if (p_fb == (FILEBUF *) NULL) { |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int xrc = 0; |
||||
|
|
||||
|
{ |
||||
|
void *p; |
||||
|
if ((p = p_fb->p_buf) != NULL) { |
||||
|
free(p); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
{ |
||||
|
FILE *fp; |
||||
|
if ((fp = p_fb->fp) != (FILE *) NULL) { |
||||
|
xrc = fclose(fp); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
free(p_fb); |
||||
|
return xrc; |
||||
|
} /* end of function fbclose */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* This function gets the next object converting it to the most desired |
||||
|
* type. |
||||
|
* |
||||
|
* Parameters |
||||
|
* p_fb: FILEBUF pointer initialized using fbopen() |
||||
|
* n_type_wanted: number of desired type conversions for data from highest |
||||
|
* priority to lowest. |
||||
|
* p_type_wanted: Desired type conversions for data from highest priority |
||||
|
* to lowest. |
||||
|
* p_type_found: Address to receive the type of the data obtained |
||||
|
* p_fbobj: Address of an FBOBJ structure to receive the data |
||||
|
* |
||||
|
* Return codes |
||||
|
* +1: EOF reached |
||||
|
* 0: Normal return |
||||
|
* -1: Error. Use errno for further details. |
||||
|
* |
||||
|
* Remarks |
||||
|
* Type BUF_TYPE_STRING is always implicitly added to the list of wanted |
||||
|
* types as the final choice, which any data will satisfy |
||||
|
* |
||||
|
* A string may be double-quoted. In this case the quotes are not supplied |
||||
|
* to the caller as part of the data. Double-quoting ensures that a string |
||||
|
* will not be converted to any other type. Within double quotes, a double |
||||
|
* qoute and a backslash are escaped by a backslash, and a final unescaped |
||||
|
* double quote is impilcitly added if EOF is reached when scanning for a |
||||
|
* closing quote. |
||||
|
* |
||||
|
* A "*" or a "#" not within a quoted expression begins a comment that |
||||
|
* extends to the end of the line. |
||||
|
* |
||||
|
* When called p_fb has data from the last get or it is the first call. |
||||
|
* |
||||
|
* Return Codes |
||||
|
* +1: EOF |
||||
|
* 0: Normal |
||||
|
* -1: Error |
||||
|
*/ |
||||
|
int fbget(FILEBUF *p_fb, |
||||
|
unsigned int n_type_wanted, FBTYPE *p_type_wanted, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj) |
||||
|
{ |
||||
|
/* Test for existing EOF */ |
||||
|
if (p_fb->is_eof && p_fb->p_data_cur == p_fb->p_data_end) { /* no data */ |
||||
|
return +1; |
||||
|
} |
||||
|
|
||||
|
/* Init to no object */ |
||||
|
p_fb->p_obj_start = (char *) NULL; |
||||
|
|
||||
|
/* Skip the comment if the initiating character was processed during |
||||
|
* the last call to fbget */ |
||||
|
if (p_fb->f_skip_to_eol) { |
||||
|
const int rc = fb_skip_to_eol(p_fb); |
||||
|
if (rc != 0) { /* EOF or error */ |
||||
|
return rc; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
{ |
||||
|
const int rc = fb_skip_whitespace(p_fb); |
||||
|
if (rc != 0) { /* EOF or error */ |
||||
|
return rc; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Current char exists and starts the item */ |
||||
|
if (*p_fb->p_data_cur == '"') { /* quoted string */ |
||||
|
return fbget_quoted_string(p_fb, p_type_found, p_fbobj); |
||||
|
} |
||||
|
|
||||
|
/* Else unquoted string */ |
||||
|
return fbget_unquoted_string(p_fb, n_type_wanted, p_type_wanted, |
||||
|
p_type_found, p_fbobj); |
||||
|
} /* end of function fbget */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Get a quoted string given at a quote. On entry p_fb->p_data_cur points |
||||
|
* to the quote starting the quoted string. On return it points to the first |
||||
|
* character after the current item or equals p_fb->p_data_end if the |
||||
|
* current item extens to the end of the current data string. */ |
||||
|
static int fbget_quoted_string(FILEBUF *p_fb, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj) |
||||
|
{ |
||||
|
/* Advance past the opening quote to the true start of the string */ |
||||
|
if (++p_fb->p_data_cur == p_fb->p_data_end) { |
||||
|
/* The leading quote ended the current data */ |
||||
|
const int rc = fb_fill(p_fb); /* Try to refill buffer */ |
||||
|
if (rc != 0) { /* EOF or error */ |
||||
|
if (rc < 0) { /* error */ |
||||
|
return -1; |
||||
|
} |
||||
|
/* Else EOF. This item is an empty string that ended without the |
||||
|
* closing quote, so add an implicit closing quote, i.e., end |
||||
|
* the string to form "". |
||||
|
* |
||||
|
* Since the object was started at the beginning of the buffer |
||||
|
* and the buffer has at leat 1 byte a NULL to create the |
||||
|
* string "" can be written here */ |
||||
|
*(p_fb->p_obj_end = p_fb->p_obj_start = p_fb->p_buf) = '\0'; |
||||
|
return fb_return_string(p_fb, p_type_found, p_fbobj); |
||||
|
} |
||||
|
/* Else data is now available at p_fb->p_data_cur */ |
||||
|
} /* end of case that at end of data from file */ |
||||
|
|
||||
|
/* Save the start of the string as the current position */ |
||||
|
p_fb->p_obj_start = p_fb->p_data_cur; |
||||
|
|
||||
|
/* Continue processing as an unescaped string, unless the contrary |
||||
|
* is found to be true */ |
||||
|
return fbget_quoted_unescaped_string(p_fb, p_type_found, p_fbobj); |
||||
|
} /* end of function fbget_quoted_string */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Get a quoted string with no escape. The start has already been set on |
||||
|
* entry. If an escape is found, processing continues as an escaped string */ |
||||
|
int fbget_quoted_unescaped_string(FILEBUF *p_fb, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj) |
||||
|
{ |
||||
|
/* Step through characters until end or escape */ |
||||
|
char *p_data_cur = p_fb->p_data_cur; |
||||
|
char *p_data_end = p_fb->p_data_end; |
||||
|
for ( ; ; ) { /* continue until done */ |
||||
|
for ( ; p_data_cur != p_data_end; ++p_data_cur) { /* current data */ |
||||
|
const char ch_cur = *p_data_cur; |
||||
|
if (ch_cur == '"') { /* Closing quote, so done */ |
||||
|
*(p_fb->p_obj_end = p_data_cur) = '\0'; |
||||
|
p_fb->p_data_cur = p_data_cur + 1; |
||||
|
return fb_return_string(p_fb, p_type_found, p_fbobj); |
||||
|
} |
||||
|
if (ch_cur == '\\') { /* Escape */ |
||||
|
/* After an escape, data must be moved to fill in the gap |
||||
|
* left by the escape character */ |
||||
|
p_fb->p_data_cur = p_data_cur; /* Reprocess the escape */ |
||||
|
return fbget_quoted_escaped_string(p_fb, |
||||
|
p_type_found, p_fbobj); |
||||
|
} |
||||
|
/* Else the character is part of the quoted string */ |
||||
|
} /* end of loop over current text */ |
||||
|
|
||||
|
p_fb->p_data_cur = p_data_cur; /* update current position */ |
||||
|
const int rc = fb_fill(p_fb); /* Try to refill buffer */ |
||||
|
if (rc != 0) { /* EOF or error */ |
||||
|
if (rc < 0) { /* error */ |
||||
|
return -1; |
||||
|
} |
||||
|
/* Else EOF. Ended without closing quote, so add an implicit |
||||
|
* closing quote, i.e., end the string. Since fb_fill() |
||||
|
* did not return -1, there is at least 1 byte at the end of |
||||
|
* the buffer where the read would have gone. */ |
||||
|
*(p_fb->p_obj_end = p_fb->p_data_cur) = '\0'; |
||||
|
return fb_return_string(p_fb, p_type_found, p_fbobj); |
||||
|
} |
||||
|
p_data_cur = p_fb->p_data_cur; /* Update after fill */ |
||||
|
p_data_end = p_fb->p_data_end; |
||||
|
} /* end of loop processing until done or escape */ |
||||
|
} /* end of function fbget_quoted_unescaped_string */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Get a quoted string with an escape. The start has already been set on |
||||
|
* entry */ |
||||
|
static int fbget_quoted_escaped_string(FILEBUF *p_fb, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj) |
||||
|
{ |
||||
|
/* Step through characters until end */ |
||||
|
char *p_data_src = p_fb->p_data_cur; /* at current char */ |
||||
|
char *p_data_dst = p_data_src; /* at current char */ |
||||
|
char *p_data_end = p_fb->p_data_end; |
||||
|
bool f_escape_in_progress = false; |
||||
|
for ( ; ; ) { /* continue until done */ |
||||
|
for ( ; p_data_src != p_data_end; ++p_data_src) { /* current data */ |
||||
|
const char ch_cur = *p_data_src; |
||||
|
if (f_escape_in_progress) { /* Always copy the char */ |
||||
|
f_escape_in_progress = false; |
||||
|
*p_data_dst++ = ch_cur; |
||||
|
} |
||||
|
else { /* Not an escaped character */ |
||||
|
if (ch_cur == '"') { /* Closing quote, so done */ |
||||
|
p_fb->p_data_cur = p_data_src + 1; |
||||
|
*(p_fb->p_obj_end = p_data_dst) = '\0'; |
||||
|
return fb_return_string(p_fb, p_type_found, p_fbobj); |
||||
|
} |
||||
|
if (ch_cur == '\\') { /* Escape */ |
||||
|
f_escape_in_progress = true; |
||||
|
/* Do not copy the escape or advancd p_data_dst */ |
||||
|
} |
||||
|
else { /* ordinary character */ |
||||
|
*p_data_dst++ = ch_cur; |
||||
|
} |
||||
|
} /* end of case of not an escaped character */ |
||||
|
/* Else the character is part of the quoted string */ |
||||
|
} /* end of loop over current text */ |
||||
|
|
||||
|
/* Indicate that there is no more unprocessed data */ |
||||
|
p_fb->p_data_end = p_fb->p_data_cur = p_data_dst; |
||||
|
|
||||
|
/* If no pending escape, can switch back to unescaped version and |
||||
|
* avoid the moves */ |
||||
|
if (!f_escape_in_progress) { |
||||
|
return fbget_quoted_unescaped_string(p_fb, p_type_found, |
||||
|
p_fbobj); |
||||
|
} |
||||
|
|
||||
|
/* Else escape must be processed, so continue with escaped version */ |
||||
|
const int rc = fb_fill(p_fb); /* Try to refill buffer */ |
||||
|
if (rc != 0) { /* EOF or error */ |
||||
|
if (rc < 0) { /* error */ |
||||
|
return -1; |
||||
|
} |
||||
|
/* Else EOF. Ended without closing quote, so add an implicit |
||||
|
* closing quote, i.e., end the string. Since fb_fill() |
||||
|
* did not return -1, there is at least 1 byte at the end of |
||||
|
* the buffer where the read would have gone. */ |
||||
|
*(p_fb->p_obj_end = p_fb->p_data_cur) = '\0'; |
||||
|
return fb_return_string(p_fb, p_type_found, p_fbobj); |
||||
|
} |
||||
|
p_data_dst = p_data_src = p_fb->p_data_cur; /* Update after fill */ |
||||
|
p_data_end = p_fb->p_data_end; |
||||
|
} /* end of loop processing until done or escape */ |
||||
|
} /* end of function fbget_quoted_escaped_string */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Get an unquoted string starting at the current position */ |
||||
|
static int fbget_unquoted_string(FILEBUF *p_fb, |
||||
|
unsigned int n_type_wanted, FBTYPE *p_type_wanted, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj) |
||||
|
{ |
||||
|
/* Save the start of the string as the current position */ |
||||
|
p_fb->p_obj_start = p_fb->p_data_cur; |
||||
|
|
||||
|
static const signed char p_map[1 << CHAR_BIT] = { |
||||
|
[(unsigned char ) ' '] = (signed char) +1, |
||||
|
[(unsigned char ) '\t'] = (signed char) +1, |
||||
|
[(unsigned char ) '\n'] = (signed char) +1, |
||||
|
[(unsigned char ) '\r'] = (signed char) +1, |
||||
|
[(unsigned char ) '\v'] = (signed char) +1, |
||||
|
[(unsigned char ) '\f'] = (signed char) +1, |
||||
|
[(unsigned char ) '*'] = (signed char) -1, |
||||
|
[(unsigned char ) '#'] = (signed char) -1 |
||||
|
}; |
||||
|
/* Step through characters until whitespace or comment */ |
||||
|
char *p_data_cur = p_fb->p_data_cur; |
||||
|
char *p_data_end = p_fb->p_data_end; |
||||
|
for ( ; ; ) { /* continue until done */ |
||||
|
for ( ; p_data_cur != p_data_end; ++p_data_cur) { /* current data */ |
||||
|
const char ch_cur = *p_data_cur; |
||||
|
const signed char map_cur = p_map[(unsigned char) ch_cur]; |
||||
|
if (map_cur != 0) { /* ws or comment start, so done */ |
||||
|
*(p_fb->p_obj_end = p_data_cur) = '\0'; |
||||
|
p_fb->p_data_cur = p_data_cur + 1; /* 1st char past string */ |
||||
|
p_fb->f_skip_to_eol = map_cur < 0; |
||||
|
return fb_return_obj(p_fb, n_type_wanted, p_type_wanted, |
||||
|
p_type_found, p_fbobj); |
||||
|
} |
||||
|
/* Else more of the string */ |
||||
|
} /* end of loop over current text */ |
||||
|
|
||||
|
p_fb->p_data_cur = p_data_cur; /* update current position */ |
||||
|
const int rc = fb_fill(p_fb); /* Try to refill buffer */ |
||||
|
if (rc != 0) { /* EOF or error */ |
||||
|
if (rc < 0) { /* error */ |
||||
|
return -1; |
||||
|
} |
||||
|
/* Else EOF. Ended without closing quote, so add an implicit |
||||
|
* closing quote, i.e., end the string. Since fb_fill() |
||||
|
* did not return -1, there is at least 1 byte at the end of |
||||
|
* the buffer where the read would have gone. */ |
||||
|
*(p_fb->p_obj_end = p_fb->p_data_cur) = '\0'; |
||||
|
return fb_return_obj(p_fb, n_type_wanted, p_type_wanted, |
||||
|
p_type_found, p_fbobj); |
||||
|
} |
||||
|
p_data_cur = p_fb->p_data_cur; /* Update after fill */ |
||||
|
p_data_end = p_fb->p_data_end; |
||||
|
} /* end of loop processing until done or escape */ |
||||
|
} /* end of function fbget_unquoted_string */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* This function fills the buffer. First it moves all data in the interval |
||||
|
* [start current object, current position) to the beginning of the buffer. |
||||
|
* |
||||
|
* If there is no space left at the end of the buffer after the move (so |
||||
|
* that the move did not occur and data extends to the end), the buffer is |
||||
|
* doubled in size. In either case, the end of the buffer is filled with |
||||
|
* data read from the file. */ |
||||
|
static int fb_fill(FILEBUF *p_fb) |
||||
|
{ |
||||
|
/* Exit if EOF already */ |
||||
|
if (p_fb->is_eof) { |
||||
|
return +1; |
||||
|
} |
||||
|
|
||||
|
/* Move the data in use to the front of the buffer if not already and |
||||
|
* enlarge the buffer if still no space. Returned value is bytes |
||||
|
* available at the end of the buffer at p_data_end */ |
||||
|
const size_t n_byte_read = fb_make_space_at_end(p_fb); |
||||
|
if (n_byte_read == 0) { |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
const size_t n_got = fread(p_fb->p_data_end, 1, n_byte_read, p_fb->fp); |
||||
|
if (n_got < n_byte_read) { /* EOF or error */ |
||||
|
if (ferror(p_fb->fp)) { |
||||
|
return -1; |
||||
|
} |
||||
|
/* Else mark as EOF for subsequent calls */ |
||||
|
p_fb->is_eof = true; |
||||
|
|
||||
|
if (n_got == 0) { /* Nothing to return for this call */ |
||||
|
return +1; |
||||
|
} |
||||
|
/* Else partial buffer to return */ |
||||
|
} |
||||
|
|
||||
|
/* Extend the end of the data by the bytes obtained */ |
||||
|
p_fb->p_data_end += n_got; |
||||
|
return 0; |
||||
|
} /* end of function fb_fill */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Make space at the end of the buffer by moving data of current object |
||||
|
* to the front of the buffer and enlarging if there is still no room |
||||
|
* |
||||
|
* Return value: Number of spaces that are free at the end of p_buf on |
||||
|
* return. If 0, more space could not be obtained. |
||||
|
*/ |
||||
|
static size_t fb_make_space_at_end(FILEBUF *p_fb) |
||||
|
{ |
||||
|
const char * const p_obj_start = p_fb->p_obj_start; |
||||
|
const char * const p_src = p_obj_start == (char *) NULL ? |
||||
|
p_fb->p_data_cur : p_obj_start; |
||||
|
char * const p_dst = p_fb->p_buf; |
||||
|
|
||||
|
/* Shift data in use to the front of the buffer if not already */ |
||||
|
if (p_dst != p_src) { /* object is not at start of buffer */ |
||||
|
const size_t n = p_fb->p_data_end - p_src; |
||||
|
if (n > 0) { /* Will be 0 if skipping whitespace and comments */ |
||||
|
(void) memmove(p_dst, p_src, n); |
||||
|
} |
||||
|
|
||||
|
/* Adjust pointers after the move */ |
||||
|
ptrdiff_t delta = p_src - p_dst; |
||||
|
p_fb->p_data_cur -= delta; |
||||
|
p_fb->p_data_end -= delta; |
||||
|
|
||||
|
if (p_obj_start != (char *) NULL) { |
||||
|
p_fb->p_obj_start -= delta; |
||||
|
} |
||||
|
/* never called when p_obj_end is valid */ |
||||
|
} |
||||
|
else { /* already at front */ |
||||
|
if (p_fb->p_buf_end - p_fb->p_data_end == 0) { |
||||
|
const size_t n_alloc_orig = p_fb->n_byte_buf_alloc; |
||||
|
|
||||
|
/* For debugging, this added size can be made very small to |
||||
|
* force many reallocs and to have strings end at "hard" |
||||
|
* locations such as right before where a terminating null |
||||
|
* should be added to a string */ |
||||
|
//const size_t n_added = 1; |
||||
|
const size_t n_added = n_alloc_orig; |
||||
|
|
||||
|
const size_t n_byte_buf_alloc_new = n_alloc_orig + n_added; |
||||
|
void * const p = realloc(p_fb->p_buf, n_byte_buf_alloc_new); |
||||
|
if (p == NULL) { |
||||
|
return 0; |
||||
|
} |
||||
|
/* Else allocation OK, so update buffer and internal pointers */ |
||||
|
ptrdiff_t delta = (char *) p - p_fb->p_buf; |
||||
|
p_fb->p_buf = (char *) p; |
||||
|
p_fb->p_buf_end = (char *) p + n_byte_buf_alloc_new; |
||||
|
p_fb->n_byte_buf_alloc = n_byte_buf_alloc_new; |
||||
|
p_fb->p_data_cur += delta; |
||||
|
p_fb->p_data_end += delta; |
||||
|
|
||||
|
if (p_obj_start != (char *) NULL) { |
||||
|
p_fb->p_obj_start += delta; |
||||
|
} |
||||
|
/* never called when p_obj_end is valid */ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return p_fb->p_buf_end - p_fb->p_data_end; |
||||
|
} /* end of function fb_make_space_at_end */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Skip whitespace, including comments starting at the current position */ |
||||
|
static int fb_skip_whitespace(FILEBUF *p_fb) |
||||
|
{ |
||||
|
static const signed char p_map[1 << CHAR_BIT] = { |
||||
|
[(unsigned char ) ' '] = (signed char) +1, |
||||
|
[(unsigned char ) '\t'] = (signed char) +1, |
||||
|
[(unsigned char ) '\n'] = (signed char) +1, |
||||
|
[(unsigned char ) '\r'] = (signed char) +1, |
||||
|
[(unsigned char ) '\v'] = (signed char) +1, |
||||
|
[(unsigned char ) '\f'] = (signed char) +1, |
||||
|
[(unsigned char ) '*'] = (signed char) -1, |
||||
|
[(unsigned char ) '#'] = (signed char) -1 |
||||
|
}; |
||||
|
/* Step through characters until not whitespace (including comments) */ |
||||
|
char *p_data_cur = p_fb->p_data_cur; |
||||
|
char *p_data_end = p_fb->p_data_end; |
||||
|
for ( ; ; ) { /* continue until done */ |
||||
|
for ( ; p_data_cur != p_data_end; ++p_data_cur) { /* current data */ |
||||
|
const char ch_cur = *p_data_cur; |
||||
|
const signed char map_cur = p_map[(unsigned char) ch_cur]; |
||||
|
if (map_cur == 0) { /* not in ws or at comment start, so done */ |
||||
|
p_fb->p_data_cur = p_data_cur; |
||||
|
return 0; |
||||
|
} |
||||
|
if (map_cur == -1) { /* a comment has started */ |
||||
|
p_fb->p_data_cur = p_data_cur + 1; /* after comment start */ |
||||
|
const int rc = fb_skip_to_eol(p_fb); |
||||
|
if (rc != 0) { /* EOF or error */ |
||||
|
return rc; |
||||
|
} |
||||
|
|
||||
|
/* Update local variables. Note that p_fb->p_data_cur is at |
||||
|
* the character after the comment, which is a \n or \r. |
||||
|
* These characters are whitespace that will be skipped, |
||||
|
* so incrementing past it in the ++p_data_cur of the for() |
||||
|
* only skips a character that will be skipped anyhow. |
||||
|
* (A long comment to say that |
||||
|
* p_data_cur = p_fb->p_data_cur - 1 is not necessary.) */ |
||||
|
p_data_cur = p_fb->p_data_cur; |
||||
|
p_data_end = p_fb->p_data_end; |
||||
|
} /* end of comment processing */ |
||||
|
/* Else whitespace, which is skipped */ |
||||
|
} /* end of loop over current text */ |
||||
|
|
||||
|
p_fb->p_data_cur = p_data_cur; /* update current position */ |
||||
|
const int rc = fb_fill(p_fb); /* Try to refill buffer */ |
||||
|
if (rc != 0) { /* EOF or error */ |
||||
|
return rc; |
||||
|
} |
||||
|
/* Else got more text to process */ |
||||
|
p_data_cur = p_fb->p_data_cur; /* Update after fill */ |
||||
|
p_data_end = p_fb->p_data_end; |
||||
|
} /* end of loop over text pieces */ |
||||
|
} /* end of function fb_skip_whitespace */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Skip text to EOL char, starting at the current position */ |
||||
|
static int fb_skip_to_eol(FILEBUF *p_fb) |
||||
|
{ |
||||
|
/* Step through characters until not whitespace (including comments) */ |
||||
|
char *p_data_cur = p_fb->p_data_cur; |
||||
|
char *p_data_end = p_fb->p_data_end; |
||||
|
for ( ; ; ) { /* continue until done */ |
||||
|
for ( ; p_data_cur != p_data_end; ++p_data_cur) { /* current data */ |
||||
|
const char ch_cur = *p_data_cur; |
||||
|
if (ch_cur == '\n' || ch_cur == '\r') { |
||||
|
p_fb->p_data_cur = p_data_cur; |
||||
|
return 0; |
||||
|
} |
||||
|
/* Else not EOL, which is skipped */ |
||||
|
} /* end of loop over current text */ |
||||
|
|
||||
|
p_fb->p_data_cur = p_data_cur; /* update current position */ |
||||
|
const int rc = fb_fill(p_fb); /* Try to refill buffer */ |
||||
|
if (rc != 0) { /* EOF or error */ |
||||
|
return rc; |
||||
|
} |
||||
|
/* Else got more text to process */ |
||||
|
p_data_cur = p_fb->p_data_cur; /* Update after fill */ |
||||
|
p_data_end = p_fb->p_data_end; |
||||
|
} /* end of loop over text pieces */ |
||||
|
} /* end of function fb_skip_to_eol */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Return the data found in the most preferred format possible */ |
||||
|
static int fb_return_obj(FILEBUF *p_fb, |
||||
|
unsigned int n_type_wanted, FBTYPE *p_type_wanted, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj) |
||||
|
{ |
||||
|
const char * const p_obj_start = p_fb->p_obj_start; /* data to convert */ |
||||
|
const char * const p_obj_end = p_fb->p_obj_end; |
||||
|
|
||||
|
/* Must test for null string separately since strto* does not set |
||||
|
* errno in this case. Aside from that, it can only be returned |
||||
|
* as a string anyhow. */ |
||||
|
if (p_obj_start != p_obj_end) { /* have a string besides "" */ |
||||
|
unsigned int i; |
||||
|
for (i = 0; i < n_type_wanted; ++i) { |
||||
|
FBTYPE type_cur = p_type_wanted[i]; |
||||
|
errno = 0; |
||||
|
if (type_cur == BUF_TYPE_ULONG) { |
||||
|
char *p_end; |
||||
|
unsigned long val = strtoul(p_obj_start, &p_end, 10); |
||||
|
/* Test for processing of full string. Note that checking |
||||
|
* for the end of the string rather than a NULL handles the |
||||
|
* case of an embedded NULL which the latter test would |
||||
|
* not */ |
||||
|
if (errno == 0 && p_end == p_obj_end) { |
||||
|
*p_type_found = BUF_TYPE_ULONG; |
||||
|
p_fbobj->ulong_value = val; |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
else if (type_cur == BUF_TYPE_LONG) { |
||||
|
char *p_end; |
||||
|
long val = strtol(p_obj_start, &p_end, 10); |
||||
|
if (errno == 0 && p_end == p_obj_end) { |
||||
|
*p_type_found = BUF_TYPE_LONG; |
||||
|
p_fbobj->long_value = val; |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
else if (type_cur == BUF_TYPE_DOUBLE) { |
||||
|
char *p_end; |
||||
|
double val = strtod(p_obj_start, &p_end); |
||||
|
if (errno == 0 && p_end == p_obj_end) { |
||||
|
*p_type_found = BUF_TYPE_DOUBLE; |
||||
|
p_fbobj->dbl_value = val; |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
else if (type_cur == BUF_TYPE_STRING) { |
||||
|
break; /* exit loop and use default return of string */ |
||||
|
} |
||||
|
else { /* unknown type */ |
||||
|
print_error("Unknown output data type %d is ignored.", |
||||
|
(int) type_cur); |
||||
|
} |
||||
|
} /* end of loop trying types */ |
||||
|
} /* end of case that string is not "" */ |
||||
|
|
||||
|
/* If no rquested type was converted OK or string requested, return as |
||||
|
* a string */ |
||||
|
return fb_return_string(p_fb, p_type_found, p_fbobj); |
||||
|
} /* end of function fb_return_obj */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Return string */ |
||||
|
static int fb_return_string(FILEBUF *p_fb, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj) |
||||
|
{ |
||||
|
const char *p_data_start = |
||||
|
p_fbobj->str_value.sz = p_fb->p_obj_start; |
||||
|
p_fbobj->str_value.n_char = p_fb->p_obj_end - p_data_start; |
||||
|
*p_type_found = BUF_TYPE_STRING; |
||||
|
return 0; |
||||
|
} /* end of function fb_return_string */ |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,58 @@ |
|||||
|
#ifndef file_buffer_h_included |
||||
|
#define file_buffer_h_included |
||||
|
|
||||
|
#include <stdbool.h> |
||||
|
|
||||
|
|
||||
|
/* Null-terminated string prefixed by length excluding null */ |
||||
|
typedef struct Filebuf_len_str { |
||||
|
size_t n_char; /* length of string excluding null termination */ |
||||
|
char *sz; /* Start of string */ |
||||
|
} FBSTRING; |
||||
|
|
||||
|
/* Union for returned value */ |
||||
|
typedef union Filebuf_obj { |
||||
|
FBSTRING str_value; |
||||
|
unsigned long ulong_value; |
||||
|
long long_value; |
||||
|
double dbl_value; |
||||
|
} FBOBJ; |
||||
|
|
||||
|
/* Structure for getting file data */ |
||||
|
typedef struct Filebuf { |
||||
|
FILE *fp; /* handle to file */ |
||||
|
bool is_eof; /* flag that EOF reached */ |
||||
|
bool f_skip_to_eol; |
||||
|
/* Flag that text until the next EOL character should be |
||||
|
* skipped before getting the next item from the buffer. |
||||
|
* This flag is set when a comment terminates an item, |
||||
|
* such as "abc# This is a comment." */ |
||||
|
size_t n_byte_buf_alloc; /* Allocated buffer size */ |
||||
|
char *p_buf; /* buffer to receive data from file */ |
||||
|
char *p_obj_start; /* start of object being returned */ |
||||
|
char *p_obj_end; /* byte past object being returned */ |
||||
|
char *p_data_cur; /* current position in buffer. Depending on |
||||
|
* circumstances, it points to either the character |
||||
|
* being processed or the next character to process */ |
||||
|
char *p_data_end; /* byte past end of data in buffer */ |
||||
|
char *p_buf_end; /* byte past end of allocated buffer size, equal to |
||||
|
* p_buf + n_byte_buf_alloc, so it is redundant, but |
||||
|
* convenient to have available */ |
||||
|
} FILEBUF; |
||||
|
|
||||
|
/* Types of data */ |
||||
|
typedef enum FBtype { |
||||
|
BUF_TYPE_STRING, /* value type string (always possible) */ |
||||
|
BUF_TYPE_ULONG, /* value type an unsigned int */ |
||||
|
BUF_TYPE_LONG, /* value type an int */ |
||||
|
BUF_TYPE_DOUBLE /* value type double */ |
||||
|
} FBTYPE; |
||||
|
|
||||
|
|
||||
|
FILEBUF *fbopen(const char *filename, size_t n_byte_buf_init); |
||||
|
int fbget(FILEBUF *p_fb, unsigned int n_type_wanted, FBTYPE *p_type_wanted, |
||||
|
FBTYPE *p_type_found, FBOBJ *p_fbobj); |
||||
|
int fbclose(FILEBUF *fbp); |
||||
|
|
||||
|
|
||||
|
#endif /* include guard */ |
||||
@ -0,0 +1,10 @@ |
|||||
|
#ifndef dlmain_h_included |
||||
|
#define dlmain_h_included |
||||
|
|
||||
|
#include "ngspice/cmproto.h" |
||||
|
#include "ngspice/mifproto.h" |
||||
|
#include "ngspice/dllitf.h" |
||||
|
|
||||
|
struct coreInfo_t *coreitf; |
||||
|
|
||||
|
#endif /* dlmain.h */ |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue