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