From 6ce9003d3d8849cacf938bcdbe0e30a415e6299c Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Wed, 12 Feb 2020 23:13:08 +0100 Subject: [PATCH] update to cmpp by J. Monte --- src/xspice/cmpp/file_buffer.c | 698 +++++++++++++++++++++++++++++++ src/xspice/cmpp/file_buffer.h | 58 +++ src/xspice/icm/dlmain.h | 10 + visualc/xspice/cmpp/cmpp.vcxproj | 10 +- 4 files changed, 772 insertions(+), 4 deletions(-) create mode 100644 src/xspice/cmpp/file_buffer.c create mode 100644 src/xspice/cmpp/file_buffer.h create mode 100644 src/xspice/icm/dlmain.h diff --git a/src/xspice/cmpp/file_buffer.c b/src/xspice/cmpp/file_buffer.c new file mode 100644 index 000000000..27a9d10ff --- /dev/null +++ b/src/xspice/cmpp/file_buffer.c @@ -0,0 +1,698 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 */ + + + diff --git a/src/xspice/cmpp/file_buffer.h b/src/xspice/cmpp/file_buffer.h new file mode 100644 index 000000000..bee1f76fc --- /dev/null +++ b/src/xspice/cmpp/file_buffer.h @@ -0,0 +1,58 @@ +#ifndef file_buffer_h_included +#define file_buffer_h_included + +#include + + +/* 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 */ diff --git a/src/xspice/icm/dlmain.h b/src/xspice/icm/dlmain.h new file mode 100644 index 000000000..c442f1a14 --- /dev/null +++ b/src/xspice/icm/dlmain.h @@ -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 */ diff --git a/visualc/xspice/cmpp/cmpp.vcxproj b/visualc/xspice/cmpp/cmpp.vcxproj index 45cecc0ec..f14055b33 100644 --- a/visualc/xspice/cmpp/cmpp.vcxproj +++ b/visualc/xspice/cmpp/cmpp.vcxproj @@ -87,7 +87,7 @@ _CRT_SECURE_NO_WARNINGS;YY_NO_UNISTD_H;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) false MultiThreadedDebug - ..\..\..\src\xspice\cmpp + $(ProjectDir)..\..\..\src\xspice\cmpp; true @@ -106,7 +106,7 @@ false MultiThreaded ProgramDatabase - ..\..\..\src\xspice\cmpp + $(ProjectDir)..\..\..\src\xspice\cmpp; true @@ -128,7 +128,7 @@ false Default MultiThreadedDebug - ..\..\..\src\xspice\cmpp;%(AdditionalIncludeDirectories) + $(ProjectDir)..\..\..\src\xspice\cmpp;%(AdditionalIncludeDirectories) true @@ -145,7 +145,7 @@ _CRT_SECURE_NO_WARNINGS;YY_NO_UNISTD_H;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) false CompileAsC - ..\..\..\src\xspice\cmpp;%(AdditionalIncludeDirectories) + $(ProjectDir)..\..\..\src\xspice\cmpp;%(AdditionalIncludeDirectories) true @@ -169,11 +169,13 @@ + +