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