1157 lines
32 KiB
C
1157 lines
32 KiB
C
/*
|
|
* Copyright (c) 2006-2011 Stephen Williams (steve@icarus.com)
|
|
*
|
|
* This source code is free software; you can redistribute it
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
* General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
*/
|
|
|
|
/* round() is ISO C99 from math.h. This define should enable it. */
|
|
# define _ISOC99_SOURCE 1
|
|
# define _SVID_SOURCE 1
|
|
|
|
# include "sys_priv.h"
|
|
# include <ctype.h>
|
|
# include <errno.h>
|
|
# include <string.h>
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
# include <math.h>
|
|
# include <limits.h>
|
|
# include <assert.h>
|
|
|
|
/*
|
|
* The wrapper routines below get a value from either a string or a file.
|
|
* This structure is used to determine which one is used. The unused one
|
|
* must be assigned a zero value.
|
|
*/
|
|
struct byte_source {
|
|
const char *str;
|
|
FILE *fd;
|
|
};
|
|
|
|
/*
|
|
* Wrapper routine to get a byte from either a string or a file descriptor.
|
|
*/
|
|
static int byte_getc(struct byte_source *src)
|
|
{
|
|
if (src->str) {
|
|
assert(! src->fd);
|
|
if (src->str[0] == 0) return EOF;
|
|
|
|
return *(src->str)++;
|
|
}
|
|
|
|
assert(src->fd);
|
|
return fgetc(src->fd);
|
|
}
|
|
|
|
/*
|
|
* Wrapper routine to unget a byte to either a string or a file descriptor.
|
|
*/
|
|
static void byte_ungetc(struct byte_source *src, int ch)
|
|
{
|
|
if (ch == EOF) return;
|
|
|
|
if (src->str) {
|
|
assert(! src->fd);
|
|
src->str -= 1;
|
|
return;
|
|
}
|
|
|
|
assert(src->fd);
|
|
ungetc(ch, src->fd);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function matches the input characters of a floating point
|
|
* number and generates a floating point (double) from that string.
|
|
*
|
|
* Do we need support for +-Inf and NaN? It's not in the standard.
|
|
*
|
|
* The match variable is set to 1 for a match or 0 for no match.
|
|
*/
|
|
static double get_float(struct byte_source *src, unsigned width, int *match)
|
|
{
|
|
char *endptr;
|
|
char *strval = malloc(1);
|
|
unsigned len = 0;
|
|
double result;
|
|
int ch;
|
|
|
|
/* Skip any leading space. */
|
|
ch = byte_getc(src);
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
/* If we are being asked for no digits then return a match fail. */
|
|
if (width == 0) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
*match = 0;
|
|
return 0.0;
|
|
}
|
|
|
|
/* Look for the optional sign. */
|
|
if ((ch == '+') || (ch == '-')) {
|
|
/* If we have a sign then the width must not be
|
|
* one since we need a sign and a digit. */
|
|
if (width == 1) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
*match = 0;
|
|
return 0.0;
|
|
}
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
ch = byte_getc(src);
|
|
}
|
|
|
|
/* Get any digits before the optional decimal point, but no more
|
|
* than width. */
|
|
while (isdigit(ch) && (len < width)) {
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
ch = byte_getc(src);
|
|
}
|
|
|
|
/* Get the optional decimal point and any following digits, but
|
|
* no more than width total characters are copied. */
|
|
if ((ch == '.') && (len < width)) {
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
ch = byte_getc(src);
|
|
/* Get any trailing digits. */
|
|
while (isdigit(ch) && (len < width)) {
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
ch = byte_getc(src);
|
|
}
|
|
}
|
|
|
|
/* No leading digits were matched. */
|
|
if ((len == 0) ||
|
|
((len == 1) && ((strval[0] == '+') || (strval[0] == '-')))) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
*match = 0;
|
|
return 0.0;
|
|
}
|
|
|
|
/* Match an exponent. */
|
|
if (((ch == 'e') || (ch == 'E')) && (len < width)) {
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
ch = byte_getc(src);
|
|
|
|
/* We must have enough space for at least one digit after
|
|
* the exponent. */
|
|
if (len == width) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
*match = 0;
|
|
return 0.0;
|
|
}
|
|
|
|
/* Check to see if the exponent has a sign. */
|
|
if ((ch == '-') || (ch == '+')) {
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
ch = byte_getc(src);
|
|
/* We must have enough space for at least one digit
|
|
* after the exponent sign. */
|
|
if (len == width) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
*match = 0;
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
/* We must have at least one digit after the exponent/sign. */
|
|
if (! isdigit(ch)) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
*match = 0;
|
|
return 0.0;
|
|
}
|
|
|
|
/* Get the exponent digits, but no more than width total
|
|
* characters are copied. */
|
|
while (isdigit(ch) && (len < width)) {
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
ch = byte_getc(src);
|
|
}
|
|
}
|
|
strval[len] = 0;
|
|
|
|
/* Put the last character back. */
|
|
byte_ungetc(src, ch);
|
|
|
|
/* Calculate and return the result. */
|
|
result = strtod(strval, &endptr);
|
|
/* If this asserts then there is a bug in the code above.*/
|
|
assert(*endptr == 0);
|
|
free(strval);
|
|
*match = 1;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Routine to return a floating point value (implements %e, %f and %g).
|
|
*
|
|
* Return: 1 for a match, 0 for no match/variable and -1 for a
|
|
* suppressed match. No variable is fatal.
|
|
*/
|
|
static int scan_format_float(vpiHandle callh, vpiHandle argv,
|
|
struct byte_source *src, unsigned width,
|
|
unsigned suppress_flag, PLI_BYTE8 *name,
|
|
char code)
|
|
{
|
|
vpiHandle arg;
|
|
int match;
|
|
s_vpi_value val;
|
|
double result;
|
|
|
|
/* Get the real value. */
|
|
result = get_float(src, width, &match);
|
|
|
|
/* Nothing was matched. */
|
|
if (match == 0) return 0;
|
|
|
|
/* If this match is being suppressed then return after consuming
|
|
* the digits and report that no arguments were used. */
|
|
if (suppress_flag) return -1;
|
|
|
|
/* We must have a variable to put the double value into. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s() ran out of variables for %%%c format code",
|
|
name, code);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* Put the value into the variable. */
|
|
val.format = vpiRealVal;
|
|
val.value.real = result;
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
|
|
/* We always consume one variable if it is available. */
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Routine to return a floating point value scaled and rounded to the
|
|
* current time scale and precision (implements %t).
|
|
*
|
|
* Return: 1 for a match, 0 for no match/variable and -1 for a
|
|
* suppressed match. No variable is fatal.
|
|
*/
|
|
static int scan_format_float_time(vpiHandle callh, vpiHandle argv,
|
|
struct byte_source*src, unsigned width,
|
|
unsigned suppress_flag, PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle scope = vpi_handle(vpiScope, callh);
|
|
int time_units = vpi_get(vpiTimeUnit, scope);
|
|
vpiHandle arg;
|
|
int match;
|
|
s_vpi_value val;
|
|
double result;
|
|
double scale;
|
|
|
|
/* Get the raw real value. */
|
|
result = get_float(src, width, &match);
|
|
|
|
/* Nothing was matched. */
|
|
if (match == 0) return 0;
|
|
|
|
/* If this match is being suppressed then return after consuming
|
|
* the digits and report that no arguments were used. */
|
|
if (suppress_flag) return -1;
|
|
|
|
/* Round the raw value to the specified precision. Handle this
|
|
by shifting the decimal point to the precision where we want
|
|
to round, do the rounding, then shift the decimal point back */
|
|
scale = pow(10.0, timeformat_info.prec);
|
|
result = round(result*scale) / scale;
|
|
|
|
/* Scale the value from the timeformat units to the current
|
|
* timescale units. To minimize the error keep the scale an
|
|
* integer value. */
|
|
if (timeformat_info.units >= time_units) {
|
|
scale = pow(10.0, timeformat_info.units - time_units);
|
|
result *= scale;
|
|
} else {
|
|
scale = pow(10.0, time_units - timeformat_info.units);
|
|
result /= scale;
|
|
}
|
|
|
|
/* We must have a variable to put the double value into. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s() ran out of variables for %%t format code", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* Put the value into the variable. */
|
|
val.format = vpiRealVal;
|
|
val.value.real = result;
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
|
|
/* We always consume one variable if it is available. */
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Base routine for getting binary, octal and hex values.
|
|
*
|
|
* Return: 1 for a match, 0 for no match/variable and -1 for a
|
|
* suppressed match. No variable is fatal.
|
|
*/
|
|
static int scan_format_base(vpiHandle callh, vpiHandle argv,
|
|
struct byte_source *src, unsigned width,
|
|
unsigned suppress_flag, PLI_BYTE8 *name,
|
|
const char *match, char code,
|
|
PLI_INT32 type)
|
|
{
|
|
vpiHandle arg;
|
|
char *strval = malloc(1);
|
|
unsigned len = 0;
|
|
s_vpi_value val;
|
|
int ch;
|
|
|
|
/* Skip any leading space. */
|
|
ch = byte_getc(src);
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
/* If we are being asked for no digits or the first character is
|
|
* an underscore then return a match fail. */
|
|
if ((width == 0) || (ch == '_')) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
/* Get all the digits, but no more than width. */
|
|
while (strchr(match , ch) && (len < width)) {
|
|
if (ch == '?') ch = 'x';
|
|
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
|
|
ch = byte_getc(src);
|
|
}
|
|
strval[len] = 0;
|
|
|
|
/* Put the last character back. */
|
|
byte_ungetc(src, ch);
|
|
|
|
/* Nothing was matched. */
|
|
if (len == 0) {
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
/* If this match is being suppressed then return after consuming
|
|
* the digits and report that no arguments were used. */
|
|
if (suppress_flag) {
|
|
free(strval);
|
|
return -1;
|
|
}
|
|
|
|
/* We must have a variable to put the binary value into. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s() ran out of variables for %%%c format code",
|
|
name, code);
|
|
vpi_control(vpiFinish, 1);
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
/* Put the value into the variable. */
|
|
val.format = type;
|
|
val.value.str = strval;
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
free(strval);
|
|
|
|
/* We always consume one variable if it is available. */
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Routine to return a binary value (implements %b).
|
|
*/
|
|
static int scan_format_binary(vpiHandle callh, vpiHandle argv,
|
|
struct byte_source *src, int width,
|
|
unsigned suppress_flag, PLI_BYTE8 *name)
|
|
{
|
|
return scan_format_base(callh, argv, src, width, suppress_flag, name,
|
|
"01xzXZ?_", 'b', vpiBinStrVal);
|
|
}
|
|
|
|
/*
|
|
* Routine to return a character value (implements %c).
|
|
*
|
|
* Return: 1 for a match, 0 for no match/variable and -1 for a
|
|
* suppressed match. No variable is fatal.
|
|
*/
|
|
static int scan_format_char(vpiHandle callh, vpiHandle argv,
|
|
struct byte_source *src, unsigned width,
|
|
unsigned suppress_flag, PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle arg;
|
|
s_vpi_value val;
|
|
int ch;
|
|
|
|
/* If we are being asked for no digits then return a match fail. */
|
|
if (width == 0) return 0;
|
|
|
|
/* Get the character to return. */
|
|
ch = byte_getc(src);
|
|
|
|
/* Nothing was matched. */
|
|
if (ch == EOF) return 0;
|
|
|
|
/* If this match is being suppressed then return after consuming
|
|
* the character and report that no arguments were used. */
|
|
if (suppress_flag) return -1;
|
|
|
|
/* We must have a variable to put the character value into. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s() ran out of variables for %%c format code", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* Put the character into the variable. */
|
|
val.format = vpiIntVal;
|
|
val.value.integer = ch;
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
|
|
/* We always consume one variable if it is available. */
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Routine to return a decimal value (implements %d).
|
|
*
|
|
* Return: 1 for a match, 0 for no match/variable and -1 for a
|
|
* suppressed match. No variable is fatal.
|
|
*/
|
|
static int scan_format_decimal(vpiHandle callh, vpiHandle argv,
|
|
struct byte_source *src, unsigned width,
|
|
unsigned suppress_flag, PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle arg;
|
|
char *strval = malloc(1);
|
|
unsigned len = 0;
|
|
s_vpi_value val;
|
|
int ch;
|
|
|
|
/* Skip any leading space. */
|
|
ch = byte_getc(src);
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
/* If we are being asked for no digits or the first character is
|
|
* an underscore then return a match fail. */
|
|
if ((width == 0) || (ch == '_')) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
/* A decimal can match a single x/X, ? or z/Z character. */
|
|
if (strchr("xX?", ch)) {
|
|
strval = realloc(strval, 2);
|
|
strval[0] = 'x';
|
|
strval[1] = 0;
|
|
} else if (strchr("zZ", ch)) {
|
|
strval = realloc(strval, 2);
|
|
strval[0] = 'z';
|
|
strval[1] = 0;
|
|
} else {
|
|
|
|
/* To match a + or - we must have a digit after it. */
|
|
if (ch == '+') {
|
|
/* If we have a '+' sign then the width must not be
|
|
* one since we need a sign and a digit. */
|
|
if (width == 1) {
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
ch = byte_getc(src);
|
|
if (! isdigit(ch)) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
/* The '+' used up a character. */
|
|
width -= 1;
|
|
} else if (ch == '-') {
|
|
/* If we have a '-' sign then the width must not be
|
|
* one since we need a sign and a digit. */
|
|
if (width == 1) {
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
ch = byte_getc(src);
|
|
if (isdigit(ch)) {
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = '-';
|
|
} else {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Get all the characters, but no more than width. */
|
|
while ((isdigit(ch) || ch == '_') && (len < width)) {
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
|
|
ch = byte_getc(src);
|
|
}
|
|
strval[len] = 0;
|
|
|
|
/* Put the last character back. */
|
|
byte_ungetc(src, ch);
|
|
|
|
/* Nothing was matched. */
|
|
if (len == 0) {
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* If this match is being suppressed then return after consuming
|
|
* the digits and report that no arguments were used. */
|
|
if (suppress_flag) {
|
|
free(strval);
|
|
return -1;
|
|
}
|
|
|
|
/* We must have a variable to put the decimal value into. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s() ran out of variables for %%d format code", name);
|
|
vpi_control(vpiFinish, 1);
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
/* Put the decimal value into the variable. */
|
|
val.format = vpiDecStrVal;
|
|
val.value.str = strval;
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
free(strval);
|
|
|
|
/* We always consume one variable if it is available. */
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Routine to return a hex value (implements %h).
|
|
*/
|
|
static int scan_format_hex(vpiHandle callh, vpiHandle argv,
|
|
struct byte_source *src, unsigned width,
|
|
unsigned suppress_flag, PLI_BYTE8 *name)
|
|
{
|
|
return scan_format_base(callh, argv, src, width, suppress_flag, name,
|
|
"0123456789abcdefxzABCDEFXZ?_", 'h',
|
|
vpiHexStrVal);
|
|
}
|
|
|
|
/*
|
|
* Routine to return an octal value (implements %o).
|
|
*/
|
|
static int scan_format_octal(vpiHandle callh, vpiHandle argv,
|
|
struct byte_source *src, unsigned width,
|
|
unsigned suppress_flag, PLI_BYTE8 *name)
|
|
{
|
|
return scan_format_base(callh, argv, src, width, suppress_flag, name,
|
|
"01234567xzXZ?_", 'o', vpiOctStrVal);
|
|
}
|
|
|
|
|
|
/*
|
|
* Routine to return the current hierarchical path (implements %m).
|
|
*/
|
|
static int scan_format_module_path(vpiHandle callh, vpiHandle argv,
|
|
unsigned suppress_flag, PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle scope, arg;
|
|
char *module_path;
|
|
s_vpi_value val;
|
|
|
|
/* If this format code is being suppressed then just return that no
|
|
* arguments were used. */
|
|
if (suppress_flag) return -1;
|
|
|
|
/* We must have a variable to put the hierarchical path into. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s() ran out of variables for %%m format code", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* Get the current hierarchical path. */
|
|
scope = vpi_handle(vpiScope, callh);
|
|
assert(scope);
|
|
module_path = vpi_get_str(vpiFullName, scope);
|
|
|
|
/* Put the hierarchical path into the variable. */
|
|
val.format = vpiStringVal;
|
|
val.value.str = module_path;
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
|
|
/* We always consume one variable if it is available. */
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Routine to return a string value (implements %s).
|
|
*
|
|
* Return: 1 for a match, 0 for no match/variable and -1 for a
|
|
* suppressed match. No variable is fatal.
|
|
*/
|
|
static int scan_format_string(vpiHandle callh, vpiHandle argv,
|
|
struct byte_source *src, unsigned width,
|
|
unsigned suppress_flag, PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle arg;
|
|
char *strval = malloc(1);
|
|
unsigned len = 0;
|
|
s_vpi_value val;
|
|
int ch;
|
|
|
|
/* Skip any leading space. */
|
|
ch = byte_getc(src);
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
/* If we are being asked for no digits then return a match fail. */
|
|
if (width == 0) {
|
|
byte_ungetc(src, ch);
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
/* Get all the non-space characters, but no more than width. */
|
|
while (! isspace(ch) && (len < width)) {
|
|
if (ch == EOF) break;
|
|
|
|
strval = realloc(strval, len+2);
|
|
strval[len++] = ch;
|
|
|
|
ch = byte_getc(src);
|
|
}
|
|
strval[len] = 0;
|
|
|
|
/* Nothing was matched (this can only happen at EOF). */
|
|
if (len == 0) {
|
|
assert(ch == EOF);
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
/* Put the last character back. */
|
|
byte_ungetc(src, ch);
|
|
|
|
/* If this match is being suppressed then return after consuming
|
|
* the string and report that no arguments were used. */
|
|
if (suppress_flag) {
|
|
free(strval);
|
|
return -1;
|
|
}
|
|
|
|
/* We must have a variable to put the string into. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s() ran out of variables for %%s format code", name);
|
|
vpi_control(vpiFinish, 1);
|
|
free(strval);
|
|
return 0;
|
|
}
|
|
|
|
/* Put the string into the variable. */
|
|
val.format = vpiStringVal;
|
|
val.value.str = strval;
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
free(strval);
|
|
|
|
/* We always consume one variable if it is available. */
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* The $fscanf and $sscanf functions are the same except for the first
|
|
* argument, which is the source. The wrapper functions below peel off
|
|
* the first argument and make a byte_source object that then gets
|
|
* passed to this function, which processes the rest of the function.
|
|
*/
|
|
static int scan_format(vpiHandle callh, struct byte_source*src, vpiHandle argv,
|
|
PLI_BYTE8 *name)
|
|
{
|
|
s_vpi_value val;
|
|
vpiHandle item;
|
|
PLI_INT32 len, words, idx, mask;
|
|
|
|
char *fmt, *fmtp;
|
|
int rc = 0;
|
|
int ch;
|
|
|
|
int match = 1;
|
|
|
|
/* Get the format string. */
|
|
item = vpi_scan(argv);
|
|
assert(item);
|
|
/* Look for an undefined bit (X/Z) in the format string. If one is
|
|
* found just return EOF. */
|
|
len = vpi_get(vpiSize, item);
|
|
words = ((len + 31) / 32) - 1;
|
|
/* The mask is defined to be 32 bits. */
|
|
mask = 0xffffffff >> (32 - ((len - 1) % 32 + 1));
|
|
val.format = vpiVectorVal;
|
|
vpi_get_value(item, &val);
|
|
/* Check the full words for an undefined bit. */
|
|
for (idx = 0; idx < words; idx += 1) {
|
|
if (val.value.vector[idx].bval) {
|
|
match = 0;
|
|
rc = EOF;
|
|
break;
|
|
}
|
|
}
|
|
/* Check the top word for an undefined bit. */
|
|
if (match && (val.value.vector[words].bval & mask)) {
|
|
match = 0;
|
|
rc = EOF;
|
|
}
|
|
|
|
/* Now get the format as a string. */
|
|
val.format = vpiStringVal;
|
|
vpi_get_value(item, &val);
|
|
fmtp = fmt = strdup(val.value.str);
|
|
|
|
/* See if we are at EOF before we even start. */
|
|
ch = byte_getc(src);
|
|
if (ch == EOF) {
|
|
rc = EOF;
|
|
match = 0;
|
|
}
|
|
byte_ungetc(src, ch);
|
|
|
|
while ( fmtp && *fmtp != 0 && match) {
|
|
|
|
if (isspace((int)*fmtp)) {
|
|
/* White space matches a string of white space in the
|
|
* input. The number of spaces is not relevant, and
|
|
* the match may be 0 or more spaces. */
|
|
while (*fmtp && isspace((int)*fmtp)) fmtp += 1;
|
|
|
|
ch = byte_getc(src);
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
byte_ungetc(src, ch);
|
|
|
|
} else if (*fmtp != '%') {
|
|
/* Characters other than % match themselves. */
|
|
ch = byte_getc(src);
|
|
if (ch != *fmtp) {
|
|
byte_ungetc(src, ch);
|
|
break;
|
|
}
|
|
|
|
fmtp += 1;
|
|
|
|
} else {
|
|
|
|
/* We are at a pattern character. The pattern has
|
|
* the format %<N>x no matter what the x code, so
|
|
* parse it generically first. */
|
|
|
|
unsigned suppress_flag = 0;
|
|
unsigned max_width = UINT_MAX;
|
|
int code = 0;
|
|
|
|
/* Look for the suppression character '*'. */
|
|
fmtp += 1;
|
|
if (*fmtp == '*') {
|
|
suppress_flag = 1;
|
|
fmtp += 1;
|
|
}
|
|
/* Look for the maximum match width. */
|
|
if (isdigit((int)*fmtp)) {
|
|
max_width = 0;
|
|
while (isdigit((int)*fmtp)) {
|
|
max_width *= 10;
|
|
max_width += *fmtp - '0';
|
|
fmtp += 1;
|
|
}
|
|
}
|
|
|
|
/* Get the format character. */
|
|
code = *fmtp;
|
|
fmtp += 1;
|
|
|
|
/* The format string is parsed:
|
|
* - max_width is the width,
|
|
* - code is the format code character,
|
|
* - suppress_flag is true if the match is to be ignored.
|
|
* Now interpret the code. */
|
|
switch (code) {
|
|
|
|
/* Read a '%' character from the input. */
|
|
case '%':
|
|
assert(max_width == -1);
|
|
assert(suppress_flag == 0);
|
|
ch = byte_getc(src);
|
|
if (ch != '%') {
|
|
byte_ungetc(src, ch);
|
|
match = 0;
|
|
}
|
|
break;
|
|
|
|
case 'b':
|
|
match = scan_format_binary(callh, argv, src, max_width,
|
|
suppress_flag, name);
|
|
if (match == 1) rc += 1;
|
|
break;
|
|
|
|
case 'c':
|
|
match = scan_format_char(callh, argv, src, max_width,
|
|
suppress_flag, name);
|
|
if (match == 1) rc += 1;
|
|
break;
|
|
|
|
case 'd':
|
|
match = scan_format_decimal(callh, argv, src, max_width,
|
|
suppress_flag, name);
|
|
if (match == 1) rc += 1;
|
|
break;
|
|
|
|
case 'e':
|
|
case 'f':
|
|
case 'g':
|
|
match = scan_format_float(callh, argv, src, max_width,
|
|
suppress_flag, name, code);
|
|
if (match == 1) rc += 1;
|
|
break;
|
|
|
|
case 'h':
|
|
case 'x':
|
|
match = scan_format_hex(callh, argv, src, max_width,
|
|
suppress_flag, name);
|
|
if (match == 1) rc += 1;
|
|
break;
|
|
|
|
case 'm':
|
|
/* Since this code does not consume any characters
|
|
* the width makes no difference. */
|
|
match = scan_format_module_path(callh, argv,
|
|
suppress_flag, name);
|
|
if (match == 1) rc += 1;
|
|
break;
|
|
|
|
case 'o':
|
|
match = scan_format_octal(callh, argv, src, max_width,
|
|
suppress_flag, name);
|
|
if (match == 1) rc += 1;
|
|
break;
|
|
|
|
case 's':
|
|
match = scan_format_string(callh, argv, src, max_width,
|
|
suppress_flag, name);
|
|
if (match == 1) rc += 1;
|
|
break;
|
|
|
|
case 't':
|
|
match = scan_format_float_time(callh, argv, src,
|
|
max_width,
|
|
suppress_flag, name);
|
|
if (match == 1) rc += 1;
|
|
break;
|
|
|
|
case 'u':
|
|
case 'v':
|
|
case 'z':
|
|
vpi_printf("SORRY: %s:%d: ",
|
|
vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s() format code '%%%c' is not "
|
|
"currently supported.\n", name, code);
|
|
vpi_control(vpiFinish, 1);
|
|
break;
|
|
|
|
default:
|
|
vpi_printf("ERROR: %s:%d: ",
|
|
vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s() was given an invalid format code: "
|
|
"%%%c\n", name, code);
|
|
vpi_control(vpiFinish, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clean up the allocated memory. */
|
|
free(fmt);
|
|
vpi_free_object(argv);
|
|
|
|
/* Return the number of successful matches. */
|
|
val.format = vpiIntVal;
|
|
val.value.integer = rc;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Is the object a variable/register or a piece of one.
|
|
*/
|
|
static int is_assignable_obj(vpiHandle obj)
|
|
{
|
|
unsigned rtn = 0;
|
|
|
|
assert(obj);
|
|
|
|
switch(vpi_get(vpiType, obj)) {
|
|
/* We can not assign to a vpiNetArray. */
|
|
case vpiMemoryWord:
|
|
if (vpi_get(vpiType, vpi_handle(vpiParent, obj)) == vpiMemory) {
|
|
rtn = 1;
|
|
}
|
|
break;
|
|
case vpiPartSelect:
|
|
if (! is_assignable_obj(vpi_handle(vpiParent, obj))) break;
|
|
case vpiIntegerVar:
|
|
case vpiRealVar:
|
|
case vpiReg:
|
|
case vpiTimeVar:
|
|
rtn = 1;
|
|
break;
|
|
}
|
|
|
|
return rtn;
|
|
}
|
|
|
|
static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle arg;
|
|
int cnt = 3, rtn = 0;
|
|
|
|
/* The format (2nd) argument must be a string. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires at least three argument.\n", name);
|
|
return 1;
|
|
}
|
|
if(! is_string_obj(arg)) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s format argument must be a string.\n", name);
|
|
rtn = 1;
|
|
}
|
|
|
|
/* The rest of the arguments must be assignable. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires at least three argument.\n", name);
|
|
return 1;
|
|
}
|
|
|
|
do {
|
|
if (! is_assignable_obj(arg)) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s argument %d (a %s) is not assignable.\n",
|
|
name, cnt, vpi_get_str(vpiType, arg));
|
|
rtn = 1;
|
|
}
|
|
arg = vpi_scan(argv);
|
|
cnt += 1;
|
|
} while (arg);
|
|
|
|
return rtn;
|
|
}
|
|
|
|
static PLI_INT32 sys_fscanf_compiletf(PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
|
|
/* Check that there are arguments. */
|
|
if (argv == 0) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires at least three argument.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* The first argument must be a file descriptor. */
|
|
if (! is_numeric_obj(vpi_scan(argv))) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's first argument (fd) must be numeric.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
vpi_free_object(argv);
|
|
return 0;
|
|
}
|
|
|
|
if (sys_check_args(callh, argv, name)) vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_fscanf_calltf(PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
s_vpi_value val;
|
|
struct byte_source src;
|
|
FILE *fd;
|
|
errno = 0;
|
|
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(vpi_scan(argv), &val);
|
|
fd = vpi_get_file(val.value.integer);
|
|
if (!fd) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n",
|
|
(int)val.value.integer, name);
|
|
errno = EBADF;
|
|
val.format = vpiIntVal;
|
|
val.value.integer = EOF;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
vpi_free_object(argv);
|
|
return 0;
|
|
}
|
|
|
|
src.str = 0;
|
|
src.fd = fd;
|
|
scan_format(callh, &src, argv, name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_sscanf_compiletf(PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle reg;
|
|
|
|
/* Check that there are arguments. */
|
|
if (argv == 0) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires at least three argument.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* The first argument must be a register or constant string. */
|
|
reg = vpi_scan(argv); /* This should never be zero. */
|
|
switch(vpi_get(vpiType, reg)) {
|
|
case vpiReg:
|
|
break;
|
|
case vpiConstant:
|
|
case vpiParameter:
|
|
if (vpi_get(vpiConstType, reg) == vpiStringConst) break;
|
|
|
|
default:
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's first argument must be a register or constant "
|
|
"string.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
vpi_free_object(argv);
|
|
return 0;
|
|
}
|
|
|
|
if (sys_check_args(callh, argv, name)) vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_sscanf_calltf(PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
s_vpi_value val;
|
|
struct byte_source src;
|
|
char *str;
|
|
|
|
val.format = vpiStringVal;
|
|
vpi_get_value(vpi_scan(argv), &val);
|
|
|
|
str = strdup(val.value.str);
|
|
src.str = str;
|
|
src.fd = 0;
|
|
scan_format(callh, &src, argv, name);
|
|
free(str);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void sys_scanf_register()
|
|
{
|
|
s_vpi_systf_data tf_data;
|
|
|
|
/*============================== fscanf */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$fscanf";
|
|
tf_data.calltf = sys_fscanf_calltf;
|
|
tf_data.compiletf = sys_fscanf_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fscanf";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== sscanf */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$sscanf";
|
|
tf_data.calltf = sys_sscanf_calltf;
|
|
tf_data.compiletf = sys_sscanf_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$sscanf";
|
|
vpi_register_systf(&tf_data);
|
|
}
|