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