xref: /openbsd-src/gnu/lib/libreadline/examples/fileman.c (revision 15b117eae8ea0caffec2fbd5cbd366a5ccee20ab)
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 
61 extern char *xmalloc ();
62 
63 /* The names of functions that actually do the manipulation. */
64 int com_list PARAMS((char *));
65 int com_view PARAMS((char *));
66 int com_rename PARAMS((char *));
67 int com_stat PARAMS((char *));
68 int com_pwd PARAMS((char *));
69 int com_delete PARAMS((char *));
70 int com_help PARAMS((char *));
71 int com_cd PARAMS((char *));
72 int com_quit PARAMS((char *));
73 
74 /* A structure which contains information on the commands this program
75    can understand. */
76 
77 typedef 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 
83 COMMAND 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. */
99 char *stripwhite ();
100 COMMAND *find_command ();
101 
102 /* The name of this program, as taken from argv[0]. */
103 char *progname;
104 
105 /* When non-zero, this global means the user is done using this program. */
106 int done;
107 
108 char *
dupstr(s)109 dupstr (s)
110      char *s;
111 {
112   char *r;
113 
114   r = xmalloc (strlen (s) + 1);
115   strcpy (r, s);
116   return (r);
117 }
118 
main(argc,argv)119 main (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. */
154 int
execute_line(line)155 execute_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. */
194 COMMAND *
find_command(name)195 find_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. */
209 char *
stripwhite(string)210 stripwhite (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 
235 char *command_generator PARAMS((const char *, int));
236 char **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. */
initialize_readline()241 initialize_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. */
255 char **
fileman_completion(text,start,end)256 fileman_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. */
276 char *
command_generator(text,state)277 command_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. */
314 static char syscom[1024];
315 
316 /* List the file(s) named in arg. */
com_list(arg)317 com_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 
com_view(arg)327 com_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 
com_rename(arg)342 com_rename (arg)
343      char *arg;
344 {
345   too_dangerous ("rename");
346   return (1);
347 }
348 
com_stat(arg)349 com_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 
com_delete(arg)377 com_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. */
com_help(arg)386 com_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. */
com_cd(arg)425 com_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. */
com_pwd(ignore)439 com_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. */
com_quit(arg)456 com_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. */
too_dangerous(caller)464 too_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. */
474 int
valid_argument(caller,arg)475 valid_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