rc

[fork] interactive rc shell
git clone https://hhvn.uk/rc
git clone git://hhvn.uk/rc
Log | Files | Refs | README | LICENSE

fn.c (6423B)


      1 /*
      2    fn.c: functions for adding and deleting functions from the symbol table.
      3    Support for signal handlers is also found here.
      4 */
      5 
      6 #include "rc.h"
      7 
      8 #include <signal.h>
      9 #include <errno.h>
     10 
     11 #include "input.h"
     12 #include "sigmsgs.h"
     13 
     14 static void fn_handler(int), dud_handler(int);
     15 
     16 static bool runexit = FALSE;
     17 static Node *handlers[NUMOFSIGNALS], null;
     18 static void (*def_sigint)(int) = SIG_DFL;
     19 static void (*def_sigquit)(int) = SIG_DFL;
     20 static void (*def_sigterm)(int) = SIG_DFL;
     21 
     22 /*
     23    Set signals to default values for rc. This means that interactive
     24    shells ignore SIGTERM, etc.
     25 */
     26 
     27 extern void inithandler() {
     28 	int i;
     29 	null.type = nBody;
     30 	null.u[0].p = null.u[1].p = NULL;
     31 	for (i = 1; i < NUMOFSIGNALS; i++)
     32 		if (i != SIGCHLD && sighandlers[i] == SIG_IGN)
     33 			fnassign(signals[i].name, NULL); /* ignore incoming ignored signals */
     34 	if (interactive || sighandlers[SIGINT] != SIG_IGN) {
     35 		def_sigint = sigint;
     36 		fnrm("sigint"); /* installs SIGINT catcher if not inherited ignored */
     37 	}
     38 	if (!dashdee) {
     39 		if (interactive || sighandlers[SIGQUIT] != SIG_IGN) {
     40 			def_sigquit = dud_handler;
     41 			fnrm("sigquit"); /* "ignores" SIGQUIT unless inherited ignored */
     42 		}
     43 		if (interactive) {
     44 			def_sigterm = dud_handler;
     45 			fnrm("sigterm"); /* ditto for SIGTERM */
     46 		}
     47 	}
     48 }
     49 
     50 /* only run this in a child process! resets signals to their default values */
     51 
     52 extern void setsigdefaults(bool sysvbackground) {
     53 	int i;
     54 	/*
     55 	   General housekeeping: setsigdefaults happens after fork(),
     56 	   so it's a convenient place to clean up open file descriptors.
     57 	   (history file, scripts, etc.)
     58 	*/
     59 	closefds();
     60 	/*
     61 	   Restore signals to SIG_DFL, paying close attention to
     62 	   a few quirks: SIGINT, SIGQUIT and are treated specially
     63 	   depending on whether we are doing v7-style backgrounding
     64 	   or not; the default action for SIGINT, SIGQUIT and SIGTERM
     65 	   must be set to the appropriate action; finally, care must
     66 	   be taken not to set to SIG_DFL any signals which are being
     67 	   ignored.
     68 	*/
     69 	for (i = 1; i < NUMOFSIGNALS; i++)
     70 		if (sighandlers[i] != SIG_IGN) {
     71 			handlers[i] = NULL;
     72 			switch (i) {
     73 			case SIGINT:
     74 				if (sysvbackground) {
     75 					def_sigint = SIG_IGN;
     76 					fnassign("sigint", NULL); /* ignore */
     77 				} else {
     78 					def_sigint = SIG_DFL;
     79 					goto sigcommon;
     80 				}
     81 				break;
     82 			case SIGQUIT:
     83 				if (sysvbackground) {
     84 					def_sigquit = SIG_IGN;
     85 					fnassign("sigquit", NULL); /* ignore */
     86 				} else {
     87 					def_sigquit = SIG_DFL;
     88 					goto sigcommon;
     89 				}
     90 				break;
     91 			case SIGTERM:
     92 				def_sigterm = SIG_DFL;
     93 				/* FALLTHROUGH */
     94 			sigcommon:
     95 			default:
     96 				if (sighandlers[i] != SIG_DFL) {
     97 					rc_signal(i, SIG_DFL);
     98 					delete_fn(signals[i].name);
     99 				}
    100 			}
    101 		}
    102 	delete_fn("sigexit");
    103 	runexit = FALSE; /* No sigexit on subshells */
    104 }
    105 
    106 /* rc's exit. if runexit is set, run the sigexit function. */
    107 
    108 extern void rc_exit(int stat) {
    109 	if (runexit) {
    110 		char *sig[2];
    111 		sig[0] = "sigexit";
    112 		sig[1] = NULL;
    113 		runexit = FALSE;
    114 		funcall(sig);
    115 		stat = getstatus();
    116 	}
    117 	exit(stat);
    118 }
    119 
    120 /* The signal handler for all functions. calls walk() */
    121 
    122 static void fn_handler(int s) {
    123 	char *sig[2];
    124 	int olderrno;
    125 	if (s < 1 || s >= NUMOFSIGNALS)
    126 		panic("unknown signal");
    127 	olderrno = errno;
    128 	sig[0] = signals[s].name;
    129 	sig[1] = NULL;
    130 	funcall(sig);
    131 	errno = olderrno;
    132 }
    133 
    134 /* A dud signal handler for SIGQUIT and SIGTERM */
    135 
    136 static void dud_handler(int ignore) {
    137 }
    138 
    139 /*
    140    Assign a function in Node form. Check to see if the function is also
    141    a signal, and set the signal vectors appropriately.
    142 */
    143 
    144 extern void fnassign(char *name, Node *def) {
    145 	Node *newdef = treecpy(def == NULL ? &null : def, ealloc); /* important to do the treecopy first */
    146 	rc_Function *new = get_fn_place(name);
    147 	int i;
    148 	new->def = newdef;
    149 	new->extdef = NULL;
    150 	if (strncmp(name, "sig", conststrlen("sig")) == 0) { /* slight optimization */
    151 		if (streq(name, "sigchld") || streq(name, "sigcld"))
    152 			rc_error("can't trap SIGCHLD");
    153 		if (streq(name, "sigexit"))
    154 			runexit = TRUE;
    155 		for (i = 1; i < NUMOFSIGNALS; i++) /* zero is a bogus signal */
    156 			if (streq(signals[i].name, name)) {
    157 				handlers[i] = newdef;
    158 				if (def == NULL)
    159 					rc_signal(i, SIG_IGN);
    160 				else
    161 					rc_signal(i, fn_handler);
    162 				break;
    163 			}
    164 	}
    165 }
    166 
    167 /* Assign a function from the environment. Store just the external representation */
    168 
    169 extern void fnassign_string(char *extdef) {
    170 	char *name = get_name(extdef+3); /* +3 to skip over "fn_" */
    171 	rc_Function *new;
    172 	if (name == NULL)
    173 		return;
    174 	new = get_fn_place(name);
    175 	new->def = NULL;
    176 	new->extdef = ecpy(extdef);
    177 }
    178 
    179 /* Return a function in Node form, evaluating an entry from the environment if necessary */
    180 
    181 extern Node *fnlookup(char *name) {
    182 	rc_Function *look = lookup_fn(name);
    183 	Node *ret;
    184 	if (look == NULL)
    185 		return NULL; /* not found */
    186 	if (look->def != NULL)
    187 		return look->def;
    188 	if (look->extdef == NULL) /* function was set to null, e.g., fn foo {} */
    189 		return &null;
    190 	ret = parse_fn(look->extdef);
    191 	if (ret == NULL) {
    192 		efree(look->extdef);
    193 		look->extdef = NULL;
    194 		return &null;
    195 	} else {
    196 		return look->def = treecpy(ret, ealloc); /* Need to take it out of nalloc space */
    197 	}
    198 }
    199 
    200 /* Return a function in string form (used by makeenv) */
    201 
    202 extern char *fnlookup_string(char *name) {
    203 	rc_Function *look = lookup_fn(name);
    204 
    205 	if (look == NULL)
    206 		return NULL;
    207 	if (look->extdef != NULL)
    208 		return look->extdef;
    209 	return look->extdef = mprint("fn_%F={%T}", name, look->def);
    210 }
    211 
    212 /*
    213    Remove a function from the symbol table. If it also defines a signal
    214    handler, restore the signal handler to its default value.
    215 */
    216 
    217 extern void fnrm(char *name) {
    218 	int i;
    219 	for (i = 1; i < NUMOFSIGNALS; i++)
    220 		if (streq(signals[i].name, name)) {
    221 			handlers[i] = NULL;
    222 			switch (i) {
    223 			case SIGINT:
    224 				rc_signal(i, def_sigint);
    225 				break;
    226 			case SIGQUIT:
    227 				rc_signal(i, def_sigquit);
    228 				break;
    229 			case SIGTERM:
    230 				rc_signal(i, def_sigterm);
    231 				break;
    232 			default:
    233 				rc_signal(i, SIG_DFL);
    234 			}
    235 		}
    236 	if (streq(name, "sigexit"))
    237 		runexit = FALSE;
    238 	delete_fn(name);
    239 }
    240 
    241 extern void whatare_all_signals() {
    242 	int i;
    243 	for (i = 1; i < NUMOFSIGNALS; i++)
    244 		if (*signals[i].name != '\0') {
    245 			if (sighandlers[i] == SIG_IGN)
    246 				fprint(1, "fn %s {}\n", signals[i].name);
    247 			else if (sighandlers[i] == fn_handler)
    248 				fprint(1, "fn %S {%T}\n", signals[i].name, handlers[i]);
    249 			else
    250 				fprint(1, "fn %s\n", signals[i].name);
    251 		}
    252 }
    253 
    254 extern void prettyprint_fn(int fd, char *name, Node *n) {
    255 	fprint(fd, "fn %S {%T}\n", name, n);
    256 }