Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/builtin/pty.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1992-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* David Korn <[email protected]> *
19
* *
20
***********************************************************************/
21
#pragma prototyped
22
23
static const char usage[] =
24
"[-?\n@(#)pty (AT&T Research) 2012-06-11\n]"
25
USAGE_LICENSE
26
"[+NAME?pty - create pseudo terminal and run command]"
27
"[+DESCRIPTION?\bpty\b creates a pseudo pty and then runs \bcommand\b "
28
"with arguments given by \aarg\a and the standard input, standard "
29
"output, and standard error connected to the pseudo terminal. By "
30
"default, the \bpty\b creates a new session.]"
31
"[+?If \bcommand\b does not contain a \b/\b, the \bPATH\b variable will "
32
"be used to locate the \bcommand\b.]"
33
"[+?Input to \bpty\b will be written to the standard input of this "
34
"command. The standard output and standard error from the command will "
35
"be written to the standard output of \bpty\b.]"
36
"[+?The \bpty\b commmand terminates when the command completes.]"
37
"[d:dialogue?Execute the dialogue on the standard input. A dialogue is a "
38
"sequence of commands, one command per line. All \are\a patterns are "
39
"extended regular expressions. The \are\a \b?1\b will print the subject "
40
"string on the standard error and match the string; the \are\a \b?0\b "
41
"will print the subject string on the standard error and not match the "
42
"string. The \are\a \b?.\b matches EOF. The commands are:]"
43
"{"
44
"[\b#\b \acomment\a?comment line]"
45
"[c \atext\a?write \atext\a to the master; C style escapes "
46
"in text are converted, including \\E for ESC and \\cX for "
47
"control-X]"
48
"[d \amilliseconds\a?set the delay before each master write to "
49
"\amilliseconds\a; the default is no delay]"
50
"[i \are\a?read a line from the master; if it matches \are\a "
51
"then execute lines until matching \be\b or \bf\b]"
52
"[e [re]]?else [if match re]] then execute lines until matching "
53
"\be\b or \bf\b]"
54
"[f?end of \bi\b/\be\b block]"
55
"[m \atext\a?write \atext\a to the standard error]"
56
"[p \atext\a?peek input until \atext\a is found at the beginning "
57
"of a line; input is not consumed]"
58
"[r [\are\a]]?read a line from the master [and it should match "
59
"re]]]"
60
"[s \amilliseconds\a?sleep for \amilliseconds\a]"
61
"[t \amilliseconds\a?set the master read timout to "
62
"\amilliseconds\a; the default is \b1000\b]"
63
"[u \are\a?read lines from the master until one matches \are\a]"
64
"[v \alevel\a?set the verbose trace \alevel\a, more output for "
65
"higher levels, disabled for level 0]"
66
"[w \atext\a?write \atext\a\\r\\n to the master; C style escapes "
67
"in text are converted, including \\E for ESC and \\cX for "
68
"control-X]"
69
"[x [\acode\a]]"
70
"?exit \bpty\b with exit code \b0\b [\acode\a]]]"
71
"[I \are\a?ignore master lines matching \are\a]"
72
"[L \alabel\a?prefix all diagnostics with \alabel\a:]"
73
"[P \atext\a?delay each master write until the beginning of "
74
"an unread input line exactly matches \atext\a]"
75
"}"
76
"[D:debug?Set the debug trace \alevel\a, higher levels produce more "
77
"output, disabled for level 0.]#[level]"
78
"[l:log?Log the master stdout and stderr to \afile\a.]:[file]"
79
"[m:messages?Redirect diagnostic message output to \afile\a.]:[file]"
80
"[s!:session?Create a separate session for the process started by "
81
"\bpty\b.]"
82
"[t:timeout?Set the master read timeout to "
83
"\amilliseconds\a.]#[milliseconds:=1000]"
84
"[T:tty?Pass \astty\a to the \bstty\b(1) command to initialize the "
85
"pty.]:[stty]"
86
"[w:delay?Set the delay before each master write to "
87
"\amilliseconds\a.]#[milliseconds:=0]"
88
89
"\n"
90
"\ncommand [arg ...]\n"
91
"\n"
92
93
"[+EXIT STATUS?If the command determined by \bcommand\b is run the exit "
94
"status of \bpty\b is that of this command. Otherwise, the exit status "
95
"is one of the following:]"
96
"{"
97
"[+127?The command is found but cannot be executed.]"
98
"[+128?The command could not be found.]"
99
"}"
100
"[+SEE ALSO?\bcommand\b(1), \bexec\b(1)]"
101
;
102
103
#include <cmd.h>
104
#include <error.h>
105
#include <fcntl.h>
106
#include <termios.h>
107
#include <proc.h>
108
#include <ctype.h>
109
#include <regex.h>
110
#include <vmalloc.h>
111
#include <ast_time.h>
112
#include <sys/wait.h>
113
#include <sys/stat.h>
114
#include "FEATURE/pty"
115
116
#define MODE_666 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
117
#define MAXNAME 64
118
119
#if !_lib_openpty && !_lib__getpty && !defined(_pty_clone)
120
# if !_lib_grantpt || !_lib_unlock
121
# if !_lib_ptsname
122
static char *slavename(const char *name)
123
{
124
static char sname[MAXNAME];
125
char *last;
126
strncpy(sname,name,sizeof(sname));
127
last = strrchr(sname,'/');
128
last[1] = 't';
129
return(sname);
130
}
131
# endif
132
133
static char *master_name(char *name)
134
{
135
static char sname[MAXNAME];
136
int n;
137
if(!name)
138
{
139
strcpy(sname,_pty_first);
140
return(sname);
141
}
142
n = strlen(_pty_first);
143
if(name[n-1]=='9')
144
name[n-1]='a';
145
else if(name[n-1]=='f')
146
{
147
if(_pty_first[n-2]=='0' && name[n-2]=='9')
148
{
149
name[n-2]='0';
150
if(name[n-3]=='9' || name[n-3]=='z')
151
return(NULL);
152
name[n-3]++;
153
}
154
if(_pty_first[n-2]=='p' && (name[n-2]=='z' || name[n-2]=='Z'))
155
{
156
if(name[n-2]=='z')
157
name[n-2]=='P';
158
else
159
return(0);
160
}
161
else
162
name[n-2]++;
163
name[n-1]='0';
164
}
165
else
166
name[n-1]++;
167
return(name);
168
}
169
#endif
170
171
#if !_lib_openpty
172
static char *ptymopen(int *master)
173
{
174
char *slave=0;
175
# if _lib__getpty
176
return(_getpty(master,O_RDWR,MODE_666,0));
177
# else
178
# if defined(_pty_clone)
179
*master = open(_pty_clone,O_RDWR|O_CREAT,MODE_666);
180
if(*master>=0)
181
slave = ptsname(*master);
182
# else
183
int fdm;
184
char *name=0;
185
while(name=master_name(name))
186
{
187
fdm = open(name,O_RDWR|O_CREAT,MODE_666);
188
if(fdm >= 0)
189
{
190
*master = fdm;
191
# if _lib_ptsname
192
slave = ptsname(fdm);
193
# else
194
slave = slavename(name);
195
# endif
196
break;
197
}
198
}
199
# endif
200
# endif
201
return(slave);
202
}
203
# endif
204
#endif
205
206
static int
207
mkpty(int* master, int* slave)
208
{
209
struct termios tty;
210
struct termios tst;
211
struct termios* ttyp;
212
#ifdef TIOCGWINSZ
213
struct winsize win;
214
struct winsize* winp;
215
#endif
216
#if !_lib_openpty
217
char* sname;
218
#endif
219
/*
220
* some systems hang hard during the handshake
221
* if you know why then please let us know
222
*/
223
224
alarm(4);
225
if (tcgetattr(STDERR_FILENO, &tty) >= 0)
226
ttyp = &tty;
227
else
228
{
229
ttyp = 0;
230
error(-1, "unable to get standard error terminal attributes");
231
}
232
#ifdef TIOCGWINSZ
233
if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) >= 0)
234
winp = &win;
235
else
236
{
237
winp = 0;
238
error(-1, "unable to get standard error window size");
239
}
240
#endif
241
#if _lib_openpty
242
if (openpty(master, slave, NULL, ttyp, winp) < 0)
243
return -1;
244
#else
245
#if _lib_grantpt && _lib_unlockpt
246
#if !_lib_posix_openpt
247
#ifndef _pty_clone
248
#define _pty_clone "/dev/ptmx"
249
#endif
250
#define posix_openpt(m) open(_pty_clone,m)
251
#endif
252
if ((*master = posix_openpt(O_RDWR)) < 0)
253
return -1;
254
if (grantpt(*master) || unlockpt(*master) || !(sname = ptsname(*master)) || (*slave = open(sname, O_RDWR|O_cloexec)) < 0)
255
{
256
close(*master);
257
return -1;
258
}
259
#else
260
if (!(sname = ptymopen(master)) || (*slave = open(sname, O_RDWR|O_cloexec)) < 0)
261
return -1;
262
#endif
263
#ifdef I_PUSH
264
if (tcgetattr(*slave, &tst) < 0 && (ioctl(*slave, I_PUSH, "ptem") < 0 || ioctl(*slave, I_PUSH, "ldterm") < 0))
265
{
266
close(*slave);
267
close(*master);
268
return -1;
269
}
270
#endif
271
#endif
272
if (ttyp && tcsetattr(*slave, TCSANOW, ttyp) < 0)
273
error(ERROR_warn(0), "unable to set pty terminal attributes");
274
#ifdef TIOCSWINSZ
275
if (winp && ioctl(*slave, TIOCSWINSZ, winp) < 0)
276
error(ERROR_warn(0), "unable to set pty window size");
277
#endif
278
fcntl(*master, F_SETFD, FD_CLOEXEC);
279
#if !O_cloexec
280
fcntl(*slave, F_SETFD, FD_CLOEXEC);
281
#endif
282
alarm(0);
283
return 0;
284
}
285
286
static Proc_t*
287
runcmd(char** argv, int slave, int session)
288
{
289
long ops[4];
290
291
if (session)
292
{
293
ops[0] = PROC_FD_CTTY(slave);
294
ops[1] = 0;
295
}
296
else
297
{
298
ops[0] = PROC_FD_DUP(slave, 0, PROC_FD_CHILD);
299
ops[1] = PROC_FD_DUP(slave, 1, PROC_FD_CHILD);
300
ops[2] = PROC_FD_DUP(slave, 2, PROC_FD_CHILD);
301
ops[3] = 0;
302
}
303
return procopen(argv[0], argv, NiL, ops, 0);
304
}
305
306
/*
307
* default master dance
308
*/
309
310
static int
311
process(Sfio_t* mp, Sfio_t* lp, int delay, int timeout)
312
{
313
int i;
314
int n;
315
int t;
316
ssize_t r;
317
char* s;
318
Sfio_t* ip;
319
Sfio_t* sps[2];
320
321
ip = sfstdin;
322
for (;;)
323
{
324
i = 0;
325
t = timeout;
326
if (mp)
327
sps[i++] = mp;
328
if (ip)
329
{
330
sps[i++] = ip;
331
t = -1;
332
}
333
if (!i)
334
break;
335
if ((n = sfpoll(sps, i, t)) <= 0)
336
{
337
if (n < 0)
338
error(ERROR_SYSTEM|2, "poll failed");
339
if (t < 0)
340
break;
341
}
342
else
343
for (i = 0; i < n; i++)
344
{
345
if (!(sfvalue(sps[i]) & SF_READ))
346
/*skip*/;
347
else if (sps[i] == mp)
348
{
349
if (!(s = (char*)sfreserve(mp, SF_UNBOUND, -1)))
350
{
351
sfclose(mp);
352
mp = 0;
353
}
354
else if ((r = sfvalue(mp)) > 0 && (sfwrite(sfstdout, s, r) != r || sfsync(sfstdout)))
355
{
356
error(ERROR_SYSTEM|2, "output write failed");
357
goto done;
358
}
359
}
360
else
361
{
362
if (!(s = sfgetr(ip, '\n', 1)))
363
ip = 0;
364
else if (sfputr(mp, s, '\r') < 0 || sfsync(mp))
365
{
366
error(ERROR_SYSTEM|2, "write failed");
367
goto done;
368
}
369
}
370
}
371
}
372
done:
373
if (mp)
374
sfclose(mp);
375
return error_info.errors != 0;
376
}
377
378
/*
379
* return 1 is extended re pattern matches text
380
*/
381
382
static int
383
match(char* pattern, char* text, int must)
384
{
385
regex_t* re;
386
int code;
387
char buf[64];
388
389
if (!pattern[0])
390
return 1;
391
if (pattern[0] == '?' && pattern[1] && !pattern[2])
392
{
393
switch (pattern[1])
394
{
395
case '0':
396
case '1':
397
if (text)
398
error(2, "got \"%s\"", fmtesq(text, "\""));
399
else
400
error(2, "got EOF");
401
return pattern[1] == '1';
402
case '.':
403
if (!text)
404
return 1;
405
if (must)
406
error(2, "expected EOF, got \"%s\"", fmtesq(text, "\""));
407
return 0;
408
}
409
}
410
if (!text)
411
{
412
if (must)
413
error(2, "expected \"%s\", got EOF", pattern);
414
return 0;
415
}
416
if (!(re = regcache(pattern, REG_EXTENDED, &code)))
417
{
418
regerror(code, re, buf, sizeof(buf));
419
error(2, "%s: %s", pattern, buf);
420
return 0;
421
}
422
if (regexec(re, text, 0, NiL, 0))
423
{
424
if (must)
425
error(2, "expected \"%s\", got \"%s\"", pattern, fmtesq(text, "\""));
426
return 0;
427
}
428
return 1;
429
}
430
431
typedef struct Master_s
432
{
433
Vmalloc_t* vm; /* allocation region */
434
char* ignore; /* ignore master lines matching this re */
435
char* peek; /* peek buffer pointer */
436
char* cur; /* current line */
437
char* nxt; /* next line */
438
char* end; /* end of lines */
439
char* max; /* end of buf */
440
char* buf; /* current buffer */
441
char* prompt; /* peek prompt */
442
int cursor; /* cursor in buf, 0 if fresh line */
443
int line; /* prompt line number */
444
int restore; /* previous line save char */
445
} Master_t;
446
447
/*
448
* read one line from the master
449
*/
450
451
#define MASTER_EOF (-1)
452
#define MASTER_TIMEOUT (-2)
453
454
static char*
455
masterline(Sfio_t* mp, Sfio_t* lp, char* prompt, int must, int timeout, Master_t* bp)
456
{
457
char* r;
458
char* s;
459
char* t;
460
ssize_t n;
461
ssize_t a;
462
size_t promptlen;
463
ptrdiff_t d;
464
char promptbuf[64];
465
466
if (prompt)
467
promptlen = sfsprintf(promptbuf, sizeof(promptbuf), prompt, ++bp->line);
468
again:
469
if (prompt)
470
{
471
if (bp->cur < bp->end && bp->restore >= 0)
472
*bp->cur = bp->restore;
473
if (strneq(bp->cur, promptbuf, promptlen))
474
r = bp->cur;
475
else
476
r = 0;
477
if (bp->cur < bp->end && bp->restore >= 0)
478
*bp->cur = 0;
479
if (r)
480
{
481
error(-1, "p \"%s\"", fmtnesq(promptbuf, "\"", promptlen));
482
return r;
483
}
484
if (r = bp->nxt)
485
{
486
if (strneq(r, promptbuf, promptlen))
487
{
488
error(-1, "p \"%s\"", fmtnesq(promptbuf, "\"", promptlen));
489
return r;
490
}
491
while (r = memchr(r, '\n', bp->end - r))
492
{
493
if (strneq(r, promptbuf, promptlen))
494
{
495
error(-1, "p \"%s\"", fmtnesq(promptbuf, "\"", promptlen));
496
return r;
497
}
498
r++;
499
}
500
}
501
*bp->cur = 0;
502
}
503
else if (bp->nxt)
504
{
505
if (bp->restore >= 0)
506
*bp->cur = bp->restore;
507
r = bp->cur;
508
bp->restore = *bp->nxt;
509
*bp->nxt = 0;
510
if (bp->nxt >= bp->end)
511
{
512
bp->cur = bp->end = bp->buf;
513
bp->nxt = 0;
514
}
515
else
516
{
517
bp->cur = bp->nxt;
518
if (bp->nxt = memchr(bp->nxt + 1, '\n', bp->end - bp->nxt - 1))
519
bp->nxt++;
520
}
521
goto done;
522
}
523
if ((n = sfpoll(&mp, 1, timeout)) <= 0 || !((int)sfvalue(mp) & SF_READ))
524
{
525
if (n < 0)
526
{
527
if (must)
528
error(ERROR_SYSTEM|2, "poll failed");
529
else
530
error(-1, "r poll failed");
531
}
532
else if (bp->cur < bp->end)
533
{
534
if (bp->restore >= 0)
535
{
536
*bp->cur = bp->restore;
537
bp->restore = -1;
538
}
539
r = bp->cur;
540
*bp->end = 0;
541
bp->nxt = 0;
542
if (prompt && strneq(r, promptbuf, promptlen))
543
{
544
error(-1, "p \"%s\"", fmtnesq(promptbuf, "\"", promptlen));
545
return r;
546
}
547
bp->cur = bp->end = bp->buf;
548
goto done;
549
}
550
else if (must >= 0)
551
error(2, "read timeout");
552
else
553
{
554
errno = 0;
555
error(-1, "r EOF");
556
}
557
return 0;
558
}
559
if (!(s = sfreserve(mp, SF_UNBOUND, -1)))
560
{
561
if (!prompt)
562
{
563
if (bp->cur < bp->end)
564
{
565
if (bp->restore >= 0)
566
{
567
*bp->cur = bp->restore;
568
bp->restore = -1;
569
}
570
r = bp->cur;
571
*bp->end = 0;
572
bp->cur = bp->end = bp->buf;
573
bp->nxt = 0;
574
goto done;
575
}
576
else
577
{
578
errno = 0;
579
error(-1, "r EOF");
580
}
581
}
582
return 0;
583
}
584
n = sfvalue(mp);
585
error(-2, "b \"%s\"", fmtnesq(s, "\"", n));
586
if ((bp->max - bp->end) < n)
587
{
588
a = roundof(bp->max - bp->buf + n, SF_BUFSIZE);
589
r = bp->buf;
590
if (!(bp->buf = vmnewof(bp->vm, bp->buf, char, a, 0)))
591
{
592
error(ERROR_SYSTEM|2, "out of space");
593
return 0;
594
}
595
bp->max = bp->buf + a;
596
if (bp->buf != r)
597
{
598
d = bp->buf - r;
599
bp->cur += d;
600
bp->end += d;
601
}
602
}
603
memcpy(bp->end, s, n);
604
bp->end += n;
605
if ((r = bp->cur) > bp->buf && bp->restore >= 0)
606
*r = bp->restore;
607
if (bp->cur = memchr(bp->cur, '\n', bp->end - bp->cur))
608
{
609
bp->restore = *++bp->cur;
610
*bp->cur = 0;
611
if (bp->cur >= bp->end)
612
{
613
bp->cur = bp->end = bp->buf;
614
bp->nxt = 0;
615
}
616
else if (bp->nxt = memchr(bp->cur + 1, '\n', bp->end - bp->cur - 1))
617
bp->nxt++;
618
if (prompt)
619
goto again;
620
}
621
else
622
{
623
bp->restore = -1;
624
bp->cur = r;
625
bp->nxt = 0;
626
must = 0;
627
goto again;
628
}
629
done:
630
error(-3, "Q \"%s\"", fmtesq(r, "\""));
631
s = r;
632
if (bp->cursor)
633
{
634
r -= bp->cursor;
635
bp->cursor = 0;
636
}
637
for (t = 0, n = 0; *s; s++)
638
if (*s == '\n')
639
{
640
if (t)
641
{
642
*t++ = '\n';
643
*t = 0;
644
t = 0;
645
n = 0;
646
}
647
}
648
else if (*s == '\r' && *(s + 1) != '\n')
649
{
650
if (t = strchr(s + 1, '\r'))
651
n += t - s;
652
else
653
n += strlen(s);
654
t = r;
655
}
656
else if (*s == '\a')
657
{
658
if (!t)
659
t = s;
660
*t = ' ';
661
n++;
662
}
663
else if (*s == '\b')
664
{
665
if (!t)
666
t = s;
667
if (t > r)
668
t--;
669
else
670
n++;
671
}
672
else if (t)
673
*t++ = *s;
674
if (t)
675
error(-3, "R \"%s\"", fmtesq(r, "\""));
676
if (n)
677
*(r + strlen(r) - n) = 0;
678
error(-1, "r \"%s\"", fmtesq(r, "\""));
679
if (lp)
680
sfputr(lp, fmtesq(r, "\""), '\n');
681
if (t)
682
bp->cursor = t - r;
683
if (bp->ignore && match(bp->ignore, r, 0))
684
goto again;
685
return r;
686
}
687
688
/*
689
* execute dialogue script on stdin
690
*/
691
692
#define NESTING 64
693
694
#define ELSE 0x01
695
#define IF 0x02
696
#define KEPT 0x04
697
#define SKIP 0x08
698
699
struct Cond_s;
700
typedef struct Cond_s Cond_t;
701
702
struct Cond_s
703
{
704
Cond_t* next;
705
Cond_t* prev;
706
char* text;
707
int flags;
708
};
709
710
static int
711
dialogue(Sfio_t* mp, Sfio_t* lp, int delay, int timeout)
712
{
713
int op;
714
int line;
715
int n;
716
char* s;
717
char* m;
718
char* e;
719
char* id;
720
Vmalloc_t* vm;
721
Cond_t* cond;
722
Master_t* master;
723
724
int status = 0;
725
726
if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) ||
727
!(cond = vmnewof(vm, 0, Cond_t, 1, 0)) ||
728
!(master = vmnewof(vm, 0, Master_t, 1, 0)) ||
729
!(master->buf = vmnewof(vm, 0, char, 2 * SF_BUFSIZE, 0)))
730
{
731
error(ERROR_SYSTEM|2, "out of space");
732
goto done;
733
}
734
master->vm = vm;
735
master->cur = master->end = master->buf;
736
master->max = master->buf + 2 * SF_BUFSIZE - 1;
737
master->restore = -1;
738
errno = 0;
739
id = error_info.id;
740
error_info.id = 0;
741
line = error_info.line;
742
error_info.line = 0;
743
while (s = sfgetr(sfstdin, '\n', 1))
744
{
745
error_info.line++;
746
while (isspace(*s))
747
s++;
748
if ((op = *s++) && isspace(*s))
749
s++;
750
switch (op)
751
{
752
case 0:
753
case '#':
754
break;
755
case 'c':
756
case 'w':
757
if (cond->flags & SKIP)
758
continue;
759
if (master->prompt && !masterline(mp, lp, master->prompt, 0, timeout, master))
760
goto done;
761
if (delay)
762
usleep((unsigned long)delay * 1000);
763
if (op == 'w')
764
error(-1, "w \"%s\\r\"", s);
765
else
766
error(-1, "w \"%s\"", s);
767
if ((n = stresc(s)) >= 0)
768
s[n] = 0;
769
if (sfputr(mp, s, op == 'w' ? '\n' : -1) < 0 || sfsync(mp))
770
{
771
error(ERROR_SYSTEM|2, "write failed");
772
goto done;
773
}
774
if (delay)
775
usleep((unsigned long)delay * 1000);
776
break;
777
case 'd':
778
delay = (int)strtol(s, &e, 0);
779
if (*e)
780
error(2, "%s: invalid delay -- milliseconds expected", s);
781
break;
782
case 'i':
783
if (!cond->next && !(cond->next = vmnewof(vm, 0, Cond_t, 1, 0)))
784
{
785
error(ERROR_SYSTEM|2, "out of space");
786
goto done;
787
}
788
cond = cond->next;
789
cond->flags = IF;
790
if ((cond->prev->flags & SKIP) && !(cond->text = 0) || !(cond->text = masterline(mp, lp, 0, 0, timeout, master)))
791
cond->flags |= KEPT|SKIP;
792
else if (match(s, cond->text, 0))
793
cond->flags |= KEPT;
794
else
795
cond->flags |= SKIP;
796
break;
797
case 'e':
798
if (!(cond->flags & IF))
799
{
800
error(2, "no matching i for e");
801
goto done;
802
}
803
if (!*s)
804
{
805
if (cond->flags & ELSE)
806
{
807
error(2, "i block already has a default e");
808
goto done;
809
}
810
cond->flags |= ELSE;
811
if (cond->flags & KEPT)
812
cond->flags |= SKIP;
813
else
814
{
815
cond->flags |= KEPT;
816
cond->flags &= ~SKIP;
817
}
818
}
819
else if ((cond->flags & KEPT) || !match(s, cond->text, 0))
820
cond->flags |= SKIP;
821
else
822
cond->flags |= KEPT;
823
break;
824
case 'f':
825
if (!(cond->flags & IF))
826
{
827
error(2, "no matching i for f");
828
goto done;
829
}
830
cond = cond->prev;
831
break;
832
case 'm':
833
if (cond->flags & SKIP)
834
continue;
835
if (sfputr(sfstderr, s, '\n') < 0 || sfsync(sfstderr))
836
{
837
error(ERROR_SYSTEM|2, "standard error write failed");
838
goto done;
839
}
840
break;
841
case 'p':
842
if (cond->flags & SKIP)
843
continue;
844
if (!(m = masterline(mp, lp, s, 1, timeout, master)))
845
goto done;
846
break;
847
case 'r':
848
if (cond->flags & SKIP)
849
continue;
850
if (!(m = masterline(mp, lp, 0, s[0] == '?' && s[1] == '.' ? -1 : 1, timeout, master)))
851
goto done;
852
match(s, m, 1);
853
break;
854
case 's':
855
n = (int)strtol(s, &e, 0);
856
if (*e)
857
error(2, "%s: invalid delay -- milliseconds expected", s);
858
if (n)
859
usleep((unsigned long)n * 1000);
860
break;
861
case 't':
862
timeout = (int)strtol(s, &e, 0);
863
if (*e)
864
error(2, "%s: invalid timeout -- milliseconds expected", s);
865
break;
866
case 'u':
867
if (cond->flags & SKIP)
868
continue;
869
do
870
{
871
if (!(m = masterline(mp, lp, 0, -1, timeout, master)))
872
{
873
match(s, m, 1);
874
goto done;
875
}
876
} while (!match(s, m, 0));
877
break;
878
case 'v':
879
error_info.trace = -(int)strtol(s, &e, 0);
880
if (*e)
881
error(2, "%s: invalid verbose level -- number expected", s);
882
break;
883
case 'x':
884
status = (int)strtol(s, &e, 0);
885
if (*e)
886
error(2, "%s: invalid exit code", s);
887
break;
888
case 'I':
889
if (master->ignore)
890
{
891
vmfree(vm, master->ignore);
892
master->ignore = 0;
893
}
894
if (*s && !(master->ignore = vmstrdup(vm, s)))
895
{
896
error(ERROR_SYSTEM|2, "out of space");
897
goto done;
898
}
899
break;
900
case 'L':
901
if (error_info.id)
902
{
903
vmfree(vm, error_info.id);
904
error_info.id = 0;
905
}
906
if (*s && !(error_info.id = vmstrdup(vm, s)))
907
{
908
error(ERROR_SYSTEM|2, "out of space");
909
goto done;
910
}
911
break;
912
case 'P':
913
if (master->prompt)
914
{
915
vmfree(vm, master->prompt);
916
master->prompt = 0;
917
}
918
if (*s && !(master->prompt = vmstrdup(vm, s)))
919
{
920
error(ERROR_SYSTEM|2, "out of space");
921
goto done;
922
}
923
break;
924
default:
925
if (cond->flags & SKIP)
926
continue;
927
error(2, "'%c': unknown op", op);
928
goto done;
929
}
930
}
931
if (cond->prev)
932
error(2, "missing 1 or more f statements");
933
done:
934
if (mp)
935
sfclose(mp);
936
error_info.id = id;
937
error_info.line = line;
938
if (vm)
939
vmclose(vm);
940
return status ? status : error_info.errors != 0;
941
}
942
943
typedef struct Argv_s
944
{
945
char** argv;
946
char* args;
947
int argc;
948
} Argv_t;
949
950
int
951
b_pty(int argc, char** argv, Shbltin_t* context)
952
{
953
int master;
954
int slave;
955
int fd;
956
int drop;
957
int n;
958
char* s;
959
Proc_t* proc;
960
Sfio_t* mp;
961
Sfio_t* lp;
962
Argv_t* ap;
963
char buf[64];
964
965
int delay = 0;
966
char* log = 0;
967
char* messages = 0;
968
char* stty = 0;
969
int session = 1;
970
int timeout = 1000;
971
int (*fun)(Sfio_t*,Sfio_t*,int,int) = process;
972
973
cmdinit(argc, argv, context, ERROR_CATALOG, 0);
974
for (;;)
975
{
976
switch (optget(argv, usage))
977
{
978
case 'd':
979
if (opt_info.num)
980
fun = dialogue;
981
continue;
982
case 'D':
983
error_info.trace = -(int)opt_info.num;
984
continue;
985
case 'l':
986
log = opt_info.arg;
987
case 'm':
988
messages = opt_info.arg;
989
continue;
990
case 's':
991
session = !!opt_info.num;
992
continue;
993
case 't':
994
timeout = (int)opt_info.num;
995
continue;
996
case 'T':
997
stty = opt_info.arg;
998
continue;
999
case 'w':
1000
delay = (int)opt_info.num;
1001
continue;
1002
case ':':
1003
break;
1004
case '?':
1005
error(ERROR_usage(2), "%s", opt_info.arg);
1006
break;
1007
}
1008
break;
1009
}
1010
argv += opt_info.index;
1011
if (!argv[0])
1012
error(ERROR_exit(1), "command must be specified");
1013
if (mkpty(&master, &slave) < 0)
1014
error(ERROR_system(1), "unable to create pty");
1015
if (!(mp = sfnew(NiL, 0, SF_UNBOUND, master, SF_READ|SF_WRITE)))
1016
error(ERROR_system(1), "cannot open master stream");
1017
if (stty)
1018
{
1019
n = 2;
1020
for (s = stty; *s; s++)
1021
if (isspace(*s))
1022
n++;
1023
if (!(ap = newof(0, Argv_t, 1, (n + 2) * sizeof(char*) + (s - stty + 1))))
1024
error(ERROR_system(1), "out of space");
1025
ap->argc = n + 1;
1026
ap->argv = (char**)(ap + 1);
1027
ap->args = (char*)(ap->argv + n + 2);
1028
strcpy(ap->args, stty);
1029
ap->argv[0] = "stty";
1030
sfsprintf(ap->argv[1] = buf, sizeof(buf), "--fd=%d", slave);
1031
ap->argv[2] = s = ap->args;
1032
for (n = 2; *s; s++)
1033
if (isspace(*s))
1034
{
1035
*s = 0;
1036
ap->argv[++n] = s + 1;
1037
}
1038
ap->argv[n + 1] = 0;
1039
b_stty(ap->argc, ap->argv, 0);
1040
}
1041
if (!log)
1042
lp = 0;
1043
else if (!(lp = sfopen(NiL, log, "w")))
1044
error(ERROR_system(1), "%s: cannot write", log);
1045
if (!(proc = runcmd(argv, slave, session)))
1046
error(ERROR_system(1), "unable run %s", argv[0]);
1047
close(slave);
1048
if (messages)
1049
{
1050
drop = 1;
1051
if (strneq(messages, "/dev/fd/", 8))
1052
fd = atoi(messages + 8);
1053
else if (streq(messages, "/dev/stdout"))
1054
fd = 1;
1055
else if ((fd = open(messages, O_CREAT|O_WRONLY, MODE_666)) >= 0)
1056
drop = 0;
1057
else
1058
error(ERROR_system(1), "%s: cannot redirect messages", messages);
1059
close(2);
1060
dup(fd);
1061
if (drop)
1062
close(fd);
1063
}
1064
slave = (*fun)(mp, lp, delay, timeout);
1065
master = procclose(proc);
1066
if (lp && sfclose(lp))
1067
error(ERROR_system(1), "%s: write error", log);
1068
return slave ? slave : master;
1069
}
1070
1071