2006-08-03 07:06:04 +02:00
|
|
|
/*
|
2010-01-12 18:49:55 +01:00
|
|
|
* Copyright (c) 2006-2010 Stephen Williams (steve@icarus.com)
|
2006-08-03 07:06:04 +02:00
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
|
2007-12-01 06:11:27 +01:00
|
|
|
/* round() is ISO C99 from math.h. This define should enable it. */
|
|
|
|
|
# define _ISOC99_SOURCE 1
|
|
|
|
|
# define _SVID_SOURCE 1
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
# include "sys_priv.h"
|
|
|
|
|
# include <ctype.h>
|
2009-07-31 22:06:49 +02:00
|
|
|
# include <errno.h>
|
2006-08-03 07:06:04 +02:00
|
|
|
# include <string.h>
|
|
|
|
|
# include <stdio.h>
|
|
|
|
|
# include <stdlib.h>
|
2006-08-12 05:38:12 +02:00
|
|
|
# include <math.h>
|
2006-08-03 07:06:04 +02:00
|
|
|
# include <assert.h>
|
|
|
|
|
|
|
|
|
|
struct byte_source {
|
|
|
|
|
const char*str;
|
|
|
|
|
FILE*fd;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int byte_getc(struct byte_source*byte)
|
|
|
|
|
{
|
2007-04-16 02:09:58 +02:00
|
|
|
int ch;
|
2006-08-03 07:06:04 +02:00
|
|
|
if (byte->str) {
|
2009-02-27 21:34:48 +01:00
|
|
|
if (byte->str[0] == 0) return EOF;
|
2007-04-16 02:09:58 +02:00
|
|
|
|
|
|
|
|
return *(byte->str)++;
|
2006-08-03 07:06:04 +02:00
|
|
|
}
|
|
|
|
|
|
2007-04-16 02:09:58 +02:00
|
|
|
ch = fgetc(byte->fd);
|
|
|
|
|
return ch;
|
2006-08-03 07:06:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void byte_ungetc(struct byte_source*src, int ch)
|
|
|
|
|
{
|
2009-02-27 21:34:48 +01:00
|
|
|
if (ch == EOF) return;
|
2006-08-03 07:06:04 +02:00
|
|
|
|
|
|
|
|
if (src->str) {
|
|
|
|
|
src->str -= 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(src->fd);
|
|
|
|
|
ungetc(ch, src->fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-08-12 05:38:12 +02:00
|
|
|
/*
|
|
|
|
|
* This function matches the input characters of a floating point
|
|
|
|
|
* number and generates a floating point (double) from that string.
|
2009-02-27 21:34:48 +01:00
|
|
|
*
|
|
|
|
|
* Need support for +-Inf and NaN. Look at the $plusargs code.
|
2006-08-12 05:38:12 +02:00
|
|
|
*/
|
|
|
|
|
static double float_string(struct byte_source*src)
|
|
|
|
|
{
|
|
|
|
|
int ch;
|
|
|
|
|
char*str = 0;
|
|
|
|
|
size_t len;
|
|
|
|
|
double sign_flag = 1.0;
|
|
|
|
|
|
|
|
|
|
ch = byte_getc(src);
|
2009-04-04 00:26:27 +02:00
|
|
|
/* Skip leading space. */
|
|
|
|
|
while (isspace(ch)) ch = byte_getc(src);
|
2006-08-12 05:38:12 +02:00
|
|
|
|
|
|
|
|
if (ch == '+') {
|
|
|
|
|
sign_flag = 1.0;
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
} else if (ch == '-') {
|
|
|
|
|
sign_flag = -1.0;
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
str = malloc(1);
|
|
|
|
|
len = 0;
|
|
|
|
|
*str = 0;
|
|
|
|
|
|
|
|
|
|
while (isdigit(ch)) {
|
|
|
|
|
str = realloc(str, len+2);
|
|
|
|
|
str[len++] = ch;
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ch == '.') {
|
|
|
|
|
str = realloc(str, len+2);
|
|
|
|
|
str[len++] = ch;
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
while (isdigit(ch)) {
|
|
|
|
|
str = realloc(str, len+2);
|
|
|
|
|
str[len++] = ch;
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ch == 'e' || ch == 'E') {
|
|
|
|
|
str = realloc(str, len+2);
|
|
|
|
|
str[len++] = ch;
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
|
|
|
|
|
if (ch == '-' || ch == '+') {
|
|
|
|
|
str = realloc(str, len+2);
|
|
|
|
|
str[len++] = ch;
|
2007-03-07 01:38:15 +01:00
|
|
|
ch = byte_getc(src);
|
2006-08-12 05:38:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (isdigit(ch)) {
|
2010-01-12 18:49:55 +01:00
|
|
|
str = realloc(str, len+2);
|
2006-08-12 05:38:12 +02:00
|
|
|
str[len++] = ch;
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
str[len] = 0;
|
|
|
|
|
|
|
|
|
|
sign_flag *= strtod(str, 0);
|
|
|
|
|
free(str);
|
|
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
if (ch != EOF) byte_ungetc(src, ch);
|
2006-08-12 05:38:12 +02:00
|
|
|
|
|
|
|
|
return sign_flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int scan_format_float(struct byte_source*src,
|
|
|
|
|
vpiHandle arg)
|
|
|
|
|
{
|
|
|
|
|
s_vpi_value val;
|
|
|
|
|
|
|
|
|
|
val.format = vpiRealVal;
|
|
|
|
|
val.value.real = float_string(src);
|
|
|
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
static int scan_format_float_time(vpiHandle callh,
|
2006-08-12 05:38:12 +02:00
|
|
|
struct byte_source*src,
|
|
|
|
|
vpiHandle arg)
|
|
|
|
|
{
|
2009-02-27 21:34:48 +01:00
|
|
|
vpiHandle scope = vpi_handle(vpiScope, callh);
|
2006-08-12 05:38:12 +02:00
|
|
|
int time_units = vpi_get(vpiTimeUnit, scope);
|
|
|
|
|
double scale;
|
|
|
|
|
|
|
|
|
|
s_vpi_value val;
|
|
|
|
|
|
|
|
|
|
val.format = vpiRealVal;
|
|
|
|
|
val.value.real = float_string(src);
|
|
|
|
|
|
|
|
|
|
/* Round the value to the specified precision. Handle this bit
|
|
|
|
|
by shifting the decimal point to the precision where we
|
|
|
|
|
want to round, do the rounding, then shift the point back */
|
|
|
|
|
scale = pow(10.0, timeformat_info.prec);
|
|
|
|
|
val.value.real = round(val.value.real*scale) / scale;
|
|
|
|
|
|
|
|
|
|
/* Change the units from the timeformat to the timescale. */
|
|
|
|
|
scale = pow(10.0, timeformat_info.units - time_units);
|
|
|
|
|
val.value.real *= scale;
|
|
|
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-12 06:16:07 +02:00
|
|
|
/*
|
|
|
|
|
* Match the %s format by loading a string of non-space characters.
|
|
|
|
|
*/
|
|
|
|
|
static int scan_format_string(struct byte_source*src, vpiHandle arg)
|
|
|
|
|
{
|
|
|
|
|
char*tmp = malloc(1);
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
s_vpi_value val;
|
|
|
|
|
int ch;
|
|
|
|
|
|
|
|
|
|
*tmp = 0;
|
|
|
|
|
|
|
|
|
|
ch = byte_getc(src);
|
2009-04-04 00:26:27 +02:00
|
|
|
/* Skip leading space. */
|
|
|
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
|
|
2006-08-12 06:16:07 +02:00
|
|
|
while (!isspace(ch)) {
|
2009-02-27 21:34:48 +01:00
|
|
|
if (ch == EOF) break;
|
2006-08-12 06:16:07 +02:00
|
|
|
|
2008-02-18 21:48:36 +01:00
|
|
|
tmp = realloc(tmp, len+2);
|
2006-08-12 06:16:07 +02:00
|
|
|
tmp[len++] = ch;
|
|
|
|
|
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
if (ch == EOF && len == 0) return 0;
|
2006-08-12 06:16:07 +02:00
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
if (ch != EOF) byte_ungetc(src, ch);
|
2006-08-12 06:16:07 +02:00
|
|
|
|
|
|
|
|
tmp[len] = 0;
|
|
|
|
|
val.format = vpiStringVal;
|
|
|
|
|
val.value.str = tmp;
|
|
|
|
|
vpi_put_value(arg, &val, 0, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
free(tmp);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2009-02-27 21:34:48 +01:00
|
|
|
static int scan_format(vpiHandle callh, struct byte_source*src, vpiHandle argv)
|
2006-08-03 07:06:04 +02:00
|
|
|
{
|
|
|
|
|
s_vpi_value val;
|
|
|
|
|
vpiHandle item;
|
|
|
|
|
|
|
|
|
|
char*fmt, *fmtp;
|
|
|
|
|
int rc = 0;
|
|
|
|
|
int ch;
|
|
|
|
|
|
2007-04-16 02:09:58 +02:00
|
|
|
int match_fail = 0;
|
|
|
|
|
|
|
|
|
|
/* Get the format string. */
|
2006-08-03 07:06:04 +02:00
|
|
|
item = vpi_scan(argv);
|
|
|
|
|
assert(item);
|
|
|
|
|
|
|
|
|
|
val.format = vpiStringVal;
|
|
|
|
|
vpi_get_value(item, &val);
|
|
|
|
|
fmtp = fmt = strdup(val.value.str);
|
|
|
|
|
|
2009-07-23 23:34:54 +02:00
|
|
|
/* See if we are at EOF before we even start. */
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
if (ch == EOF) {
|
|
|
|
|
rc = EOF;
|
|
|
|
|
match_fail = 1;
|
|
|
|
|
}
|
|
|
|
|
byte_ungetc(src, ch);
|
|
|
|
|
|
2007-04-16 02:09:58 +02:00
|
|
|
while ( fmtp && *fmtp != 0 && !match_fail) {
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2010-04-11 20:00:39 +02:00
|
|
|
if (isspace((int)*fmtp)) {
|
2006-08-03 07:06:04 +02:00
|
|
|
/* White space matches a string of white space in
|
|
|
|
|
the input. The number of spaces is not
|
2008-06-12 19:04:29 +02:00
|
|
|
relevant, and the match may be 0 or more
|
2006-08-03 07:06:04 +02:00
|
|
|
spaces. */
|
2010-04-11 20:00:39 +02:00
|
|
|
while (*fmtp && isspace((int)*fmtp)) fmtp += 1;
|
2006-08-03 07:06:04 +02:00
|
|
|
|
|
|
|
|
ch = byte_getc(src);
|
2009-02-27 21:34:48 +01:00
|
|
|
while (isspace(ch)) ch = byte_getc(src);
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
if (ch != EOF) byte_ungetc(src, ch);
|
2006-08-03 07:06:04 +02:00
|
|
|
|
|
|
|
|
} else if (*fmtp != '%') {
|
2007-03-22 17:08:14 +01:00
|
|
|
/* Characters other than % match themselves. */
|
2006-08-03 07:06:04 +02:00
|
|
|
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. */
|
|
|
|
|
|
|
|
|
|
int suppress_flag = 0;
|
|
|
|
|
int length_field = -1;
|
|
|
|
|
int code = 0;
|
|
|
|
|
PLI_INT32 value;
|
|
|
|
|
|
|
|
|
|
char*tmp;
|
|
|
|
|
|
|
|
|
|
fmtp += 1;
|
|
|
|
|
if (*fmtp == '*') {
|
|
|
|
|
suppress_flag = 1;
|
|
|
|
|
fmtp += 1;
|
|
|
|
|
}
|
2010-04-11 20:00:39 +02:00
|
|
|
if (isdigit((int)*fmtp)) {
|
2006-08-03 07:06:04 +02:00
|
|
|
length_field = 0;
|
2010-04-11 20:00:39 +02:00
|
|
|
while (isdigit((int)*fmtp)) {
|
2006-08-03 07:06:04 +02:00
|
|
|
length_field *= 10;
|
|
|
|
|
length_field += *fmtp - '0';
|
|
|
|
|
fmtp += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
code = *fmtp;
|
|
|
|
|
fmtp += 1;
|
|
|
|
|
|
2007-04-16 02:09:58 +02:00
|
|
|
/* The format string is parsed:
|
|
|
|
|
- length_field is the length,
|
|
|
|
|
- code is the format code character,
|
|
|
|
|
- suppress_flag is true if the length is an arg.
|
|
|
|
|
Now we interpret the code. */
|
2006-08-03 07:06:04 +02:00
|
|
|
switch (code) {
|
2007-04-16 02:09:58 +02:00
|
|
|
|
|
|
|
|
/* Read a '%' character from the input. */
|
2006-08-03 07:06:04 +02:00
|
|
|
case '%':
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
if (ch != '%') {
|
|
|
|
|
byte_ungetc(src, ch);
|
|
|
|
|
fmtp = 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2007-04-16 02:09:58 +02:00
|
|
|
/* Read a binary integer. If there is a match,
|
|
|
|
|
store that integer in the next argument and
|
|
|
|
|
increment the completion count. */
|
2006-08-03 07:06:04 +02:00
|
|
|
case 'b':
|
|
|
|
|
/* binary integer */
|
|
|
|
|
tmp = malloc(2);
|
|
|
|
|
value = 0;
|
|
|
|
|
tmp[0] = 0;
|
2007-04-16 02:09:58 +02:00
|
|
|
match_fail = 1;
|
2006-08-03 07:06:04 +02:00
|
|
|
|
|
|
|
|
ch = byte_getc(src);
|
2009-04-04 00:26:27 +02:00
|
|
|
/* Skip leading space. */
|
|
|
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
while (strchr("01xXzZ?_", ch)) {
|
2007-04-16 02:09:58 +02:00
|
|
|
match_fail = 0;
|
2009-02-27 21:34:48 +01:00
|
|
|
if (ch == '?') ch = 'x';
|
2006-08-03 07:06:04 +02:00
|
|
|
if (ch != '_') {
|
|
|
|
|
ch = tolower(ch);
|
|
|
|
|
tmp[value++] = ch;
|
|
|
|
|
tmp = realloc(tmp, value+1);
|
|
|
|
|
tmp[value] = 0;
|
|
|
|
|
}
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
}
|
|
|
|
|
byte_ungetc(src, ch);
|
|
|
|
|
|
2007-04-16 02:09:58 +02:00
|
|
|
if (match_fail) {
|
|
|
|
|
free(tmp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Matched a binary value, put it to an argument. */
|
2006-08-03 07:06:04 +02:00
|
|
|
item = vpi_scan(argv);
|
|
|
|
|
assert(item);
|
|
|
|
|
|
|
|
|
|
val.format = vpiBinStrVal;
|
|
|
|
|
val.value.str = tmp;
|
|
|
|
|
vpi_put_value(item, &val, 0, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
free(tmp);
|
|
|
|
|
rc += 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2006-08-12 06:16:07 +02:00
|
|
|
case 'c':
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
item = vpi_scan(argv);
|
|
|
|
|
assert(item);
|
|
|
|
|
|
|
|
|
|
val.format = vpiIntVal;
|
|
|
|
|
val.value.integer = ch;
|
|
|
|
|
vpi_put_value(item, &val, 0, vpiNoDelay);
|
|
|
|
|
rc += 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
case 'd':
|
2008-07-28 20:24:08 +02:00
|
|
|
/* decimal integer */
|
|
|
|
|
tmp = malloc(2);
|
|
|
|
|
value = 0;
|
|
|
|
|
tmp[0] = 0;
|
2007-04-16 02:09:58 +02:00
|
|
|
match_fail = 1;
|
2008-07-28 20:24:08 +02:00
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
ch = byte_getc(src);
|
2009-04-04 00:26:27 +02:00
|
|
|
/* Skip leading space. */
|
|
|
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
|
|
2008-07-28 20:50:00 +02:00
|
|
|
while (isdigit(ch) || ch == '_' || (value == 0 && ch == '-')) {
|
2007-04-16 02:09:58 +02:00
|
|
|
match_fail = 0;
|
2008-07-28 20:50:00 +02:00
|
|
|
if (ch != '_') {
|
|
|
|
|
tmp[value++] = ch;
|
|
|
|
|
tmp = realloc(tmp, value+1);
|
|
|
|
|
tmp[value] = 0;
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
}
|
2006-08-03 07:06:04 +02:00
|
|
|
}
|
2008-07-28 20:24:08 +02:00
|
|
|
byte_ungetc(src, ch);
|
2007-04-16 02:09:58 +02:00
|
|
|
|
2008-07-28 20:24:08 +02:00
|
|
|
if (match_fail) {
|
|
|
|
|
free(tmp);
|
2007-04-16 02:09:58 +02:00
|
|
|
break;
|
2008-07-28 20:24:08 +02:00
|
|
|
}
|
2007-04-16 02:09:58 +02:00
|
|
|
|
2008-07-28 20:24:08 +02:00
|
|
|
/* Matched a decimal value, put it to an argument. */
|
2006-08-03 07:06:04 +02:00
|
|
|
item = vpi_scan(argv);
|
|
|
|
|
assert(item);
|
|
|
|
|
|
2008-07-28 20:24:08 +02:00
|
|
|
val.format = vpiDecStrVal;
|
|
|
|
|
val.value.str = tmp;
|
2006-08-03 07:06:04 +02:00
|
|
|
vpi_put_value(item, &val, 0, vpiNoDelay);
|
2008-07-28 20:24:08 +02:00
|
|
|
|
|
|
|
|
free(tmp);
|
2006-08-03 07:06:04 +02:00
|
|
|
rc += 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2006-08-12 05:38:12 +02:00
|
|
|
case 'e':
|
|
|
|
|
case 'f':
|
|
|
|
|
case 'g':
|
|
|
|
|
item = vpi_scan(argv);
|
|
|
|
|
assert(item);
|
|
|
|
|
rc += scan_format_float(src, item);
|
|
|
|
|
break;
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
case 'h':
|
|
|
|
|
case 'x':
|
2007-04-16 02:09:58 +02:00
|
|
|
match_fail = 1;
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
/* Hex integer */
|
|
|
|
|
tmp = malloc(2);
|
|
|
|
|
value = 0;
|
|
|
|
|
tmp[0] = 0;
|
|
|
|
|
|
|
|
|
|
ch = byte_getc(src);
|
2009-04-04 00:26:27 +02:00
|
|
|
/* Skip leading space. */
|
|
|
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
while (strchr("0123456789abcdefABCDEFxXzZ?_", ch)) {
|
2007-04-16 02:09:58 +02:00
|
|
|
match_fail = 0;
|
2009-02-27 21:34:48 +01:00
|
|
|
if (ch == '?') ch = 'x';
|
2006-08-03 07:06:04 +02:00
|
|
|
if (ch != '_') {
|
|
|
|
|
ch = tolower(ch);
|
|
|
|
|
tmp[value++] = ch;
|
|
|
|
|
tmp = realloc(tmp, value+1);
|
|
|
|
|
tmp[value] = 0;
|
|
|
|
|
}
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
}
|
|
|
|
|
byte_ungetc(src, ch);
|
|
|
|
|
|
2007-04-16 02:09:58 +02:00
|
|
|
if (match_fail) {
|
|
|
|
|
free(tmp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
item = vpi_scan(argv);
|
|
|
|
|
assert(item);
|
|
|
|
|
|
|
|
|
|
val.format = vpiHexStrVal;
|
|
|
|
|
val.value.str = tmp;
|
|
|
|
|
vpi_put_value(item, &val, 0, vpiNoDelay);
|
|
|
|
|
free(tmp);
|
|
|
|
|
rc += 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'o':
|
2007-04-16 02:09:58 +02:00
|
|
|
match_fail = 1;
|
2006-08-03 07:06:04 +02:00
|
|
|
/* binary integer */
|
|
|
|
|
tmp = malloc(2);
|
|
|
|
|
value = 0;
|
|
|
|
|
tmp[0] = 0;
|
|
|
|
|
|
|
|
|
|
ch = byte_getc(src);
|
2009-04-04 00:26:27 +02:00
|
|
|
/* Skip leading space. */
|
|
|
|
|
while (isspace(ch)) ch = byte_getc(src);
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
while (strchr("01234567xXzZ?_", ch)) {
|
2007-04-16 02:09:58 +02:00
|
|
|
match_fail = 0;
|
2009-02-27 21:34:48 +01:00
|
|
|
if (ch == '?') ch = 'x';
|
2006-08-03 07:06:04 +02:00
|
|
|
if (ch != '_') {
|
|
|
|
|
ch = tolower(ch);
|
|
|
|
|
tmp[value++] = ch;
|
|
|
|
|
tmp = realloc(tmp, value+1);
|
|
|
|
|
tmp[value] = 0;
|
|
|
|
|
}
|
|
|
|
|
ch = byte_getc(src);
|
|
|
|
|
}
|
|
|
|
|
byte_ungetc(src, ch);
|
|
|
|
|
|
2007-04-16 02:09:58 +02:00
|
|
|
if (match_fail) {
|
|
|
|
|
free(tmp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
item = vpi_scan(argv);
|
|
|
|
|
assert(item);
|
|
|
|
|
|
|
|
|
|
val.format = vpiOctStrVal;
|
|
|
|
|
val.value.str = tmp;
|
|
|
|
|
vpi_put_value(item, &val, 0, vpiNoDelay);
|
|
|
|
|
free(tmp);
|
|
|
|
|
rc += 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2006-08-12 06:16:07 +02:00
|
|
|
case 's':
|
|
|
|
|
item = vpi_scan(argv);
|
|
|
|
|
assert(item);
|
|
|
|
|
rc += scan_format_string(src, item);
|
|
|
|
|
break;
|
|
|
|
|
|
2006-08-12 05:38:12 +02:00
|
|
|
case 't':
|
|
|
|
|
item = vpi_scan(argv);
|
|
|
|
|
assert(item);
|
2009-02-27 21:34:48 +01:00
|
|
|
rc += scan_format_float_time(callh, src, item);
|
2006-08-12 05:38:12 +02:00
|
|
|
break;
|
|
|
|
|
|
2006-08-03 07:06:04 +02:00
|
|
|
default:
|
2009-02-27 21:34:48 +01:00
|
|
|
vpi_printf("ERROR: %s:%d: ",
|
|
|
|
|
vpi_get_str(vpiFile, callh),
|
|
|
|
|
(int)vpi_get(vpiLineNo, callh));
|
2006-08-03 07:06:04 +02:00
|
|
|
vpi_printf("$scanf: Unknown format code: %c\n", code);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(fmt);
|
|
|
|
|
|
|
|
|
|
vpi_free_object(argv);
|
|
|
|
|
|
|
|
|
|
val.format = vpiIntVal;
|
|
|
|
|
val.value.integer = rc;
|
2009-02-27 21:34:48 +01:00
|
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
2006-08-03 07:06:04 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
/*
|
|
|
|
|
* Is the object a variable/register or a piece of one.
|
|
|
|
|
*/
|
|
|
|
|
static int is_assignable_obj(vpiHandle obj)
|
2006-08-03 07:06:04 +02:00
|
|
|
{
|
2009-02-27 21:34:48 +01:00
|
|
|
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;
|
2010-05-23 23:30:48 +02:00
|
|
|
}
|
2009-02-27 21:34:48 +01:00
|
|
|
break;
|
|
|
|
|
case vpiPartSelect:
|
|
|
|
|
if (! is_assignable_obj(vpi_handle(vpiParent, obj))) break;
|
|
|
|
|
case vpiIntegerVar:
|
|
|
|
|
case vpiRealVar:
|
|
|
|
|
case vpiReg:
|
|
|
|
|
case vpiTimeVar:
|
|
|
|
|
rtn = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
return rtn;
|
|
|
|
|
}
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
static int sys_check_args(vpiHandle callh, vpiHandle argv, 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;
|
|
|
|
|
}
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-04 18:31:01 +02:00
|
|
|
static PLI_INT32 sys_fscanf_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
2009-02-27 21:34:48 +01:00
|
|
|
{
|
|
|
|
|
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;
|
2008-02-18 19:15:59 +01:00
|
|
|
}
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
/* 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);
|
2009-08-12 01:35:49 +02:00
|
|
|
vpi_free_object(argv);
|
2009-02-27 21:34:48 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sys_check_args(callh, argv, name)) vpi_control(vpiFinish, 1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-04 18:31:01 +02:00
|
|
|
static PLI_INT32 sys_fscanf_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
2009-02-27 21:34:48 +01:00
|
|
|
{
|
|
|
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
|
|
|
s_vpi_value val;
|
|
|
|
|
struct byte_source src;
|
|
|
|
|
FILE *fd;
|
2009-07-31 22:06:49 +02:00
|
|
|
errno = 0;
|
2009-02-27 21:34:48 +01:00
|
|
|
|
|
|
|
|
val.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(vpi_scan(argv), &val);
|
2006-08-03 07:06:04 +02:00
|
|
|
fd = vpi_get_file(val.value.integer);
|
2009-02-27 21:34:48 +01:00
|
|
|
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",
|
2009-12-10 21:49:39 +01:00
|
|
|
(int)val.value.integer, name);
|
2009-07-31 22:06:49 +02:00
|
|
|
errno = EBADF;
|
2009-02-27 21:34:48 +01:00
|
|
|
val.format = vpiIntVal;
|
|
|
|
|
val.value.integer = EOF;
|
|
|
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
2009-07-31 22:06:49 +02:00
|
|
|
vpi_free_object(argv);
|
2009-02-27 21:34:48 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
2006-08-03 07:06:04 +02:00
|
|
|
|
|
|
|
|
src.str = 0;
|
|
|
|
|
src.fd = fd;
|
2009-02-27 21:34:48 +01:00
|
|
|
scan_format(callh, &src, argv);
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2008-02-16 00:05:39 +01:00
|
|
|
return 0;
|
2006-08-03 07:06:04 +02:00
|
|
|
}
|
|
|
|
|
|
2010-10-04 18:31:01 +02:00
|
|
|
static PLI_INT32 sys_sscanf_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
2006-08-03 07:06:04 +02:00
|
|
|
{
|
2009-02-27 21:34:48 +01:00
|
|
|
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);
|
2009-08-12 01:35:49 +02:00
|
|
|
vpi_free_object(argv);
|
2009-02-27 21:34:48 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sys_check_args(callh, argv, name)) vpi_control(vpiFinish, 1);
|
2006-08-03 07:06:04 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-04 18:31:01 +02:00
|
|
|
static PLI_INT32 sys_sscanf_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
2006-08-03 07:06:04 +02:00
|
|
|
{
|
2009-02-27 21:34:48 +01:00
|
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
2006-08-03 07:06:04 +02:00
|
|
|
s_vpi_value val;
|
|
|
|
|
struct byte_source src;
|
2009-02-27 21:34:48 +01:00
|
|
|
char *str;
|
2006-08-03 07:06:04 +02:00
|
|
|
|
|
|
|
|
val.format = vpiStringVal;
|
2009-02-27 21:34:48 +01:00
|
|
|
vpi_get_value(vpi_scan(argv), &val);
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
str = strdup(val.value.str);
|
2006-08-03 07:06:04 +02:00
|
|
|
src.str = str;
|
|
|
|
|
src.fd = 0;
|
2009-02-27 21:34:48 +01:00
|
|
|
scan_format(callh, &src, argv);
|
2006-08-03 07:06:04 +02:00
|
|
|
free(str);
|
|
|
|
|
|
2009-02-27 21:34:48 +01:00
|
|
|
return 0;
|
2006-08-03 07:06:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void sys_scanf_register()
|
|
|
|
|
{
|
|
|
|
|
s_vpi_systf_data tf_data;
|
2010-04-12 08:39:08 +02:00
|
|
|
vpiHandle res;
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2008-09-30 03:06:47 +02:00
|
|
|
/*============================== fscanf */
|
2009-02-27 21:34:48 +01:00
|
|
|
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";
|
2010-04-12 08:39:08 +02:00
|
|
|
res = vpi_register_systf(&tf_data);
|
|
|
|
|
vpip_make_systf_system_defined(res);
|
2006-08-03 07:06:04 +02:00
|
|
|
|
2008-09-30 03:06:47 +02:00
|
|
|
/*============================== sscanf */
|
2009-02-27 21:34:48 +01:00
|
|
|
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";
|
2010-04-12 08:39:08 +02:00
|
|
|
res = vpi_register_systf(&tf_data);
|
|
|
|
|
vpip_make_systf_system_defined(res);
|
2006-08-03 07:06:04 +02:00
|
|
|
}
|