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