1 /* @(#)abbrev.c 1.74 18/06/26 Copyright 1985-2018 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)abbrev.c 1.74 18/06/26 Copyright 1985-2018 J. Schilling";
6 #endif
7 /*
8 * Abbreviation symbol handling
9 *
10 * Copyright (c) 1985-2018 J. Schilling
11 *
12 * .global & .local alias abbreviations are handled here
13 *
14 * Exported functions:
15 * ab_getaltowner(tab) Get alternate file owner that
16 * is trusted
17 * ab_getaltoname(tab) Get name of alternate file
18 * owner that is trusted
19 * ab_setaltowner(tab, usrname) Set alternate file owner that
20 * is trusted
21 * ab_read(tab, fname) Read new abbrev table from fname
22 * ab_use(tab, fname) Use new abbrev table from fname
23 * ab_close(tab) Shut down abbrev table
24 * ab_delete(tab, name, flags) Pop or delete abbrev from table
25 * flags & AB_POP allows to
26 * implement the POSIX "unalias -a"
27 * without modifying
28 * .globals/.locals
29 * ab_deleteall(tab, flags) Pop or delete all entries
30 * flags & AB_POP allows to
31 * implement the POSIX "unalias -a"
32 * without modifying
33 * .globals/.locals
34 * ab_dump(tab, f, flags) Print all abbrev entries to file
35 * flags & AB_HISTORY sends a copy
36 * to the command history
37 * ab_insert(tab, name, val, flag) Insert n/v pair to abbrev table
38 * flags & AB_BEGIN defines a begin
39 * alias
40 * ab_list(tab, pat, f, flags) Print matched entries to file
41 * flags & AB_HISTORY sends a copy
42 * to the command history
43 * ab_push(tab, name, val, flag) Push n/v pair to abbrev table
44 * flags & AB_BEGIN defines a begin
45 * alias
46 * ab_sname(tab, fname) Set filename for _ab_output
47 * ab_gname(tab) Get filename from tab
48 * ab_value(tab, name, seen, flag) Perform n/v translation for tab
49 * flags & AB_BEGIN looks for begin
50 * alias only
51 *
52 * Imported functions:
53 * any_match from expand.c
54 * append_line from inputc.c
55 * berror from bsh.c
56 *
57 * Imported vars:
58 * ctlc from bsh.c
59 * ebadpattern from str.c
60 * for_ru .
61 * for_wct .
62 * sn_badfile .
63 * sn_badtab .
64 * sn_no_mem .
65 */
66 /*
67 * The contents of this file are subject to the terms of the
68 * Common Development and Distribution License, Version 1.0 only
69 * (the "License"). You may not use this file except in compliance
70 * with the License.
71 *
72 * See the file CDDL.Schily.txt in this distribution for details.
73 * A copy of the CDDL is also available via the Internet at
74 * http://www.opensource.org/licenses/cddl1.txt
75 *
76 * When distributing Covered Code, include this CDDL HEADER in each
77 * file and include the License file CDDL.Schily.txt from this distribution.
78 */
79
80 #ifdef BOURNE_SHELL
81
82 #include "defs.h"
83 #undef tab
84 #define LOCAL static
85 #define EXPORT
86 #include "abbrev.h"
87 #include <schily/fcntl.h>
88 #include <schily/stat.h>
89 #include <schily/pwd.h>
90 #include <schily/shedit.h>
91
92 LOCAL char sn_badtab[] = "bad_astab_number";
93 LOCAL char sn_no_mem[] = "no_memory";
94 LOCAL char sn_badfile[] = "bad_sym_file";
95
96 /*
97 * The Bourne shell does not use stdio, so we need to redefine some functions.
98 * Be sure to first #undef things that might be #define'd in schily/schily.h.
99 */
100 #undef filemopen
101 #define filemopen(n, m, c) open(n, m, c)
102 #undef fileopen
103 #define fileopen(n, m) open(n, m, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
104 #define fileread read
105 #undef filestat
106 #define filestat fstat
107 #define fclose close
108 #define fflush(f)
109 #undef filesize
110 #define filesize ab_fsize
111 #define open_failed(f) (f < 0)
112
113 #define for_wct (O_WRONLY|O_CREAT|O_TRUNC)
114 #define for_ru (O_RDONLY)
115
116 #define raisecond(n, v) error(n)
117 #define malloc alloc
118
119 #define ctlc bosh.intrcnt
120
121 #ifdef HAVE_SNPRINTF
122 /*
123 * Try to avoid js_snprintf() in the Bourne Shell
124 * to allow to lazyload libschily
125 */
126 #undef snprintf
127 #define js_snprintf snprintf
128 #endif
129
130 #else /* !BOURNE_SHELL */
131 #include <schily/stdio.h>
132 #include <schily/string.h>
133 #include <schily/stdlib.h>
134 #include <schily/time.h>
135 #include <schily/stat.h>
136 #include <schily/pwd.h>
137 #include "bsh.h"
138 #include "abbrev.h"
139 #include "str.h"
140 #include "strsubs.h"
141 #include <schily/patmatch.h>
142
143 #define open_failed(f) (f == (FILE *)0)
144 #endif /* !BOURNE_SHELL */
145
146 /*
147 * Replacement node entry, one is allocated for each abbrev/alias replacement
148 *
149 * ab_name is only set in first node of the ab_push list
150 * ab_name is NULL in further nodes of the ab_push list
151 * ab_value is NULL if the symbol has been deleted
152 * ab_flags & ABF_POP is != 0 if the symbol has been popped but not deleted
153 * ab_value is kept in that case to be able to keep the
154 * content of .globals/.locals
155 */
156 typedef struct abent abent_t;
157
158 struct abent {
159 abent_t *ab_next; /* next entry in list */
160 abent_t *ab_push; /* next in list of old values */
161 abent_t *ab_seen; /* next in list of seen values */
162 char *ab_name; /* abbreviation/alias name */
163 char *ab_value; /* replacement value */
164 int ab_flags; /* various flags */
165 };
166
167 /*
168 * Definitions for ab_flags:
169 */
170 #define ABF_BEGIN 1 /* replace only if begin of cmd */
171 #define ABF_POP 2 /* pop only, but keep in .globals/.locals */
172
173 #define AB_NULL (abent_t *)0
174
175 /*
176 * Table entry, one is allocated for each abbreviation type.
177 *
178 * at_ent contains a list of nodes for the related abbreviation table
179 */
180 typedef struct abtab {
181 abent_t *at_ent; /* first entry in list */
182 char *at_fname; /* name of file for _ab_output() */
183 char *at_blk; /* start of monolitic malloc() */
184 char *at_blkend; /* end of monolitic malloc() */
185 time_t at_mtime; /* our time stamp for at_fname */
186 uid_t at_altowner; /* alternate permitted file owner */
187 } abtab_t;
188
189 LOCAL abtab_t ab_tabs[ABTABS] = { {0, 0, 0, 0, 0, (uid_t)-1},
190 {0, 0, 0, 0, 0, (uid_t)-1}};
191
192 EXPORT abidx_t deftab = GLOBAL_AB; /* Use .globals by default */
193
194 LOCAL abtab_t *_ab_down __PR((abidx_t tab));
195 LOCAL abent_t *_ab_lookup __PR((abtab_t *ap, char *name, BOOL new));
196 LOCAL abent_t *_ab_newnode __PR((void));
197 LOCAL void _ab_output __PR((abtab_t *ap));
198 LOCAL BOOL _ab_print __PR((abidx_t tab, char *name, FILE_p f,
199 int aflags));
200 LOCAL void _ab_dump __PR((abent_t *np, FILE_p f, int aflags));
201 LOCAL void _ab_list __PR((abent_t *np, FILE_p f, int aflags));
202 LOCAL BOOL _ab_match __PR((abent_t *np, FILE_p f, int aflags,
203 char *pattern, int *aux, int alt, int *state));
204 LOCAL void _ab_close __PR((abent_t *np, abidx_t tab));
205 LOCAL char *ab_beginword __PR((char *p, abtab_t *ap));
206 LOCAL char *ab_endword __PR((char *p, abtab_t *ap));
207 LOCAL char *ab_endline __PR((char *p, abtab_t *ap));
208 LOCAL BOOL ab_inblock __PR((abidx_t tab, char *p));
209 LOCAL time_t ab_statmtime __PR((struct stat *sp));
210 LOCAL time_t ab_filemtime __PR((char *fname));
211 EXPORT void ab_read __PR((abidx_t tab, char *fname));
212 EXPORT void ab_sname __PR((abidx_t tab, char *fname));
213 EXPORT char *ab_gname __PR((abidx_t tab));
214 EXPORT void ab_use __PR((abidx_t tab, char *fname));
215 EXPORT void ab_close __PR((abidx_t tab));
216 EXPORT BOOL ab_insert __PR((abidx_t tab, char *name, char *val,
217 int aflags));
218 EXPORT BOOL ab_push __PR((abidx_t tab, char *name, char *val,
219 int aflags));
220 LOCAL void _ab_delete __PR((abent_t *np, abidx_t tab, int aflags));
221 EXPORT BOOL ab_delete __PR((abidx_t tab, char *name, int aflags));
222 LOCAL void _ab_deleteall __PR((abent_t *np, abidx_t tab, int aflags));
223 EXPORT void ab_deleteall __PR((abidx_t tab, int aflags));
224 EXPORT char *ab_value __PR((abidx_t tab, char *name, void **seen,
225 int aflags));
226 EXPORT void ab_dump __PR((abidx_t tab, FILE_p f, int aflags));
227 EXPORT BOOL ab_list __PR((abidx_t tab, char *pattern, FILE_p f,
228 int aflags));
229 LOCAL void ab_eupdated __PR((abtab_t *ap));
230 LOCAL void _ab_pr __PR((abent_t *np, FILE_p f, int aflags));
231 LOCAL void _ab_prposix __PR((abent_t *np, FILE_p f, int aflags));
232 #ifdef INTERACTIVE
233 LOCAL void _ab_phist __PR((abent_t *np, FILE_p f, int aflags));
234 #endif
235 #ifdef BOURNE_SHELL
236 LOCAL off_t filesize __PR((int f));
237 LOCAL BOOL any_match __PR((char *s));
238 #endif
239
240 /*
241 * Do range check for 'tab' and return abtab_t structure for 'tab'.
242 */
243 LOCAL abtab_t *
_ab_down(tab)244 _ab_down(tab)
245 abidx_t tab;
246 {
247 if (tab < 0 || tab >= ABTABS)
248 raisecond(sn_badtab, 0L);
249 return (&ab_tabs[tab]);
250 }
251
252 #ifdef PROTOTYPES
253 LOCAL abent_t *
_ab_lookup(abtab_t * ap,char * name,BOOL new)254 _ab_lookup(abtab_t *ap, char *name, BOOL new)
255 #else
256 LOCAL abent_t *
257 _ab_lookup(ap, name, new)
258 abtab_t *ap;
259 char *name;
260 BOOL new;
261 #endif
262 {
263 register abent_t *np; /* Current node pointer */
264 register abent_t *lp; /* Last node pinter */
265 #ifdef notdef
266 register int cmp;
267 #endif
268
269 for (lp = AB_NULL, np = ap->at_ent; np != AB_NULL; ) {
270 register char *s1;
271 register char *s2;
272
273 #ifndef notdef
274 s1 = name;
275 s2 = np->ab_name;
276 for (; *s1 == *s2; s1++, s2++) /* viel schneller als strcmp */
277 if (*s1 == '\0')
278 return (np);
279 if (*s1 > *s2)
280 break;
281 #else
282 if ((cmp = strcmp(name, np->ab_name)) == 0)
283 return (np);
284 if (cmp > 0)
285 break;
286 #endif
287 lp = np;
288 np = np->ab_next;
289 }
290 if (!new)
291 return (AB_NULL);
292
293 np = _ab_newnode();
294 np->ab_name = name;
295 if (lp == AB_NULL) { /* make first in chain */
296 np->ab_next = ap->at_ent;
297 ap->at_ent = np;
298 } else { /* in middle of chain */
299 np->ab_next = lp->ab_next;
300 lp->ab_next = np;
301 }
302 return (np);
303 }
304
305
306 LOCAL abent_t *
_ab_newnode()307 _ab_newnode()
308 {
309 register abent_t *new;
310
311 if ((new = (abent_t *)malloc(sizeof (abent_t))) == AB_NULL)
312 raisecond(sn_no_mem, (long)"_ab_newnode");
313 new->ab_next = AB_NULL;
314 new->ab_push = AB_NULL;
315 new->ab_seen = AB_NULL;
316 new->ab_name = NULL;
317 new->ab_value = NULL;
318 new->ab_flags = 0;
319
320 return (new);
321 }
322
323 /*
324 * Output the current list of entries to ap->at_fname
325 * This funktioon is used to update $HOME/.globals and .locals
326 */
327 LOCAL void
_ab_output(ap)328 _ab_output(ap)
329 register abtab_t *ap;
330 {
331 register FILE_p f;
332 time_t mtime;
333 struct stat sb;
334
335 #ifdef DEBUG
336 berror("updating: %s", ap->at_fname);
337 #endif
338 if (ap->at_fname == NULL)
339 return;
340 mtime = ab_filemtime(ap->at_fname);
341 /*
342 * If ap->at_mtime == 0, the file did not exist for us.
343 */
344 if (mtime > ap->at_mtime) {
345 ab_eupdated(ap);
346 return;
347 }
348 if (lstat(ap->at_fname, &sb) < 0) /* Check whether a symlink */
349 /* EMPTY */; /* No file yet */
350 else if (S_ISLNK(sb.st_mode))
351 return;
352
353 f = filemopen(ap->at_fname, for_wct, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
354 if (open_failed(f)) {
355 raisecond(sn_badfile, (long)ap->at_fname);
356 } else {
357 #ifdef BOURNE_SHELL
358 int oldf = setb(f); /* Set new fd for prs() */
359 #endif
360 /*
361 * AB_PERSIST -> no pushed entries
362 * non interruptable
363 * no copy into history
364 */
365 _ab_dump(ap->at_ent, f, AB_PERSIST);
366 #ifdef BOURNE_SHELL
367 setb(oldf); /* Restore fd for prs() */
368 #endif
369 fclose(f);
370 ap->at_mtime = ab_filemtime(ap->at_fname);
371 }
372 }
373
374 /*
375 * Print a single name/value replacement entry to an open file.
376 */
377 LOCAL BOOL
_ab_print(tab,name,f,aflags)378 _ab_print(tab, name, f, aflags)
379 abidx_t tab;
380 char *name;
381 FILE_p f;
382 int aflags; /* should push into History? */
383 {
384 register abent_t *np;
385
386 np = _ab_lookup(_ab_down(tab), name, FALSE);
387 if (np == NULL)
388 return (FALSE);
389
390 _ab_list(np, f, aflags);
391 fflush(f);
392 return (TRUE);
393 }
394
395 /*
396 * Dump the whole list.
397 * Flags controls whether pushed entries appear in the output, whether
398 * interruptable and whether a copy is pushed into the command history.
399 */
400 LOCAL void
_ab_dump(np,f,aflags)401 _ab_dump(np, f, aflags)
402 register abent_t *np;
403 FILE_p f;
404 int aflags;
405 {
406 register abent_t *tp;
407
408 if (np == AB_NULL)
409 return;
410 _ab_dump(np->ab_next, f, aflags);
411 tp = np;
412 /*
413 * With AB_PERSIST skip pushed values and only dump the
414 * persistent base entry.
415 */
416 if (aflags & AB_PERSIST)
417 while (tp->ab_push != AB_NULL)
418 tp = tp->ab_push;
419 if (!(ctlc && (aflags & AB_INTR)))
420 _ab_list(tp, f, aflags);
421 }
422
423 /*
424 * The argument "f" is not used in the Bourne Shell
425 */
426 /* ARGSUSED */
427 LOCAL void
_ab_list(np,f,aflags)428 _ab_list(np, f, aflags)
429 register abent_t *np;
430 FILE_p f;
431 int aflags; /* should push into History? */
432 {
433 /*
434 * With AB_PERSIST skip pushed values and only list the
435 * persistent base entry.
436 */
437 if (np != AB_NULL && (aflags & AB_PERSIST)) {
438 while (np->ab_push != AB_NULL)
439 np = np->ab_push;
440 }
441 if (np != AB_NULL && np->ab_value != NULL &&
442 ((np->ab_flags & ABF_POP) == 0 || aflags & AB_PERSIST)) {
443 if ((aflags & AB_POSIX)) {
444 _ab_prposix(np, f, aflags);
445 } else {
446 _ab_pr(np, f, aflags);
447 }
448 #ifdef INTERACTIVE
449 if (aflags & AB_HISTORY) {
450 _ab_phist(np, f, aflags);
451 }
452 #endif
453 }
454 }
455
456
457 LOCAL BOOL
_ab_match(np,f,aflags,pattern,aux,alt,state)458 _ab_match(np, f, aflags, pattern, aux, alt, state)
459 register abent_t *np;
460 FILE_p f;
461 int aflags; /* should push into History? */
462 char *pattern;
463 int *aux;
464 int alt;
465 int *state;
466 {
467 register abent_t *tp;
468 #ifndef BOURNE_SHELL
469 register char *p;
470 #endif
471 BOOL ok;
472
473 if (np == AB_NULL)
474 return (FALSE);
475 ok = _ab_match(np->ab_next, f, aflags, pattern, aux, alt, state);
476 if (ctlc)
477 return (FALSE);
478 tp = np;
479 while (tp->ab_push != AB_NULL)
480 tp = tp->ab_push;
481 #ifdef BOURNE_SHELL
482 if (gmatch(tp->ab_name, pattern)) {
483 _ab_list(tp, f, aflags);
484 return (TRUE);
485 }
486 #else
487 p = (char *)patmatch((unsigned char *)pattern, aux,
488 (unsigned char *)tp->ab_name, 0, strlen(tp->ab_name),
489 alt, state);
490 if (p && *p == '\0') {
491 _ab_list(tp, f, aflags);
492 return (TRUE);
493 }
494 #endif
495 return (ok);
496 }
497
498 /*
499 * Free all symbols associated with this table.
500 */
501 LOCAL void
_ab_close(np,tab)502 _ab_close(np, tab)
503 register abent_t *np;
504 abidx_t tab;
505 {
506 register abent_t *tp;
507 register abent_t *sp;
508
509 while (np != AB_NULL) {
510 if (!ab_inblock(tab, np->ab_name))
511 free(np->ab_name);
512 tp = np;
513 np = np->ab_next; /* remember next ptr now */
514 /*
515 * Now free all pushed values including this one.
516 */
517 do {
518 if (tp->ab_value != NULL &&
519 !ab_inblock(tab, tp->ab_value))
520 free(tp->ab_value);
521 sp = tp->ab_push; /* Save push ptr */
522 free((char *)tp);
523 } while ((tp = sp) != AB_NULL); /* Use remembered push ptr */
524 }
525 }
526
527
528 /*
529 * Find the next begin of word inside the allocated monolitic block.
530 */
531 LOCAL char *
ab_beginword(p,ap)532 ab_beginword(p, ap)
533 register char *p;
534 abtab_t *ap;
535 {
536 register char *rend = ap->at_blkend;
537
538 while (p < rend && (*p == ' ' || *p == '\t' || *p == '\n'))
539 p++;
540 return (p);
541 }
542
543
544 /*
545 * Find the next end of word inside the allocated monolitic block.
546 */
547 LOCAL char *
ab_endword(p,ap)548 ab_endword(p, ap)
549 register char *p;
550 abtab_t *ap;
551 {
552 register char *rend = ap->at_blkend;
553
554 while (p < rend && (*p != ' ' && *p != '\t' && *p != '\n'))
555 p++;
556 return (p);
557 }
558
559 /*
560 * Find the next end of line inside the allocated monolitic block.
561 */
562 LOCAL char *
ab_endline(p,ap)563 ab_endline(p, ap)
564 register char *p;
565 abtab_t *ap;
566 {
567 register char *rend = ap->at_blkend;
568
569 while (p < rend && *p != '\n')
570 p++;
571 return (p);
572 }
573
574 /*
575 * Check whether a character pointer points into the monolitic block
576 * that is allocated when the file is read by ab_read().
577 */
578 LOCAL BOOL
ab_inblock(tab,p)579 ab_inblock(tab, p)
580 abidx_t tab;
581 char *p;
582 {
583 register abtab_t *ap = _ab_down(tab);
584
585 return ((p >= ap->at_blk) && (p <= ap->at_blkend));
586 }
587
588 LOCAL time_t
ab_statmtime(sp)589 ab_statmtime(sp)
590 struct stat *sp;
591 {
592 if (sp->st_mtime == (time_t)0)
593 sp->st_mtime++;
594 return (sp->st_mtime);
595 }
596
597 LOCAL time_t
ab_filemtime(fname)598 ab_filemtime(fname)
599 char *fname;
600 {
601 struct stat sb;
602
603 if (stat(fname, &sb) < 0)
604 return ((time_t)0);
605
606 return (ab_statmtime(&sb));
607 }
608
609 /*
610 * Get alternate file owner that is trusted in addition to the current
611 * shell user.
612 */
613 EXPORT uid_t
ab_getaltowner(tab)614 ab_getaltowner(tab)
615 abidx_t tab;
616 {
617 register abtab_t *ap = _ab_down(tab);
618
619 return (ap->at_altowner);
620 }
621
622 EXPORT char *
ab_getaltoname(tab)623 ab_getaltoname(tab)
624 abidx_t tab;
625 {
626 register abtab_t *ap = _ab_down(tab);
627 register struct passwd *pw;
628 static char oname[12];
629
630 pw = getpwuid(ap->at_altowner);
631 endpwent();
632 if (pw)
633 return (pw->pw_name);
634 oname[0] = '\0';
635
636 #if defined(BOURNE_SHELL) && defined(HAVE_SNPRINTF)
637 /*
638 * Try to avoid js_snprintf() in the Bourne Shell
639 * to allow to lazyload libschily
640 */
641 snprintf(oname, sizeof (oname),
642 "%d", ap->at_altowner);
643 #else
644 js_snprintf(oname, sizeof (oname),
645 "%d", ap->at_altowner);
646 #endif
647 return (oname);
648 }
649
650 /*
651 * Set alternate file owner that is trusted in addition to the current
652 * shell user.
653 */
654 EXPORT void
ab_setaltowner(tab,usrname)655 ab_setaltowner(tab, usrname)
656 abidx_t tab;
657 char *usrname;
658 {
659 register abtab_t *ap = _ab_down(tab);
660 register struct passwd *pw;
661 register char *p = usrname;
662
663 if (*usrname == '\0') {
664 ap->at_altowner = (uid_t)-1;
665 return;
666 }
667 pw = getpwnam(usrname);
668 endpwent();
669
670 if (pw) {
671 ap->at_altowner = pw->pw_uid;
672 return;
673 }
674 while (*p) {
675 if (*p < '0')
676 return;
677 if (*p++ > '9')
678 return;
679 }
680 ap->at_altowner = atoi(usrname);
681 }
682
683 /*
684 * Read file 'fname' and build a new abbreviation/alias replacement table
685 */
686 EXPORT void
ab_read(tab,fname)687 ab_read(tab, fname)
688 abidx_t tab;
689 char *fname;
690 {
691 register char *line;
692 register char *name;
693 register char *val;
694 FILE_p f;
695 register abtab_t *ap = _ab_down(tab);
696 off_t fsize;
697 register int beg;
698 struct stat sb;
699
700 /*
701 * Make sure that ap->at_fname is NULL to avoid writing back to the
702 * file when calling ab_insert().
703 */
704 ab_close(tab);
705
706 if (fname == NULL)
707 return;
708
709 if (lstat(fname, &sb) < 0) /* Check whether a symlink */
710 return; /* No file to open, return */
711 if (S_ISLNK(sb.st_mode))
712 return;
713
714 f = fileopen(fname, for_ru);
715 if (open_failed(f))
716 return;
717
718 if (filestat(f, &sb) >= 0) {
719 if (geteuid() == sb.st_uid) {
720 if (sb.st_mode & (S_IWGRP|S_IWOTH)) {
721 fclose(f);
722 return;
723 }
724 } else if ((ap->at_altowner == (uid_t)-1 ||
725 ap->at_altowner != sb.st_uid)) {
726 fclose(f);
727 return;
728 }
729 ap->at_mtime = ab_statmtime(&sb);
730 }
731 fsize = filesize(f);
732 if (ap->at_mtime == 0)
733 ap->at_mtime = ab_filemtime(fname);
734 #ifdef DEBUG
735 berror("ab_read(%d, %s)-> %d %.24s", tab, fname,
736 ap->at_mtime, ctime(&ap->at_mtime));
737 #endif
738 if ((ap->at_blk = malloc((size_t)fsize)) == NULL) {
739 raisecond(sn_no_mem, (long)"ab_read");
740 fclose(f);
741 return;
742 }
743 ap->at_blkend = ap->at_blk + fsize - 1;
744 if (fileread(f, ap->at_blk, (int)fsize) != fsize) {
745 free(ap->at_blk);
746 ap->at_blk = NULL;
747 fclose(f);
748 return;
749 }
750 fclose(f);
751
752 line = ap->at_blk;
753 while (line < ap->at_blkend) {
754 line = ab_beginword(line, ap);
755 if (*line++ != '#') {
756 line = ab_endline(line, ap);
757 continue;
758 }
759 if (*line == 'a') {
760 beg = 0;
761 } else if (*line == 'b') {
762 beg = AB_BEGIN;
763 } else {
764 line = ab_endline(line, ap);
765 continue;
766 }
767 name = line = ab_beginword(ab_endword(line, ap), ap);
768 line = ab_endword(line, ap);
769 *line++ = '\0';
770 val = line = ab_beginword(line, ap);
771 line = ab_endline(line, ap);
772 *line++ = '\0';
773 ab_insert(tab, name, val, beg);
774 }
775 }
776
777
778 /*
779 * Set filename to use for _ab_output()
780 */
781 EXPORT void
ab_sname(tab,fname)782 ab_sname(tab, fname)
783 abidx_t tab;
784 char *fname;
785 {
786 abtab_t *ap = _ab_down(tab);
787 #ifdef BOURNE_SHELL
788 free(ap->at_fname); /* Bourne Shell only frees what need free() */
789 #endif
790 ap->at_fname = fname;
791 }
792
793 /*
794 * Get filename from tab
795 */
796 EXPORT char *
ab_gname(tab)797 ab_gname(tab)
798 abidx_t tab;
799 {
800 abtab_t *ap = _ab_down(tab);
801
802 return (ap->at_fname);
803 }
804
805 /*
806 * Use new abbrev file
807 */
808 EXPORT void
ab_use(tab,fname)809 ab_use(tab, fname)
810 abidx_t tab;
811 char *fname;
812 {
813 ab_read(tab, fname);
814 ab_sname(tab, fname);
815 }
816
817 /*
818 * Shut down a table, remove all name/value translations from this tab before.
819 */
820 EXPORT void
ab_close(tab)821 ab_close(tab)
822 abidx_t tab;
823 {
824 register abtab_t *ap = _ab_down(tab);
825
826 _ab_close(ap->at_ent, tab);
827 ap->at_fname = NULL;
828 ap->at_mtime = (time_t)0;
829 ap->at_ent = AB_NULL;
830 if (ap->at_blk != NULL) {
831 free(ap->at_blk);
832 ap->at_blk = NULL;
833 }
834 }
835
836
837 /*
838 * Insert a new name/value pair into an abbreviation/alias table.
839 */
840 EXPORT BOOL
ab_insert(tab,name,val,aflags)841 ab_insert(tab, name, val, aflags)
842 abidx_t tab;
843 char *name;
844 char *val;
845 int aflags;
846 {
847 abtab_t *ap = _ab_down(tab);
848 register abent_t *np;
849
850 np = _ab_lookup(ap, name, TRUE);
851 if (np == NULL)
852 return (FALSE);
853 if (np->ab_value != NULL && !ab_inblock(tab, np->ab_value))
854 free(np->ab_value);
855 np->ab_value = val;
856 if (aflags & AB_BEGIN)
857 np->ab_flags |= ABF_BEGIN;
858 else
859 np->ab_flags &= ~ABF_BEGIN;
860 /*
861 * If this entry has not been pushed on top of old replacements,
862 * update the underlying file storage.
863 */
864 if (np->ab_push == AB_NULL)
865 _ab_output(ap);
866 return (TRUE);
867 }
868
869
870 /*
871 * Push a new name/value pair on top of an abbreviation/alias table entry.
872 */
873 EXPORT BOOL
ab_push(tab,name,val,aflags)874 ab_push(tab, name, val, aflags)
875 abidx_t tab;
876 char *name;
877 char *val;
878 int aflags;
879 {
880 abtab_t *ap = _ab_down(tab);
881 register abent_t *np;
882 register abent_t *new;
883
884 np = _ab_lookup(ap, name, TRUE);
885 if (np == NULL)
886 return (FALSE);
887 new = _ab_newnode(); /* Get space for node to push */
888 if (new == NULL)
889 return (FALSE);
890 new->ab_name = np->ab_name; /* Dup to allow to list all */
891 new->ab_value = np->ab_value; /* First save old node data */
892 new->ab_flags = np->ab_flags;
893 new->ab_push = np->ab_push;
894 np->ab_push = new; /* Then make pushed node active */
895 np->ab_value = val;
896 if (aflags & AB_BEGIN)
897 np->ab_flags |= ABF_BEGIN;
898 else
899 np->ab_flags &= ~ABF_BEGIN;
900 np->ab_flags &= ~ABF_POP;
901 return (TRUE);
902 }
903
904
905 /*
906 * Pop a new name/value pair from the top of an abbreviation/alias table entry.
907 * If there is no pushed entry left over, then the whole entry is deleted
908 * unless this is a POP operation. A POP on the last entry for that name just
909 * sets the ABF_POP flag to be able to keep the content in .globals and .locals
910 * Deletion is done by setting ab_value to NULL.
911 */
912 LOCAL void
_ab_delete(np,tab,aflags)913 _ab_delete(np, tab, aflags)
914 register abent_t *np;
915 abidx_t tab;
916 int aflags;
917 {
918 register abent_t *op;
919
920 if (np != AB_NULL && np->ab_value != NULL) {
921 if (np->ab_push != AB_NULL) { /* If saved old value */
922 do {
923 op = np->ab_push;
924 if (!ab_inblock(tab, np->ab_value))
925 free(np->ab_value);
926 np->ab_value = op->ab_value; /* Pop top entry */
927 np->ab_flags = op->ab_flags;
928 np->ab_push = op->ab_push;
929 free((char *)op);
930 } while (np->ab_push && (aflags & AB_POPALL));
931 }
932
933 if (np->ab_push == AB_NULL) { /* The last definition */
934 if (aflags & AB_POP) {
935 np->ab_flags |= ABF_POP;
936 } else {
937 if (!ab_inblock(tab, np->ab_value))
938 free(np->ab_value);
939 np->ab_value = NULL;
940 if ((np->ab_flags & ABF_POP) == 0)
941 _ab_output(_ab_down(tab));
942 }
943 }
944 }
945 }
946
947 EXPORT BOOL
ab_delete(tab,name,aflags)948 ab_delete(tab, name, aflags)
949 abidx_t tab;
950 char *name;
951 int aflags;
952 {
953 abtab_t *ap = _ab_down(tab);
954 abent_t *np;
955
956 np = _ab_lookup(ap, name, FALSE);
957 if (np == NULL)
958 return (FALSE);
959 _ab_delete(np, tab, aflags);
960 return (TRUE);
961 }
962
963 LOCAL void
_ab_deleteall(np,tab,aflags)964 _ab_deleteall(np, tab, aflags)
965 register abent_t *np;
966 abidx_t tab;
967 int aflags;
968 {
969 if (np == AB_NULL)
970 return;
971 _ab_deleteall(np->ab_next, tab, aflags);
972 if (!(ctlc && (aflags & AB_INTR)))
973 _ab_delete(np, tab, aflags | AB_POPALL);
974 }
975
976 EXPORT void
ab_deleteall(tab,aflags)977 ab_deleteall(tab, aflags)
978 abidx_t tab;
979 int aflags;
980 {
981 _ab_deleteall(_ab_down(tab)->at_ent, tab, aflags);
982 }
983
984 /*
985 * Perform a name/value translation for a named abbreviation/alias table.
986 * The seen pointer holds a list of already expanded aliases and is used
987 * to avoid endless loops in alias expansion in a POSIX compliant way.
988 */
989 EXPORT char *
ab_value(tab,name,seen,aflags)990 ab_value(tab, name, seen, aflags)
991 abidx_t tab;
992 char *name;
993 void **seen;
994 int aflags; /* lookup begin abbreviations also? */
995 {
996 register abent_t *np;
997
998 np = _ab_lookup(_ab_down(tab), name, FALSE);
999 if (np == AB_NULL)
1000 return (NULL);
1001 if (np->ab_flags & ABF_POP)
1002 return (NULL);
1003 if ((np->ab_flags & ABF_BEGIN) == 0 || (aflags & AB_BEGIN)) {
1004 if (seen) {
1005 register abent_t *sp = *seen;
1006
1007 while (sp) {
1008 if (sp == np)
1009 return (NULL);
1010 sp = sp->ab_seen;
1011 }
1012 np->ab_seen = *seen;
1013 *seen = np;
1014 }
1015 return (np->ab_value);
1016 }
1017 return (NULL);
1018 }
1019
1020
1021 /*
1022 * Print all name/value replacement entries to an open file.
1023 */
1024 EXPORT void
ab_dump(tab,f,aflags)1025 ab_dump(tab, f, aflags)
1026 abidx_t tab;
1027 FILE_p f;
1028 int aflags; /* should push into History? */
1029 {
1030 /*
1031 * Don't use AB_PERSIST -> output pushed entries
1032 * be interruptable
1033 */
1034 _ab_dump(_ab_down(tab)->at_ent, f, aflags | AB_INTR);
1035 fflush(f);
1036 }
1037
1038
1039 /*
1040 * Print all name/value replacements with matched entries to an open file.
1041 */
1042 EXPORT BOOL
ab_list(tab,pattern,f,aflags)1043 ab_list(tab, pattern, f, aflags)
1044 abidx_t tab;
1045 register char *pattern;
1046 FILE_p f;
1047 int aflags; /* should push into History? */
1048 {
1049 register int *aux = NULL; /* auxiliary array */
1050 register int *state = NULL; /* state array */
1051 register int alt = 0; /* outermost alternate */
1052 #ifndef BOURNE_SHELL
1053 register int patlen; /* pattern lenght */
1054 #endif
1055
1056 if ((aflags & AB_POSIX) == 0 && (tab != deftab)) {
1057 if (tab == GLOBAL_AB)
1058 aflags |= AB_PGLOBAL;
1059 else
1060 aflags |= AB_PLOCAL;
1061 }
1062
1063 if (!any_match(pattern)) {
1064 return (_ab_print(tab, pattern, f, aflags));
1065 } else {
1066 BOOL ok;
1067
1068 #ifndef BOURNE_SHELL
1069 patlen = strlen(pattern);
1070 aux = (int *)malloc((size_t)patlen * sizeof (int));
1071 state = (int *)malloc((size_t)(patlen+1) * sizeof (int));
1072 alt = patcompile((unsigned char *)pattern, patlen, aux);
1073 if (alt) {
1074 ok = _ab_match(_ab_down(tab)->at_ent,
1075 f, aflags, pattern, aux, alt, state);
1076 fflush(f);
1077 } else {
1078 berror("%s", ebadpattern);
1079 free((char *)aux);
1080 free((char *)state);
1081 return (FALSE);
1082 }
1083 free((char *)aux);
1084 free((char *)state);
1085 #else
1086 ok = _ab_match(_ab_down(tab)->at_ent,
1087 f, aflags, pattern, aux, alt, state);
1088 fflush(f);
1089 #endif
1090 return (ok);
1091 }
1092 }
1093
1094 LOCAL void
ab_eupdated(ap)1095 ab_eupdated(ap)
1096 abtab_t *ap;
1097 {
1098 #ifdef BOURNE_SHELL
1099 gfailure(UC ap->at_fname,
1100 "updated by another shell, cannot write back.");
1101 #else
1102 berror("'%s' was updated by another shell, cannot write back.",
1103 ap->at_fname);
1104 #endif
1105 }
1106
1107 /*
1108 * The argument "f" is not used in the Bourne Shell
1109 */
1110 /* ARGSUSED */
1111 LOCAL void
_ab_pr(np,f,aflags)1112 _ab_pr(np, f, aflags)
1113 register abent_t *np;
1114 FILE_p f;
1115 int aflags;
1116 {
1117 #ifdef BOURNE_SHELL
1118 int len;
1119 #endif
1120
1121 if (np->ab_value == NULL)
1122 return;
1123 if (np->ab_push && (aflags & AB_ALL))
1124 _ab_pr(np->ab_push, f, aflags);
1125
1126 #ifdef BOURNE_SHELL
1127 prc_buff('#');
1128 if (np->ab_push)
1129 prc_buff('p');
1130 prc_buff((np->ab_flags & ABF_BEGIN) ? 'b':'a');
1131 if (aflags & AB_PLOCAL)
1132 prc_buff('l');
1133 else if (aflags & AB_PGLOBAL)
1134 prc_buff('g');
1135 prc_buff(' ');
1136 prs_buff(UC np->ab_name);
1137 len = length(UC np->ab_name);
1138 do {
1139 prc_buff(' ');
1140 } while (len++ <= 8);
1141 prs_buff(UC np->ab_value);
1142 prc_buff(NL);
1143 #else
1144 fprintf(f, "#%s%c%s %-8s %s\n",
1145 np->ab_push ? "p":"",
1146 np->ab_flags & ABF_BEGIN ? 'b':'a',
1147 (aflags & AB_PLOCAL) ? "l":
1148 (aflags & AB_PGLOBAL) ? "g":"",
1149 np->ab_name, np->ab_value);
1150 #endif
1151 }
1152
1153 /*
1154 * The argument "f" is not used in the Bourne Shell
1155 */
1156 /* ARGSUSED */
1157 LOCAL void
_ab_prposix(np,f,aflags)1158 _ab_prposix(np, f, aflags)
1159 register abent_t *np;
1160 FILE_p f;
1161 int aflags;
1162 {
1163 if (np->ab_value == NULL)
1164 return;
1165 if (aflags & AB_PARSE) {
1166 if (np->ab_push && (aflags & AB_ALL))
1167 _ab_prposix(np->ab_push, f, aflags);
1168
1169 #ifdef BOURNE_SHELL
1170 prs_buff(UC "alias ");
1171 if ((np->ab_flags & ABF_BEGIN) == 0)
1172 prs_buff(UC "-a ");
1173 if (np->ab_push)
1174 prs_buff(UC "-p ");
1175 if (aflags & AB_PGLOBAL)
1176 prs_buff(UC "-g ");
1177 if (aflags & AB_PLOCAL)
1178 prs_buff(UC "-l ");
1179 #else
1180 fprintf(f, "alias %s%s%s%s",
1181 (np->ab_flags & ABF_BEGIN) ? "":"-a ",
1182 np->ab_push ? "-p ":"",
1183 (aflags & AB_PGLOBAL) ? "-g ":"",
1184 (aflags & AB_PLOCAL) ? "-l ":"");
1185 #endif
1186 }
1187 #ifdef BOURNE_SHELL
1188 prs_buff(UC np->ab_name);
1189 prs_buff(UC "='");
1190 { unsigned char *p = UC np->ab_value;
1191 while (*p) {
1192 if (*p == '\'')
1193 prs_buff(UC "'\\'");
1194 prc_buff(*p++);
1195 }
1196 }
1197 if ((aflags & AB_PARSE) == 0 &&
1198 (np->ab_flags & ABF_BEGIN) == 0)
1199 prs_buff(UC "' # allexpand\n");
1200 else
1201 prs_buff(UC "'\n");
1202 #else
1203 fprintf(f, "'%s=", np->ab_name);
1204 { char *p = np->ab_value;
1205 while (*p) {
1206 if (*p == '\'')
1207 fprintf(f, "\\");
1208 fputc(*p++, f);
1209 }
1210 }
1211 fprintf(f, "'%s\n",
1212 ((np->ab_flags & ABF_BEGIN) == 0) ? " # allexpand":"");
1213 #endif
1214 }
1215
1216 #ifdef INTERACTIVE
1217 /*
1218 * The argument "f" is not used in the Bourne Shell
1219 */
1220 /* ARGSUSED */
1221 LOCAL void
_ab_phist(np,f,aflags)1222 _ab_phist(np, f, aflags)
1223 register abent_t *np;
1224 FILE_p f;
1225 int aflags;
1226 {
1227 #ifdef BOURNE_SHELL
1228 #define append_line shedit_append_line
1229 #endif
1230 if (np->ab_value == NULL)
1231 return;
1232 if (np->ab_push && (aflags & AB_ALL))
1233 _ab_phist(np->ab_push, f, aflags);
1234
1235 { char buf[8192];
1236 unsigned int len;
1237
1238 js_snprintf(buf, sizeof (buf),
1239 "#%s%c%s %-8s %s",
1240 np->ab_push ? "p":"",
1241 np->ab_flags & ABF_BEGIN ? 'b':'a',
1242 (aflags & AB_PLOCAL) ? "l":
1243 (aflags & AB_PGLOBAL) ? "g":"",
1244 np->ab_name, np->ab_value);
1245 len = strlen(buf);
1246 append_line(buf, len + 1, len);
1247 }
1248 }
1249 #endif /* INTERACTIVE */
1250
1251 #ifdef BOURNE_SHELL
1252 LOCAL off_t
filesize(f)1253 filesize(f)
1254 int f;
1255 {
1256 struct stat sb;
1257
1258 if (fstat(f, &sb) < 0)
1259 return ((off_t)-1);
1260
1261 return (sb.st_size);
1262 }
1263
1264 LOCAL BOOL
any_match(s)1265 any_match(s)
1266 register char *s;
1267 {
1268 register unsigned char *rm = UC "[]?*";
1269
1270 while (*s && !any(*s, rm))
1271 s++;
1272 return ((BOOL) *s);
1273 }
1274 #endif
1275