/* $Id: interface.c,v 1.9 2001/05/30 15:47:03 harbourn Exp $
* This module handles user interface processing (via commands)
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/wait.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <fnmatch.h>
#include <unistd.h>
#include <errno.h>
#include "dirtree.h"
#include "util.h"
#include "fat.h"
#include "vbr.h"
#include "output.h"
#include "recovery.h"
#include "fatback.h"
#include "interface.h"
#include "interface_data.h"
#include "vars.h"
/* to change to libreadline, uncomment the
* #include and comment out the prototype */
/*#include <readline/readline.h>*/
static char *readline(char *);
static int ending(char *);
typedef char* (*cmdline_hook_t)(char *);
static char *stripwhite(char *);
static char *stripcomments(char *);
static char *strippipe(char *);
static char *pipe_scan(char *);
static int whitespace(char);
static command_t *find_command(char *);
static char **split_line(char *);
static char *cmdline_car(char *, int *);
/*
* Display a menu of the possible partitions,
* and prompt the user as to which one to recover.
* let them undelete that partition, and keep looping
* until we recieve the stop code.
*/
void partition_menu(int num_parts, int flags)
{
int i;
unsigned part;
char *choice;
/* if only one partition exists, dont bother with the menu */
if (num_parts == 1) {
display(VERBOSE, "Only one partition detected: Entering single partition mode\n");
undel_partition(0, flags);
return;
}
for (;;) {
display(NORMAL, "Please select one of the following partitions:");
for (i = 0; i < num_parts; i++)
display(NORMAL, " %d", i);
display(NORMAL, "\n");
choice = readline(">");
display(LOGONLY, "%s\n");
part = atoi(choice);
free(choice);
if (part > num_parts)
display(NORMAL, "Invalid partition number\n");
else if (undel_partition(part, flags) == STOPCODE_QUIT)
return;
}
}
/*
* Initialize variables in the interface.
* this should be called each time a new partition is to be edited
*/
void interface_init(dirent_t *tree, clust_t *clust_array, vbr_t myvbr)
{
assert(tree && clust_array && myvbr);
stop_code = 0;
cwd = root_dir = tree;
clusts = clust_array;
vbr = myvbr;
}
/*
* Prompt the user for a command and process that command.
*/
int process_commands(void)
{
char *line, *s;
while (!stop_code) {
fbvar_t *prompt_var;
char *tmp_prompt, *prompt;
char *pipe_command;
FILE *tmp_pipe;
errno = 0;
prompt_var = get_fbvar("prompt");
tmp_prompt = prompt_var->val.sval;
free(prompt_var);
prompt = emalloc(strlen(tmp_prompt) + 2);
strcpy(prompt, tmp_prompt);
prompt[strlen(tmp_prompt)] = ' ';
prompt[strlen(tmp_prompt) + 1] = '\0';
display(LOGONLY, "%s", prompt);
line = readline(prompt ? prompt : "> ");
free(prompt);
if (!line)
break;
if (pipe_command = pipe_scan(line)) {
if ((tmp_pipe = popen(pipe_command, "w")) == NULL) {
perror("cannot create pipe");
break;
}
set_ostream(tmp_pipe);
}
exec_line(line);
free(line);
if (pipe_command) {
free(pipe_command);
pclose(tmp_pipe);
reset_ostream();
}
}
return stop_code;
}
/*
* Execute a command as specified by argument.
*/
void exec_line(char *line)
{
command_t *command;
char *newline;
char **argv;
int argc, i;
cmdline_hook_t cmdline_hooks[] =
{
stripcomments,
strippipe,
stripwhite,
NULL
};
assert(line);
/* skip the line if it is a comment */
if (line[0] == '#')
return;
newline = strdup(line);
/* log this line to the audit log. */
display(LOGONLY, "%s\n", line);
/* Apply the command line hooks */
for (i = 0; cmdline_hooks[i]; i++) {
char *tmp = (*cmdline_hooks[i])(newline);
free(newline);
newline = tmp;
}
/* split the line into an argv[] array */
if (!(argv = split_line(newline)))
return;
/* make argc the number of argv elements */
for (argc = 0; argv[argc]; argc++);
/* look up the command in the command table */
command = find_command(argv[0]);
if (!command) {
display(NORMAL, "Invalid command\n");
return;
}
(*(command->func))(argc, argv); /* run the command! */
/* free newly split command line */
for (i = 0; i < argc; i++)
free(argv[i]);
free(argv);
}
/*
* Concatenate an array of strings.
* Works the opposite of splitline.
*/
char *argvcat(char *argv[])
{
char *retval = NULL;
int i;
/* known bug: this algorithm leaves a trailing space at the
* end of each line it creates. */
for (i = 0; argv[i]; i++) {
char *tmp = retval;
int retval_len;
retval_len = (retval ? strlen(retval) : 0) + strlen(argv[i]) + 2;
retval = emalloc(retval_len);
*retval = '\0';
if (tmp)
strcpy(retval, tmp);
strcat(retval, argv[i]);
/* terminate each string with a space and a null */
retval[retval_len - 2] = ' ';
retval[retval_len - 1] = '\0';
if (tmp)
free(tmp);
}
return retval;
}
/*
* Lookup a command in the command table
*/
static command_t *find_command(char *name)
{
int i;
/* the names of commands are simply strings, so just
* loop over the commands[] array strcmp'ing the names
*/
if (!name)
return NULL;
for (i = 0; commands[i].name; i++) {
if (strcmp(name, commands[i].name) == 0)
return &commands[i];
}
return NULL;
}
/*
* Strip the white space from the ends of a string
*/
static char *stripwhite(char *string)
{
char *retval;
int i, head_ws, tail_ws;
int retvallen, stringlen;
assert(string);
stringlen = strlen(string);
if (stringlen == 0)
return strdup(string);
/* count the initial whitespace */
for (i = 0; whitespace(string[i]); i++)
;
head_ws = i;
/* count the amount of tail whitespace */
for (i = stringlen - 1; (i >= 0) && whitespace(string[i]); i--)
;
tail_ws = stringlen - (i + 1);
retvallen = stringlen - (head_ws + tail_ws);
/* create the new string */
retval = emalloc(retvallen + 1);
strncpy(retval, &string[head_ws], retvallen);
retval[retvallen] = '\0';
return retval;
}
/*
* Strip out every thing on a line after
* the comment character ('#')
*/
static char *stripcomments(char *string)
{
int i;
char *retval;
assert(string);
/* Find the comment (if any) on the line */
for (i = 0; string[i] && (string[i] != '#'); i++)
;
retval = emalloc(i + 1);
strncpy(retval, string, i);
retval[i] = '\0';
return retval;
}
/*
* Look up a file in a directory tree based on a name
*/
dirent_t *find_in_tree(dirent_t *dir, dirent_t *entry, char *name)
{
char *entry_name, *remainder;
dirent_t *ent;
/* this funciton works by recursively breaking apart a file name
* into its layers of directories.
*/
assert(dir && name);
/* find the first part of the file name */
entry_name = fn_car(name);
remainder = fn_cdr(name);
if (!entry_name) {
if (name[0] == delim)
return root_dir;
else
return NULL;
}
if (strcmp(entry_name, ".") == 0) {
free(entry_name);
if (!remainder)
return dir;
else
return find_in_tree(dir, dir->child, remainder);
}
if (strcmp(entry_name, "..") == 0) {
free(entry_name);
if (!remainder)
return dir->parent;
else
return find_in_tree(dir->parent, dir->parent->child, remainder);
}
if (!entry)
return NULL;
/* find an entry in the current directory that matches entry_name */
for (ent = entry; ent; ent = ent->next) {
dirent_t *matched_ent = NULL;
int match, match_lfn;
match = fnmatch(entry_name, ent->filename, 0);
if (ent->lfn)
match_lfn = fnmatch(entry_name, ent->lfn, 0);
else
match_lfn = FNM_NOMATCH;
if (match == 0 || match_lfn == 0) {
if (!remainder) {
free(entry_name);
return ent;
} else if (ent->attrs & ATTR_DIR) {
matched_ent = find_in_tree(ent, ent->child, remainder);
if (matched_ent) {
free(entry_name);
free(remainder);
return matched_ent;
}
}
}
}
free(entry_name);
if (remainder)
free(remainder);
return NULL;
}
/*
* givin an array of strings (probably from an argv[])
* find all the files that match, put them into a linked
* list and return them.
*/
entlist_t *find_files(int num, char *strings[])
{
unsigned i;
entlist_t *list_head = NULL, *list_tail = NULL;
/* loop over all arguments */
for (i = 0; i < num; i++) {
/* find all entries matching that pattern */
dirent_t *ent, *next = cwd->child;
int found = 0;
while (next && (ent = find_in_tree(cwd, next, strings[i]))) {
entlist_t *tmp = emalloc(sizeof *tmp);
found++;
tmp->next = NULL;
tmp->ent = ent;
if (!list_head)
list_head = tmp;
else
list_tail->next = tmp;
list_tail = tmp;
next = ent->next;
}
}
return list_head;
}
/*
* Extract the first piece of a file name.
* ("car" comes from the lisp primitive car, which
* means take the first element of a list.)
*/
char *fn_car(char *name)
{
int i=0, start, length;
char *retval;
assert(name);
/* first we find the length of the first part */
if (name[0] == delim)
i++;
start = i;
while (name[i] != '\0' && name[i] != delim)
i++;
length = i - start;
/* allocate space for our new string */
if (length == 0)
return NULL;
retval = emalloc(length + 1);
/* copy the fragment into the new string */
strncpy(retval, &name[start], length);
retval[length] = '\0';
return retval;
}
/* Extract all but the first piece of a file name.
* (similar to "car", "cdr" is taken from lisp as
* well, it means take all but the first element of
* a list.)
*/
char *fn_cdr(char *name)
{
int i=0, start, length;
char *retval;
assert(name);
/* find the length of the remainder */
if (name[0] == delim)
i++;
while (name[i] != '\0' && name[i] != delim)
i++;
start = i + 1; /* increment past the delimeter */
if (name[i] == '\0')
return NULL;
while (name[i] != '\0')
i++;
length = i - start;
/* allocate space for our new string */
if (length == 0)
return NULL;
retval = emalloc(length + 1);
/* copy the remainder into the new string */
strncpy(retval, &name[start], length);
retval[length] = '\0';
return retval;
}
/*
* Take all but the last portion of a file name.
* (There is no lisp primitive for rcdr, I made it up.
*/
char *fn_rcdr(char *name)
{
int i, name_strlen;
char *retval;
assert(name);
/* calculate the length of the name string */
name_strlen = strlen(name);
if (!name_strlen)
return NULL;
/* position the index at the end of the string */
i = name_strlen - 1;
/* back up to before the delimeter, if any */
if (name[name_strlen - 1] == delim)
i--;
if (i < 0)
return NULL;
/* step backwards through the string until we
* find a delimeter, or we hit the beginning */
while ((i >= 0) && (name[i] != delim))
i--;
/* place all the data up to the index into a new string */
if (i < 0)
return NULL;
retval = emalloc(i + 2);
strncpy(retval, name, i + 1);
return retval;
}
/*
* Take a string delimeted by whitespace, and form
* it into an array of strings.
*/
static char **split_line(char *line)
{
int i, total = 0;
char **list = NULL;
for (i = 0; line[i] != '\0'; ) {
char *word = cmdline_car(line, &i);
if (word) {
list = erealloc(list, (++total + 1) * sizeof list);
list[total - 1] = word;
list[total] = NULL;
}
}
return list;
}
/*
* Get the first datum in a command line
*/
static char *cmdline_car(char *line, int *index)
{
int i, j = 0, begin, end, quote_count = 0;
char *retval;
/* skip over leading whitespace */
for (i = *index; whitespace(line[i]); i++)
;
begin = i;
/* now count the number of char's in word */
while (line[i] != '\0' && !whitespace(line[i])) {
/* skip over anything enclosed in double quotes */
if (line[i] == '\"') {
quote_count++;
for (i++; line[i] && line[i] != '\"'; i++);
if (line[i] == '\0') {
display(NORMAL, "Error: unfinished quote\n");
return NULL;
} else if (line[i] == '\"')
quote_count++;
}
i++;
}
/* determine how much space will be needed to hold our
* new string */
end = i;
if ((end - begin == 0) || (end - begin - quote_count == 0))
return NULL;
retval = emalloc(end - begin - quote_count + 1);
/* copy over the string */
i = begin;
while(i < end) {
if (line[i] != '\"')
retval[j++] = line[i];
i++;
}
retval[j] = '\0';
*index = i;
return retval;
}
/*
* Strip out every thing on a line after
* the pipe character ('|').
*/
static char *strippipe(char *string)
{
int i;
char *retval;
assert(string);
for (i = 0; string[i] && (string[i] != '|'); i++)
;
retval = emalloc(i + 1);
strncpy(retval, string, i);
retval[i] = '\0';
return retval;
}
/*
* Find and return the remainder of a line
* of ther the pipe ('|') character
*/
static char *pipe_scan(char *line)
{
int i;
char *retval;
for (i = 0; line[i] && line[i] != '|'; i++)
;
if (!i || i == strlen(line))
return NULL;
retval = emalloc(strlen(line) - i + 1);
strcpy(retval, line + i + 1);
return retval;
}
/*
* this is a mock readline funciton. if libreadline is
* ever added, this will need to be removed.
*/
#define FBRL_BUFLEN 256
static char *readline(char *prompt)
{
struct textlist_s {
char *text;
struct textlist_s *next;
};
char buffer[FBRL_BUFLEN];
char *retval, *rtmp;
size_t total_len = 0;
struct textlist_s *list_head = NULL, *list_tail = NULL, *tmp;
printf("%s", prompt);
/* read all input into a list of buffers */
do {
tmp = emalloc(sizeof *tmp);
fgets(buffer, FBRL_BUFLEN, stdin);
tmp->text = strdup(buffer);
tmp->next = NULL;
if (!list_head)
list_head = tmp;
if (list_tail)
list_tail->next = tmp;
list_tail = tmp;
} while (!ending(tmp->text));
/* combine the list of buffers into
* a single string
*/
/* first calculate to total length. */
for (tmp = list_head; tmp; tmp = tmp->next)
total_len += tmp->next ? FBRL_BUFLEN - 1 : strlen(tmp->text) + 1;
/* now create a single buffer */
if (!total_len)
return NULL;
retval = emalloc(total_len);
for (rtmp = retval, tmp = list_head; tmp; tmp = tmp->next, rtmp += FBRL_BUFLEN - 1)
strcpy(rtmp, tmp->text);
return retval;
}
/*
* Determine if there exists a '\n' in the given buffer
* also, convert any newlines to \0's if found.
*/
static int ending(char *buffer)
{
while (*buffer) {
if (*buffer == '\n') {
*buffer = '\0';
return 1;
}
buffer++;
}
return 0;
}
/* remove this when libncurses is added */
static int whitespace(char x)
{
return (x == ' ' || x == '\t');
}
syntax highlighted by Code2HTML, v. 0.9.1