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