move kermit to standard
[unix-history] / usr / src / bin / csh / file.c
CommitLineData
b79f4fa9
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
094e80ed 3 * All rights reserved. The Berkeley Software License Agreement
b79f4fa9
DF
4 * specifies the terms and conditions for redistribution.
5 */
6
5b182e7a 7#ifndef lint
094e80ed
EW
8static char *sccsid = "@(#)file.c 5.2 (Berkeley) %G%";
9#endif
d8bd96dd 10
35371dec 11#ifdef FILEC
d8bd96dd
KL
12/*
13 * Tenex style file name recognition, .. and more.
14 * History:
15 * Author: Ken Greer, Sept. 1975, CMU.
16 * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
17 */
18
35371dec 19#include "sh.h"
d8bd96dd
KL
20#include <sgtty.h>
21#include <sys/dir.h>
d8bd96dd
KL
22#include <pwd.h>
23
d8bd96dd
KL
24#define TRUE 1
25#define FALSE 0
26#define ON 1
27#define OFF 0
d8bd96dd
KL
28
29#define ESC '\033'
30
31typedef enum {LIST, RECOGNIZE} COMMAND;
32
d8bd96dd
KL
33static struct tchars tchars; /* INT, QUIT, XON, XOFF, EOF, BRK */
34
35371dec
EW
35/*
36 * Put this here so the binary can be patched with adb to enable file
37 * completion by default.
38 */
39bool filec = 0;
40
d8bd96dd 41static
5b182e7a
SL
42setup_tty(on)
43 int on;
d8bd96dd 44{
d8bd96dd 45 struct sgttyb sgtty;
5b182e7a
SL
46 int omask;
47
48 omask = sigblock(sigmask(SIGINT));
49 if (on) {
35371dec 50 (void) ioctl(SHIN, TIOCGETC, (char *)&tchars);
5b182e7a 51 tchars.t_brkc = ESC;
35371dec 52 (void) ioctl(SHIN, TIOCSETC, (char *)&tchars);
5b182e7a
SL
53 /*
54 * This is a useful feature in it's own right...
55 * The shell makes sure that the tty is not in some weird state
56 * and fixes it if it is. But it should be noted that the
57 * tenex routine will not work correctly in CBREAK or RAW mode
58 * so this code below is, therefore, mandatory.
59 */
35371dec 60 (void) ioctl(SHIN, TIOCGETP, (char *)&sgtty);
5b182e7a
SL
61 if (sgtty.sg_flags & (RAW|CBREAK)) {
62 sgtty.sg_flags &= ~(RAW|CBREAK);
35371dec 63 (void) ioctl(SHIN, TIOCSETP, (char *)&sgtty);
5b182e7a
SL
64 }
65 } else {
66 tchars.t_brkc = -1;
35371dec 67 (void) ioctl(SHIN, TIOCSETC, (char *)&tchars);
d8bd96dd 68 }
35371dec 69 (void) sigsetmask (omask);
d8bd96dd
KL
70}
71
72/*
73 * Move back to beginning of current line
74 */
75static
5b182e7a 76back_to_col_1()
d8bd96dd 77{
5b182e7a
SL
78 struct sgttyb tty, tty_normal;
79 int omask;
80
81 omask = sigblock(sigmask(SIGINT));
35371dec 82 (void) ioctl(SHIN, TIOCGETP, (char *)&tty);
5b182e7a
SL
83 tty_normal = tty;
84 tty.sg_flags &= ~CRMOD;
35371dec 85 (void) ioctl(SHIN, TIOCSETN, (char *)&tty);
5b182e7a 86 (void) write(SHOUT, "\r", 1);
35371dec
EW
87 (void) ioctl(SHIN, TIOCSETN, (char *)&tty_normal);
88 (void) sigsetmask(omask);
d8bd96dd
KL
89}
90
91/*
92 * Push string contents back into tty queue
93 */
94static
5b182e7a
SL
95pushback(string)
96 char *string;
d8bd96dd 97{
5b182e7a
SL
98 register char *p;
99 struct sgttyb tty, tty_normal;
100 int omask;
101
102 omask = sigblock(sigmask(SIGINT));
35371dec 103 (void) ioctl(SHOUT, TIOCGETP, (char *)&tty);
5b182e7a
SL
104 tty_normal = tty;
105 tty.sg_flags &= ~ECHO;
35371dec 106 (void) ioctl(SHOUT, TIOCSETN, (char *)&tty);
5b182e7a
SL
107
108 for (p = string; *p; p++)
35371dec
EW
109 (void) ioctl(SHOUT, TIOCSTI, p);
110 (void) ioctl(SHOUT, TIOCSETN, (char *)&tty_normal);
111 (void) sigsetmask(omask);
d8bd96dd
KL
112}
113
114/*
5b182e7a 115 * Concatenate src onto tail of des.
d8bd96dd
KL
116 * Des is a string whose maximum length is count.
117 * Always null terminate.
118 */
5b182e7a
SL
119catn(des, src, count)
120 register char *des, *src;
121 register count;
d8bd96dd 122{
5b182e7a
SL
123
124 while (--count >= 0 && *des)
125 des++;
126 while (--count >= 0)
127 if ((*des++ = *src++) == 0)
128 return;
129 *des = '\0';
d8bd96dd
KL
130}
131
132static
5b182e7a 133max(a, b)
d8bd96dd 134{
5b182e7a
SL
135
136 return (a > b ? a : b);
d8bd96dd
KL
137}
138
139/*
5b182e7a 140 * Like strncpy but always leave room for trailing \0
d8bd96dd
KL
141 * and always null terminate.
142 */
5b182e7a
SL
143copyn(des, src, count)
144 register char *des, *src;
145 register count;
d8bd96dd 146{
5b182e7a
SL
147
148 while (--count >= 0)
149 if ((*des++ = *src++) == 0)
150 return;
151 *des = '\0';
d8bd96dd
KL
152}
153
154/*
155 * For qsort()
156 */
157static
5b182e7a
SL
158fcompare(file1, file2)
159 char **file1, **file2;
d8bd96dd 160{
5b182e7a
SL
161
162 return (strcmp(*file1, *file2));
d8bd96dd
KL
163}
164
165static char
5b182e7a
SL
166filetype(dir, file)
167 char *dir, *file;
d8bd96dd 168{
d8bd96dd
KL
169 char path[512];
170 struct stat statb;
5b182e7a
SL
171
172 if (dir) {
35371dec 173 catn(strcpy(path, dir), file, sizeof path);
5b182e7a
SL
174 if (stat(path, &statb) >= 0) {
175 if (statb.st_mode & S_IFDIR)
176 return ('/');
177 if (statb.st_mode & 0111)
178 return ('*');
179 }
d8bd96dd 180 }
5b182e7a 181 return (' ');
d8bd96dd
KL
182}
183
184/*
185 * Print sorted down columns
186 */
187static
5b182e7a
SL
188print_by_column(dir, items, count)
189 char *dir, *items[];
d8bd96dd 190{
5b182e7a
SL
191 register int i, rows, r, c, maxwidth = 0, columns;
192
193 for (i = 0; i < count; i++)
194 maxwidth = max(maxwidth, strlen(items[i]));
195 maxwidth += 2; /* for the file tag and space */
196 columns = 78 / maxwidth;
197 rows = (count + (columns - 1)) / columns;
198 for (r = 0; r < rows; r++) {
199 for (c = 0; c < columns; c++) {
200 i = c * rows + r;
201 if (i < count) {
202 register int w;
203
204 printf("%s", items[i]);
205 putchar(filetype(dir, items[i]));
206 if (c < columns - 1) { /* last column? */
207 w = strlen(items[i]) + 1;
208 for (; w < maxwidth; w++)
209 printf(" ");
210 }
211 }
212 }
213 printf ("\n");
d8bd96dd 214 }
d8bd96dd
KL
215}
216
217/*
5b182e7a
SL
218 * Expand file name with possible tilde usage
219 * ~person/mumble
d8bd96dd 220 * expands to
5b182e7a 221 * home_directory_of_person/mumble
d8bd96dd 222 */
d8bd96dd 223char *
5b182e7a
SL
224tilde(new, old)
225 char *new, *old;
d8bd96dd 226{
5b182e7a
SL
227 register char *o, *p;
228 register struct passwd *pw;
229 static char person[40];
5b182e7a
SL
230
231 if (old[0] != '~')
232 return (strcpy(new, old));
233
234 for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
235 ;
236 *p = '\0';
6f04d0da
EW
237 if (person[0] == '\0')
238 (void) strcpy(new, value("home"));
239 else {
5b182e7a 240 pw = getpwnam(person);
6f04d0da
EW
241 if (pw == NULL)
242 return (NULL);
243 (void) strcpy(new, pw->pw_dir);
244 }
5b182e7a
SL
245 (void) strcat(new, o);
246 return (new);
d8bd96dd
KL
247}
248
249/*
250 * Cause pending line to be printed
251 */
252static
5b182e7a 253retype()
d8bd96dd 254{
5b182e7a
SL
255 int pending_input = LPENDIN;
256
35371dec 257 (void) ioctl(SHOUT, TIOCLBIS, (char *)&pending_input);
d8bd96dd
KL
258}
259
260static
5b182e7a 261beep()
d8bd96dd 262{
5b182e7a
SL
263
264 (void) write(SHOUT, "\07", 1);
d8bd96dd
KL
265}
266
267/*
268 * Erase that silly ^[ and
269 * print the recognized part of the string
270 */
271static
5b182e7a
SL
272print_recognized_stuff(recognized_part)
273 char *recognized_part;
d8bd96dd 274{
5b182e7a
SL
275
276 /* An optimized erasing of that silly ^[ */
277 switch (strlen(recognized_part)) {
278
d8bd96dd 279 case 0: /* erase two characters: ^[ */
5b182e7a
SL
280 printf("\210\210 \210\210");
281 break;
282
d8bd96dd 283 case 1: /* overstrike the ^, erase the [ */
5b182e7a
SL
284 printf("\210\210%s \210", recognized_part);
285 break;
286
d8bd96dd 287 default: /* overstrike both characters ^[ */
5b182e7a
SL
288 printf("\210\210%s", recognized_part);
289 break;
290 }
291 flush();
d8bd96dd
KL
292}
293
294/*
5b182e7a 295 * Parse full path in file into 2 parts: directory and file names
d8bd96dd
KL
296 * Should leave final slash (/) at end of dir.
297 */
298static
5b182e7a
SL
299extract_dir_and_name(path, dir, name)
300 char *path, *dir, *name;
d8bd96dd 301{
5b182e7a 302 register char *p;
5b182e7a 303
35371dec 304 p = rindex(path, '/');
5b182e7a
SL
305 if (p == NULL) {
306 copyn(name, path, MAXNAMLEN);
307 dir[0] = '\0';
308 } else {
309 copyn(name, ++p, MAXNAMLEN);
310 copyn(dir, path, p - path);
311 }
d8bd96dd
KL
312}
313
d8bd96dd 314char *
5b182e7a
SL
315getentry(dir_fd, looking_for_lognames)
316 DIR *dir_fd;
d8bd96dd 317{
d8bd96dd 318 register struct passwd *pw;
d8bd96dd 319 register struct direct *dirp;
5b182e7a
SL
320
321 if (looking_for_lognames) {
322 if ((pw = getpwent ()) == NULL)
323 return (NULL);
324 return (pw->pw_name);
325 }
326 if (dirp = readdir(dir_fd))
327 return (dirp->d_name);
d8bd96dd 328 return (NULL);
d8bd96dd
KL
329}
330
331static
5b182e7a
SL
332free_items(items)
333 register char **items;
d8bd96dd 334{
5b182e7a
SL
335 register int i;
336
337 for (i = 0; items[i]; i++)
338 free(items[i]);
35371dec 339 free((char *)items);
d8bd96dd
KL
340}
341
5b182e7a
SL
342#define FREE_ITEMS(items) { \
343 int omask;\
9208cf61 344\
5b182e7a
SL
345 omask = sigblock(sigmask(SIGINT));\
346 free_items(items);\
347 items = NULL;\
35371dec 348 (void) sigsetmask(omask);\
d8bd96dd
KL
349}
350
351/*
352 * Perform a RECOGNIZE or LIST command on string "word".
353 */
354static
5b182e7a
SL
355search(word, command, max_word_length)
356 char *word;
357 COMMAND command;
d8bd96dd 358{
5b182e7a
SL
359 static char **items = NULL;
360 register DIR *dir_fd;
361 register numitems, name_length, looking_for_lognames;
35371dec 362 char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
9208cf61 363 char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN+1];
5b182e7a
SL
364 char *entry;
365#define MAXITEMS 1024
d8bd96dd 366
5b182e7a
SL
367 if (items != NULL)
368 FREE_ITEMS(items);
369
370 looking_for_lognames = (*word == '~') && (index(word, '/') == NULL);
371 if (looking_for_lognames) {
35371dec 372 (void) setpwent();
5b182e7a
SL
373 copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */
374 } else {
375 extract_dir_and_name(word, dir, name);
376 if (tilde(tilded_dir, dir) == 0)
377 return (0);
378 dir_fd = opendir(*tilded_dir ? tilded_dir : ".");
379 if (dir_fd == NULL)
380 return (0);
381 }
382 name_length = strlen(name);
383 for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames); ) {
384 if (!is_prefix(name, entry))
385 continue;
386 /* Don't match . files on null prefix match */
387 if (name_length == 0 && entry[0] == '.' &&
388 !looking_for_lognames)
389 continue;
390 if (command == LIST) {
5b182e7a
SL
391 if (numitems >= MAXITEMS) {
392 printf ("\nYikes!! Too many %s!!\n",
393 looking_for_lognames ?
394 "names in password file":"files");
395 break;
396 }
e3f1ec6b 397 if (items == NULL)
5b182e7a
SL
398 items = (char **) calloc(sizeof (items[1]),
399 MAXITEMS);
e3f1ec6b 400 items[numitems] = xalloc((unsigned)strlen(entry) + 1);
5b182e7a
SL
401 copyn(items[numitems], entry, MAXNAMLEN);
402 numitems++;
403 } else /* RECOGNIZE command */
404 if (recognize(extended_name, entry, name_length,
405 ++numitems))
406 break;
407 }
d8bd96dd 408 if (looking_for_lognames)
35371dec 409 (void) endpwent();
d8bd96dd 410 else
5b182e7a
SL
411 closedir(dir_fd);
412 if (command == RECOGNIZE && numitems > 0) {
413 if (looking_for_lognames)
414 copyn(word, "~", 1);
415 else
416 /* put back dir part */
417 copyn(word, dir, max_word_length);
418 /* add extended name */
419 catn(word, extended_name, max_word_length);
420 return (numitems);
421 }
422 if (command == LIST) {
35371dec 423 qsort((char *)items, numitems, sizeof(items[1]), fcompare);
5b182e7a
SL
424 print_by_column(looking_for_lognames ? NULL : tilded_dir,
425 items, numitems);
426 if (items != NULL)
427 FREE_ITEMS(items);
428 }
429 return (0);
d8bd96dd
KL
430}
431
432/*
433 * Object: extend what user typed up to an ambiguity.
434 * Algorithm:
435 * On first match, copy full entry (assume it'll be the only match)
436 * On subsequent matches, shorten extended_name to the first
437 * character mismatch between extended_name and entry.
438 * If we shorten it back to the prefix length, stop searching.
439 */
5b182e7a
SL
440recognize(extended_name, entry, name_length, numitems)
441 char *extended_name, *entry;
d8bd96dd 442{
5b182e7a
SL
443
444 if (numitems == 1) /* 1st match */
445 copyn(extended_name, entry, MAXNAMLEN);
446 else { /* 2nd and subsequent matches */
447 register char *x, *ent;
448 register int len = 0;
449
450 x = extended_name;
451 for (ent = entry; *x && *x == *ent++; x++, len++)
452 ;
453 *x = '\0'; /* Shorten at 1st char diff */
454 if (len == name_length) /* Ambiguous to prefix? */
455 return (-1); /* So stop now and save time */
456 }
457 return (0);
d8bd96dd
KL
458}
459
460/*
5b182e7a 461 * Return true if check items initial chars in template
d8bd96dd
KL
462 * This differs from PWB imatch in that if check is null
463 * it items anything
464 */
465static
5b182e7a
SL
466is_prefix(check, template)
467 char *check, *template;
d8bd96dd 468{
5b182e7a
SL
469 register char *check_char, *template_char;
470
471 check_char = check;
472 template_char = template;
473 do
474 if (*check_char == 0)
475 return (TRUE);
476 while (*check_char++ == *template_char++);
477 return (FALSE);
d8bd96dd
KL
478}
479
5b182e7a
SL
480tenex(inputline, inputline_size)
481 char *inputline;
482 int inputline_size;
d8bd96dd 483{
5b182e7a
SL
484 register int numitems, num_read;
485
486 setup_tty(ON);
487 while ((num_read = read (SHIN, inputline, inputline_size)) > 0) {
488 static char *delims = " '\"\t;&<>()|^%";
489 register char *str_end, *word_start, last_char, should_retype;
490 register int space_left;
491 COMMAND command;
492
493 last_char = inputline[num_read - 1] & 0177;
494
495 if (last_char == '\n' || num_read == inputline_size)
496 break;
497 command = (last_char == ESC) ? RECOGNIZE : LIST;
498 if (command == LIST)
499 putchar ('\n');
500 str_end = &inputline[num_read];
501 if (last_char == ESC)
502 --str_end; /* wipeout trailing cmd char */
503 *str_end = '\0';
504 /*
505 * Find LAST occurence of a delimiter in the inputline.
506 * The word start is one character past it.
507 */
508 for (word_start = str_end; word_start > inputline; --word_start)
509 if (index(delims, word_start[-1]))
510 break;
511 space_left = inputline_size - (word_start - inputline) - 1;
512 numitems = search(word_start, command, space_left);
513
514 if (command == RECOGNIZE) {
515 /* print from str_end on */
516 print_recognized_stuff(str_end);
517 if (numitems != 1) /* Beep = No match/ambiguous */
518 beep ();
519 }
520
521 /*
522 * Tabs in the input line cause trouble after a pushback.
523 * tty driver won't backspace over them because column
524 * positions are now incorrect. This is solved by retyping
525 * over current line.
526 */
527 should_retype = FALSE;
528 if (index(inputline, '\t')) { /* tab char in input line? */
529 back_to_col_1();
530 should_retype = TRUE;
531 }
532 if (command == LIST) /* Always retype after a LIST */
533 should_retype = TRUE;
534 if (should_retype)
535 printprompt();
536 pushback(inputline);
537 if (should_retype)
538 retype ();
d8bd96dd 539 }
5b182e7a
SL
540 setup_tty (OFF);
541 return (num_read);
d8bd96dd 542}
35371dec 543#endif FILEC