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