1/*
2 *
3Copyright 1990, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 *
25 * Author:  Keith Packard, MIT X Consortium
26 */
27
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include <X11/Xfuncproto.h>
34
35#include <X11/Intrinsic.h>
36#include <X11/StringDefs.h>
37#include <X11/Xatom.h>
38
39#include <X11/Xmu/Atoms.h>
40#include <X11/Xmu/StdSel.h>
41#include <X11/Xmu/SysUtil.h>
42
43#include <X11/Xaw/Form.h>
44#include <X11/Xaw/Label.h>
45#include <X11/Xaw/Command.h>
46#include <X11/Xaw/AsciiText.h>
47#include <X11/Xaw/Dialog.h>
48#include <X11/Xaw/Cardinals.h>
49#include <X11/Xaw/Paned.h>
50#include <X11/Xaw/Box.h>
51
52extern char *_XawTextGetSTRING(TextWidget ctx, XawTextPosition left,
53			       XawTextPosition right);
54
55#include <X11/Xos.h>
56#include <X11/Xfuncs.h>
57#include <sys/stat.h>
58#ifndef _POSIX_SOURCE
59#define _POSIX_SOURCE
60#include <stdio.h>
61#undef _POSIX_SOURCE
62#else
63#include <stdio.h>
64#endif
65#include <X11/Shell.h>
66#include <ctype.h>
67#include <stdlib.h>
68#ifdef HAS_OPENPTY
69# ifdef HAVE_UTIL_H
70#  include <util.h>
71# endif
72# ifdef HAVE_LIBUTIL_H
73#  include <libutil.h>
74# endif
75# ifdef HAVE_PTY_H
76#  include <pty.h>
77# endif
78#endif
79
80static void inputReady(XtPointer w, int *source, XtInputId *id);
81static long TextLength(Widget w);
82static void TextReplace(Widget w, int start, int end, XawTextBlock *block);
83static void TextAppend(Widget w, char *s, int len);
84static void TextInsert(Widget w, char *s, int len);
85static Bool ExceededMaxLines(Widget w);
86static void ScrollLine(Widget w);
87
88static Widget		top, text;
89
90static XtInputId	input_id;
91
92static FILE		*input;
93static Boolean		regularFile = FALSE;
94
95static Boolean		notified;
96static Boolean		iconified;
97
98static Atom		wm_delete_window;
99static Atom		mit_console;
100
101#define MIT_CONSOLE_LEN 12
102#define MIT_CONSOLE "MIT_CONSOLE_"
103static char		mit_console_name[255 + MIT_CONSOLE_LEN + 1] = MIT_CONSOLE;
104
105static struct _app_resources {
106    char    *file;
107    Boolean stripNonprint;
108    Boolean notify;
109    Boolean daemon;
110    Boolean verbose;
111    Boolean exitOnFail;
112    int     saveLines;
113} app_resources;
114
115#define Offset(field) XtOffsetOf(struct _app_resources, field)
116
117static XtResource  resources[] = {
118    {"file",	"File",	    XtRString,	sizeof (char *),
119	Offset (file),	XtRString,  "console" },
120    {"notify",	"Notify",   XtRBoolean,	sizeof (Boolean),
121	Offset (notify), XtRImmediate, (XtPointer)True },
122    {"stripNonprint",	"StripNonprint",    XtRBoolean, sizeof (Boolean),
123	Offset (stripNonprint), XtRImmediate, (XtPointer)True },
124    {"daemon",		"Daemon",	    XtRBoolean,	sizeof (Boolean),
125	Offset (daemon), XtRImmediate, (XtPointer)False},
126    {"verbose",		"Verbose",	    XtRBoolean,	sizeof (Boolean),
127	Offset (verbose),XtRImmediate, (XtPointer)False},
128    {"exitOnFail",	"ExitOnFail",    XtRBoolean,	sizeof (Boolean),
129	Offset (exitOnFail),XtRImmediate, (XtPointer)False},
130    {"saveLines",	"SaveLines",	XtRInt,	sizeof (int),
131	Offset (saveLines), XtRImmediate, (XtPointer) 0 },
132};
133
134#undef Offset
135
136static XrmOptionDescRec options[] = {
137    {"-file",		"*file",		XrmoptionSepArg,	NULL},
138    {"-notify",		"*notify",		XrmoptionNoArg,		"TRUE"},
139    {"-nonotify",	"*notify",		XrmoptionNoArg,		"FALSE"},
140    {"-daemon",		"*daemon",		XrmoptionNoArg,		"TRUE"},
141    {"-verbose",	"*verbose",		XrmoptionNoArg,		"TRUE"},
142    {"-exitOnFail",	"*exitOnFail",		XrmoptionNoArg,		"TRUE"},
143    {"-saveLines",	"*saveLines",		XrmoptionSepArg,	NULL},
144};
145
146
147#ifdef linux
148#define USE_FILE
149#define FILE_NAME	"/dev/xconsole"
150# if defined (__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
151/*
152 * Linux distribution based on glibc 2.1 and higher should use
153 * devpts. This is the fallback if open file FILE_NAME fails.
154 * <werner@suse.de>
155 */
156#  define USE_PTS
157# endif
158#endif
159
160#if !defined (USE_FILE) || defined (linux)
161# include <sys/ioctl.h>
162# if defined (SVR4) || defined (USE_PTS)
163#  include <termios.h>
164#  ifndef HAS_OPENPTY
165#  include <sys/stropts.h>		/* for I_PUSH */
166#  endif
167#  ifdef sun
168#   include <sys/strredir.h>
169#  endif
170# endif
171# if defined(TIOCCONS) || defined(SRIOCSREDIR)
172#  define USE_PTY
173static int  tty_fd, pty_fd;
174# endif
175#endif
176
177#ifdef USE_PTY
178static int get_pty(int *pty, int *tty);
179#endif
180
181/* Copied from xterm/ptyx.h */
182#ifndef PTYCHAR1
183#define PTYCHAR1        "pqrstuvwxyzPQRSTUVWXYZ"
184#endif  /* !PTYCHAR1 */
185
186#ifndef PTYCHAR2
187#ifdef __FreeBSD__
188#define PTYCHAR2        "0123456789abcdefghijklmnopqrstuv"
189#else /* !__FreeBSD__ */
190#define PTYCHAR2        "0123456789abcdef"
191#endif /* !__FreeBSD__ */
192#endif  /* !PTYCHAR2 */
193
194static void
195OpenConsole(void)
196{
197    input = 0;
198    if (app_resources.file)
199    {
200	if (!strcmp (app_resources.file, "console"))
201	{
202	    /* must be owner and have read/write permission */
203#if !defined(__NetBSD__) && !defined(__OpenBSD__)
204	    struct stat sbuf;
205# if !defined (linux)
206	    if (!stat("/dev/console", &sbuf) &&
207		(sbuf.st_uid == getuid()) &&
208		!access("/dev/console", R_OK|W_OK))
209# endif
210#endif
211	    {
212#ifdef USE_FILE
213# ifdef linux
214		if (!stat(FILE_NAME, &sbuf))
215# endif
216		    input = fopen (FILE_NAME, "r");
217#endif
218
219#ifdef USE_PTY
220		if (!input && get_pty (&pty_fd, &tty_fd) == 0)
221		{
222# ifdef TIOCCONS
223		    int on = 1;
224		    if (ioctl (tty_fd, TIOCCONS, (char *) &on) != -1)
225			input = fdopen (pty_fd, "r");
226# else
227		    int consfd = open("/dev/console", O_RDONLY);
228		    if (consfd >= 0)
229		    {
230			if (ioctl(consfd, SRIOCSREDIR, tty_fd) != -1)
231			    input = fdopen (pty_fd, "r");
232			close(consfd);
233		    }
234# endif
235		}
236#endif /* USE_PTY */
237	    }
238	    if (input && app_resources.verbose)
239	    {
240		char	*hostname;
241		TextAppend (text, "Console log for ", 16);
242		hostname = mit_console_name + MIT_CONSOLE_LEN;
243		TextAppend (text, hostname, strlen (hostname));
244		TextAppend (text, "\n", 1);
245	    }
246	}
247	else
248	{
249	    regularFile = FALSE;
250	    if (access(app_resources.file, R_OK) == 0)
251	    {
252		int fd  = open (app_resources.file,
253				O_RDONLY | O_NONBLOCK | O_NOCTTY);
254		if (fd != -1) {
255		    input = fdopen (fd, "r");
256
257		    if (input) {
258			struct stat sbuf;
259
260			if ((fstat(fd, &sbuf) == 0) && S_ISREG(sbuf.st_mode))
261			    regularFile = TRUE;
262		    }
263		    else
264			close(fd);
265		}
266	    }
267	}
268	if (!input)
269	{
270	    if (app_resources.exitOnFail)
271		exit(0);
272	    TextAppend (text, "Couldn't open ", 14);
273	    TextAppend (text, app_resources.file, strlen (app_resources.file));
274	    TextAppend (text, "\n", 1);
275	}
276    }
277    else
278	input = stdin;
279
280    if (input)
281    {
282	input_id = XtAddInput (fileno (input), (XtPointer) XtInputReadMask,
283			       inputReady, (XtPointer) text);
284    }
285}
286
287static void
288CloseConsole (void)
289{
290    if (input)
291    {
292	XtRemoveInput (input_id);
293	fclose (input);
294    }
295#ifdef USE_PTY
296    close (tty_fd);
297#endif
298}
299
300/*ARGSUSED*/
301static void _X_NORETURN
302Quit(Widget widget, XEvent *event, String *params, Cardinal *num_params)
303{
304    exit (0);
305}
306
307static void
308Notify(void)
309{
310    Arg	    arglist[1];
311    char    *oldName;
312    char    *newName;
313
314    if (!iconified || !app_resources.notify || notified)
315	return;
316    XtSetArg (arglist[0], XtNiconName, &oldName);
317    XtGetValues (top, arglist, 1);
318    newName = malloc (strlen (oldName) + 3);
319    if (!newName)
320	return;
321    sprintf (newName, "%s *", oldName);
322    XtSetArg (arglist[0], XtNiconName, newName);
323    XtSetValues (top, arglist, 1);
324    free (newName);
325    notified = True;
326}
327
328/*ARGSUSED*/
329static void
330Deiconified(Widget widget, XEvent *event, String *params, Cardinal *num_params)
331{
332    Arg	    arglist[1];
333    char    *oldName;
334    char    *newName;
335    size_t  oldlen;
336
337    iconified = False;
338    if (!app_resources.notify || !notified)
339	return;
340    XtSetArg (arglist[0], XtNiconName, &oldName);
341    XtGetValues (top, arglist, 1);
342    oldlen = strlen (oldName);
343    if (oldlen >= 2)
344    {
345	newName = malloc (oldlen - 1);
346	if (!newName)
347	    return;
348	strncpy (newName, oldName, oldlen - 2);
349	newName[oldlen - 2] = '\0';
350	XtSetArg (arglist[0], XtNiconName, newName);
351	XtSetValues (top, arglist, 1);
352	free (newName);
353    }
354    notified = False;
355}
356
357/*ARGSUSED*/
358static void
359Iconified(Widget widget, XEvent *event, String *params, Cardinal *num_params)
360{
361    iconified = True;
362}
363
364/*ARGSUSED*/
365static void
366Clear(Widget widget, XEvent *event, String *params, Cardinal *num_params)
367{
368    long	    last;
369    XawTextBlock    block;
370
371    last = TextLength (text);
372    block.ptr = "";
373    block.firstPos = 0;
374    block.length = 0;
375    block.format = FMT8BIT;
376    TextReplace (text, 0, last, &block);
377}
378
379static XtActionsRec actions[] = {
380    { "Quit",		Quit },
381    { "Iconified",	Iconified },
382    { "Deiconified",	Deiconified },
383    { "Clear",		Clear },
384};
385
386static void
387stripNonprint(char *b)
388{
389    char    *c;
390
391    c = b;
392    while (*b)
393    {
394	if (isprint (*b) || (isspace (*b) && *b != '\r'))
395	{
396	    if (c != b)
397		*c = *b;
398	    ++c;
399	}
400	++b;
401    }
402    *c = '\0';
403}
404
405static void
406inputReady(XtPointer w, int *source, XtInputId *id)
407{
408    char    buffer[1025];
409    int	    n;
410
411    n = read (*source, buffer, sizeof (buffer) - 1);
412    if (n <= 0)
413    {
414	if (app_resources.file && regularFile && n == 0)
415	{
416	    if (XPending(XtDisplay(w)))
417		return;
418
419	    sleep(1);
420	    return;
421	}
422
423	fclose (input);
424	XtRemoveInput (*id);
425
426	/* try to reopen if pipe; this can be caused by syslog restart */
427	if (app_resources.file && !regularFile && n == 0)
428	{
429	    OpenConsole();
430	}
431    } else {
432	Notify();
433	buffer[n] = '\0';
434	if (app_resources.stripNonprint)
435	{
436	    stripNonprint (buffer);
437	    n = strlen (buffer);
438	}
439
440	TextAppend ((Widget) text, buffer, n);
441    }
442}
443
444static Boolean
445ConvertSelection(Widget w, Atom *selection, Atom *target, Atom *type,
446		 XtPointer *value, unsigned long *length, int *format)
447{
448    Display* d = XtDisplay(w);
449    XSelectionRequestEvent* req =
450	XtGetSelectionRequest(w, *selection, (XtRequestId)NULL);
451
452    if (*target == XA_TARGETS(d))
453    {
454	Atom* targetP;
455	Atom* std_targets;
456	unsigned long std_length;
457	XmuConvertStandardSelection(w, req->time, selection, target, type,
458				    (XPointer *)&std_targets, &std_length,
459				    format);
460	*value = (XtPointer)XtMalloc(sizeof(Atom)*(std_length + 5));
461	targetP = *(Atom**)value;
462	*targetP++ = XA_STRING;
463	*targetP++ = XA_TEXT(d);
464	*targetP++ = XA_LENGTH(d);
465	*targetP++ = XA_LIST_LENGTH(d);
466	*targetP++ = XA_CHARACTER_POSITION(d);
467	*length = std_length + (targetP - (*(Atom **) value));
468	memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length);
469	XtFree((char*)std_targets);
470	*type = XA_ATOM;
471	*format = 32;
472	return True;
473    }
474
475    if (*target == XA_LIST_LENGTH(d) ||
476	*target == XA_LENGTH(d))
477    {
478	long * temp;
479
480	temp = (long *) XtMalloc(sizeof(long));
481	if (*target == XA_LIST_LENGTH(d))
482	  *temp = 1L;
483	else			/* *target == XA_LENGTH(d) */
484	  *temp = (long) TextLength (text);
485
486	*value = (XtPointer) temp;
487	*type = XA_INTEGER;
488	*length = 1L;
489	*format = 32;
490	return True;
491    }
492
493    if (*target == XA_CHARACTER_POSITION(d))
494    {
495	long * temp;
496
497	temp = (long *) XtMalloc(2 * sizeof(long));
498	temp[0] = (long) 0;
499	temp[1] = TextLength (text);
500	*value = (XtPointer) temp;
501	*type = XA_SPAN(d);
502	*length = 2L;
503	*format = 32;
504	return True;
505    }
506
507    if (*target == XA_STRING ||
508      *target == XA_TEXT(d) ||
509      *target == XA_COMPOUND_TEXT(d))
510    {
511	if (*target == XA_COMPOUND_TEXT(d))
512	    *type = *target;
513	else
514	    *type = XA_STRING;
515	*length = TextLength (text);
516	*value = (XtPointer)_XawTextGetSTRING((TextWidget) text, 0, *length);
517	*format = 8;
518	/*
519	 * Drop our connection to the file; the new console program
520	 * will open as soon as it receives the selection contents; there
521	 * is a small window where console output will not be redirected,
522	 * but I see no way of avoiding that without having two programs
523	 * attempt to redirect console output at the same time, which seems
524	 * worse
525	 */
526	CloseConsole ();
527	return True;
528    }
529
530    if (XmuConvertStandardSelection(w, req->time, selection, target, type,
531				    (XPointer *)value, length, format))
532	return True;
533
534    return False;
535}
536
537static void _X_NORETURN
538LoseSelection(Widget w, Atom *selection)
539{
540    Quit (w, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL);
541}
542
543/*ARGSUSED*/
544static void
545InsertSelection(Widget w, XtPointer client_data, Atom *selection, Atom *type,
546		XtPointer value, unsigned long *length, int *format)
547{
548    if (*type != XT_CONVERT_FAIL)
549	TextInsert (text, (char *) value, *length);
550    XtOwnSelection(top, mit_console, CurrentTime,
551		   ConvertSelection, LoseSelection, NULL);
552    OpenConsole ();
553}
554
555static void _X_NORETURN
556usage(int exitval)
557{
558    const char *usage_message =
559    "usage: xconsole [-toolkitoption  ...] [-file file-name] [-notify|-nonotify]\n"
560    "                [-stripNonprint] [-daemon] [-verbose] [-exitOnFail]\n"
561    "                [-saveLines count]\n"
562    "       xconsole -help\n"
563    "       xconsole -version\n";
564
565    fputs(usage_message, stderr);
566    exit(exitval);
567}
568
569int
570main(int argc, char *argv[])
571{
572    Arg arglist[10];
573    Cardinal num_args;
574
575    XtSetLanguageProc(NULL,NULL,NULL);
576
577    /* Handle args that don't require opening a display */
578    for (int n = 1; n < argc; n++) {
579	const char *argn = argv[n];
580	/* accept single or double dash for -help & -version */
581	if (argn[0] == '-' && argn[1] == '-') {
582	    argn++;
583	}
584	if (strcmp(argn, "-help") == 0) {
585	    usage(0);
586	}
587	if (strcmp(argn, "-version") == 0) {
588	    puts(PACKAGE_STRING);
589	    exit(0);
590	}
591    }
592
593    top = XtInitialize ("xconsole", "XConsole", options, XtNumber (options),
594			&argc, argv);
595    if (argc != 1) {
596        fputs("Unknown argument(s):", stderr);
597        for (int n = 1; n < argc; n++) {
598            fprintf(stderr, " %s", argv[n]);
599        }
600        fputs("\n\n", stderr);
601        usage(1);
602    }
603
604    XtGetApplicationResources (top, (XtPointer)&app_resources, resources,
605			       XtNumber (resources), NULL, 0);
606
607    if (app_resources.daemon)
608	if (fork ()) exit (0);
609    XtAddActions (actions, XtNumber (actions));
610
611    text = XtCreateManagedWidget ("text", asciiTextWidgetClass,
612				  top, NULL, 0);
613
614    XtRealizeWidget (top);
615    num_args = 0;
616    XtSetArg(arglist[num_args], XtNiconic, &iconified); num_args++;
617    XtGetValues(top, arglist, num_args);
618    if (iconified)
619	Iconified((Widget)NULL, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL);
620    else
621	Deiconified((Widget)NULL,(XEvent*)NULL,(String*)NULL,(Cardinal*)NULL);
622    wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW",
623				   False);
624    (void) XSetWMProtocols (XtDisplay(top), XtWindow(top),
625                            &wm_delete_window, 1);
626
627    XmuGetHostname (mit_console_name + MIT_CONSOLE_LEN, 255);
628
629    mit_console = XInternAtom(XtDisplay(top), mit_console_name, False);
630
631    if (XGetSelectionOwner (XtDisplay (top), mit_console))
632    {
633	XtGetSelectionValue(top, mit_console, XA_STRING, InsertSelection,
634			    NULL, CurrentTime);
635    }
636    else
637    {
638	XtOwnSelection(top, mit_console, CurrentTime,
639		       ConvertSelection, LoseSelection, NULL);
640	OpenConsole ();
641    }
642    XtMainLoop ();
643    return 0;
644}
645
646static long
647TextLength(Widget w)
648{
649    return XawTextSourceScan (XawTextGetSource (w),
650			      (XawTextPosition) 0,
651			      XawstAll, XawsdRight, 1, TRUE);
652}
653
654static void
655TextReplace(Widget w, int start, int end, XawTextBlock *block)
656{
657    Arg		    arg;
658    Widget	    source;
659    XawTextEditType edit_mode;
660
661    source = XawTextGetSource (w);
662    XtSetArg (arg, XtNeditType, &edit_mode);
663    XtGetValues (source, &arg, ONE);
664    XtSetArg (arg, XtNeditType, XawtextEdit);
665    XtSetValues (source, &arg, ONE);
666    XawTextReplace (w, start, end, block);
667    XtSetArg (arg, XtNeditType, edit_mode);
668    XtSetValues (source, &arg, ONE);
669}
670
671static void
672TextAppend(Widget w, char *s, int len)
673{
674    long	    last, current;
675    XawTextBlock    block;
676
677    current = XawTextGetInsertionPoint (w);
678    last = TextLength (w);
679    block.ptr = s;
680    block.firstPos = 0;
681    block.length = len;
682    block.format = FMT8BIT;
683    /*
684     * If saveLines is 1, just replace the entire contents of the widget
685     * each time, so the test in ExceededMaxLines() isn't fooled.
686     */
687    if (app_resources.saveLines == 1)
688	TextReplace (w, 0, last, &block);
689    else
690	TextReplace (w, last, last, &block);
691    if (current == last)
692	XawTextSetInsertionPoint (w, last + block.length);
693    if (ExceededMaxLines(w))
694	ScrollLine(w);
695}
696
697static void
698TextInsert(Widget w, char *s, int len)
699{
700    XawTextBlock    block;
701    long	    current;
702
703    current = XawTextGetInsertionPoint (w);
704    block.ptr = s;
705    block.firstPos = 0;
706    block.length = len;
707    block.format = FMT8BIT;
708    TextReplace (w, 0, 0, &block);
709    if (current == 0)
710	XawTextSetInsertionPoint (w, len);
711    if (ExceededMaxLines(w))
712	ScrollLine(w);
713}
714
715static Bool
716ExceededMaxLines(Widget w)
717{
718    XawTextPosition end_of_last_line;
719    Bool retval = False;
720
721    if (app_resources.saveLines > 0)
722    {
723    /*
724     * XawTextSourceScan() will return the end of the widget if it cannot
725     * find what it is searching for.
726     */
727	end_of_last_line = XawTextSourceScan (XawTextGetSource (w),
728					      (XawTextPosition) 0,
729					      XawstEOL, XawsdRight,
730					      app_resources.saveLines, TRUE);
731	if (TextLength(w) > end_of_last_line)
732	    retval = True;
733	else
734	    retval = False;
735    }
736    else
737	retval = False;
738    return retval;
739}
740
741static void
742ScrollLine(Widget w)
743{
744    XawTextPosition firstnewline;
745    XawTextBlock    block;
746
747    /*
748     * This is pretty inefficient but should work well enough unless the
749     * console device is getting totally spammed.  Generally, new lines
750     * only come in one at a time anyway.
751     */
752    firstnewline = XawTextSourceScan (XawTextGetSource (w),
753				      (XawTextPosition) 0,
754				      XawstEOL, XawsdRight, 1, TRUE);
755    block.ptr = "";
756    block.firstPos = 0;
757    block.length = 0;
758    block.format = FMT8BIT;
759    TextReplace (w, 0, firstnewline, &block);
760}
761
762#ifdef USE_PTY
763/*
764 * This function opens up a pty master and stuffs its value into pty.
765 * If it finds one, it returns a value of 0.  If it does not find one,
766 * it returns a value of !0.  This routine is designed to be re-entrant,
767 * so that if a pty master is found and later, we find that the slave
768 * has problems, we can re-enter this function and get another one.
769 */
770
771static int
772get_pty(int *pty, int *tty)
773{
774#ifdef HAS_OPENPTY
775	if (openpty(pty, tty, NULL, NULL, NULL) == -1) {
776		return 1;
777	}
778	return 0;
779#else
780	static char ttydev[64], ptydev[64];
781
782#if defined (SVR4) || defined (USE_PTS)
783	if ((*pty = open ("/dev/ptmx", O_RDWR)) < 0)
784	    return 1;
785	grantpt(*pty);
786	unlockpt(*pty);
787	strcpy(ttydev, (char *)ptsname(*pty));
788	if ((*tty = open(ttydev, O_RDWR)) >= 0)
789	{
790	    (void)ioctl(*tty, I_PUSH, "ttcompat");
791	    return 0;
792	}
793	if (*pty >= 0)
794	    close (*pty);
795#else /* !SVR4, need lots of code */
796#ifdef USE_GET_PSEUDOTTY
797	if ((*pty = getpseudotty (&ttydev, &ptydev)) >= 0 &&
798	    (*tty = open (ttydev, O_RDWR)) >= 0)
799	    return 0;
800	if (*pty >= 0)
801	    close (*pty);
802#else
803	static int devindex, letter = 0;
804
805	strcpy (ttydev, "/dev/ttyxx");
806	strcpy (ptydev, "/dev/ptyxx");
807	while (PTYCHAR1[letter]) {
808	    ttydev [strlen(ttydev) - 2]  = ptydev [strlen(ptydev) - 2] =
809		    PTYCHAR1 [letter];
810
811	    while (PTYCHAR2[devindex]) {
812		ttydev [strlen(ttydev) - 1] = ptydev [strlen(ptydev) - 1] =
813			PTYCHAR2 [devindex];
814		if ((*pty = open (ptydev, O_RDWR)) >= 0 &&
815		    (*tty = open (ttydev, O_RDWR)) >= 0)
816		{
817			/*
818			 * We need to set things up for our next entry
819			 * into this function!
820			 */
821			(void) devindex++;
822			return(0);
823		}
824		if (*pty >= 0)
825		    close (*pty);
826		devindex++;
827	    }
828	    devindex = 0;
829	    (void) letter++;
830	}
831#endif /* USE_GET_PSEUDOTTY */
832#endif /* SVR4 */
833	/*
834	 * We were unable to allocate a pty master!  Return an error
835	 * condition and let our caller terminate cleanly.
836	 */
837	return(1);
838#endif /* HAS_OPENPTY */
839}
840#endif
841