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