500 lines
11 KiB
C
500 lines
11 KiB
C
/*-*-c-*-*/
|
|
/*
|
|
* Routines to deal with the user interface in non X versions of the programs.
|
|
* C routines provided:
|
|
* getint, getfloat, yesno, yesono, gtstr, radion, checkn, getopt, showfi,
|
|
* showfu, errom, busy, bpause.
|
|
* FORTRAN routines provided:
|
|
* GETINT, GETRL, GETRLS, YESNO, YESONO, GTSTR, GETSTR, RADION, CHECK4,
|
|
* GETOPT, SHOWFI, SHOWFU, ERROM, BUSY, BPAUSE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <sys/types.h>
|
|
#include "userface.h"
|
|
#include "helpnmenu.h"
|
|
#include "nxhelpmenu.h"
|
|
|
|
/*--------------------------------------------------------------------------*\
|
|
|* *|
|
|
|* Routines only internally used by userface.c. *|
|
|
|* *|
|
|
\*--------------------------------------------------------------------------*/
|
|
|
|
static char rdbuf[256];
|
|
/*
|
|
* Reads a string from stdin storing (excluding newline at end) in buf.
|
|
* Args:
|
|
* buf: where to store string
|
|
* len: size of buf.
|
|
* Returns:
|
|
* length of string (0 for null str),
|
|
* -1 for help '?' (handled by this func.)
|
|
* -2 for quit '!',
|
|
* -3 for error (fgets() failed).
|
|
*/
|
|
static size_t rdstr(char *buf, size_t len) {
|
|
size_t l = 0;
|
|
|
|
*buf = '\0';
|
|
|
|
if (len == 0)
|
|
return 0;
|
|
|
|
#ifdef notdef
|
|
if (fgets(buf, len+2, stdin) == NULL) {
|
|
fputs("No input available!", stderr);
|
|
(void)fflush(stderr);
|
|
return -3;
|
|
}
|
|
|
|
/* remove trailing newline */
|
|
l = strlen(buf)-1;
|
|
/*
|
|
* A bit silly checking - if it's not got a newline on the end then
|
|
* something is wrong (we'll get it on the next read!)
|
|
*/
|
|
if (buf[l] == '\n')
|
|
buf[l]='\0';
|
|
#endif
|
|
/*
|
|
* We use our own getline code as fgets can cause some major hassles.
|
|
* Firstly if you wish to read 10 characters then you need to tell fgets
|
|
* to read 11. Then fgets is stubborn and only reads 10 of the 11 and
|
|
* leaves the newline on the input. It can be fixed by reading 12 of
|
|
* course - but it's a bit hacky!
|
|
*/
|
|
for(;;) {
|
|
int c = getchar();
|
|
|
|
if (c == EOF) {
|
|
fputs("No input available!", stderr);
|
|
(void)fflush(stderr);
|
|
return -3;
|
|
}
|
|
|
|
if (c == '\n')
|
|
break;
|
|
buf[l++] = c;
|
|
}
|
|
buf[l] = '\0';
|
|
|
|
if (l > 0)
|
|
if (*buf == '?') {
|
|
/* if already in interactive help mode then 'press 1' */
|
|
if (query_opt() == -2) {
|
|
buf[0] = '1';
|
|
buf[1] = '\0';
|
|
return 1;
|
|
}
|
|
switch(l) {
|
|
default:
|
|
if (buf[1] == '?') {
|
|
ihelp();
|
|
break;
|
|
} else if (query_opt() == -1) {
|
|
help2(atoi(&buf[1]));
|
|
break;
|
|
}
|
|
case 1:
|
|
help();
|
|
}
|
|
return -1;
|
|
} else if (*buf == '!')
|
|
return -2;
|
|
|
|
return l;
|
|
}
|
|
|
|
/*
|
|
* Reads an integer from stdin. Note that '6G' returns '6'.
|
|
* Args:
|
|
* status: returned status indicating validity of returned integer.
|
|
* >1 = success
|
|
* 0 = null entry
|
|
* -1 = re ask question (help called or non number typed in)
|
|
* -2 = quit requested
|
|
* -3 = read error.
|
|
* Returns:
|
|
* the integer read (if valid) or 0 otherwise.
|
|
*/
|
|
static int rdint(int *status) {
|
|
int num;
|
|
char *end;
|
|
|
|
*status = rdstr(rdbuf, sizeof(rdbuf));
|
|
|
|
if (*status > 0) {
|
|
num = (int)strtol(rdbuf, &end, 10);
|
|
while (*end == ' ' || *end == '\t')
|
|
end++;
|
|
return *end ? -1 : num;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Reads an float from stdin. Note that '6G' returns '6'.
|
|
* Args:
|
|
* status: returned status indicating validity of returned float.
|
|
* >1 = success
|
|
* 0 = null entry
|
|
* -1 = re ask question (help called or non number typed in)
|
|
* -2 = quit requested
|
|
* -3 = read error.
|
|
* Returns:
|
|
* the float read (if valid) or 0 otherwise.
|
|
*/
|
|
static float rdfloat(int *status) {
|
|
float num;
|
|
char *end;
|
|
|
|
*status = rdstr(rdbuf, sizeof(rdbuf));
|
|
|
|
if (*status > 0) {
|
|
num = (float)strtod(rdbuf, &end);
|
|
while (*end == ' ' || *end == '\t')
|
|
end++;
|
|
return *end ? -1 : num;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*\
|
|
|* *|
|
|
|* C interface routines *|
|
|
|* *|
|
|
\*--------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* Reads an integer in a given range from stdin.
|
|
* Args:
|
|
* minval: lower end of range (inclusive)
|
|
* maxval: upper end of range (inclusive)
|
|
* defval: default integer
|
|
* prompt: question to hassle user with
|
|
* status: returned status on validity of returned value
|
|
* 0 = ok
|
|
* -2 = quit
|
|
* -3 = error
|
|
* Returns:
|
|
* minval <= int <= maxval
|
|
*/
|
|
int getint(int minval, int maxval, int defval, char *prompt, int *status) {
|
|
int val;
|
|
|
|
do {
|
|
printf(" ? %s (%d-%d) (%d) =", prompt, minval, maxval, defval);
|
|
(void)fflush(stdout);
|
|
val = rdint(status);
|
|
} while (*status == -1 || (*status > 0 && (val<minval || val>maxval)));
|
|
|
|
if (*status == 0)
|
|
return defval;
|
|
else if (*status > 0)
|
|
return *status = 0, val;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Reads a float in a given range from stdin.
|
|
* Args:
|
|
* minval: lower end of range (inclusive)
|
|
* maxval: upper end of range (inclusive)
|
|
* defval: default float
|
|
* prompt: question to hassle user with
|
|
* status: returned status on validity of returned value
|
|
* 0 = ok
|
|
* -2 = quit
|
|
* -3 = error
|
|
* precision: how accurate to display the range and default values.
|
|
* Returns:
|
|
* minval <= float <= maxval
|
|
*/
|
|
float getfloat(float minval, float maxval, float defval, char *prompt,
|
|
int *status, int precision) {
|
|
float val;
|
|
|
|
do {
|
|
printf(" ? %s (%.*f-%.*f) (%.*f) =", prompt, precision, minval,
|
|
precision, maxval, precision, defval);
|
|
(void)fflush(stdout);
|
|
val = rdfloat(status);
|
|
} while (*status == -1 || (*status > 0 && (val<minval || val>maxval)));
|
|
|
|
if (*status == 0)
|
|
return defval;
|
|
else if (*status > 0)
|
|
return *status = 0, val;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Prompts the user with a yes/no question (defaults to yes).
|
|
* Args:
|
|
* prompt: what to ask.
|
|
* Returns:
|
|
* 0 = yes
|
|
* 1 = no
|
|
* -1 = cancel
|
|
*/
|
|
int yesno(char *prompt) {
|
|
int answer, val;
|
|
|
|
do {
|
|
printf(" ? %s (y/n) (y) = ", prompt);
|
|
(void)fflush(stdout);
|
|
val = rdstr(rdbuf, sizeof(rdbuf));
|
|
if (val == 0)
|
|
answer = 0; /* default yes */
|
|
else if (*rdbuf == 'y' || *rdbuf == 'Y')
|
|
answer = 0;
|
|
else if (*rdbuf == 'n' || *rdbuf == 'N')
|
|
answer = 1;
|
|
else
|
|
answer = -1;
|
|
} while (val > -2 && answer < 0);
|
|
|
|
if (val >= 0)
|
|
return answer;
|
|
else /* val == -2 (quit) || val == -3 (err) */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Reads in a string from stdin.
|
|
* Args:
|
|
* prompt: obvious
|
|
* defval: default string (if user types in nothing)
|
|
* out : where to store the actual string read.
|
|
* outlen: sizeof(out)
|
|
* Returns:
|
|
* -1 : cancel
|
|
* 0 : ok
|
|
* 1 : ok, but used default (blank string if no default)
|
|
*/
|
|
int gtstr(char *prompt, char *defval, char *out, size_t outlen) {
|
|
size_t l;
|
|
|
|
do {
|
|
if (defval)
|
|
printf(" Default %s=%s\n", prompt, defval);
|
|
printf(" ? %s=", prompt);
|
|
(void)fflush(stdout);
|
|
|
|
l = rdstr(out, outlen);
|
|
switch (l) {
|
|
case -3:
|
|
case -2:
|
|
*out = '\0';
|
|
return -1;
|
|
case 0:
|
|
if (defval) {
|
|
l = strlen(defval);
|
|
strncpy(out, defval, (l>outlen)?outlen:l);
|
|
} else
|
|
*out = '\0';
|
|
return 1;
|
|
case -1:
|
|
/* help called */
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
} while (1);
|
|
}
|
|
|
|
/*
|
|
* Displays a menu (analogue of X radio buttons) of options and asks
|
|
* for a selection. An 'X' is displayed next to the default option.
|
|
* Args:
|
|
* title : short description of menu
|
|
* options: list of options to display
|
|
* numopts: how many options to display
|
|
* def : default option to chose.
|
|
* Returns:
|
|
* -1 = cancel
|
|
* otherwise the option number selected.
|
|
*/
|
|
int radion(char *title, char **options, int numopts, int def) {
|
|
int i, status, ret;
|
|
|
|
do {
|
|
printf(" %s\n", title);
|
|
/* display options and prompt for selection */
|
|
for (i=0; i<numopts; i++)
|
|
printf(" %c%3d %s\n", ((i+1) == def)?'X':' ', i+1, options[i]);
|
|
printf(" ? Selection (1-%d) (%d) =", numopts, def);
|
|
(void)fflush(stdout);
|
|
|
|
/* get the user response */
|
|
ret = rdint(&status);
|
|
} while (status == -1 || (status > 0 && (ret > numopts || ret < 1)));
|
|
|
|
if (status >= 0)
|
|
return (ret==0)?def:ret;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Displays a list of 'n' toggle-able items. An 'X' is displayed next to any
|
|
* currently selected items. User types in a number to toggle each item, or
|
|
* 0 to quit.
|
|
* Args:
|
|
* num : how many items to toggle
|
|
* prompts: list of names for each item
|
|
* bools : location of list of initial boolean states for items (set or
|
|
* unset). Also when returning, the final selected boolean states.
|
|
* Returns:
|
|
* -1 = cancel
|
|
* 0 = ok
|
|
*/
|
|
int checkn(int num, char **prompts, int **bools) {
|
|
int i, ret, status;
|
|
|
|
do {
|
|
/* display options to toggle */
|
|
puts(" checkbox: those set marked X");
|
|
for (i=0; i<num; i++)
|
|
printf(" %c%2d %s\n", (*bools)[i]?'X':' ', i+1, prompts[i]);
|
|
printf(" ? (0-%d) =", num);
|
|
(void)fflush(stdout);
|
|
|
|
/* read user response */
|
|
ret = rdint(&status);
|
|
if (status >= 0 && ret >= 1 && ret <= num) {
|
|
/* toggle flag by exclusive or-ing with 1 */
|
|
(*bools)[ret-1] ^= 1;
|
|
}
|
|
} while (status >= -1 && ret != 0);
|
|
if (status < 0)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Reads in an 'option number' from stdin.
|
|
* Takes into account requesting dialogue on an option (d), menu listing (m),
|
|
* and help (?) on general or specific items.
|
|
* Args:
|
|
* status: contains information about the 'int' value returned.
|
|
* -3 = error
|
|
* -2 = quit (!) (always returns 2)
|
|
* -1 = general help requested.
|
|
* 0 = normal
|
|
* 1 = dialogue requested
|
|
* 2 = help on specific subject.
|
|
* 3 = menu option
|
|
* Returns:
|
|
* negative value if a menu asked for (menu 'x' returns '-x')
|
|
* postive value for option selected.
|
|
* 0 for no selection.
|
|
*/
|
|
int getcopt(int *status) {
|
|
int ret;
|
|
|
|
ret = rdstr(rdbuf, sizeof(rdbuf));
|
|
if (ret <= -2) { /* quit/error */
|
|
*status = ret;
|
|
return 2;
|
|
} else if (ret == -1) { /* help */
|
|
*status = -1;
|
|
return 0;
|
|
#if 0
|
|
if (rdbuf[1] != '\0') {
|
|
*status = 2;
|
|
return atoi(&rdbuf[1]);
|
|
} else {
|
|
*status = 0;
|
|
return 1;
|
|
}
|
|
#endif
|
|
} else if (ret > 0) { /* ok - +ve length string */
|
|
if (*rdbuf == 'd' || *rdbuf == 'D') {
|
|
*status = 1;
|
|
return atoi(&rdbuf[1]);
|
|
} else if (*rdbuf == 'm' || *rdbuf == 'M') {
|
|
*status = 3;
|
|
/*
|
|
* Menus are negative option numbers.
|
|
* Should really safeguard against people typing 'm-2' to
|
|
* quit etc - but well... it's fun!
|
|
*/
|
|
return -atoi(&rdbuf[1]);
|
|
} else {
|
|
/* simple number */
|
|
*status = 0;
|
|
return atoi(rdbuf);
|
|
}
|
|
} else { /* ret == 0 (no info) */
|
|
*status = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Display name of current file - unused in non X version.
|
|
*/
|
|
void showfi(char *func) {
|
|
}
|
|
|
|
/*
|
|
* Display name of current function.
|
|
* Args:
|
|
* func: Name of function to display with no newline on the end.
|
|
*/
|
|
void showfu(char *func) {
|
|
puts(func);
|
|
(void)fflush(stdout);
|
|
}
|
|
|
|
void showfunc() {
|
|
if (helpindex[query_opt()].name) {
|
|
printf(" %s\n",helpindex[query_opt()].name);
|
|
(void)fflush(stdout);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print an error message.
|
|
* Args:
|
|
* errmsg: error with no trailing newline.
|
|
*/
|
|
void errom(char *errmsg) {
|
|
fprintf(stdout, "%s\n", errmsg);
|
|
fflush(stdout);
|
|
}
|
|
|
|
/*
|
|
* Hardly worth explaining.
|
|
*/
|
|
void busy() {
|
|
puts(" Working");
|
|
fflush(stdout);
|
|
}
|
|
|
|
/*
|
|
* Beeps and waits for return to be pressed.
|
|
* Returns:
|
|
* 0 for ok
|
|
* -1 for cancel
|
|
*/
|
|
int bpause() {
|
|
register int r;
|
|
|
|
/* assume char 7 is bell - ASCII dependant? */
|
|
putchar(7);
|
|
fflush(stdout);
|
|
|
|
r = rdstr(rdbuf, sizeof(rdbuf));
|
|
return r<-1 ? -1 : 0;
|
|
}
|
|
|