Deleted Added
sdiff udiff text old ( 21308 ) new ( 26497 )
full compact
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
14extern char *getwd ();
15extern char *xmalloc ();
16
17/* The names of functions that actually do the manipulation. */
18int com_list (), com_view (), com_rename (), com_stat (), com_pwd ();
19int com_delete (), com_help (), com_cd (), com_quit ();
20
21/* A structure which contains information on the commands this program
22 can understand. */
23
24typedef 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
30COMMAND 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. */
46char *stripwhite ();
47COMMAND *find_command ();
48
49/* The name of this program, as taken from argv[0]. */
50char *progname;
51
52/* When non-zero, this global means the user is done using this program. */
53int done;
54
55char *
56dupstr (s)
57 int s;
58{
59 char *r;
60
61 r = xmalloc (strlen (s) + 1);
62 strcpy (r, s);
63 return (r);
64}
65
66main (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. */
101int
102execute_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. */
141COMMAND *
142find_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. */
156char *
157stripwhite (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
182char *command_generator ();
183char **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. */
188initialize_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. */
202char **
203fileman_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. */
223char *
224command_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. */
261static char syscom[1024];
262
263/* List the file(s) named in arg. */
264com_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
274com_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
284com_rename (arg)
285 char *arg;
286{
287 too_dangerous ("rename");
288 return (1);
289}
290
291com_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
318com_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. */
327com_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. */
366com_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. */
380com_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. */
397com_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. */
405too_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. */
415int
416valid_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}