1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
|
.TH SH 2
.SH NAME
Sh \- module interface to the shell
.SH SYNOPSIS
.EX
.ps -1
.vs -1
include "sh.m";
sh := load Sh Sh->PATH;
Context, Listnode: import sh;
system: fn(drawctxt: ref Draw->Context, cmd: string): string;
run: fn(drawctxt: ref Draw->Context, argv: list of string): string;
parse: fn(s: string): (ref Cmd, string);
cmd2string: fn(c: ref Cmd): string;
list2stringlist: fn(nl: list of ref Listnode): list of string;
stringlist2list: fn(sl: list of string): list of ref Listnode;
Context: adt {
new: fn(drawcontext: ref Draw->Context): ref Context;
get: fn(c: self ref Context,
name: string): list of ref Listnode;
set: fn(c: self ref Context,
name: string,
value: list of ref Listnode);
setlocal: fn(c: self ref Context,
name: string,
value: list of ref Listnode);
envlist: fn(c: self ref Context):
list of (string, list of ref Listnode);
push, pop: fn(c: self ref Context);
copy: fn(c: self ref Context, copyenv: int): ref Context;
run: fn(c: self ref Context,
args: list of ref Listnode,
last: int): string;
addmodule: fn(c: self ref Context, name: string,
mod: Shellbuiltin);
addbuiltin: fn(c: self ref Context, name: string,
mod: Shellbuiltin);
removebuiltin: fn(c: self ref Context, name: string,
mod: Shellbuiltin);
addsbuiltin: fn(c: self ref Context, name: string,
mod: Shellbuiltin);
removesbuiltin: fn(c: self ref Context, name: string,
mod: Shellbuiltin);
fail: fn(c: self ref Context, ename, msg: string);
options: fn(c: self ref Context): int;
setoptions: fn(c: self ref Context, flags, on: int): int;
};
Listnode: adt {
cmd: ref Cmd;
word: string;
};
Cmd: adt {
# private data
};
Shellbuiltin: module {
initbuiltin: fn(ctxt: ref Context, sh: Sh): string;
whatis: fn(ctxt: ref Sh->Context, sh: Sh,
name: string, wtype: int): string;
runbuiltin: fn(ctxt: ref Context, sh: Sh,
cmd: list of ref Listnode,
last: int): string;
runsbuiltin: fn(ctxt: ref Context, sh: Sh,
cmd: list of ref Listnode): list of ref Listnode;
getself: fn(): Shellbuiltin;
};
.ps +1
.vs +1
.EE
.SH DESCRIPTION
.I Sh
is a command-line interpreter and a scripting language;
it also presents a module interface to allow Limbo
modules to access its functionality at a lower level.
The
.B Sh
module can be used in several different ways.
At the simplest level, it can be run as a command-line
program; for details of this, see
.IR sh (1).
The simplest access at the Limbo level is through
the
.B system
function, which given a
.I draw
.B Context
(see
.IR draw-context (2))
and a string
executes the
.I sh
command contained in
.I s
and returns its result. It catches any exceptions raised by the command.
Almost as simple is
.BR exec ,
which runs
.I argv
as a command, taking the first word as the command to be
executed (it can be a braced block) and giving the rest as arguments,
catching any exceptions raised.
.PP
Although program arguments are passed to external programs
as lists of strings, at the
.B Sh
module level, an argument list is held as a
.BR "list of ref Listnode" .
A
.B Listnode
holds either a simple string, or a braced block
that has been parsed by the shell. Sometimes it can hold
both; in this case the string and the block both represent
the same thing.
.B Parse
converts from a string to a
.B Cmd
(a braced block). It returns a tuple
.RI ( cmd ,\ error )
where
.I cmd
holds the parsed block,
and
.I error
is non-empty if an error has occurred doing so.
.B Cmd2string
performs the opposite conversion; it returns
a string that when parsed will yield the same command
block it was passed.
The utility functions
.B List2stringlist
and
.B stringlist2list
convert from and to a
.B list of ref Listnode
to or from a
.B list of string
respectively.
.PP
A
.B Context
holds all the state information needed by a currently running
.I sh
process; this adt holds current values of environment variables
and a list of currently loaded modules and builtin commands.
It is specific to the process within which it was created.
If it is desired to run
.I sh
commands in a newly spawned process, a new
.B Context
must be created, or a copy of an existing Context made (making
sure to synchronise access until the copy has been made).
.TP 10
.BI Context.new( drawcontext )
.B New
creates a new context.
.I Drawcontext
represents the current graphics context
within which
.I sh
commands will be run
(see
.IR draw-context (2)).
.TP
.IB ctxt .get(\fPname\fP)
.B Get
retrieves the value of environment variable
.I name
from
.IR ctxt .
It is retrieved from the innermost scope in which
a value for
.I name
has been set.
.TP
.IB ctxt .set(\fPname\fP,\ \fPvalue\fP)
.B Set
sets the value of environment variable
.I name
in
.IR ctxt
to
.IR value .
It is set in the innermost scope in which a value
for
.I name
has been set, or the outermost level if it has
not been set.
.TP
.IB ctxt .setlocal(\fPname\fP,\ \fPvalue\fP)
Similar to
.B set()
except that the value is set in the innermost scope
that has been pushed.
.TP
.IB ctxt .envlist()
.B Envlist
retrieves the list of all the environment variables
currently in scope, and their values.
It returns a list of
.RI ( name ,\ value )
tuples, where
.I name
is the name of the variable and
.I value
is its value.
.TP
.IB ctxt .push()
.B Push
creates a new innermost environment variable scope.
.TP
.IB ctxt .pop()
.B Pop
discards the current innermost scope, losing the
values of all variables that have been defined there.
It is an error to
.B pop
a context that has not been pushed.
Care must be taken to ensure that a
.B push
is always matched by a
.BR pop.
In particular, exceptions should be caught,
the context popped, and the exception re-raised.
.TP
.IB ctxt .copy(\fPcopyenv\fP)
The shell's
.B Context
is associated with a particular process;
.B copy
returns a copy of
.I ctxt
associated with the current process. If
.I copyenv
is non-zero, the whole environment will be copied - this
should be set if the new process is to run asynchronously - i.e.
if there is a chance that there might be two processes accessing the
context in parallel. It is an error to copy a context if not
within a new process.
.TP
.IB ctxt .run(\fPargs\fP,\ \fPlast\fP)
.B Run
executes a
.I sh
command.
.I Last
should be non-zero if this is the last time
that
.B run
will be called, so
.I sh
does not have to spawn a new process in order
to hide file redirection side-effects.
.TP
.IB ctxt .addmodule(\fPname\fP,\ \fPmod\fP)
.B Addmodule
adds the
.B Shellbuiltin
module
.I mod
to its list of builtin modules.
The module will be initialised as described in
``Builtin modules'', below.
.TP
.IB ctxt .addbuiltin(\fPname\fP,\ \fPmod\fP)
.B Addbuiltin
may be called by a module that has previously
been loaded by
.B addmodule
or by the
.B load
.I sh
command to add a new builtin command
to the shell. Any subsequent invocation of
.I name
within
.I ctxt
will result in a call of
.B runbuiltin()
to
.IR mod .
Any attempt to redefine the command
.RB `` builtin ''
will be ignored.
.TP
.IB ctxt .removebuiltin(\fPname\fP,\ \fPmod\fP)
.B Removebuiltin
removes
.I name
from the list of builtin commands in
.IR ctxt .
If
.I name
had not previously been defined by
.IR mod ,
or had subsequently been replaced, then
this function does nothing.
.TP
.IB ctxt .addsbuiltin(\fPname\fP,\ \fPmod\fP)
.B Addsbuiltin
may be called by a module that has previously
been loaded by
.B addmodule
or by the
.B load
.I sh
command to add a new builtin substitution operator
to the shell.
Any subsequent invocation of
.BI ${ name }
within
.I ctxt
will result in a call of
.B runsbuiltin()
to
.IR mod .
.TP
.IB ctxt .removesbuiltin(\fPname\fP,\ \fPmod\fP)
.B Removesbuiltin
removes
.I name
from the list of builtin substitution operators in
.IR ctxt .
If
.I name
had not previously been defined by
.IR mod ,
or had subsequently been replaced, then
this function does nothing.
.TP
.IB ctxt .fail(\fPename\fP,\ \fPmsg\fP)
.B Fail
prints
.I msg
to the standard error if message printing
is currently enabled, and raises
the exception
.BI fail: ename\f1.\fP
.TP
.IB ctxt .options()
.B Options
returns a bitmask of the options currently enabled in
.IR ctxt .
The bits are defined by constants declared within
.BR Context .
They include:
.RS
.TP
.IB ctxt .INTERACTIVE
.I Sh
is currently being run from an interactive command-line.
.TP
.IB ctxt .VERBOSE
Message printing is currently enabled.
.TP
.IB ctxt .EXECPRINT
Commands are printed to standard error
as they are executed.
.TP
.IB ctxt .ERROREXIT
An exception will be raised when the first
simple command returns an error status.
.PP
Options are defined in the innermost scope
of
.I ctxt
and will be lost when it is
.BR pop ped.
.RE
.TP
.IB ctxt .setoptions(\fPflags\fP,\ \fPon\fP)
.B Setoptions
sets the specified
.I flags
within
.IR ctxt .
.I Flags
is a bitmask of options, as described in
.BR options ,
above. If
.I on
is non-zero, the specified bits will be set;
otherwise they will be reset.
.B Setoptions
returns the previously set options bitmask.
.SS Builtin modules
.B Shellbuiltin
specifies the interface to a loadable
.I sh
builtin module. Any Limbo module
.I mod
adhering to this
interface may be loaded into the shell.
.TP 10
.IB mod ->initbuiltin(\fPctxt\fP,\ \fPsh\fP)
.B Initbuiltin
is called when
.I sh
loads
.I mod
either via the
.B load
command, or via the
.B loadmodule()
function.
.I Ctxt
is the context within which the builtin has been
loaded, and
.I sh
is the
.B Sh
module itself. When
.B initbuiltin
is called,
.I mod
is expected to call
.IB ctxt .addbuiltin
and
.IB ctxt .addsbuiltin
to define any builtin commands and builtin substitution
operators that it wants. If an error occurs on
initialisation,
.B initbuiltin
should return a non-nil value; this will cause the load to fail.
.TP
.IB mod ->runbuiltin(\fPctxt\fP,\ \fPsh\fP,\ \fPcmd\fP,\ \fPlast\fP)
.B Runbuiltin
is invoked when
.I sh
executes a command that has previously been
defined as a builtin command by
.IR mod .
.I Ctxt
is the current execution context (which may not be
the original context passed to
.BR initbuiltin() ),
.I sh
is the running
.B Sh
module, and
.I cmd
is the command to be executed.
.I Last
is true if this is the last command to be executed
in the current process; it can be passed to
.IB ctxt .run()
as appropriate.
The name of the command can be found in
.BR "(hd cmd).word" .
.B Runbuiltin
returns its exit status; by convention this
is the exit status of the last command executed.
A non-nil exit status is usually treated as false.
By convention, if an invalid set of arguments are
passed to a builtin command, a
.B usage
exception is raised by calling
.IB ctxt .fail
with
.B "usage"
and an explanatory usage message as arguments.
.TP
.IB mod ->runsbuiltin(\fPctxt\fP,\ \fPsh\fP,\ \fPcmd\fP)
Similar to
.BR runbuiltin ,
.B runsbuiltin
is called when
.I sh
encounters a builtin substitution operator
that has previously been defined by
.IR mod .
It returns the list of values that will be
substituted in place of the operator.
.TP
.IB mod ->getself()
.B Getself
should return the
.B Shellbuiltin
module handle for
.IR mod ,
usually obtained by invoking
.BR "load $self" .
N.B. it is important that the value returned
by
.B getself
is the same as that passed to
.B addbuiltin
or
.BR addsbuiltin .
As the Limbo
.B load
operator returns a different value each time,
the value to be returned by
.B getself()
should be initialised once,
during the call to
.BR initbuiltin() .
.TP 10
.IB mod ->whatis(\fPctxt\fP,\ \fPsh\fP,\ \fPname\fP,\ \fPwtype\fP)
.B Whatis
is called by the shell's
.B whatis
command to query the definition of a name.
.I Wtype
gives the type of name that is being asked about; it can be
.B BUILTIN
(conventional commands),
.BR SBUILTIN
(substitution builtins),
or
.BR OTHER
(any other names that the module defines).
Return
.B nil
to get the usual default behaviour. The
.B std
module, for example, uses this feature to
display the definition of a shell function
correctly.
.SS Exceptions
The exceptions used within
.I sh
are exactly the same as those used within Limbo,
except that all exceptions generated by the
shell are prefixed by the string
.RB `` fail: '',
and any exception caught with the prefix
.B fail:
has its first 5 characters removed before
being made available to the
.I sh
script.
This adheres to the convention defined by
other shells within Inferno that a process
that raises an exception with a
.B fail:
prefix is just returning a non-zero exit status,
and should not be left in a Broken state.
It also means that the number of bytes available
for the exception string is reduced by 5
(to 59). Care must therefore be taken to avoid
generating an exception with a name that is too long;
.I sh
takes the pragmatic approach of truncating any
exception string that is too long.
.SH FILES
.TP 10
.BI /prog/ pid /wait
The file used by the shell to wait for dead child processes.
.SH SOURCE
.B /appl/cmd/sh/sh.y
.SH SEE ALSO
.IR sh (1),
.IR sh-std (1),
.IR sh-expr (1),
.IR sh-tk (1),
.IR sys-exception (2)
|