fileman.c revision 75406
1/* fileman.c -- A tiny application which demonstrates how to use the 2 GNU Readline library. This application interactively allows users 3 to manipulate files and their modes. */ 4 5#ifdef HAVE_CONFIG_H 6# include <config.h> 7#endif 8 9#include <sys/types.h> 10#ifdef HAVE_SYS_FILE_H 11# include <sys/file.h> 12#endif 13#include <sys/stat.h> 14 15#ifdef HAVE_UNISTD_H 16# include <unistd.h> 17#endif 18 19#include <fcntl.h> 20#include <stdio.h> 21#include <errno.h> 22 23#if defined (HAVE_STRING_H) 24# include <string.h> 25#else /* !HAVE_STRING_H */ 26# include <strings.h> 27#endif /* !HAVE_STRING_H */ 28 29#ifdef HAVE_STDLIB_H 30# include <stdlib.h> 31#endif 32 33#ifdef READLINE_LIBRARY 34# include "readline.h" 35# include "history.h" 36#else 37# include <readline/readline.h> 38# include <readline/history.h> 39#endif 40 41extern char *xmalloc (); 42 43/* The names of functions that actually do the manipulation. */ 44int com_list __P((char *)); 45int com_view __P((char *)); 46int com_rename __P((char *)); 47int com_stat __P((char *)); 48int com_pwd __P((char *)); 49int com_delete __P((char *)); 50int com_help __P((char *)); 51int com_cd __P((char *)); 52int com_quit __P((char *)); 53 54/* A structure which contains information on the commands this program 55 can understand. */ 56 57typedef struct { 58 char *name; /* User printable name of the function. */ 59 rl_icpfunc_t *func; /* Function to call to do the job. */ 60 char *doc; /* Documentation for this function. */ 61} COMMAND; 62 63COMMAND commands[] = { 64 { "cd", com_cd, "Change to directory DIR" }, 65 { "delete", com_delete, "Delete FILE" }, 66 { "help", com_help, "Display this text" }, 67 { "?", com_help, "Synonym for `help'" }, 68 { "list", com_list, "List files in DIR" }, 69 { "ls", com_list, "Synonym for `list'" }, 70 { "pwd", com_pwd, "Print the current working directory" }, 71 { "quit", com_quit, "Quit using Fileman" }, 72 { "rename", com_rename, "Rename FILE to NEWNAME" }, 73 { "stat", com_stat, "Print out statistics on FILE" }, 74 { "view", com_view, "View the contents of FILE" }, 75 { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } 76}; 77 78/* Forward declarations. */ 79char *stripwhite (); 80COMMAND *find_command (); 81 82/* The name of this program, as taken from argv[0]. */ 83char *progname; 84 85/* When non-zero, this global means the user is done using this program. */ 86int done; 87 88char * 89dupstr (s) 90 char *s; 91{ 92 char *r; 93 94 r = xmalloc (strlen (s) + 1); 95 strcpy (r, s); 96 return (r); 97} 98 99main (argc, argv) 100 int argc; 101 char **argv; 102{ 103 char *line, *s; 104 105 progname = argv[0]; 106 107 initialize_readline (); /* Bind our completer. */ 108 109 /* Loop reading and executing lines until the user quits. */ 110 for ( ; done == 0; ) 111 { 112 line = readline ("FileMan: "); 113 114 if (!line) 115 break; 116 117 /* Remove leading and trailing whitespace from the line. 118 Then, if there is anything left, add it to the history list 119 and execute it. */ 120 s = stripwhite (line); 121 122 if (*s) 123 { 124 add_history (s); 125 execute_line (s); 126 } 127 128 free (line); 129 } 130 exit (0); 131} 132 133/* Execute a command line. */ 134int 135execute_line (line) 136 char *line; 137{ 138 register int i; 139 COMMAND *command; 140 char *word; 141 142 /* Isolate the command word. */ 143 i = 0; 144 while (line[i] && whitespace (line[i])) 145 i++; 146 word = line + i; 147 148 while (line[i] && !whitespace (line[i])) 149 i++; 150 151 if (line[i]) 152 line[i++] = '\0'; 153 154 command = find_command (word); 155 156 if (!command) 157 { 158 fprintf (stderr, "%s: No such command for FileMan.\n", word); 159 return (-1); 160 } 161 162 /* Get argument to command, if any. */ 163 while (whitespace (line[i])) 164 i++; 165 166 word = line + i; 167 168 /* Call the function. */ 169 return ((*(command->func)) (word)); 170} 171 172/* Look up NAME as the name of a command, and return a pointer to that 173 command. Return a NULL pointer if NAME isn't a command name. */ 174COMMAND * 175find_command (name) 176 char *name; 177{ 178 register int i; 179 180 for (i = 0; commands[i].name; i++) 181 if (strcmp (name, commands[i].name) == 0) 182 return (&commands[i]); 183 184 return ((COMMAND *)NULL); 185} 186 187/* Strip whitespace from the start and end of STRING. Return a pointer 188 into STRING. */ 189char * 190stripwhite (string) 191 char *string; 192{ 193 register char *s, *t; 194 195 for (s = string; whitespace (*s); s++) 196 ; 197 198 if (*s == 0) 199 return (s); 200 201 t = s + strlen (s) - 1; 202 while (t > s && whitespace (*t)) 203 t--; 204 *++t = '\0'; 205 206 return s; 207} 208 209/* **************************************************************** */ 210/* */ 211/* Interface to Readline Completion */ 212/* */ 213/* **************************************************************** */ 214 215char *command_generator __P((const char *, int)); 216char **fileman_completion __P((const char *, int, int)); 217 218/* Tell the GNU Readline library how to complete. We want to try to complete 219 on command names if this is the first word in the line, or on filenames 220 if not. */ 221initialize_readline () 222{ 223 /* Allow conditional parsing of the ~/.inputrc file. */ 224 rl_readline_name = "FileMan"; 225 226 /* Tell the completer that we want a crack first. */ 227 rl_attempted_completion_function = fileman_completion; 228} 229 230/* Attempt to complete on the contents of TEXT. START and END bound the 231 region of rl_line_buffer that contains the word to complete. TEXT is 232 the word to complete. We can use the entire contents of rl_line_buffer 233 in case we want to do some simple parsing. Return the array of matches, 234 or NULL if there aren't any. */ 235char ** 236fileman_completion (text, start, end) 237 const char *text; 238 int start, end; 239{ 240 char **matches; 241 242 matches = (char **)NULL; 243 244 /* If this word is at the start of the line, then it is a command 245 to complete. Otherwise it is the name of a file in the current 246 directory. */ 247 if (start == 0) 248 matches = rl_completion_matches (text, command_generator); 249 250 return (matches); 251} 252 253/* Generator function for command completion. STATE lets us know whether 254 to start from scratch; without any state (i.e. STATE == 0), then we 255 start at the top of the list. */ 256char * 257command_generator (text, state) 258 const char *text; 259 int state; 260{ 261 static int list_index, len; 262 char *name; 263 264 /* If this is a new word to complete, initialize now. This includes 265 saving the length of TEXT for efficiency, and initializing the index 266 variable to 0. */ 267 if (!state) 268 { 269 list_index = 0; 270 len = strlen (text); 271 } 272 273 /* Return the next name which partially matches from the command list. */ 274 while (name = commands[list_index].name) 275 { 276 list_index++; 277 278 if (strncmp (name, text, len) == 0) 279 return (dupstr(name)); 280 } 281 282 /* If no names matched, then return NULL. */ 283 return ((char *)NULL); 284} 285 286/* **************************************************************** */ 287/* */ 288/* FileMan Commands */ 289/* */ 290/* **************************************************************** */ 291 292/* String to pass to system (). This is for the LIST, VIEW and RENAME 293 commands. */ 294static char syscom[1024]; 295 296/* List the file(s) named in arg. */ 297com_list (arg) 298 char *arg; 299{ 300 if (!arg) 301 arg = ""; 302 303 sprintf (syscom, "ls -FClg %s", arg); 304 return (system (syscom)); 305} 306 307com_view (arg) 308 char *arg; 309{ 310 if (!valid_argument ("view", arg)) 311 return 1; 312 313#if defined (__MSDOS__) 314 /* more.com doesn't grok slashes in pathnames */ 315 sprintf (syscom, "less %s", arg); 316#else 317 sprintf (syscom, "more %s", arg); 318#endif 319 return (system (syscom)); 320} 321 322com_rename (arg) 323 char *arg; 324{ 325 too_dangerous ("rename"); 326 return (1); 327} 328 329com_stat (arg) 330 char *arg; 331{ 332 struct stat finfo; 333 334 if (!valid_argument ("stat", arg)) 335 return (1); 336 337 if (stat (arg, &finfo) == -1) 338 { 339 perror (arg); 340 return (1); 341 } 342 343 printf ("Statistics for `%s':\n", arg); 344 345 printf ("%s has %d link%s, and is %d byte%s in length.\n", 346 arg, 347 finfo.st_nlink, 348 (finfo.st_nlink == 1) ? "" : "s", 349 finfo.st_size, 350 (finfo.st_size == 1) ? "" : "s"); 351 printf ("Inode Last Change at: %s", ctime (&finfo.st_ctime)); 352 printf (" Last access at: %s", ctime (&finfo.st_atime)); 353 printf (" Last modified at: %s", ctime (&finfo.st_mtime)); 354 return (0); 355} 356 357com_delete (arg) 358 char *arg; 359{ 360 too_dangerous ("delete"); 361 return (1); 362} 363 364/* Print out help for ARG, or for all of the commands if ARG is 365 not present. */ 366com_help (arg) 367 char *arg; 368{ 369 register int i; 370 int printed = 0; 371 372 for (i = 0; commands[i].name; i++) 373 { 374 if (!*arg || (strcmp (arg, commands[i].name) == 0)) 375 { 376 printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc); 377 printed++; 378 } 379 } 380 381 if (!printed) 382 { 383 printf ("No commands match `%s'. Possibilties are:\n", arg); 384 385 for (i = 0; commands[i].name; i++) 386 { 387 /* Print in six columns. */ 388 if (printed == 6) 389 { 390 printed = 0; 391 printf ("\n"); 392 } 393 394 printf ("%s\t", commands[i].name); 395 printed++; 396 } 397 398 if (printed) 399 printf ("\n"); 400 } 401 return (0); 402} 403 404/* Change to the directory ARG. */ 405com_cd (arg) 406 char *arg; 407{ 408 if (chdir (arg) == -1) 409 { 410 perror (arg); 411 return 1; 412 } 413 414 com_pwd (""); 415 return (0); 416} 417 418/* Print out the current working directory. */ 419com_pwd (ignore) 420 char *ignore; 421{ 422 char dir[1024], *s; 423 424 s = getcwd (dir, sizeof(dir) - 1); 425 if (s == 0) 426 { 427 printf ("Error getting pwd: %s\n", dir); 428 return 1; 429 } 430 431 printf ("Current directory is %s\n", dir); 432 return 0; 433} 434 435/* The user wishes to quit using this program. Just set DONE non-zero. */ 436com_quit (arg) 437 char *arg; 438{ 439 done = 1; 440 return (0); 441} 442 443/* Function which tells you that you can't do this. */ 444too_dangerous (caller) 445 char *caller; 446{ 447 fprintf (stderr, 448 "%s: Too dangerous for me to distribute. Write it yourself.\n", 449 caller); 450} 451 452/* Return non-zero if ARG is a valid argument for CALLER, else print 453 an error message and return zero. */ 454int 455valid_argument (caller, arg) 456 char *caller, *arg; 457{ 458 if (!arg || !*arg) 459 { 460 fprintf (stderr, "%s: Argument required.\n", caller); 461 return (0); 462 } 463 464 return (1); 465} 466