modern syntax for asgops & inits cause Donn's latest ccom rejects the old.
[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
87558dc4 8static char *sccsid = "@(#)file.c 5.6 (Berkeley) %G%";
094e80ed 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
b1c9ab11
JL
33int sortscmp(); /* defined in sh.glob.c */
34
35371dec
EW
35/*
36 * Put this here so the binary can be patched with adb to enable file
4d9099d4
EW
37 * completion by default. Filec controls completion, nobeep controls
38 * ringing the terminal bell on incomplete expansions.
35371dec
EW
39 */
40bool filec = 0;
41
d8bd96dd 42static
5b182e7a
SL
43setup_tty(on)
44 int on;
d8bd96dd 45{
d8bd96dd 46 struct sgttyb sgtty;
6722a695 47 static struct tchars tchars; /* INT, QUIT, XON, XOFF, EOF, BRK */
5b182e7a 48
5b182e7a 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 53 /*
6722a695
JL
54 * This must be done after every command: if
55 * the tty gets into raw or cbreak mode the user
56 * can't even type 'reset'.
5b182e7a 57 */
35371dec 58 (void) ioctl(SHIN, TIOCGETP, (char *)&sgtty);
5b182e7a
SL
59 if (sgtty.sg_flags & (RAW|CBREAK)) {
60 sgtty.sg_flags &= ~(RAW|CBREAK);
35371dec 61 (void) ioctl(SHIN, TIOCSETP, (char *)&sgtty);
5b182e7a
SL
62 }
63 } else {
64 tchars.t_brkc = -1;
35371dec 65 (void) ioctl(SHIN, TIOCSETC, (char *)&tchars);
d8bd96dd 66 }
d8bd96dd
KL
67}
68
69/*
70 * Move back to beginning of current line
71 */
72static
5b182e7a 73back_to_col_1()
d8bd96dd 74{
5b182e7a
SL
75 struct sgttyb tty, tty_normal;
76 int omask;
77
78 omask = sigblock(sigmask(SIGINT));
35371dec 79 (void) ioctl(SHIN, TIOCGETP, (char *)&tty);
5b182e7a
SL
80 tty_normal = tty;
81 tty.sg_flags &= ~CRMOD;
35371dec 82 (void) ioctl(SHIN, TIOCSETN, (char *)&tty);
5b182e7a 83 (void) write(SHOUT, "\r", 1);
35371dec
EW
84 (void) ioctl(SHIN, TIOCSETN, (char *)&tty_normal);
85 (void) sigsetmask(omask);
d8bd96dd
KL
86}
87
88/*
89 * Push string contents back into tty queue
90 */
91static
5b182e7a
SL
92pushback(string)
93 char *string;
d8bd96dd 94{
5b182e7a
SL
95 register char *p;
96 struct sgttyb tty, tty_normal;
97 int omask;
98
99 omask = sigblock(sigmask(SIGINT));
35371dec 100 (void) ioctl(SHOUT, TIOCGETP, (char *)&tty);
5b182e7a
SL
101 tty_normal = tty;
102 tty.sg_flags &= ~ECHO;
35371dec 103 (void) ioctl(SHOUT, TIOCSETN, (char *)&tty);
5b182e7a
SL
104
105 for (p = string; *p; p++)
35371dec
EW
106 (void) ioctl(SHOUT, TIOCSTI, p);
107 (void) ioctl(SHOUT, TIOCSETN, (char *)&tty_normal);
108 (void) sigsetmask(omask);
d8bd96dd
KL
109}
110
111/*
5b182e7a 112 * Concatenate src onto tail of des.
d8bd96dd
KL
113 * Des is a string whose maximum length is count.
114 * Always null terminate.
115 */
6722a695 116static
5b182e7a
SL
117catn(des, src, count)
118 register char *des, *src;
119 register count;
d8bd96dd 120{
5b182e7a
SL
121
122 while (--count >= 0 && *des)
123 des++;
124 while (--count >= 0)
125 if ((*des++ = *src++) == 0)
126 return;
127 *des = '\0';
d8bd96dd
KL
128}
129
d8bd96dd 130/*
5b182e7a 131 * Like strncpy but always leave room for trailing \0
d8bd96dd
KL
132 * and always null terminate.
133 */
6722a695 134static
5b182e7a
SL
135copyn(des, src, count)
136 register char *des, *src;
137 register count;
d8bd96dd 138{
5b182e7a
SL
139
140 while (--count >= 0)
141 if ((*des++ = *src++) == 0)
142 return;
143 *des = '\0';
d8bd96dd
KL
144}
145
d8bd96dd 146static char
5b182e7a
SL
147filetype(dir, file)
148 char *dir, *file;
d8bd96dd 149{
b1c9ab11 150 char path[MAXPATHLEN];
d8bd96dd 151 struct stat statb;
5b182e7a 152
b1c9ab11
JL
153 catn(strcpy(path, dir), file, sizeof path);
154 if (lstat(path, &statb) == 0) {
155 switch(statb.st_mode & S_IFMT) {
156 case S_IFDIR:
157 return ('/');
158
159 case S_IFLNK:
160 if (stat(path, &statb) == 0 && /* follow it out */
161 (statb.st_mode & S_IFMT) == S_IFDIR)
162 return ('>');
163 else
164 return ('@');
165
166 case S_IFSOCK:
167 return ('=');
168
169 default:
5b182e7a
SL
170 if (statb.st_mode & 0111)
171 return ('*');
172 }
d8bd96dd 173 }
5b182e7a 174 return (' ');
d8bd96dd
KL
175}
176
b1c9ab11
JL
177static struct winsize win;
178
d8bd96dd
KL
179/*
180 * Print sorted down columns
181 */
182static
5b182e7a
SL
183print_by_column(dir, items, count)
184 char *dir, *items[];
d8bd96dd 185{
5b182e7a
SL
186 register int i, rows, r, c, maxwidth = 0, columns;
187
87558dc4
JL
188 if (ioctl(SHOUT, TIOCGWINSZ, (char *)&win) < 0 || win.ws_col == 0)
189 win.ws_col = 80;
5b182e7a 190 for (i = 0; i < count; i++)
b1c9ab11 191 maxwidth = maxwidth > (r = strlen(items[i])) ? maxwidth : r;
5b182e7a 192 maxwidth += 2; /* for the file tag and space */
b1c9ab11
JL
193 columns = win.ws_col / maxwidth;
194 if (columns == 0)
195 columns = 1;
5b182e7a
SL
196 rows = (count + (columns - 1)) / columns;
197 for (r = 0; r < rows; r++) {
198 for (c = 0; c < columns; c++) {
199 i = c * rows + r;
200 if (i < count) {
201 register int w;
202
203 printf("%s", items[i]);
b1c9ab11 204 putchar(dir ? filetype(dir, items[i]) : ' ');
5b182e7a
SL
205 if (c < columns - 1) { /* last column? */
206 w = strlen(items[i]) + 1;
207 for (; w < maxwidth; w++)
b1c9ab11 208 putchar(' ');
5b182e7a
SL
209 }
210 }
211 }
b1c9ab11 212 putchar('\n');
d8bd96dd 213 }
d8bd96dd
KL
214}
215
216/*
5b182e7a
SL
217 * Expand file name with possible tilde usage
218 * ~person/mumble
d8bd96dd 219 * expands to
5b182e7a 220 * home_directory_of_person/mumble
d8bd96dd 221 */
6722a695 222static char *
5b182e7a
SL
223tilde(new, old)
224 char *new, *old;
d8bd96dd 225{
5b182e7a
SL
226 register char *o, *p;
227 register struct passwd *pw;
228 static char person[40];
5b182e7a
SL
229
230 if (old[0] != '~')
231 return (strcpy(new, old));
232
233 for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
234 ;
235 *p = '\0';
6f04d0da
EW
236 if (person[0] == '\0')
237 (void) strcpy(new, value("home"));
238 else {
5b182e7a 239 pw = getpwnam(person);
6f04d0da
EW
240 if (pw == NULL)
241 return (NULL);
242 (void) strcpy(new, pw->pw_dir);
243 }
5b182e7a
SL
244 (void) strcat(new, o);
245 return (new);
d8bd96dd
KL
246}
247
248/*
249 * Cause pending line to be printed
250 */
251static
5b182e7a 252retype()
d8bd96dd 253{
5b182e7a
SL
254 int pending_input = LPENDIN;
255
35371dec 256 (void) ioctl(SHOUT, TIOCLBIS, (char *)&pending_input);
d8bd96dd
KL
257}
258
259static
5b182e7a 260beep()
d8bd96dd 261{
5b182e7a 262
4d9099d4
EW
263 if (adrof("nobeep") == 0)
264 (void) write(SHOUT, "\007", 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
6722a695 314static char *
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) {
b1c9ab11 322 if ((pw = getpwent()) == NULL)
5b182e7a
SL
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;
4d9099d4
EW
361 register numitems = 0, ignoring = TRUE, nignored = 0;
362 register name_length, looking_for_lognames;
35371dec 363 char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
9208cf61 364 char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN+1];
5b182e7a
SL
365 char *entry;
366#define MAXITEMS 1024
d8bd96dd 367
5b182e7a
SL
368 if (items != NULL)
369 FREE_ITEMS(items);
370
371 looking_for_lognames = (*word == '~') && (index(word, '/') == NULL);
372 if (looking_for_lognames) {
35371dec 373 (void) setpwent();
5b182e7a
SL
374 copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */
375 } else {
376 extract_dir_and_name(word, dir, name);
377 if (tilde(tilded_dir, dir) == 0)
378 return (0);
379 dir_fd = opendir(*tilded_dir ? tilded_dir : ".");
380 if (dir_fd == NULL)
381 return (0);
382 }
4d9099d4
EW
383
384again: /* search for matches */
5b182e7a
SL
385 name_length = strlen(name);
386 for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames); ) {
387 if (!is_prefix(name, entry))
388 continue;
389 /* Don't match . files on null prefix match */
390 if (name_length == 0 && entry[0] == '.' &&
391 !looking_for_lognames)
392 continue;
393 if (command == LIST) {
5b182e7a
SL
394 if (numitems >= MAXITEMS) {
395 printf ("\nYikes!! Too many %s!!\n",
396 looking_for_lognames ?
397 "names in password file":"files");
398 break;
399 }
e3f1ec6b 400 if (items == NULL)
5b182e7a
SL
401 items = (char **) calloc(sizeof (items[1]),
402 MAXITEMS);
e3f1ec6b 403 items[numitems] = xalloc((unsigned)strlen(entry) + 1);
5b182e7a
SL
404 copyn(items[numitems], entry, MAXNAMLEN);
405 numitems++;
4d9099d4
EW
406 } else { /* RECOGNIZE command */
407 if (ignoring && ignored(entry))
408 nignored++;
409 else if (recognize(extended_name,
410 entry, name_length, ++numitems))
5b182e7a 411 break;
4d9099d4 412 }
5b182e7a 413 }
4d9099d4
EW
414 if (ignoring && numitems == 0 && nignored > 0) {
415 ignoring = FALSE;
416 nignored = 0;
417 if (looking_for_lognames)
b1c9ab11 418 (void) setpwent();
4d9099d4
EW
419 else
420 rewinddir(dir_fd);
421 goto again;
422 }
423
d8bd96dd 424 if (looking_for_lognames)
35371dec 425 (void) endpwent();
d8bd96dd 426 else
5b182e7a 427 closedir(dir_fd);
87558dc4
JL
428 if (numitems == 0)
429 return (0);
430 if (command == RECOGNIZE) {
5b182e7a
SL
431 if (looking_for_lognames)
432 copyn(word, "~", 1);
433 else
434 /* put back dir part */
435 copyn(word, dir, max_word_length);
436 /* add extended name */
437 catn(word, extended_name, max_word_length);
438 return (numitems);
439 }
87558dc4 440 else { /* LIST */
b1c9ab11 441 qsort((char *)items, numitems, sizeof(items[1]), sortscmp);
5b182e7a
SL
442 print_by_column(looking_for_lognames ? NULL : tilded_dir,
443 items, numitems);
444 if (items != NULL)
445 FREE_ITEMS(items);
446 }
447 return (0);
d8bd96dd
KL
448}
449
450/*
451 * Object: extend what user typed up to an ambiguity.
452 * Algorithm:
453 * On first match, copy full entry (assume it'll be the only match)
454 * On subsequent matches, shorten extended_name to the first
455 * character mismatch between extended_name and entry.
456 * If we shorten it back to the prefix length, stop searching.
457 */
6722a695 458static
5b182e7a
SL
459recognize(extended_name, entry, name_length, numitems)
460 char *extended_name, *entry;
d8bd96dd 461{
5b182e7a 462
b1c9ab11 463 if (numitems == 1) /* 1st match */
5b182e7a 464 copyn(extended_name, entry, MAXNAMLEN);
b1c9ab11 465 else { /* 2nd & subsequent matches */
5b182e7a
SL
466 register char *x, *ent;
467 register int len = 0;
468
469 x = extended_name;
470 for (ent = entry; *x && *x == *ent++; x++, len++)
471 ;
472 *x = '\0'; /* Shorten at 1st char diff */
473 if (len == name_length) /* Ambiguous to prefix? */
474 return (-1); /* So stop now and save time */
475 }
476 return (0);
d8bd96dd
KL
477}
478
479/*
6722a695 480 * Return true if check matches initial chars in template.
d8bd96dd 481 * This differs from PWB imatch in that if check is null
6722a695 482 * it matches anything.
d8bd96dd
KL
483 */
484static
5b182e7a 485is_prefix(check, template)
4d9099d4 486 register char *check, *template;
d8bd96dd 487{
5b182e7a 488
5b182e7a 489 do
4d9099d4 490 if (*check == 0)
5b182e7a 491 return (TRUE);
4d9099d4 492 while (*check++ == *template++);
5b182e7a 493 return (FALSE);
d8bd96dd
KL
494}
495
4d9099d4
EW
496/*
497 * Return true if the chars in template appear at the
498 * end of check, I.e., are it's suffix.
499 */
500static
501is_suffix(check, template)
502 char *check, *template;
503{
504 register char *c, *t;
505
506 for (c = check; *c++;)
507 ;
508 for (t = template; *t++;)
509 ;
510 for (;;) {
511 if (t == template)
512 return 1;
513 if (c == check || *--t != *--c)
514 return 0;
515 }
516}
517
5b182e7a
SL
518tenex(inputline, inputline_size)
519 char *inputline;
520 int inputline_size;
d8bd96dd 521{
5b182e7a
SL
522 register int numitems, num_read;
523
524 setup_tty(ON);
4d9099d4 525 while ((num_read = read(SHIN, inputline, inputline_size)) > 0) {
5b182e7a
SL
526 static char *delims = " '\"\t;&<>()|^%";
527 register char *str_end, *word_start, last_char, should_retype;
528 register int space_left;
529 COMMAND command;
530
531 last_char = inputline[num_read - 1] & 0177;
532
533 if (last_char == '\n' || num_read == inputline_size)
534 break;
535 command = (last_char == ESC) ? RECOGNIZE : LIST;
536 if (command == LIST)
4d9099d4 537 putchar('\n');
5b182e7a
SL
538 str_end = &inputline[num_read];
539 if (last_char == ESC)
540 --str_end; /* wipeout trailing cmd char */
541 *str_end = '\0';
542 /*
543 * Find LAST occurence of a delimiter in the inputline.
544 * The word start is one character past it.
545 */
546 for (word_start = str_end; word_start > inputline; --word_start)
547 if (index(delims, word_start[-1]))
548 break;
549 space_left = inputline_size - (word_start - inputline) - 1;
550 numitems = search(word_start, command, space_left);
551
552 if (command == RECOGNIZE) {
553 /* print from str_end on */
554 print_recognized_stuff(str_end);
555 if (numitems != 1) /* Beep = No match/ambiguous */
4d9099d4 556 beep();
5b182e7a
SL
557 }
558
559 /*
560 * Tabs in the input line cause trouble after a pushback.
561 * tty driver won't backspace over them because column
562 * positions are now incorrect. This is solved by retyping
563 * over current line.
564 */
565 should_retype = FALSE;
566 if (index(inputline, '\t')) { /* tab char in input line? */
567 back_to_col_1();
568 should_retype = TRUE;
569 }
570 if (command == LIST) /* Always retype after a LIST */
571 should_retype = TRUE;
572 if (should_retype)
573 printprompt();
574 pushback(inputline);
575 if (should_retype)
4d9099d4 576 retype();
d8bd96dd 577 }
4d9099d4 578 setup_tty(OFF);
5b182e7a 579 return (num_read);
d8bd96dd 580}
4d9099d4
EW
581
582static
583ignored(entry)
584 register char *entry;
585{
586 struct varent *vp;
587 register char **cp;
588
589 if ((vp = adrof("fignore")) == NULL || (cp = vp->vec) == NULL)
590 return (FALSE);
591 for (; *cp != NULL; cp++)
592 if (is_suffix(entry, *cp))
593 return (TRUE);
594 return (FALSE);
595}
35371dec 596#endif FILEC