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