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