version 1.11.22.1, 2008/03/23 00:25:43 |
version 1.12, 2008/01/28 06:16:13 |
|
|
/* execute.c,v 1.11 2004/01/27 20:30:30 jsm Exp */ |
/* $NetBSD$ */ |
|
|
/* |
/* |
* Copyright (c) 1980, 1993 |
* Copyright (c) 1980, 1993 |
|
|
#if 0 |
#if 0 |
static char sccsid[] = "@(#)execute.c 8.1 (Berkeley) 5/31/93"; |
static char sccsid[] = "@(#)execute.c 8.1 (Berkeley) 5/31/93"; |
#else |
#else |
__RCSID("execute.c,v 1.11 2004/01/27 20:30:30 jsm Exp"); |
__RCSID("$NetBSD$"); |
#endif |
#endif |
#endif /* not lint */ |
#endif /* not lint */ |
|
|
|
#include "monop.ext" |
#include <fcntl.h> |
#include <fcntl.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <limits.h> |
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/time.h> |
#include <sys/time.h> |
#include <time.h> |
#include <time.h> |
#include <errno.h> |
|
|
|
#include "deck.h" |
#define SEGSIZE 8192 |
#include "monop.h" |
|
|
|
#define MIN_FORMAT_VERSION 1 |
|
#define CUR_FORMAT_VERSION 1 |
|
#define MAX_FORMAT_VERSION 1 |
|
|
|
typedef struct stat STAT; |
typedef struct stat STAT; |
typedef struct tm TIME; |
typedef struct tm TIME; |
Line 61 typedef struct tm TIME; |
|
Line 55 typedef struct tm TIME; |
|
static char buf[257]; |
static char buf[257]; |
|
|
static bool new_play; /* set if move on to new player */ |
static bool new_play; /* set if move on to new player */ |
|
extern void *heapstart; |
|
|
static void show_move(void); |
static void show_move(void); |
|
|
static void restore_reset(void); |
|
static int restore_parseline(char *txt); |
|
static int restore_toplevel_attr(const char *attribute, char *txt); |
|
static int restore_player_attr(const char *attribute, char *txt); |
|
static int restore_deck_attr(const char *attribute, char *txt); |
|
static int restore_square_attr(const char *attribute, char *txt); |
|
static int getnum(const char *what, char *txt, int min, int max, int *ret); |
|
static int getnum_withbrace(const char *what, char *txt, int min, int max, |
|
int *ret); |
|
|
|
/* |
/* |
* This routine executes the given command by index number |
* This routine executes the given command by index number |
*/ |
*/ |
void |
void |
execute(com_num) |
execute(com_num) |
int com_num; |
int com_num; |
{ |
{ |
new_play = FALSE; /* new_play is true if fixing */ |
new_play = FALSE; /* new_play is true if fixing */ |
(*func[com_num])(); |
(*func[com_num])(); |
|
|
* This routine moves a piece around. |
* This routine moves a piece around. |
*/ |
*/ |
void |
void |
do_move() |
do_move() |
{ |
{ |
int r1, r2; |
int r1, r2; |
bool was_jail; |
bool was_jail; |
|
|
*/ |
*/ |
void |
void |
move(rl) |
move(rl) |
int rl; |
int rl; |
{ |
{ |
int old_loc; |
int old_loc; |
|
|
|
|
* This routine shows the results of a move |
* This routine shows the results of a move |
*/ |
*/ |
static void |
static void |
show_move() |
show_move() |
{ |
{ |
SQUARE *sqp; |
SQUARE *sqp; |
|
|
|
|
printf("That is a safe place\n"); |
printf("That is a safe place\n"); |
break; |
break; |
case CC: |
case CC: |
cc(); |
cc(); break; |
break; |
|
case CHANCE: |
case CHANCE: |
chance(); |
chance(); break; |
break; |
|
case INC_TAX: |
case INC_TAX: |
inc_tax(); |
inc_tax(); break; |
break; |
|
case GOTO_J: |
case GOTO_J: |
goto_jail(); |
goto_jail(); break; |
break; |
|
case LUX_TAX: |
case LUX_TAX: |
lux_tax(); |
lux_tax(); break; |
break; |
|
case PRPTY: |
case PRPTY: |
case RR: |
case RR: |
case UTIL: |
case UTIL: |
|
|
} |
} |
|
|
/* |
/* |
* Reset the game state. |
|
*/ |
|
static void |
|
reset_game(void) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < N_SQRS; i++) { |
|
board[i].owner = -1; |
|
if (board[i].type == PRPTY) { |
|
board[i].desc->morg = 0; |
|
board[i].desc->houses = 0; |
|
} else if (board[i].type == RR || board[i].type == UTIL) { |
|
board[i].desc->morg = 0; |
|
} |
|
} |
|
|
|
for (i = 0; i < 2; i++) { |
|
deck[i].top_card = 0; |
|
deck[i].gojf_used = FALSE; |
|
} |
|
|
|
if (play) { |
|
for (i = 0; i < num_play; i++) { |
|
free(play[i].name); |
|
play[i].name = NULL; |
|
} |
|
free(play); |
|
play = NULL; |
|
} |
|
|
|
for (i = 0; i < MAX_PL+2; i++) { |
|
name_list[i] = NULL; |
|
} |
|
|
|
cur_p = NULL; |
|
num_play = 0; |
|
player = 0; |
|
num_doub = 0; |
|
fixing = FALSE; |
|
trading = FALSE; |
|
told_em = FALSE; |
|
spec = FALSE; |
|
} |
|
|
|
|
|
/* |
|
* This routine saves the current game for use at a later date |
* This routine saves the current game for use at a later date |
*/ |
*/ |
void |
void |
save() |
save() |
{ |
{ |
char *sp; |
char *sp; |
FILE *outf; |
int outf, num; |
time_t t; |
time_t t; |
struct stat sb; |
struct stat sb; |
int i, j; |
char *start, *end; |
|
|
printf("Which file do you wish to save it in? "); |
printf("Which file do you wish to save it in? "); |
fgets(buf, sizeof(buf), stdin); |
sp = buf; |
if (feof(stdin)) |
while ((*sp++=getchar()) != '\n') |
return; |
continue; |
sp = strchr(buf, '\n'); |
*--sp = '\0'; |
if (sp) |
|
*sp = '\0'; |
|
|
|
/* |
/* |
* check for existing files, and confirm overwrite if needed |
* check for existing files, and confirm overwrite if needed |
*/ |
*/ |
|
|
if (stat(buf, &sb) == 0 |
if (stat(buf, &sb) > -1 |
&& getyn("File exists. Do you wish to overwrite? ") > 0) |
&& getyn("File exists. Do you wish to overwrite? ") > 0) |
return; |
return; |
|
|
outf = fopen(buf, "w"); |
if ((outf=creat(buf, 0644)) < 0) { |
if (outf == NULL) { |
perror(buf); |
warn("%s", buf); |
|
return; |
return; |
} |
} |
printf("\"%s\" ", buf); |
printf("\"%s\" ", buf); |
time(&t); /* get current time */ |
time(&t); /* get current time */ |
|
|
/* Header */ |
|
fprintf(outf, "NetBSD monop format v%d\n", CUR_FORMAT_VERSION); |
|
fprintf(outf, "time %s", ctime(&t)); /* ctime includes a \n */ |
|
fprintf(outf, "numplayers %d\n", num_play); |
|
fprintf(outf, "currentplayer %d\n", player); |
|
fprintf(outf, "doubles %d\n", num_doub); |
|
|
|
/* Players */ |
|
for (i = 0; i < num_play; i++) { |
|
fprintf(outf, "player %d {\n", i); |
|
fprintf(outf, " name %s\n", name_list[i]); |
|
fprintf(outf, " money %d\n", play[i].money); |
|
fprintf(outf, " loc %d\n", play[i].loc); |
|
fprintf(outf, " num_gojf %d\n", play[i].num_gojf); |
|
fprintf(outf, " in_jail %d\n", play[i].in_jail); |
|
fprintf(outf, "}\n"); |
|
} |
|
|
|
/* Decks */ |
|
for (i = 0; i < 2; i++) { |
|
fprintf(outf, "deck %d {\n", i); |
|
fprintf(outf, " numcards %d\n", deck[i].num_cards); |
|
fprintf(outf, " topcard %d\n", deck[i].top_card); |
|
fprintf(outf, " gojf_used %d\n", deck[i].gojf_used); |
|
fprintf(outf, " cards"); |
|
for (j = 0; j < deck[i].num_cards; j++) |
|
fprintf(outf, " %d", deck[i].cards[j]); |
|
fprintf(outf, "\n"); |
|
fprintf(outf, "}\n"); |
|
} |
|
|
|
/* Board */ |
|
for (i = 0; i < N_SQRS; i++) { |
|
fprintf(outf, "square %d {\n", i); |
|
fprintf(outf, "owner %d\n", board[i].owner); |
|
if (board[i].owner < 0) { |
|
/* nothing */ |
|
} else if (board[i].type == PRPTY) { |
|
fprintf(outf, "morg %d\n", board[i].desc->morg); |
|
fprintf(outf, "houses %d\n", board[i].desc->houses); |
|
} else if (board[i].type == RR || board[i].type == UTIL) { |
|
fprintf(outf, "morg %d\n", board[i].desc->morg); |
|
} |
|
fprintf(outf, "}\n"); |
|
} |
|
if (ferror(outf) || fflush(outf)) |
|
warnx("write error"); |
|
fclose(outf); |
|
|
|
strcpy(buf, ctime(&t)); |
strcpy(buf, ctime(&t)); |
for (sp = buf; *sp != '\n'; sp++) |
for (sp = buf; *sp != '\n'; sp++) |
continue; |
continue; |
*sp = '\0'; |
*sp = '\0'; |
|
start = heapstart; |
|
end = sbrk(0); |
|
while (start < end) { /* write out entire data space */ |
|
num = start + 16 * 1024 > end ? end - start : 16 * 1024; |
|
write(outf, start, num); |
|
start += num; |
|
} |
|
close(outf); |
printf("[%s]\n", buf); |
printf("[%s]\n", buf); |
} |
} |
|
|
|
|
* This routine restores an old game from a file |
* This routine restores an old game from a file |
*/ |
*/ |
void |
void |
restore(void) |
restore() |
{ |
{ |
char *sp; |
char *sp; |
|
|
for (;;) { |
printf("Which file do you wish to restore from? "); |
printf("Which file do you wish to restore from? "); |
for (sp = buf; (*sp=getchar()) != '\n'; sp++) |
fgets(buf, sizeof(buf), stdin); |
continue; |
if (feof(stdin)) |
*sp = '\0'; |
return; |
rest_f(buf); |
sp = strchr(buf, '\n'); |
|
if (sp) |
|
*sp = '\0'; |
|
if (rest_f(buf) == 0) |
|
break; |
|
} |
|
} |
} |
|
|
/* |
/* |
* This does the actual restoring. It returns zero on success, |
* This does the actual restoring. It returns TRUE if the |
* and -1 on failure. |
* backup was successful, else false. |
*/ |
*/ |
int |
int |
rest_f(const char *file) |
rest_f(file) |
|
const char *file; |
{ |
{ |
char *sp; |
char *sp; |
FILE *inf; |
int inf, num; |
char xbuf[80]; |
char xbuf[80]; |
|
char *start, *end; |
STAT sbuf; |
STAT sbuf; |
char readbuf[512]; |
|
int ret = 0; |
|
|
|
inf = fopen(file, "r"); |
if ((inf=open(file, O_RDONLY)) < 0) { |
if (inf == NULL) { |
perror(file); |
warn("%s", file); |
return FALSE; |
return -1; |
|
} |
} |
printf("\"%s\" ", file); |
printf("\"%s\" ", file); |
if (fstat(fileno(inf), &sbuf) < 0) { |
if (fstat(inf, &sbuf) < 0) { /* get file stats */ |
err(1, "%s: fstat", file); |
perror(file); |
|
exit(1); |
|
} |
|
start = heapstart; |
|
brk(end = start + sbuf.st_size); |
|
while (start < end) { /* write out entire data space */ |
|
num = start + 16 * 1024 > end ? end - start : 16 * 1024; |
|
read(inf, start, num); |
|
start += num; |
} |
} |
|
close(inf); |
/* Clear the game state to prevent brokenness on misordered files. */ |
|
reset_game(); |
|
|
|
/* Reset the parser */ |
|
restore_reset(); |
|
|
|
/* Note: can't use buf[], file might point at it. (Lame...) */ |
|
while (fgets(readbuf, sizeof(readbuf), inf)) { |
|
/* |
|
* The input buffer is long enough to handle anything |
|
* that's supposed to be in the output buffer, so if |
|
* we get a partial line, complain. |
|
*/ |
|
sp = strchr(readbuf, '\n'); |
|
if (sp == NULL) { |
|
printf("file is corrupt: long lines.\n"); |
|
ret = -1; |
|
break; |
|
} |
|
*sp = '\0'; |
|
|
|
if (restore_parseline(readbuf)) { |
|
ret = -1; |
|
break; |
|
} |
|
} |
|
|
|
if (ferror(inf)) |
|
warnx("%s: read error", file); |
|
fclose(inf); |
|
|
|
if (ret < 0) |
|
return -1; |
|
|
|
name_list[num_play] = "done"; |
|
|
|
if (play == NULL || cur_p == NULL || num_play < 2) { |
|
printf("save file is incomplete.\n"); |
|
return -1; |
|
} |
|
|
|
/* |
|
* We could at this point crosscheck the following: |
|
* - there are only two GOJF cards floating around |
|
* - total number of houses and hotels does not exceed maximums |
|
* - no props are both built and mortgaged |
|
* but for now we don't. |
|
*/ |
|
|
|
strcpy(xbuf, ctime(&sbuf.st_mtime)); |
strcpy(xbuf, ctime(&sbuf.st_mtime)); |
for (sp = xbuf; *sp != '\n'; sp++) |
for (sp = xbuf; *sp != '\n'; sp++) |
continue; |
continue; |
*sp = '\0'; |
*sp = '\0'; |
printf("[%s]\n", xbuf); |
printf("[%s]\n", xbuf); |
return 0; |
return TRUE; |
} |
|
|
|
/* |
|
* State of the restore parser |
|
*/ |
|
static int restore_version; |
|
static enum { |
|
RI_NONE, |
|
RI_PLAYER, |
|
RI_DECK, |
|
RI_SQUARE |
|
} restore_item; |
|
static int restore_itemnum; |
|
|
|
/* |
|
* Reset the restore parser |
|
*/ |
|
static void |
|
restore_reset(void) |
|
{ |
|
restore_version = -1; |
|
restore_item = RI_NONE; |
|
restore_itemnum = -1; |
|
} |
|
|
|
/* |
|
* Handle one line of the save file |
|
*/ |
|
static int |
|
restore_parseline(char *txt) |
|
{ |
|
char *attribute; |
|
char *s; |
|
|
|
if (restore_version < 0) { |
|
/* Haven't seen the header yet. Demand it right away. */ |
|
if (!strncmp(txt, "NetBSD monop format v", 21)) { |
|
return getnum("format version", txt+21, |
|
MIN_FORMAT_VERSION, |
|
MAX_FORMAT_VERSION, |
|
&restore_version); |
|
} |
|
printf("file is not a monop save file.\n"); |
|
return -1; |
|
} |
|
|
|
/* Check for lines that are right braces. */ |
|
if (!strcmp(txt, "}")) { |
|
if (restore_item == RI_NONE) { |
|
printf("mismatched close brace.\n"); |
|
return -1; |
|
} |
|
restore_item = RI_NONE; |
|
restore_itemnum = -1; |
|
return 0; |
|
} |
|
|
|
/* Any other line must begin with a word, which is the attribute. */ |
|
s = txt; |
|
while (*s==' ') |
|
s++; |
|
attribute = s; |
|
s = strchr(attribute, ' '); |
|
if (s == NULL) { |
|
printf("file is corrupt: attribute %s lacks value.\n", |
|
attribute); |
|
return -1; |
|
} |
|
*(s++) = '\0'; |
|
while (*s==' ') |
|
s++; |
|
/* keep the remaining text for further handling */ |
|
txt = s; |
|
|
|
switch (restore_item) { |
|
case RI_NONE: |
|
/* toplevel attributes */ |
|
return restore_toplevel_attr(attribute, txt); |
|
|
|
case RI_PLAYER: |
|
/* player attributes */ |
|
return restore_player_attr(attribute, txt); |
|
|
|
case RI_DECK: |
|
/* deck attributes */ |
|
return restore_deck_attr(attribute, txt); |
|
|
|
case RI_SQUARE: |
|
/* board square attributes */ |
|
return restore_square_attr(attribute, txt); |
|
} |
|
/* NOTREACHED */ |
|
printf("internal logic error\n"); |
|
return -1; |
|
} |
|
|
|
static int |
|
restore_toplevel_attr(const char *attribute, char *txt) |
|
{ |
|
if (!strcmp(attribute, "time")) { |
|
/* nothing */ |
|
} else if (!strcmp(attribute, "numplayers")) { |
|
if (getnum("numplayers", txt, 2, MAX_PL, &num_play) < 0) { |
|
return -1; |
|
} |
|
if (play != NULL) { |
|
printf("numplayers: multiple settings\n"); |
|
return -1; |
|
} |
|
play = calloc((size_t)num_play, sizeof(play[0])); |
|
if (play == NULL) { |
|
err(1, "calloc"); |
|
} |
|
} else if (!strcmp(attribute, "currentplayer")) { |
|
if (getnum("currentplayer", txt, 0, num_play-1, &player) < 0) { |
|
return -1; |
|
} |
|
if (play == NULL) { |
|
printf("currentplayer: before numplayers\n"); |
|
return -1; |
|
} |
|
cur_p = &play[player]; |
|
} else if (!strcmp(attribute, "doubles")) { |
|
if (getnum("doubles", txt, 0, 2, &num_doub) < 0) { |
|
return -1; |
|
} |
|
} else if (!strcmp(attribute, "player")) { |
|
if (getnum_withbrace("player", txt, 0, num_play-1, |
|
&restore_itemnum) < 0) { |
|
return -1; |
|
} |
|
restore_item = RI_PLAYER; |
|
} else if (!strcmp(attribute, "deck")) { |
|
if (getnum_withbrace("deck", txt, 0, 1, |
|
&restore_itemnum) < 0) { |
|
return -1; |
|
} |
|
restore_item = RI_DECK; |
|
} else if (!strcmp(attribute, "square")) { |
|
if (getnum_withbrace("square", txt, 0, N_SQRS-1, |
|
&restore_itemnum) < 0) { |
|
return -1; |
|
} |
|
restore_item = RI_SQUARE; |
|
} else { |
|
printf("unknown attribute %s\n", attribute); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
static int |
|
restore_player_attr(const char *attribute, char *txt) |
|
{ |
|
PLAY *pp; |
|
int tmp; |
|
|
|
if (play == NULL) { |
|
printf("player came before numplayers.\n"); |
|
return -1; |
|
} |
|
pp = &play[restore_itemnum]; |
|
|
|
if (!strcmp(attribute, "name")) { |
|
if (pp->name != NULL) { |
|
printf("player has multiple names.\n"); |
|
return -1; |
|
} |
|
/* XXX should really systematize the max name length */ |
|
if (strlen(txt) > 256) { |
|
txt[256] = 0; |
|
} |
|
pp->name = strdup(txt); |
|
if (pp->name == NULL) |
|
err(1, "strdup"); |
|
name_list[restore_itemnum] = pp->name; |
|
} else if (!strcmp(attribute, "money")) { |
|
if (getnum(attribute, txt, 0, INT_MAX, &pp->money) < 0) { |
|
return -1; |
|
} |
|
} else if (!strcmp(attribute, "loc")) { |
|
/* note: not N_SQRS-1 */ |
|
if (getnum(attribute, txt, 0, N_SQRS, &tmp) < 0) { |
|
return -1; |
|
} |
|
pp->loc = tmp; |
|
} else if (!strcmp(attribute, "num_gojf")) { |
|
if (getnum(attribute, txt, 0, 2, &tmp) < 0) { |
|
return -1; |
|
} |
|
pp->num_gojf = tmp; |
|
} else if (!strcmp(attribute, "in_jail")) { |
|
if (getnum(attribute, txt, 0, 3, &tmp) < 0) { |
|
return -1; |
|
} |
|
pp->in_jail = tmp; |
|
if (pp->in_jail > 0 && pp->loc != JAIL) { |
|
printf("player escaped from jail?\n"); |
|
return -1; |
|
} |
|
} else { |
|
printf("unknown attribute %s\n", attribute); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
static int |
|
restore_deck_attr(const char *attribute, char *txt) |
|
{ |
|
int tmp, j; |
|
char *s; |
|
DECK *dp; |
|
|
|
dp = &deck[restore_itemnum]; |
|
|
|
if (!strcmp(attribute, "numcards")) { |
|
if (getnum(attribute, txt, dp->num_cards, dp->num_cards, |
|
&tmp) < 0) { |
|
return -1; |
|
} |
|
} else if (!strcmp(attribute, "topcard")) { |
|
if (getnum(attribute, txt, 0, dp->num_cards, |
|
&dp->top_card) < 0) { |
|
return -1; |
|
} |
|
} else if (!strcmp(attribute, "gojf_used")) { |
|
if (getnum(attribute, txt, 0, 1, &tmp) < 0) { |
|
return -1; |
|
} |
|
dp->gojf_used = tmp; |
|
} else if (!strcmp(attribute, "cards")) { |
|
errno = 0; |
|
s = txt; |
|
for (j = 0; j<dp->num_cards; j++) { |
|
tmp = strtol(s, &s, 10); |
|
if (tmp < 0 || tmp >= dp->num_cards) { |
|
printf("cards: out of range value\n"); |
|
return -1; |
|
} |
|
dp->cards[j] = tmp; |
|
} |
|
if (errno) { |
|
printf("cards: invalid values\n"); |
|
return -1; |
|
} |
|
} else { |
|
printf("unknown attribute %s\n", attribute); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
static int |
|
restore_square_attr(const char *attribute, char *txt) |
|
{ |
|
SQUARE *sp = &board[restore_itemnum]; |
|
int tmp; |
|
|
|
if (!strcmp(attribute, "owner")) { |
|
if (getnum(attribute, txt, -1, num_play-1, &tmp) < 0) { |
|
return -1; |
|
} |
|
sp->owner = tmp; |
|
if (tmp >= 0) |
|
add_list(tmp, &play[tmp].own_list, restore_itemnum); |
|
} else if (!strcmp(attribute, "morg")) { |
|
if (sp->type != PRPTY && sp->type != RR && sp->type != UTIL) { |
|
printf("unownable property is mortgaged.\n"); |
|
return -1; |
|
} |
|
if (getnum(attribute, txt, 0, 1, &tmp) < 0) { |
|
return -1; |
|
} |
|
sp->desc->morg = tmp; |
|
} else if (!strcmp(attribute, "houses")) { |
|
if (sp->type != PRPTY) { |
|
printf("unbuildable property has houses.\n"); |
|
return -1; |
|
} |
|
if (getnum(attribute, txt, 0, 5, &tmp) < 0) { |
|
return -1; |
|
} |
|
sp->desc->houses = tmp; |
|
} else { |
|
printf("unknown attribute %s\n", attribute); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
static int |
|
getnum(const char *what, char *txt, int min, int max, int *ret) |
|
{ |
|
char *s; |
|
long l; |
|
|
|
errno = 0; |
|
l = strtol(txt, &s, 10); |
|
if (errno || strlen(s)>0) { |
|
printf("%s: not a number.\n", what); |
|
return -1; |
|
} |
|
if (l < min || l > max) { |
|
printf("%s: out of range.\n", what); |
|
} |
|
*ret = l; |
|
return 0; |
|
} |
|
|
|
static int |
|
getnum_withbrace(const char *what, char *txt, int min, int max, int *ret) |
|
{ |
|
char *s; |
|
s = strchr(txt, ' '); |
|
if (s == NULL) { |
|
printf("%s: expected open brace\n", what); |
|
return -1; |
|
} |
|
*(s++) = '\0'; |
|
while (*s == ' ') |
|
s++; |
|
if (*s != '{') { |
|
printf("%s: expected open brace\n", what); |
|
return -1; |
|
} |
|
if (s[1] != 0) { |
|
printf("%s: garbage after open brace\n", what); |
|
return -1; |
|
} |
|
return getnum(what, txt, min, max, ret); |
|
} |
} |