fileman.c revision 21308
121308Sache/* fileman.c -- A tiny application which demonstrates how to use the
221308Sache   GNU Readline library.  This application interactively allows users
321308Sache   to manipulate files and their modes. */
421308Sache
521308Sache#include <stdio.h>
621308Sache#include <sys/types.h>
721308Sache#include <sys/file.h>
821308Sache#include <sys/stat.h>
921308Sache#include <sys/errno.h>
1021308Sache
1121308Sache#include <readline/readline.h>
1221308Sache#include <readline/history.h>
1321308Sache
1421308Sacheextern char *getwd ();
1521308Sacheextern char *xmalloc ();
1621308Sache
1721308Sache/* The names of functions that actually do the manipulation. */
1821308Sacheint com_list (), com_view (), com_rename (), com_stat (), com_pwd ();
1921308Sacheint com_delete (), com_help (), com_cd (), com_quit ();
2021308Sache
2121308Sache/* A structure which contains information on the commands this program
2221308Sache   can understand. */
2321308Sache
2421308Sachetypedef struct {
2521308Sache  char *name;			/* User printable name of the function. */
2621308Sache  Function *func;		/* Function to call to do the job. */
2721308Sache  char *doc;			/* Documentation for this function.  */
2821308Sache} COMMAND;
2921308Sache
3021308SacheCOMMAND commands[] = {
3121308Sache  { "cd", com_cd, "Change to directory DIR" },
3221308Sache  { "delete", com_delete, "Delete FILE" },
3321308Sache  { "help", com_help, "Display this text" },
3421308Sache  { "?", com_help, "Synonym for `help'" },
3521308Sache  { "list", com_list, "List files in DIR" },
3621308Sache  { "ls", com_list, "Synonym for `list'" },
3721308Sache  { "pwd", com_pwd, "Print the current working directory" },
3821308Sache  { "quit", com_quit, "Quit using Fileman" },
3921308Sache  { "rename", com_rename, "Rename FILE to NEWNAME" },
4021308Sache  { "stat", com_stat, "Print out statistics on FILE" },
4121308Sache  { "view", com_view, "View the contents of FILE" },
4221308Sache  { (char *)NULL, (Function *)NULL, (char *)NULL }
4321308Sache};
4421308Sache
4521308Sache/* Forward declarations. */
4621308Sachechar *stripwhite ();
4721308SacheCOMMAND *find_command ();
4821308Sache
4921308Sache/* The name of this program, as taken from argv[0]. */
5021308Sachechar *progname;
5121308Sache
5221308Sache/* When non-zero, this global means the user is done using this program. */
5321308Sacheint done;
5421308Sache
5521308Sachechar *
5621308Sachedupstr (s)
5721308Sache     int s;
5821308Sache{
5921308Sache  char *r;
6021308Sache
6121308Sache  r = xmalloc (strlen (s) + 1);
6221308Sache  strcpy (r, s);
6321308Sache  return (r);
6421308Sache}
6521308Sache
6621308Sachemain (argc, argv)
6721308Sache     int argc;
6821308Sache     char **argv;
6921308Sache{
7021308Sache  char *line, *s;
7121308Sache
7221308Sache  progname = argv[0];
7321308Sache
7421308Sache  initialize_readline ();	/* Bind our completer. */
7521308Sache
7621308Sache  /* Loop reading and executing lines until the user quits. */
7721308Sache  for ( ; done == 0; )
7821308Sache    {
7921308Sache      line = readline ("FileMan: ");
8021308Sache
8121308Sache      if (!line)
8221308Sache        break;
8321308Sache
8421308Sache      /* Remove leading and trailing whitespace from the line.
8521308Sache         Then, if there is anything left, add it to the history list
8621308Sache         and execute it. */
8721308Sache      s = stripwhite (line);
8821308Sache
8921308Sache      if (*s)
9021308Sache        {
9121308Sache          add_history (s);
9221308Sache          execute_line (s);
9321308Sache        }
9421308Sache
9521308Sache      free (line);
9621308Sache    }
9721308Sache  exit (0);
9821308Sache}
9921308Sache
10021308Sache/* Execute a command line. */
10121308Sacheint
10221308Sacheexecute_line (line)
10321308Sache     char *line;
10421308Sache{
10521308Sache  register int i;
10621308Sache  COMMAND *command;
10721308Sache  char *word;
10821308Sache
10921308Sache  /* Isolate the command word. */
11021308Sache  i = 0;
11121308Sache  while (line[i] && whitespace (line[i]))
11221308Sache    i++;
11321308Sache  word = line + i;
11421308Sache
11521308Sache  while (line[i] && !whitespace (line[i]))
11621308Sache    i++;
11721308Sache
11821308Sache  if (line[i])
11921308Sache    line[i++] = '\0';
12021308Sache
12121308Sache  command = find_command (word);
12221308Sache
12321308Sache  if (!command)
12421308Sache    {
12521308Sache      fprintf (stderr, "%s: No such command for FileMan.\n", word);
12621308Sache      return (-1);
12721308Sache    }
12821308Sache
12921308Sache  /* Get argument to command, if any. */
13021308Sache  while (whitespace (line[i]))
13121308Sache    i++;
13221308Sache
13321308Sache  word = line + i;
13421308Sache
13521308Sache  /* Call the function. */
13621308Sache  return ((*(command->func)) (word));
13721308Sache}
13821308Sache
13921308Sache/* Look up NAME as the name of a command, and return a pointer to that
14021308Sache   command.  Return a NULL pointer if NAME isn't a command name. */
14121308SacheCOMMAND *
14221308Sachefind_command (name)
14321308Sache     char *name;
14421308Sache{
14521308Sache  register int i;
14621308Sache
14721308Sache  for (i = 0; commands[i].name; i++)
14821308Sache    if (strcmp (name, commands[i].name) == 0)
14921308Sache      return (&commands[i]);
15021308Sache
15121308Sache  return ((COMMAND *)NULL);
15221308Sache}
15321308Sache
15421308Sache/* Strip whitespace from the start and end of STRING.  Return a pointer
15521308Sache   into STRING. */
15621308Sachechar *
15721308Sachestripwhite (string)
15821308Sache     char *string;
15921308Sache{
16021308Sache  register char *s, *t;
16121308Sache
16221308Sache  for (s = string; whitespace (*s); s++)
16321308Sache    ;
16421308Sache
16521308Sache  if (*s == 0)
16621308Sache    return (s);
16721308Sache
16821308Sache  t = s + strlen (s) - 1;
16921308Sache  while (t > s && whitespace (*t))
17021308Sache    t--;
17121308Sache  *++t = '\0';
17221308Sache
17321308Sache  return s;
17421308Sache}
17521308Sache
17621308Sache/* **************************************************************** */
17721308Sache/*                                                                  */
17821308Sache/*                  Interface to Readline Completion                */
17921308Sache/*                                                                  */
18021308Sache/* **************************************************************** */
18121308Sache
18221308Sachechar *command_generator ();
18321308Sachechar **fileman_completion ();
18421308Sache
18521308Sache/* Tell the GNU Readline library how to complete.  We want to try to complete
18621308Sache   on command names if this is the first word in the line, or on filenames
18721308Sache   if not. */
18821308Sacheinitialize_readline ()
18921308Sache{
19021308Sache  /* Allow conditional parsing of the ~/.inputrc file. */
19121308Sache  rl_readline_name = "FileMan";
19221308Sache
19321308Sache  /* Tell the completer that we want a crack first. */
19421308Sache  rl_attempted_completion_function = (CPPFunction *)fileman_completion;
19521308Sache}
19621308Sache
19721308Sache/* Attempt to complete on the contents of TEXT.  START and END bound the
19821308Sache   region of rl_line_buffer that contains the word to complete.  TEXT is
19921308Sache   the word to complete.  We can use the entire contents of rl_line_buffer
20021308Sache   in case we want to do some simple parsing.  Return the array of matches,
20121308Sache   or NULL if there aren't any. */
20221308Sachechar **
20321308Sachefileman_completion (text, start, end)
20421308Sache     char *text;
20521308Sache     int start, end;
20621308Sache{
20721308Sache  char **matches;
20821308Sache
20921308Sache  matches = (char **)NULL;
21021308Sache
21121308Sache  /* If this word is at the start of the line, then it is a command
21221308Sache     to complete.  Otherwise it is the name of a file in the current
21321308Sache     directory. */
21421308Sache  if (start == 0)
21521308Sache    matches = completion_matches (text, command_generator);
21621308Sache
21721308Sache  return (matches);
21821308Sache}
21921308Sache
22021308Sache/* Generator function for command completion.  STATE lets us know whether
22121308Sache   to start from scratch; without any state (i.e. STATE == 0), then we
22221308Sache   start at the top of the list. */
22321308Sachechar *
22421308Sachecommand_generator (text, state)
22521308Sache     char *text;
22621308Sache     int state;
22721308Sache{
22821308Sache  static int list_index, len;
22921308Sache  char *name;
23021308Sache
23121308Sache  /* If this is a new word to complete, initialize now.  This includes
23221308Sache     saving the length of TEXT for efficiency, and initializing the index
23321308Sache     variable to 0. */
23421308Sache  if (!state)
23521308Sache    {
23621308Sache      list_index = 0;
23721308Sache      len = strlen (text);
23821308Sache    }
23921308Sache
24021308Sache  /* Return the next name which partially matches from the command list. */
24121308Sache  while (name = commands[list_index].name)
24221308Sache    {
24321308Sache      list_index++;
24421308Sache
24521308Sache      if (strncmp (name, text, len) == 0)
24621308Sache        return (dupstr(name));
24721308Sache    }
24821308Sache
24921308Sache  /* If no names matched, then return NULL. */
25021308Sache  return ((char *)NULL);
25121308Sache}
25221308Sache
25321308Sache/* **************************************************************** */
25421308Sache/*                                                                  */
25521308Sache/*                       FileMan Commands                           */
25621308Sache/*                                                                  */
25721308Sache/* **************************************************************** */
25821308Sache
25921308Sache/* String to pass to system ().  This is for the LIST, VIEW and RENAME
26021308Sache   commands. */
26121308Sachestatic char syscom[1024];
26221308Sache
26321308Sache/* List the file(s) named in arg. */
26421308Sachecom_list (arg)
26521308Sache     char *arg;
26621308Sache{
26721308Sache  if (!arg)
26821308Sache    arg = "";
26921308Sache
27021308Sache  sprintf (syscom, "ls -FClg %s", arg);
27121308Sache  return (system (syscom));
27221308Sache}
27321308Sache
27421308Sachecom_view (arg)
27521308Sache     char *arg;
27621308Sache{
27721308Sache  if (!valid_argument ("view", arg))
27821308Sache    return 1;
27921308Sache
28021308Sache  sprintf (syscom, "more %s", arg);
28121308Sache  return (system (syscom));
28221308Sache}
28321308Sache
28421308Sachecom_rename (arg)
28521308Sache     char *arg;
28621308Sache{
28721308Sache  too_dangerous ("rename");
28821308Sache  return (1);
28921308Sache}
29021308Sache
29121308Sachecom_stat (arg)
29221308Sache     char *arg;
29321308Sache{
29421308Sache  struct stat finfo;
29521308Sache
29621308Sache  if (!valid_argument ("stat", arg))
29721308Sache    return (1);
29821308Sache
29921308Sache  if (stat (arg, &finfo) == -1)
30021308Sache    {
30121308Sache      perror (arg);
30221308Sache      return (1);
30321308Sache    }
30421308Sache
30521308Sache  printf ("Statistics for `%s':\n", arg);
30621308Sache
30721308Sache  printf ("%s has %d link%s, and is %d byte%s in length.\n", arg,
30821308Sache          finfo.st_nlink,
30921308Sache          (finfo.st_nlink == 1) ? "" : "s",
31021308Sache          finfo.st_size,
31121308Sache          (finfo.st_size == 1) ? "" : "s");
31221308Sache  printf ("Inode Last Change at: %s", ctime (&finfo.st_ctime));
31321308Sache  printf ("      Last access at: %s", ctime (&finfo.st_atime));
31421308Sache  printf ("    Last modified at: %s", ctime (&finfo.st_mtime));
31521308Sache  return (0);
31621308Sache}
31721308Sache
31821308Sachecom_delete (arg)
31921308Sache     char *arg;
32021308Sache{
32121308Sache  too_dangerous ("delete");
32221308Sache  return (1);
32321308Sache}
32421308Sache
32521308Sache/* Print out help for ARG, or for all of the commands if ARG is
32621308Sache   not present. */
32721308Sachecom_help (arg)
32821308Sache     char *arg;
32921308Sache{
33021308Sache  register int i;
33121308Sache  int printed = 0;
33221308Sache
33321308Sache  for (i = 0; commands[i].name; i++)
33421308Sache    {
33521308Sache      if (!*arg || (strcmp (arg, commands[i].name) == 0))
33621308Sache        {
33721308Sache          printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc);
33821308Sache          printed++;
33921308Sache        }
34021308Sache    }
34121308Sache
34221308Sache  if (!printed)
34321308Sache    {
34421308Sache      printf ("No commands match `%s'.  Possibilties are:\n", arg);
34521308Sache
34621308Sache      for (i = 0; commands[i].name; i++)
34721308Sache        {
34821308Sache          /* Print in six columns. */
34921308Sache          if (printed == 6)
35021308Sache            {
35121308Sache              printed = 0;
35221308Sache              printf ("\n");
35321308Sache            }
35421308Sache
35521308Sache          printf ("%s\t", commands[i].name);
35621308Sache          printed++;
35721308Sache        }
35821308Sache
35921308Sache      if (printed)
36021308Sache        printf ("\n");
36121308Sache    }
36221308Sache  return (0);
36321308Sache}
36421308Sache
36521308Sache/* Change to the directory ARG. */
36621308Sachecom_cd (arg)
36721308Sache     char *arg;
36821308Sache{
36921308Sache  if (chdir (arg) == -1)
37021308Sache    {
37121308Sache      perror (arg);
37221308Sache      return 1;
37321308Sache    }
37421308Sache
37521308Sache  com_pwd ("");
37621308Sache  return (0);
37721308Sache}
37821308Sache
37921308Sache/* Print out the current working directory. */
38021308Sachecom_pwd (ignore)
38121308Sache     char *ignore;
38221308Sache{
38321308Sache  char dir[1024], *s;
38421308Sache
38521308Sache  s = getwd (dir);
38621308Sache  if (s == 0)
38721308Sache    {
38821308Sache      printf ("Error getting pwd: %s\n", dir);
38921308Sache      return 1;
39021308Sache    }
39121308Sache
39221308Sache  printf ("Current directory is %s\n", dir);
39321308Sache  return 0;
39421308Sache}
39521308Sache
39621308Sache/* The user wishes to quit using this program.  Just set DONE non-zero. */
39721308Sachecom_quit (arg)
39821308Sache     char *arg;
39921308Sache{
40021308Sache  done = 1;
40121308Sache  return (0);
40221308Sache}
40321308Sache
40421308Sache/* Function which tells you that you can't do this. */
40521308Sachetoo_dangerous (caller)
40621308Sache     char *caller;
40721308Sache{
40821308Sache  fprintf (stderr,
40921308Sache           "%s: Too dangerous for me to distribute.  Write it yourself.\n",
41021308Sache           caller);
41121308Sache}
41221308Sache
41321308Sache/* Return non-zero if ARG is a valid argument for CALLER, else print
41421308Sache   an error message and return zero. */
41521308Sacheint
41621308Sachevalid_argument (caller, arg)
41721308Sache     char *caller, *arg;
41821308Sache{
41921308Sache  if (!arg || !*arg)
42021308Sache    {
42121308Sache      fprintf (stderr, "%s: Argument required.\n", caller);
42221308Sache      return (0);
42321308Sache    }
42421308Sache
42521308Sache  return (1);
42621308Sache}
427