2021-10-11 18:37:13 -03:00

1025 lines
22 KiB
C

#ifndef lint
static char sccsid[] = "@(#)io.c 1.1 94/10/31";
#endif
/*
* Copyright (c) 1991 by Sun Microsystems, Inc.
*/
/*
* This file contains I/O related functions.
*/
#include "global.h"
#include <ctype.h>
#include <varargs.h>
#include "startup.h"
extern void cmdabort();
extern int _doprnt();
extern int data_lineno;
#ifdef not
extern char *space2str();
#endif not
/*
* This variable is used to determine whether a token is present in the pipe
* already.
*/
static char token_present = 0;
/*
* This routine pushes the given character back onto the input stream.
*/
pushchar(c)
int c;
{
(void) ungetc(c, stdin);
}
/*
* This routine checks the input stream for an eof condition.
*/
int
checkeof()
{
return (feof(stdin));
}
/*
* This routine gets the next token off the input stream. A token is
* basically any consecutive non-white characters.
*/
char *
gettoken(inbuf)
char *inbuf;
{
char *ptr = inbuf;
int c, quoted = 0;
retoke:
/*
* Remove any leading white-space.
*/
while ((isspace(c = getchar())) && (c != '\n'))
;
/*
* If we are at the beginning of a line and hit the comment character,
* flush the line and start again.
*/
if (!token_present && c == COMMENT_CHAR) {
token_present = 1;
flushline();
goto retoke;
}
/*
* Loop on each character until we hit unquoted white-space.
*/
while (!isspace(c) || quoted && (c != '\n')) {
/*
* If we hit eof, get out.
*/
if (checkeof())
return (NULL);
/*
* If we hit a double quote, change the state of quotedness.
*/
if (c == '"')
quoted = !quoted;
/*
* If there's room in the buffer, add the character to the end.
*/
else if (ptr - inbuf < TOKEN_SIZE)
*ptr++ = (char)c;
/*
* Get the next character.
*/
c = getchar();
}
/*
* Null terminate the token.
*/
*ptr = '\0';
/*
* Peel off white-space still in the pipe.
*/
while (isspace(c) && (c != '\n'))
c = getchar();
/*
* If we hit another token, push it back and set state.
*/
if (c != '\n') {
pushchar(c);
token_present = 1;
} else
token_present = 0;
/*
* Return the token.
*/
return (inbuf);
}
/*
* This routine removes the leading and trailing spaces from a token.
*/
clean_token(cleantoken, token)
char *cleantoken, *token;
{
char *ptr;
/*
* Strip off leading white-space.
*/
for (ptr = token; isspace(*ptr); ptr++)
;
/*
* Copy it into the clean buffer.
*/
(void) strcpy(cleantoken, ptr);
/*
* Strip off trailing white-space.
*/
for (ptr = cleantoken + strlen(cleantoken) - 1;
isspace(*ptr) && (ptr >= cleantoken); ptr--)
*ptr = '\0';
}
/*
* This routine flushes the rest of an input line if there is known
* to be data in it. The flush has to be qualified because the newline
* may have already been swallowed by the last gettoken.
*/
flushline()
{
if (token_present) {
/*
* Flush the pipe to eol or eof.
*/
while ((getchar() != '\n') && !checkeof())
;
/*
* Mark the pipe empty.
*/
token_present = 0;
}
}
/*
* This routine returns the number of characters that are identical
* between s1 and s2, stopping as soon as a mismatch is found.
*/
int
strcnt(s1, s2)
char *s1, *s2;
{
int i = 0;
while ((*s1 != '\0') && (*s1++ == *s2++))
i++;
return (i);
}
/*
* This routine converts the given token into an integer. The token
* must convert cleanly into an integer with no unknown characters.
* If the token is the wildcard string, and the wildcard parameter
* is present, the wildcard value will be returned.
*/
int
geti(str, iptr, wild)
char *str;
int *iptr, *wild;
{
char *str2;
/*
* If there's a wildcard value and the string is wild, return the
* wildcard value.
*/
if (wild != NULL && strcmp(str, WILD_STRING) == 0)
*iptr = *wild;
else {
/*
* Conver the string to an integer.
*/
*iptr = (int)strtol(str, &str2, 0);
/*
* If any characters didn't convert, it's an error.
*/
if (*str2 != '\0') {
eprint("`%s' is not an integer.\n", str);
return (-1);
}
}
return (0);
}
/*
* This routine converts the given string into a block number on the
* current disk. The format of a block number is either a self-based
* number, or a series of self-based numbers separated by slashes.
* Any number preceeding the first slash is considered a cylinder value.
* Any number succeeding the first slash but preceeding the second is
* considered a head value. Any number succeeding the second slash is
* considered a sector value. Any of these numbers can be wildcarded
* to the highest possible legal value.
*/
int
getbn(str, iptr)
char *str;
daddr_t *iptr;
{
char *cptr, *hptr, *sptr;
int cyl, head, sect, wild;
TOKEN buf;
/*
* Set cylinder pointer to beginning of string.
*/
cptr = str;
/*
* Look for the first slash.
*/
while ((*str != '\0') && (*str != '/'))
str++;
/*
* If there wasn't one, convert string to an integer and return it.
*/
if (*str == '\0') {
wild = physsects() - 1;
if (geti(cptr, (int *)iptr, &wild))
return (-1);
return (0);
}
/*
* Null out the slash and set head pointer just beyond it.
*/
*str++ = '\0';
hptr = str;
/*
* Look for the second slash.
*/
while ((*str != '\0') && (*str != '/'))
str++;
/*
* If there wasn't one, sector pointer points to a null.
*/
if (*str == '\0')
sptr = str;
/*
* If there was, null it out and set sector point just beyond it.
*/
else {
*str++ = '\0';
sptr = str;
}
/*
* Convert the cylinder part to an integer and store it.
*/
clean_token(buf, cptr);
wild = ncyl + acyl - 1;
if (geti(buf, &cyl, &wild))
return (-1);
if ((cyl < 0) || (cyl >= ncyl + acyl)) {
eprint("`%d' is out of range.\n", cyl);
return (-1);
}
/*
* Convert the head part to an integer and store it.
*/
clean_token(buf, hptr);
wild = nhead - 1;
if (geti(buf, &head, &wild))
return (-1);
if ((head < 0) || (head >= nhead)) {
eprint("`%d' is out of range.\n", head);
return (-1);
}
/*
* Convert the sector part to an integer and store it.
*/
clean_token(buf, sptr);
wild = sectors(head) - 1;
if (geti(buf, &sect, &wild))
return (-1);
if ((sect < 0) || (sect >= sectors(head))) {
eprint("`%d' is out of range.\n", sect);
return (-1);
}
/*
* Combine the pieces into a block number and return it.
*/
*iptr = chs2bn(cyl, head, sect);
return (0);
}
/*
* This routine is the basis for all input into the program. It
* understands the semantics of a set of input types, and provides
* consistent error messages for all input. It allows for default
* values and prompt strings.
*/
int
input(type, promptstr, delim, param, deflt, cmdflag)
int type, *deflt, cmdflag;
char *promptstr, delim, *param;
{
int interactive, help, i, length, index, tied;
daddr_t bn;
char **str, **strings, *ostr;
TOKEN token, cleantoken;
struct bounds *bounds;
reprompt:
help = interactive = 0;
/*
* If we are inputting a command, flush any current input in the pipe.
*/
if (cmdflag == CMD_INPUT)
flushline();
/*
* Note whether the token is already present.
*/
if (!token_present)
interactive = 1;
/*
* Print the prompt.
*/
printf(promptstr);
/*
* If there is a default value, print it in a format appropriate
* for the input type.
*/
if (deflt != NULL) {
printf(" [");
switch (type) {
case FIO_BN:
printf("%d, ", *deflt);
pr_dblock(printf, (daddr_t)*deflt);
break;
case FIO_INT:
printf("%d", *deflt);
break;
case FIO_CSTR:
case FIO_MSTR:
strings = (char **)param;
for (i = 0, str = strings; i < *deflt; i++, str++)
;
printf("%s", *str);
break;
default:
eprint("Error: unkown input type.\n");
fullabort();
}
printf("]");
}
/*
* Print the delimiter character.
*/
printf("%c ", delim);
/*
* Get the token. If we hit eof, exit the program gracefully.
*/
if (gettoken(token) == NULL)
fullabort();
/*
* Echo the token back to the user if it was in the pipe or we
* are running out of a command file.
*/
if (!interactive || option_f)
printf("%s\n", token);
/*
* If we are logging, echo the token to the log file. The else
* is necessary here because the above printf will also put the
* token in the log file.
*/
else if (log_file)
lprint("%s\n", token);
/*
* If the token was not in the pipe and it wasn't a command, flush
* the rest of the line to keep things in sync.
*/
if (interactive && cmdflag != CMD_INPUT)
flushline();
/*
* Scrub off the white-space.
*/
clean_token(cleantoken, token);
/*
* If the input was a blank line and we weren't prompting
* specifically for a blank line...
*/
if ((strcmp(cleantoken, "") == 0) && (type != FIO_BLNK)) {
/*
* If there's a default, return it.
*/
if (deflt != NULL)
return (*deflt);
/*
* If the blank was not in the pipe, just reprompt.
*/
if (interactive)
goto reprompt;
/*
* If the blank was in the pipe, it's an error.
*/
eprint("No default for this entry.\n");
cmdabort();
}
/*
* If token is a '?' or a 'h', it is a request for help.
*/
if (strcmp(cleantoken, "?") == 0 || strcmp(cleantoken, "h") == 0)
help = 1;
/*
* Switch on the type of input expected.
*/
switch (type) {
/*
* Expecting a disk block number.
*/
case FIO_BN:
/*
* Parameter is the bounds of legal block numbers.
*/
bounds = (struct bounds *)param;
/*
* Print help message if required.
*/
if (help) {
printf("Expecting a block number from %d (",
bounds->lower);
pr_dblock(printf, bounds->lower);
printf(") to %d (", bounds->upper);
pr_dblock(printf, bounds->upper);
printf(")\n");
break;
}
/*
* Convert token to a disk block number.
*/
if (getbn(cleantoken, &bn))
break;
/*
* Check to be sure it is within the legal bounds.
*/
if ((bn < bounds->lower) || (bn > bounds->upper)) {
eprint("`");
pr_dblock(eprint, bn);
eprint("' is out of range.\n");
break;
}
/*
* If it's ok, return it.
*/
return (bn);
/*
* Expecting an integer.
*/
case FIO_INT:
/*
* Parameter is the bounds of legal integers.
*/
bounds = (struct bounds *)param;
/*
* Print help message if required.
*/
if (help) {
printf("Expecting an integer from %d", bounds->lower);
printf(" to %d\n", bounds->upper);
break;
}
/*
* Convert the token into an integer.
*/
if (geti(cleantoken, (int *)&bn, (int *)NULL))
break;
/*
* Check to be sure it is within the legal bounds.
*/
if ((bn < bounds->lower) || (bn > bounds->upper)) {
eprint("`%d' is out of range.\n", bn);
break;
}
/*
* If it's ok, return it.
*/
return (bn);
/*
* Expecting a closed string. This means that the input
* string must exactly match one of the strings passed in
* as the parameter.
*/
case FIO_CSTR:
/*
* The parameter is a null terminated array of character
* pointers, each one pointing to a legal input string.
*/
strings = (char **)param;
/*
* Walk through the legal strings, seeing if any of them
* match the token. If a match is made, return the index
* of the string that was matched.
*/
for (str = strings; *str!= NULL; str++)
if (strcmp(cleantoken, *str) == 0)
return (str - strings);
/*
* Print help message if required.
*/
if (help) {
printf("Expecting one of the following:\n");
for (str = strings; *str != NULL; str++)
printf(" %s\n", *str);
} else {
eprint("`%s' is not expected.\n", cleantoken);
}
break;
/*
* Expecting a matched string. This means that the input
* string must either match one of the strings passed in,
* or be a unique abbreviation of one of them.
*/
case FIO_MSTR:
/*
* The parameter is a null terminated array of character
* pointers, each one pointing to a legal input string.
*/
strings = (char **)param;
length = index = tied = 0;
/*
* Loop through the legal input strings.
*/
for (str = strings; *str != NULL; str++) {
/*
* See how many characters of the token match
* this legal string.
*/
i = strcnt(cleantoken, *str);
/*
* If it's not the whole token, then it's not a match.
*/
if (i < strlen(cleantoken))
continue;
/*
* If it ties with another input, remember that.
*/
if (i == length)
tied = 1;
/*
* If it matches the most so far, record that.
*/
if (i > length) {
index = str - strings;
tied = 0;
length = i;
}
}
/*
* Pring help message if required.
*/
if (length == 0) {
if (help) {
printf("\n\nExpecting one of the following ");
printf("(abbreviations ok):\n");
for (str = strings; *str != NULL; str++)
printf(" %s\n", *str);
break;
}
eprint("`%s' is not expected.\n", cleantoken);
break;
}
/*
* If the abbreviation was non-unique, it's an error.
*/
if (tied) {
eprint("`%s' is ambiguous.\n", cleantoken);
break;
}
/*
* We matched one. Return the index of the string we matched.
*/
return (index);
/*
* Expecting an open string. This means that any string is legal.
*/
case FIO_OSTR:
/*
* Print a help message if required.
*/
if (help) {
printf("Expecting a string\n");
break;
}
/*
* Malloc the space needed to hold the string and fill it in.
*/
ostr = zalloc(strlen(token) + 1);
(void) strcpy(ostr, token);
/*
* Return the address of the string.
*/
return ((int)ostr);
/*
* Expecting a blank line.
*/
case FIO_BLNK:
/*
* We are always in non-echo mode when we are inputting
* this type. We echo the newline as a carriage return
* only so the prompt string will be covered over.
*/
nolprint("\015");
/*
* If we are logging, send a newline to the log file.
*/
if (log_file)
lprint("\n");
/*
* There is no value returned for this type.
*/
return (0);
/*
* If we don't recognize the input type, it's bad news.
*/
default:
eprint("Error: unknown input type.\n");
fullabort();
}
/*
* If we get here, it's because some error kept us from accepting
* the token. If we are running out of a command file, gracefully
* leave the program. If we are interacting with the user, simply
* reprompt. If the token was in the pipe, abort the current command.
*/
if (option_f)
fullabort();
else if (interactive)
goto reprompt;
else
cmdabort();
/*
* Never actually reached.
*/
return (-1);
}
/*
* This routine is a modified version of printf. It handles the cases
* of silent mode and logging; other than that it is identical to the
* library version.
*/
/*VARARGS1*/
printf(format, va_alist)
char *format;
va_dcl
{
va_list ap;
/*
* If we are running silent, skip it.
*/
if (option_s)
return;
/*
* Do the print to standard out.
*/
va_start(ap);
(void) vprintf(format, ap);
/*
* If we are logging, also print to the log file.
*/
if (log_file) {
(void) vfprintf(log_file, format, ap);
(void) fflush(log_file);
}
va_end(ap);
}
/*
* This routine is a modified version of printf. It handles the cases
* of silent mode; other than that it is identical to the
* library version. It differs from the above printf in that it does
* not print the message to a log file.
*/
/*VARARGS1*/
nolprint(format, va_alist)
char *format;
va_dcl
{
va_list ap;
/*
* If we are running silent, skip it.
*/
if (option_s)
return;
/*
* Do the print to standard out.
*/
va_start(ap);
(void) vprintf(format, ap);
va_end(ap);
}
/*
* This routine is a modified version of printf. It handles the cases
* of silent mode, and only prints the message to the log file, not
* stdout. Other than that is identical to the library version.
*/
/*VARARGS1*/
lprint(format, va_alist)
char *format;
va_dcl
{
va_list ap;
/*
* If we are running silent, skip it.
*/
if (option_s)
return;
/*
* Do the print to the log file.
*/
va_start(ap);
(void) vfprintf(log_file, format, ap);
(void) fflush(log_file);
va_end(ap);
}
/*
* This routine is identical in functionality to the fprintf library routine.
* It is missing only some error checking, and is included here
* mostly for completeness.
*/
/*VARARGS2*/
fprintf(iop, format, va_alist)
FILE *iop;
char *format;
va_dcl
{
va_list ap;
/*
* Do the print to the given file.
*/
va_start(ap);
(void) vfprintf(iop, format, ap);
va_end(ap);
}
/*
* This routine is a modified version of printf. It prints the message
* to stderr, and to the log file is appropriate.
* Other than that is identical to the library version.
*/
/*VARARGS1*/
eprint(format, va_alist)
char *format;
va_dcl
{
va_list ap;
/*
* Do the print to stderr.
*/
va_start(ap);
(void) vfprintf(stderr, format, ap);
/*
* If we are logging, also print to the log file.
*/
if (log_file) {
(void) vfprintf(log_file, format, ap);
(void) fflush(log_file);
}
va_end(ap);
}
#ifdef not
/*
* This routine prints out a message describing the given ctlr.
* The message is identical to the one printed by the kernel during
* booting.
*/
pr_ctlrline(ctlr)
register struct ctlr_info *ctlr;
{
printf(" %s%d at %s 0x%x ", ctlr->ctlr_cname, ctlr->ctlr_num,
space2str(ctlr->ctlr_space), ctlr->ctlr_addr);
if (ctlr->ctlr_vec != 0)
printf("vec 0x%x ", ctlr->ctlr_vec);
else
printf("pri %d ", ctlr->ctlr_prio);
printf("\n");
}
#endif not
/*
* This routine prints out a message describing the given disk.
* The message is identical to the one printed by the kernel during
* booting.
*/
pr_diskline(disk, num)
register struct disk_info *disk;
int num;
{
struct ctlr_info *ctlr = disk->disk_ctlr;
struct disk_type *type = disk->disk_type;
if (ctlr->ctlr_ctype->ctype_ctype != DKC_PANTHER) {
printf(" %d. %s%d at %s%d slave %d\n", num, ctlr->ctlr_dname,
disk->disk_unit, ctlr->ctlr_cname, ctlr->ctlr_num,
disk->disk_slave);
if (type != NULL) {
printf(" %s%d: <%s cyl %d alt %d hd %d sec %d>\n",
ctlr->ctlr_dname, disk->disk_unit, type->dtype_asciilabel,
type->dtype_ncyl, type->dtype_acyl, type->dtype_nhead,
type->dtype_nsect);
} else {
printf(" %s%d: <drive type unknown>\n",
ctlr->ctlr_dname, disk->disk_unit);
}
} else {
printf(" %d. %s%03x at %s%d slave %d\n", num, ctlr->ctlr_dname,
disk->disk_unit, ctlr->ctlr_cname, ctlr->ctlr_num,
disk->disk_slave);
if (type != NULL) {
printf(" %s%03x: <%s cyl %d alt %d hd %d sec %d>\n",
ctlr->ctlr_dname, disk->disk_unit, type->dtype_asciilabel,
type->dtype_ncyl, type->dtype_acyl, type->dtype_nhead,
type->dtype_nsect);
} else {
printf(" %s%03x: <drive type unknown>\n",
ctlr->ctlr_dname, disk->disk_unit);
}
}
}
/*
* This routine prints out a given disk block number in cylinder/head/sector
* format. It uses the printing routine passed in to do the actual output.
*/
pr_dblock(func, bn)
int (*func)();
daddr_t bn;
{
(void) (*func)("%d/%d/%d", bn2c(bn), bn2h(bn), bn2s(bn));
}
/*
* This routine inputs a character from the data file. It understands
* the use of '\' to prevent interpretation of a newline. It also keeps
* track of the current line in the data file via a global variable.
*/
int
sup_inputchar()
{
int c;
/*
* Input the character.
*/
c = getc(data_file);
/*
* If it's not a backslash, return it.
*/
if (c != '\\')
return (c);
/*
* It was a backslash. Get the next character.
*/
c = getc(data_file);
/*
* If it was a newline, update the line counter and get the next
* character.
*/
if (c == '\n') {
data_lineno++;
c = getc(data_file);
}
/*
* Return the character.
*/
return (c);
}
/*
* This routine pushes a character back onto the input pipe for the data file.
*/
sup_pushchar(c)
int c;
{
(void) ungetc(c, data_file);
}
/*
* This routine inputs a token from the data file. A token is a series
* of contiguous non-white characters or a recognized special delimiter
* character.
*/
int
sup_gettoken(buf)
char *buf;
{
char *ptr = buf;
int i, c, quoted = 0;
for (i = 0; i < TOKEN_SIZE + 1; i++)
buf[i] = '\0';
/*
* Strip off leading white-space.
*/
while ((isspace(c = sup_inputchar())) && (c != '\n'))
;
/*
* Read in characters until we hit unquoted white-space.
*/
for (;!isspace(c) || quoted; c = sup_inputchar()) {
/*
* If we hit eof, that's a token.
*/
if (feof(data_file))
return (SUP_EOF);
/*
* If we hit a double quote, change the state of quoting.
*/
if (c == '"') {
quoted = !quoted;
continue;
}
/*
* If we hit a newline, that delimits a token.
*/
if (c == '\n')
break;
/*
* If we hit any nonquoted special delimiters, that delimits
* a token.
*/
if (!quoted && (c == '=' || c == ',' || c == ':' ||
c == '#' || c == '|' || c == '&' || c == '~'))
break;
/*
* Store the character if there's room left.
*/
if (ptr - buf < TOKEN_SIZE)
*ptr++ = (char)c;
}
/*
* If we stored characters in the buffer, then we inputted a string.
* Push the delimiter back into the pipe and return the string.
*/
if (ptr - buf > 0) {
sup_pushchar(c);
return (SUP_STRING);
}
/*
* We didn't input a string, so we must have inputted a known delimiter.
* store the delimiter in the buffer, so it will get returned.
*/
buf[0] = c;
/*
* Switch on the delimiter. Return the appropriate value for each one.
*/
switch(c) {
case '=':
return (SUP_EQL);
case ':':
return (SUP_COLON);
case ',':
return (SUP_COMMA);
case '\n':
return (SUP_EOL);
case '|':
return (SUP_OR);
case '&':
return (SUP_AND);
case '~':
return (SUP_TILDE);
case '#':
/*
* For comments, we flush out the rest of the line and return
* an eol.
*/
while ((c = sup_inputchar()) != '\n' && !feof(data_file))
;
if (feof(data_file))
return (SUP_EOF);
else
return (SUP_EOL);
/*
* Shouldn't ever get here.
*/
default:
return (SUP_STRING);
}
}