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