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