1119610Sache/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
2119610Sache
3119610Sache   This file is part of the GNU Readline Library, a library for
4119610Sache   reading lines of text with interactive input and history editing.
5119610Sache
6119610Sache   The GNU Readline Library is free software; you can redistribute it
7119610Sache   and/or modify it under the terms of the GNU General Public License
8119610Sache   as published by the Free Software Foundation; either version 2, or
9119610Sache   (at your option) any later version.
10119610Sache
11119610Sache   The GNU Readline Library is distributed in the hope that it will be
12119610Sache   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13119610Sache   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14119610Sache   GNU General Public License for more details.
15119610Sache
16119610Sache   The GNU General Public License is often shipped with GNU software, and
17119610Sache   is generally kept in a file called COPYING or LICENSE.  If you do not
18119610Sache   have a copy of the license, write to the Free Software Foundation,
19119610Sache   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20119610Sache
2121308Sache/* fileman.c -- A tiny application which demonstrates how to use the
2221308Sache   GNU Readline library.  This application interactively allows users
2321308Sache   to manipulate files and their modes. */
2421308Sache
2526497Sache#ifdef HAVE_CONFIG_H
2658310Sache#  include <config.h>
2726497Sache#endif
2826497Sache
2921308Sache#include <sys/types.h>
3026497Sache#ifdef HAVE_SYS_FILE_H
3158310Sache#  include <sys/file.h>
3226497Sache#endif
3321308Sache#include <sys/stat.h>
3421308Sache
3558310Sache#ifdef HAVE_UNISTD_H
3658310Sache#  include <unistd.h>
3758310Sache#endif
3858310Sache
3958310Sache#include <fcntl.h>
4026497Sache#include <stdio.h>
4126497Sache#include <errno.h>
4221308Sache
4326497Sache#if defined (HAVE_STRING_H)
4426497Sache#  include <string.h>
4526497Sache#else /* !HAVE_STRING_H */
4626497Sache#  include <strings.h>
4726497Sache#endif /* !HAVE_STRING_H */
4826497Sache
4958310Sache#ifdef HAVE_STDLIB_H
5058310Sache#  include <stdlib.h>
5158310Sache#endif
5258310Sache
5326497Sache#ifdef READLINE_LIBRARY
5426497Sache#  include "readline.h"
5526497Sache#  include "history.h"
5626497Sache#else
5726497Sache#  include <readline/readline.h>
5826497Sache#  include <readline/history.h>
5926497Sache#endif
6026497Sache
6121308Sacheextern char *xmalloc ();
6221308Sache
6321308Sache/* The names of functions that actually do the manipulation. */
64119610Sacheint com_list PARAMS((char *));
65119610Sacheint com_view PARAMS((char *));
66119610Sacheint com_rename PARAMS((char *));
67119610Sacheint com_stat PARAMS((char *));
68119610Sacheint com_pwd PARAMS((char *));
69119610Sacheint com_delete PARAMS((char *));
70119610Sacheint com_help PARAMS((char *));
71119610Sacheint com_cd PARAMS((char *));
72119610Sacheint com_quit PARAMS((char *));
7321308Sache
7421308Sache/* A structure which contains information on the commands this program
7521308Sache   can understand. */
7621308Sache
7721308Sachetypedef struct {
7821308Sache  char *name;			/* User printable name of the function. */
7975406Sache  rl_icpfunc_t *func;		/* Function to call to do the job. */
8021308Sache  char *doc;			/* Documentation for this function.  */
8121308Sache} COMMAND;
8221308Sache
8321308SacheCOMMAND commands[] = {
8421308Sache  { "cd", com_cd, "Change to directory DIR" },
8521308Sache  { "delete", com_delete, "Delete FILE" },
8621308Sache  { "help", com_help, "Display this text" },
8721308Sache  { "?", com_help, "Synonym for `help'" },
8821308Sache  { "list", com_list, "List files in DIR" },
8921308Sache  { "ls", com_list, "Synonym for `list'" },
9021308Sache  { "pwd", com_pwd, "Print the current working directory" },
9121308Sache  { "quit", com_quit, "Quit using Fileman" },
9221308Sache  { "rename", com_rename, "Rename FILE to NEWNAME" },
9321308Sache  { "stat", com_stat, "Print out statistics on FILE" },
9421308Sache  { "view", com_view, "View the contents of FILE" },
9575406Sache  { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL }
9621308Sache};
9721308Sache
9821308Sache/* Forward declarations. */
9921308Sachechar *stripwhite ();
10021308SacheCOMMAND *find_command ();
10121308Sache
10221308Sache/* The name of this program, as taken from argv[0]. */
10321308Sachechar *progname;
10421308Sache
10521308Sache/* When non-zero, this global means the user is done using this program. */
10621308Sacheint done;
10721308Sache
10821308Sachechar *
10921308Sachedupstr (s)
11026497Sache     char *s;
11121308Sache{
11221308Sache  char *r;
11321308Sache
11421308Sache  r = xmalloc (strlen (s) + 1);
11521308Sache  strcpy (r, s);
11621308Sache  return (r);
11721308Sache}
11821308Sache
11921308Sachemain (argc, argv)
12021308Sache     int argc;
12121308Sache     char **argv;
12221308Sache{
12321308Sache  char *line, *s;
12421308Sache
12521308Sache  progname = argv[0];
12621308Sache
12721308Sache  initialize_readline ();	/* Bind our completer. */
12821308Sache
12921308Sache  /* Loop reading and executing lines until the user quits. */
13021308Sache  for ( ; done == 0; )
13121308Sache    {
13221308Sache      line = readline ("FileMan: ");
13321308Sache
13421308Sache      if (!line)
13521308Sache        break;
13621308Sache
13721308Sache      /* Remove leading and trailing whitespace from the line.
13821308Sache         Then, if there is anything left, add it to the history list
13921308Sache         and execute it. */
14021308Sache      s = stripwhite (line);
14121308Sache
14221308Sache      if (*s)
14321308Sache        {
14421308Sache          add_history (s);
14521308Sache          execute_line (s);
14621308Sache        }
14721308Sache
14821308Sache      free (line);
14921308Sache    }
15021308Sache  exit (0);
15121308Sache}
15221308Sache
15321308Sache/* Execute a command line. */
15421308Sacheint
15521308Sacheexecute_line (line)
15621308Sache     char *line;
15721308Sache{
15821308Sache  register int i;
15921308Sache  COMMAND *command;
16021308Sache  char *word;
16121308Sache
16221308Sache  /* Isolate the command word. */
16321308Sache  i = 0;
16421308Sache  while (line[i] && whitespace (line[i]))
16521308Sache    i++;
16621308Sache  word = line + i;
16721308Sache
16821308Sache  while (line[i] && !whitespace (line[i]))
16921308Sache    i++;
17021308Sache
17121308Sache  if (line[i])
17221308Sache    line[i++] = '\0';
17321308Sache
17421308Sache  command = find_command (word);
17521308Sache
17621308Sache  if (!command)
17721308Sache    {
17821308Sache      fprintf (stderr, "%s: No such command for FileMan.\n", word);
17921308Sache      return (-1);
18021308Sache    }
18121308Sache
18221308Sache  /* Get argument to command, if any. */
18321308Sache  while (whitespace (line[i]))
18421308Sache    i++;
18521308Sache
18621308Sache  word = line + i;
18721308Sache
18821308Sache  /* Call the function. */
18921308Sache  return ((*(command->func)) (word));
19021308Sache}
19121308Sache
19221308Sache/* Look up NAME as the name of a command, and return a pointer to that
19321308Sache   command.  Return a NULL pointer if NAME isn't a command name. */
19421308SacheCOMMAND *
19521308Sachefind_command (name)
19621308Sache     char *name;
19721308Sache{
19821308Sache  register int i;
19921308Sache
20021308Sache  for (i = 0; commands[i].name; i++)
20121308Sache    if (strcmp (name, commands[i].name) == 0)
20221308Sache      return (&commands[i]);
20321308Sache
20421308Sache  return ((COMMAND *)NULL);
20521308Sache}
20621308Sache
20721308Sache/* Strip whitespace from the start and end of STRING.  Return a pointer
20821308Sache   into STRING. */
20921308Sachechar *
21021308Sachestripwhite (string)
21121308Sache     char *string;
21221308Sache{
21321308Sache  register char *s, *t;
21421308Sache
21521308Sache  for (s = string; whitespace (*s); s++)
21621308Sache    ;
21721308Sache
21821308Sache  if (*s == 0)
21921308Sache    return (s);
22021308Sache
22121308Sache  t = s + strlen (s) - 1;
22221308Sache  while (t > s && whitespace (*t))
22321308Sache    t--;
22421308Sache  *++t = '\0';
22521308Sache
22621308Sache  return s;
22721308Sache}
22821308Sache
22921308Sache/* **************************************************************** */
23021308Sache/*                                                                  */
23121308Sache/*                  Interface to Readline Completion                */
23221308Sache/*                                                                  */
23321308Sache/* **************************************************************** */
23421308Sache
235119610Sachechar *command_generator PARAMS((const char *, int));
236119610Sachechar **fileman_completion PARAMS((const char *, int, int));
23721308Sache
23821308Sache/* Tell the GNU Readline library how to complete.  We want to try to complete
23921308Sache   on command names if this is the first word in the line, or on filenames
24021308Sache   if not. */
24121308Sacheinitialize_readline ()
24221308Sache{
24321308Sache  /* Allow conditional parsing of the ~/.inputrc file. */
24421308Sache  rl_readline_name = "FileMan";
24521308Sache
24621308Sache  /* Tell the completer that we want a crack first. */
24775406Sache  rl_attempted_completion_function = fileman_completion;
24821308Sache}
24921308Sache
25021308Sache/* Attempt to complete on the contents of TEXT.  START and END bound the
25121308Sache   region of rl_line_buffer that contains the word to complete.  TEXT is
25221308Sache   the word to complete.  We can use the entire contents of rl_line_buffer
25321308Sache   in case we want to do some simple parsing.  Return the array of matches,
25421308Sache   or NULL if there aren't any. */
25521308Sachechar **
25621308Sachefileman_completion (text, start, end)
25775406Sache     const char *text;
25821308Sache     int start, end;
25921308Sache{
26021308Sache  char **matches;
26121308Sache
26221308Sache  matches = (char **)NULL;
26321308Sache
26421308Sache  /* If this word is at the start of the line, then it is a command
26521308Sache     to complete.  Otherwise it is the name of a file in the current
26621308Sache     directory. */
26721308Sache  if (start == 0)
26875406Sache    matches = rl_completion_matches (text, command_generator);
26921308Sache
27021308Sache  return (matches);
27121308Sache}
27221308Sache
27321308Sache/* Generator function for command completion.  STATE lets us know whether
27421308Sache   to start from scratch; without any state (i.e. STATE == 0), then we
27521308Sache   start at the top of the list. */
27621308Sachechar *
27721308Sachecommand_generator (text, state)
27875406Sache     const char *text;
27921308Sache     int state;
28021308Sache{
28121308Sache  static int list_index, len;
28221308Sache  char *name;
28321308Sache
28421308Sache  /* If this is a new word to complete, initialize now.  This includes
28521308Sache     saving the length of TEXT for efficiency, and initializing the index
28621308Sache     variable to 0. */
28721308Sache  if (!state)
28821308Sache    {
28921308Sache      list_index = 0;
29021308Sache      len = strlen (text);
29121308Sache    }
29221308Sache
29321308Sache  /* Return the next name which partially matches from the command list. */
29421308Sache  while (name = commands[list_index].name)
29521308Sache    {
29621308Sache      list_index++;
29721308Sache
29821308Sache      if (strncmp (name, text, len) == 0)
29921308Sache        return (dupstr(name));
30021308Sache    }
30121308Sache
30221308Sache  /* If no names matched, then return NULL. */
30321308Sache  return ((char *)NULL);
30421308Sache}
30521308Sache
30621308Sache/* **************************************************************** */
30721308Sache/*                                                                  */
30821308Sache/*                       FileMan Commands                           */
30921308Sache/*                                                                  */
31021308Sache/* **************************************************************** */
31121308Sache
31221308Sache/* String to pass to system ().  This is for the LIST, VIEW and RENAME
31321308Sache   commands. */
31421308Sachestatic char syscom[1024];
31521308Sache
31621308Sache/* List the file(s) named in arg. */
31721308Sachecom_list (arg)
31821308Sache     char *arg;
31921308Sache{
32021308Sache  if (!arg)
32121308Sache    arg = "";
32221308Sache
32321308Sache  sprintf (syscom, "ls -FClg %s", arg);
32421308Sache  return (system (syscom));
32521308Sache}
32621308Sache
32721308Sachecom_view (arg)
32821308Sache     char *arg;
32921308Sache{
33021308Sache  if (!valid_argument ("view", arg))
33121308Sache    return 1;
33221308Sache
33358310Sache#if defined (__MSDOS__)
33458310Sache  /* more.com doesn't grok slashes in pathnames */
33558310Sache  sprintf (syscom, "less %s", arg);
33658310Sache#else
33721308Sache  sprintf (syscom, "more %s", arg);
33858310Sache#endif
33921308Sache  return (system (syscom));
34021308Sache}
34121308Sache
34221308Sachecom_rename (arg)
34321308Sache     char *arg;
34421308Sache{
34521308Sache  too_dangerous ("rename");
34621308Sache  return (1);
34721308Sache}
34821308Sache
34921308Sachecom_stat (arg)
35021308Sache     char *arg;
35121308Sache{
35221308Sache  struct stat finfo;
35321308Sache
35421308Sache  if (!valid_argument ("stat", arg))
35521308Sache    return (1);
35621308Sache
35721308Sache  if (stat (arg, &finfo) == -1)
35821308Sache    {
35921308Sache      perror (arg);
36021308Sache      return (1);
36121308Sache    }
36221308Sache
36321308Sache  printf ("Statistics for `%s':\n", arg);
36421308Sache
36526497Sache  printf ("%s has %d link%s, and is %d byte%s in length.\n",
36626497Sache	  arg,
36721308Sache          finfo.st_nlink,
36821308Sache          (finfo.st_nlink == 1) ? "" : "s",
36921308Sache          finfo.st_size,
37021308Sache          (finfo.st_size == 1) ? "" : "s");
37121308Sache  printf ("Inode Last Change at: %s", ctime (&finfo.st_ctime));
37221308Sache  printf ("      Last access at: %s", ctime (&finfo.st_atime));
37321308Sache  printf ("    Last modified at: %s", ctime (&finfo.st_mtime));
37421308Sache  return (0);
37521308Sache}
37621308Sache
37721308Sachecom_delete (arg)
37821308Sache     char *arg;
37921308Sache{
38021308Sache  too_dangerous ("delete");
38121308Sache  return (1);
38221308Sache}
38321308Sache
38421308Sache/* Print out help for ARG, or for all of the commands if ARG is
38521308Sache   not present. */
38621308Sachecom_help (arg)
38721308Sache     char *arg;
38821308Sache{
38921308Sache  register int i;
39021308Sache  int printed = 0;
39121308Sache
39221308Sache  for (i = 0; commands[i].name; i++)
39321308Sache    {
39421308Sache      if (!*arg || (strcmp (arg, commands[i].name) == 0))
39521308Sache        {
39621308Sache          printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc);
39721308Sache          printed++;
39821308Sache        }
39921308Sache    }
40021308Sache
40121308Sache  if (!printed)
40221308Sache    {
40321308Sache      printf ("No commands match `%s'.  Possibilties are:\n", arg);
40421308Sache
40521308Sache      for (i = 0; commands[i].name; i++)
40621308Sache        {
40721308Sache          /* Print in six columns. */
40821308Sache          if (printed == 6)
40921308Sache            {
41021308Sache              printed = 0;
41121308Sache              printf ("\n");
41221308Sache            }
41321308Sache
41421308Sache          printf ("%s\t", commands[i].name);
41521308Sache          printed++;
41621308Sache        }
41721308Sache
41821308Sache      if (printed)
41921308Sache        printf ("\n");
42021308Sache    }
42121308Sache  return (0);
42221308Sache}
42321308Sache
42421308Sache/* Change to the directory ARG. */
42521308Sachecom_cd (arg)
42621308Sache     char *arg;
42721308Sache{
42821308Sache  if (chdir (arg) == -1)
42921308Sache    {
43021308Sache      perror (arg);
43121308Sache      return 1;
43221308Sache    }
43321308Sache
43421308Sache  com_pwd ("");
43521308Sache  return (0);
43621308Sache}
43721308Sache
43821308Sache/* Print out the current working directory. */
43921308Sachecom_pwd (ignore)
44021308Sache     char *ignore;
44121308Sache{
44221308Sache  char dir[1024], *s;
44321308Sache
44458310Sache  s = getcwd (dir, sizeof(dir) - 1);
44521308Sache  if (s == 0)
44621308Sache    {
44721308Sache      printf ("Error getting pwd: %s\n", dir);
44821308Sache      return 1;
44921308Sache    }
45021308Sache
45121308Sache  printf ("Current directory is %s\n", dir);
45221308Sache  return 0;
45321308Sache}
45421308Sache
45521308Sache/* The user wishes to quit using this program.  Just set DONE non-zero. */
45621308Sachecom_quit (arg)
45721308Sache     char *arg;
45821308Sache{
45921308Sache  done = 1;
46021308Sache  return (0);
46121308Sache}
46221308Sache
46321308Sache/* Function which tells you that you can't do this. */
46421308Sachetoo_dangerous (caller)
46521308Sache     char *caller;
46621308Sache{
46721308Sache  fprintf (stderr,
46821308Sache           "%s: Too dangerous for me to distribute.  Write it yourself.\n",
46921308Sache           caller);
47021308Sache}
47121308Sache
47221308Sache/* Return non-zero if ARG is a valid argument for CALLER, else print
47321308Sache   an error message and return zero. */
47421308Sacheint
47521308Sachevalid_argument (caller, arg)
47621308Sache     char *caller, *arg;
47721308Sache{
47821308Sache  if (!arg || !*arg)
47921308Sache    {
48021308Sache      fprintf (stderr, "%s: Argument required.\n", caller);
48121308Sache      return (0);
48221308Sache    }
48321308Sache
48421308Sache  return (1);
48521308Sache}
486