xconsole.c revision eaad2c89
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(_AIX)
161#  define USE_PTS
162#endif
163
164#if !defined (USE_FILE) || defined (linux)
165# include <sys/ioctl.h>
166# ifdef hpux
167#  include <termios.h>
168# endif
169# if defined (SVR4) || defined (USE_PTS)
170#  include <termios.h>
171#  ifndef HAS_OPENPTY
172#  include <sys/stropts.h>		/* for I_PUSH */
173#  endif
174#  ifdef sun
175#   include <sys/strredir.h>
176#  endif
177# endif
178# if defined(TIOCCONS) || defined(SRIOCSREDIR)
179#  define USE_PTY
180static int  tty_fd, pty_fd;
181static char ttydev[64], ptydev[64];
182# endif
183#endif
184
185#if (defined(SVR4) && !defined(sun)) || (defined(SYSV) && defined(i386))
186#define USE_OSM
187#include <signal.h>
188#endif
189
190#ifdef USE_PTY
191static int get_pty(int *pty, int *tty, char *ttydev, char *ptydev);
192#endif
193
194#ifdef USE_OSM
195static FILE *osm_pipe(void);
196static int child_pid;
197#endif
198
199/* Copied from xterm/ptyx.h */
200#ifndef PTYCHAR1
201#ifdef __hpux
202#define PTYCHAR1        "zyxwvutsrqp"
203#else   /* !__hpux */
204#define PTYCHAR1        "pqrstuvwxyzPQRSTUVWXYZ"
205#endif  /* !__hpux */
206#endif  /* !PTYCHAR1 */
207
208#ifndef PTYCHAR2
209#ifdef __hpux
210#define PTYCHAR2        "fedcba9876543210"
211#else   /* !__hpux */
212#ifdef __FreeBSD__
213#define PTYCHAR2        "0123456789abcdefghijklmnopqrstuv"
214#else /* !__FreeBSD__ */
215#define PTYCHAR2        "0123456789abcdef"
216#endif /* !__FreeBSD__ */
217#endif  /* !__hpux */
218#endif  /* !PTYCHAR2 */
219
220static void
221OpenConsole(void)
222{
223    input = 0;
224    if (app_resources.file)
225    {
226	if (!strcmp (app_resources.file, "console"))
227	{
228	    /* must be owner and have read/write permission */
229#if !defined(__NetBSD__) && !defined(__OpenBSD__)
230	    struct stat sbuf;
231# if !defined (linux)
232	    if (!stat("/dev/console", &sbuf) &&
233		(sbuf.st_uid == getuid()) &&
234		!access("/dev/console", R_OK|W_OK))
235# endif
236#endif
237	    {
238#ifdef USE_FILE
239# ifdef linux
240		if (!stat(FILE_NAME, &sbuf))
241# endif
242		    input = fopen (FILE_NAME, "r");
243#endif
244
245#ifdef USE_PTY
246		if (!input && get_pty (&pty_fd, &tty_fd, ttydev, ptydev) == 0)
247		{
248# ifdef TIOCCONS
249		    int on = 1;
250		    if (ioctl (tty_fd, TIOCCONS, (char *) &on) != -1)
251			input = fdopen (pty_fd, "r");
252# else
253		    int consfd = open("/dev/console", O_RDONLY);
254		    if (consfd >= 0)
255		    {
256			if (ioctl(consfd, SRIOCSREDIR, tty_fd) != -1)
257			    input = fdopen (pty_fd, "r");
258			close(consfd);
259		    }
260# endif
261		}
262#endif /* USE_PTY */
263	    }
264#ifdef USE_OSM
265	    /* Don't have to be owner of /dev/console when using /dev/osm. */
266	    if (!input)
267		input = osm_pipe();
268#endif
269	    if (input && app_resources.verbose)
270	    {
271		char	*hostname;
272		TextAppend (text, "Console log for ", 16);
273		hostname = mit_console_name + MIT_CONSOLE_LEN;
274		TextAppend (text, hostname, strlen (hostname));
275		TextAppend (text, "\n", 1);
276	    }
277	}
278	else
279	{
280	    regularFile = FALSE;
281	    if (access(app_resources.file, R_OK) == 0)
282	    {
283		int fd  = open (app_resources.file,
284				O_RDONLY | O_NONBLOCK | O_NOCTTY);
285		if (fd != -1) {
286		    input = fdopen (fd, "r");
287
288		    if (input) {
289			struct stat sbuf;
290
291			if ((fstat(fd, &sbuf) == 0) && S_ISREG(sbuf.st_mode))
292			    regularFile = TRUE;
293		    }
294		    else
295			close(fd);
296		}
297	    }
298	}
299	if (!input)
300	{
301	    if (app_resources.exitOnFail)
302		exit(0);
303	    TextAppend (text, "Couldn't open ", 14);
304	    TextAppend (text, app_resources.file, strlen (app_resources.file));
305	    TextAppend (text, "\n", 1);
306	}
307    }
308    else
309	input = stdin;
310
311    if (input)
312    {
313	input_id = XtAddInput (fileno (input), (XtPointer) XtInputReadMask,
314			       inputReady, (XtPointer) text);
315    }
316}
317
318static void
319CloseConsole (void)
320{
321    if (input)
322    {
323	XtRemoveInput (input_id);
324	fclose (input);
325    }
326#ifdef USE_PTY
327    close (tty_fd);
328#endif
329}
330
331#ifdef USE_OSM
332static void
333KillChild(int sig)
334{
335    if (child_pid > 0)
336	kill(child_pid, SIGTERM);
337    _exit(0);
338}
339#endif
340
341/*ARGSUSED*/
342static void _X_NORETURN
343Quit(Widget widget, XEvent *event, String *params, Cardinal *num_params)
344{
345#ifdef USE_OSM
346    if (child_pid > 0)
347	kill(child_pid, SIGTERM);
348#endif
349    exit (0);
350}
351
352#ifdef USE_OSM
353static int (*ioerror)(Display *);
354
355static int
356IOError(Display *dpy)
357{
358    if (child_pid > 0)
359	kill(child_pid, SIGTERM);
360    return (*ioerror)(dpy);
361}
362#endif
363
364static void
365Notify(void)
366{
367    Arg	    arglist[1];
368    char    *oldName;
369    char    *newName;
370
371    if (!iconified || !app_resources.notify || notified)
372	return;
373    XtSetArg (arglist[0], XtNiconName, &oldName);
374    XtGetValues (top, arglist, 1);
375    newName = malloc (strlen (oldName) + 3);
376    if (!newName)
377	return;
378    sprintf (newName, "%s *", oldName);
379    XtSetArg (arglist[0], XtNiconName, newName);
380    XtSetValues (top, arglist, 1);
381    free (newName);
382    notified = True;
383}
384
385/*ARGSUSED*/
386static void
387Deiconified(Widget widget, XEvent *event, String *params, Cardinal *num_params)
388{
389    Arg	    arglist[1];
390    char    *oldName;
391    char    *newName;
392    size_t  oldlen;
393
394    iconified = False;
395    if (!app_resources.notify || !notified)
396	return;
397    XtSetArg (arglist[0], XtNiconName, &oldName);
398    XtGetValues (top, arglist, 1);
399    oldlen = strlen (oldName);
400    if (oldlen >= 2)
401    {
402	newName = malloc (oldlen - 1);
403	if (!newName)
404	    return;
405	strncpy (newName, oldName, oldlen - 2);
406	newName[oldlen - 2] = '\0';
407	XtSetArg (arglist[0], XtNiconName, newName);
408	XtSetValues (top, arglist, 1);
409	free (newName);
410    }
411    notified = False;
412}
413
414/*ARGSUSED*/
415static void
416Iconified(Widget widget, XEvent *event, String *params, Cardinal *num_params)
417{
418    iconified = True;
419}
420
421/*ARGSUSED*/
422static void
423Clear(Widget widget, XEvent *event, String *params, Cardinal *num_params)
424{
425    long	    last;
426    XawTextBlock    block;
427
428    last = TextLength (text);
429    block.ptr = "";
430    block.firstPos = 0;
431    block.length = 0;
432    block.format = FMT8BIT;
433    TextReplace (text, 0, last, &block);
434}
435
436static XtActionsRec actions[] = {
437    { "Quit",		Quit },
438    { "Iconified",	Iconified },
439    { "Deiconified",	Deiconified },
440    { "Clear",		Clear },
441};
442
443static void
444stripNonprint(char *b)
445{
446    char    *c;
447
448    c = b;
449    while (*b)
450    {
451	if (isprint (*b) || (isspace (*b) && *b != '\r'))
452	{
453	    if (c != b)
454		*c = *b;
455	    ++c;
456	}
457	++b;
458    }
459    *c = '\0';
460}
461
462static void
463inputReady(XtPointer w, int *source, XtInputId *id)
464{
465    char    buffer[1025];
466    int	    n;
467
468    n = read (*source, buffer, sizeof (buffer) - 1);
469    if (n <= 0)
470    {
471	if (app_resources.file && regularFile && n == 0)
472	{
473	    if (XPending(XtDisplay(w)))
474		return;
475
476	    sleep(1);
477	    return;
478	}
479
480	fclose (input);
481	XtRemoveInput (*id);
482
483	/* try to reopen if pipe; this can be caused by syslog restart */
484	if (app_resources.file && !regularFile && n == 0)
485	{
486	    OpenConsole();
487	}
488    } else {
489	Notify();
490	buffer[n] = '\0';
491	if (app_resources.stripNonprint)
492	{
493	    stripNonprint (buffer);
494	    n = strlen (buffer);
495	}
496
497	TextAppend ((Widget) text, buffer, n);
498    }
499}
500
501static Boolean
502ConvertSelection(Widget w, Atom *selection, Atom *target, Atom *type,
503		 XtPointer *value, unsigned long *length, int *format)
504{
505    Display* d = XtDisplay(w);
506    XSelectionRequestEvent* req =
507	XtGetSelectionRequest(w, *selection, (XtRequestId)NULL);
508
509    if (*target == XA_TARGETS(d))
510    {
511	Atom* targetP;
512	Atom* std_targets;
513	unsigned long std_length;
514	XmuConvertStandardSelection(w, req->time, selection, target, type,
515				    (XPointer *)&std_targets, &std_length,
516				    format);
517	*value = (XtPointer)XtMalloc(sizeof(Atom)*(std_length + 5));
518	targetP = *(Atom**)value;
519	*targetP++ = XA_STRING;
520	*targetP++ = XA_TEXT(d);
521	*targetP++ = XA_LENGTH(d);
522	*targetP++ = XA_LIST_LENGTH(d);
523	*targetP++ = XA_CHARACTER_POSITION(d);
524	*length = std_length + (targetP - (*(Atom **) value));
525	memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length);
526	XtFree((char*)std_targets);
527	*type = XA_ATOM;
528	*format = 32;
529	return True;
530    }
531
532    if (*target == XA_LIST_LENGTH(d) ||
533	*target == XA_LENGTH(d))
534    {
535	long * temp;
536
537	temp = (long *) XtMalloc(sizeof(long));
538	if (*target == XA_LIST_LENGTH(d))
539	  *temp = 1L;
540	else			/* *target == XA_LENGTH(d) */
541	  *temp = (long) TextLength (text);
542
543	*value = (XtPointer) temp;
544	*type = XA_INTEGER;
545	*length = 1L;
546	*format = 32;
547	return True;
548    }
549
550    if (*target == XA_CHARACTER_POSITION(d))
551    {
552	long * temp;
553
554	temp = (long *) XtMalloc(2 * sizeof(long));
555	temp[0] = (long) 0;
556	temp[1] = TextLength (text);
557	*value = (XtPointer) temp;
558	*type = XA_SPAN(d);
559	*length = 2L;
560	*format = 32;
561	return True;
562    }
563
564    if (*target == XA_STRING ||
565      *target == XA_TEXT(d) ||
566      *target == XA_COMPOUND_TEXT(d))
567    {
568	if (*target == XA_COMPOUND_TEXT(d))
569	    *type = *target;
570	else
571	    *type = XA_STRING;
572	*length = TextLength (text);
573	*value = (XtPointer)_XawTextGetSTRING((TextWidget) text, 0, *length);
574	*format = 8;
575	/*
576	 * Drop our connection to the file; the new console program
577	 * will open as soon as it receives the selection contents; there
578	 * is a small window where console output will not be redirected,
579	 * but I see no way of avoiding that without having two programs
580	 * attempt to redirect console output at the same time, which seems
581	 * worse
582	 */
583	CloseConsole ();
584	return True;
585    }
586
587    if (XmuConvertStandardSelection(w, req->time, selection, target, type,
588				    (XPointer *)value, length, format))
589	return True;
590
591    return False;
592}
593
594static void _X_NORETURN
595LoseSelection(Widget w, Atom *selection)
596{
597    Quit (w, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL);
598}
599
600/*ARGSUSED*/
601static void
602InsertSelection(Widget w, XtPointer client_data, Atom *selection, Atom *type,
603		XtPointer value, unsigned long *length, int *format)
604{
605    if (*type != XT_CONVERT_FAIL)
606	TextInsert (text, (char *) value, *length);
607    XtOwnSelection(top, mit_console, CurrentTime,
608		   ConvertSelection, LoseSelection, NULL);
609    OpenConsole ();
610}
611
612int
613main(int argc, char *argv[])
614{
615    Arg arglist[10];
616    Cardinal num_args;
617
618    XtSetLanguageProc(NULL,NULL,NULL);
619    top = XtInitialize ("xconsole", "XConsole", options, XtNumber (options),
620			&argc, argv);
621    XtGetApplicationResources (top, (XtPointer)&app_resources, resources,
622			       XtNumber (resources), NULL, 0);
623
624    if (app_resources.daemon)
625	if (fork ()) exit (0);
626    XtAddActions (actions, XtNumber (actions));
627
628    text = XtCreateManagedWidget ("text", asciiTextWidgetClass,
629				  top, NULL, 0);
630
631    XtRealizeWidget (top);
632    num_args = 0;
633    XtSetArg(arglist[num_args], XtNiconic, &iconified); num_args++;
634    XtGetValues(top, arglist, num_args);
635    if (iconified)
636	Iconified((Widget)NULL, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL);
637    else
638	Deiconified((Widget)NULL,(XEvent*)NULL,(String*)NULL,(Cardinal*)NULL);
639    wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW",
640				   False);
641    (void) XSetWMProtocols (XtDisplay(top), XtWindow(top),
642                            &wm_delete_window, 1);
643
644    XmuGetHostname (mit_console_name + MIT_CONSOLE_LEN, 255);
645
646    mit_console = XInternAtom(XtDisplay(top), mit_console_name, False);
647
648    if (XGetSelectionOwner (XtDisplay (top), mit_console))
649    {
650	XtGetSelectionValue(top, mit_console, XA_STRING, InsertSelection,
651			    NULL, CurrentTime);
652    }
653    else
654    {
655	XtOwnSelection(top, mit_console, CurrentTime,
656		       ConvertSelection, LoseSelection, NULL);
657	OpenConsole ();
658    }
659#ifdef USE_OSM
660    ioerror = XSetIOErrorHandler(IOError);
661#endif
662    XtMainLoop ();
663    return 0;
664}
665
666static long
667TextLength(Widget w)
668{
669    return XawTextSourceScan (XawTextGetSource (w),
670			      (XawTextPosition) 0,
671			      XawstAll, XawsdRight, 1, TRUE);
672}
673
674static void
675TextReplace(Widget w, int start, int end, XawTextBlock *block)
676{
677    Arg		    arg;
678    Widget	    source;
679    XawTextEditType edit_mode;
680
681    source = XawTextGetSource (w);
682    XtSetArg (arg, XtNeditType, &edit_mode);
683    XtGetValues (source, &arg, ONE);
684    XtSetArg (arg, XtNeditType, XawtextEdit);
685    XtSetValues (source, &arg, ONE);
686    XawTextReplace (w, start, end, block);
687    XtSetArg (arg, XtNeditType, edit_mode);
688    XtSetValues (source, &arg, ONE);
689}
690
691static void
692TextAppend(Widget w, char *s, int len)
693{
694    long	    last, current;
695    XawTextBlock    block;
696
697    current = XawTextGetInsertionPoint (w);
698    last = TextLength (w);
699    block.ptr = s;
700    block.firstPos = 0;
701    block.length = len;
702    block.format = FMT8BIT;
703    /*
704     * If saveLines is 1, just replace the entire contents of the widget
705     * each time, so the test in ExceededMaxLines() isn't fooled.
706     */
707    if (app_resources.saveLines == 1)
708	TextReplace (w, 0, last, &block);
709    else
710	TextReplace (w, last, last, &block);
711    if (current == last)
712	XawTextSetInsertionPoint (w, last + block.length);
713    if (ExceededMaxLines(w))
714	ScrollLine(w);
715}
716
717static void
718TextInsert(Widget w, char *s, int len)
719{
720    XawTextBlock    block;
721    long	    current;
722
723    current = XawTextGetInsertionPoint (w);
724    block.ptr = s;
725    block.firstPos = 0;
726    block.length = len;
727    block.format = FMT8BIT;
728    TextReplace (w, 0, 0, &block);
729    if (current == 0)
730	XawTextSetInsertionPoint (w, len);
731    if (ExceededMaxLines(w))
732	ScrollLine(w);
733}
734
735static Bool
736ExceededMaxLines(Widget w)
737{
738    XawTextPosition end_of_last_line;
739    Bool retval = False;
740
741    if (app_resources.saveLines > 0)
742    {
743    /*
744     * XawTextSourceScan() will return the end of the widget if it cannot
745     * find what it is searching for.
746     */
747	end_of_last_line = XawTextSourceScan (XawTextGetSource (w),
748					      (XawTextPosition) 0,
749					      XawstEOL, XawsdRight,
750					      app_resources.saveLines, TRUE);
751	if (TextLength(w) > end_of_last_line)
752	    retval = True;
753	else
754	    retval = False;
755    }
756    else
757	retval = False;
758    return retval;
759}
760
761static void
762ScrollLine(Widget w)
763{
764    XawTextPosition firstnewline;
765    XawTextBlock    block;
766
767    /*
768     * This is pretty inefficient but should work well enough unless the
769     * console device is getting totally spammed.  Generally, new lines
770     * only come in one at a time anyway.
771     */
772    firstnewline = XawTextSourceScan (XawTextGetSource (w),
773				      (XawTextPosition) 0,
774				      XawstEOL, XawsdRight, 1, TRUE);
775    block.ptr = "";
776    block.firstPos = 0;
777    block.length = 0;
778    block.format = FMT8BIT;
779    TextReplace (w, 0, firstnewline, &block);
780}
781
782#ifdef USE_PTY
783/*
784 * This function opens up a pty master and stuffs its value into pty.
785 * If it finds one, it returns a value of 0.  If it does not find one,
786 * it returns a value of !0.  This routine is designed to be re-entrant,
787 * so that if a pty master is found and later, we find that the slave
788 * has problems, we can re-enter this function and get another one.
789 */
790
791static int
792get_pty(int *pty, int *tty, char *ttydev, char *ptydev)
793{
794#ifdef HAS_OPENPTY
795	if (openpty(pty, tty, NULL, NULL, NULL) == -1) {
796		return 1;
797	}
798	return 0;
799#elif defined (SVR4) || defined (USE_PTS)
800#if defined (_AIX)
801	if ((*pty = open ("/dev/ptc", O_RDWR)) < 0)
802#else
803	if ((*pty = open ("/dev/ptmx", O_RDWR)) < 0)
804#endif
805	    return 1;
806	grantpt(*pty);
807	unlockpt(*pty);
808	strcpy(ttydev, (char *)ptsname(*pty));
809	if ((*tty = open(ttydev, O_RDWR)) >= 0)
810	{
811	    (void)ioctl(*tty, I_PUSH, "ttcompat");
812	    return 0;
813	}
814	if (*pty >= 0)
815	    close (*pty);
816#else /* !SVR4, need lots of code */
817#ifdef USE_GET_PSEUDOTTY
818	if ((*pty = getpseudotty (&ttydev, &ptydev)) >= 0 &&
819	    (*tty = open (ttydev, O_RDWR)) >= 0)
820	    return 0;
821	if (*pty >= 0)
822	    close (*pty);
823#else
824	static int devindex, letter = 0;
825
826#ifdef sgi
827	{
828	    char *slave;
829	    slave = _getpty (pty, O_RDWR, 0622, 0);
830	    if ((*tty = open (slave, O_RDWR)) != -1)
831		return 0;
832	}
833#else
834	strcpy (ttydev, "/dev/ttyxx");
835	strcpy (ptydev, "/dev/ptyxx");
836	while (PTYCHAR1[letter]) {
837	    ttydev [strlen(ttydev) - 2]  = ptydev [strlen(ptydev) - 2] =
838		    PTYCHAR1 [letter];
839
840	    while (PTYCHAR2[devindex]) {
841		ttydev [strlen(ttydev) - 1] = ptydev [strlen(ptydev) - 1] =
842			PTYCHAR2 [devindex];
843		if ((*pty = open (ptydev, O_RDWR)) >= 0 &&
844		    (*tty = open (ttydev, O_RDWR)) >= 0)
845		{
846			/*
847			 * We need to set things up for our next entry
848			 * into this function!
849			 */
850			(void) devindex++;
851			return(0);
852		}
853		if (*pty >= 0)
854		    close (*pty);
855		devindex++;
856	    }
857	    devindex = 0;
858	    (void) letter++;
859	}
860#endif /* sgi else not sgi */
861#endif /* USE_GET_PSEUDOTTY */
862#endif /* SVR4 */
863	/*
864	 * We were unable to allocate a pty master!  Return an error
865	 * condition and let our caller terminate cleanly.
866	 */
867	return(1);
868}
869#endif
870
871#ifdef USE_OSM
872/*
873 * On SYSV386 there is a special device, /dev/osm, where system messages
874 * are sent.  Problem is that we can't perform a select(2) on this device.
875 * So this routine creates a streams-pty where one end reads the device and
876 * sends the output to xconsole.
877 */
878
879#ifdef SCO325
880#define	OSM_DEVICE	"/dev/error"
881#else
882#ifdef __UNIXWARE__
883#define OSM_DEVICE	"/dev/osm2"
884#define NO_READAHEAD
885#else
886#define	OSM_DEVICE	"/dev/osm"
887#endif
888#endif
889
890static FILE *
891osm_pipe(void)
892{
893    int tty;
894    char ttydev[64];
895
896    if (access(OSM_DEVICE, R_OK) < 0)
897	return NULL;
898#if defined (_AIX)
899    if ((tty = open("/dev/ptc", O_RDWR)) < 0)
900#else
901    if ((tty = open("/dev/ptmx", O_RDWR)) < 0)
902#endif
903	return NULL;
904
905    grantpt(tty);
906    unlockpt(tty);
907    strcpy(ttydev, (char *)ptsname(tty));
908
909    if ((child_pid = fork()) == 0)
910    {
911	int pty, osm, nbytes, skip;
912	char cbuf[128];
913
914	skip = 0;
915#ifndef NO_READAHEAD
916	osm = open(OSM_DEVICE, O_RDONLY);
917	if (osm >= 0)
918	{
919	    while ((nbytes = read(osm, cbuf, sizeof(cbuf))) > 0)
920		skip += nbytes;
921	    close(osm);
922	}
923#endif
924	pty = open(ttydev, O_RDWR);
925	if (pty < 0)
926	    exit(1);
927	osm = open(OSM_DEVICE, O_RDONLY);
928	if (osm < 0)
929	    exit(1);
930	for (nbytes = 0; skip > 0 && nbytes >= 0; skip -= nbytes)
931	{
932	    nbytes = skip;
933	    if (nbytes > sizeof(cbuf))
934		nbytes = sizeof(cbuf);
935	    nbytes = read(osm, cbuf, nbytes);
936	}
937	while ((nbytes = read(osm, cbuf, sizeof(cbuf))) >= 0)
938	    write(pty, cbuf, nbytes);
939	exit(0);
940    }
941    signal(SIGHUP, KillChild);
942    signal(SIGINT, KillChild);
943    signal(SIGTERM, KillChild);
944    return fdopen(tty, "r");
945}
946#endif  /* USE_OSM */
947