xconsole.c revision 38cd97f3
1/*
2 * $Xorg: xconsole.c,v 1.5 2001/02/09 02:05:40 xorgcvs Exp $
3 *
4Copyright 1990, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25 *
26 * Author:  Keith Packard, MIT X Consortium
27 */
28
29/* $XFree86: xc/programs/xconsole/xconsole.c,v 3.31tsi Exp $ */
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
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 HAS_UTIL_H
70#  include <util.h>
71# endif
72# ifdef HAS_PTY_H
73#  include <pty.h>
74# endif
75#endif
76
77/* Fix ISC brain damage.  When using gcc fdopen isn't declared in <stdio.h>. */
78#if defined(ISC) && __STDC__ && !defined(ISC30)
79extern FILE *fdopen(int, char const *);
80#endif
81
82static void inputReady(XtPointer w, int *source, XtInputId *id);
83static long TextLength(Widget w);
84static void TextReplace(Widget w, int start, int end, XawTextBlock *block);
85static void TextAppend(Widget w, char *s, int len);
86static void TextInsert(Widget w, char *s, int len);
87static Bool ExceededMaxLines(Widget w);
88static void ScrollLine(Widget w);
89
90static Widget		top, text;
91
92static XtInputId	input_id;
93
94static FILE		*input;
95static Boolean		regularFile = FALSE;
96
97static Boolean		notified;
98static Boolean		iconified;
99
100static Atom		wm_delete_window;
101static Atom		mit_console;
102
103#define MIT_CONSOLE_LEN 12
104#define MIT_CONSOLE "MIT_CONSOLE_"
105static char		mit_console_name[255 + MIT_CONSOLE_LEN + 1] = MIT_CONSOLE;
106
107static struct _app_resources {
108    char    *file;
109    Boolean stripNonprint;
110    Boolean notify;
111    Boolean daemon;
112    Boolean verbose;
113    Boolean exitOnFail;
114    int     saveLines;
115} app_resources;
116
117#define Offset(field) XtOffsetOf(struct _app_resources, field)
118
119static XtResource  resources[] = {
120    {"file",	"File",	    XtRString,	sizeof (char *),
121	Offset (file),	XtRString,  "console" },
122    {"notify",	"Notify",   XtRBoolean,	sizeof (Boolean),
123	Offset (notify), XtRImmediate, (XtPointer)True },
124    {"stripNonprint",	"StripNonprint",    XtRBoolean, sizeof (Boolean),
125	Offset (stripNonprint), XtRImmediate, (XtPointer)True },
126    {"daemon",		"Daemon",	    XtRBoolean,	sizeof (Boolean),
127	Offset (daemon), XtRImmediate, (XtPointer)False},
128    {"verbose",		"Verbose",	    XtRBoolean,	sizeof (Boolean),
129	Offset (verbose),XtRImmediate, (XtPointer)False},
130    {"exitOnFail",	"ExitOnFail",    XtRBoolean,	sizeof (Boolean),
131	Offset (exitOnFail),XtRImmediate, (XtPointer)False},
132    {"saveLines",	"SaveLines",	XtRInt,	sizeof (int),
133	Offset (saveLines), XtRImmediate, (XtPointer) 0 },
134};
135
136#undef Offset
137
138static XrmOptionDescRec options[] = {
139    {"-file",		"*file",		XrmoptionSepArg,	NULL},
140    {"-notify",		"*notify",		XrmoptionNoArg,		"TRUE"},
141    {"-nonotify",	"*notify",		XrmoptionNoArg,		"FALSE"},
142    {"-daemon",		"*daemon",		XrmoptionNoArg,		"TRUE"},
143    {"-verbose",	"*verbose",		XrmoptionNoArg,		"TRUE"},
144    {"-exitOnFail",	"*exitOnFail",		XrmoptionNoArg,		"TRUE"},
145    {"-saveLines",	"*saveLines",		XrmoptionSepArg,	NULL},
146};
147
148#ifdef ultrix
149#define USE_FILE
150#define FILE_NAME "/dev/xcons"
151#endif
152
153#ifdef __UNIXOS2__
154#define USE_FILE
155#define FILE_NAME "/dev/console$"
156#define INCL_DOSFILEMGR
157#define INCL_DOSDEVIOCTL
158#include <os2.h>
159#endif
160
161#ifdef linux
162#define USE_FILE
163#define FILE_NAME	"/dev/xconsole"
164# if defined (__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
165/*
166 * Linux distribution based on glibc 2.1 and higher should use
167 * devpts. This is the fallback if open file FILE_NAME fails.
168 * <werner@suse.de>
169 */
170#  define USE_PTS
171# endif
172#endif
173
174#if defined(_AIX)
175#  define USE_PTS
176#endif
177
178#if !defined (USE_FILE) || defined (linux)
179# include <sys/ioctl.h>
180# ifdef hpux
181#  include <termios.h>
182# endif
183# if defined (SVR4) || defined (USE_PTS)
184#  include <termios.h>
185#  include <sys/stropts.h>		/* for I_PUSH */
186#  ifdef sun
187#   include <sys/strredir.h>
188#  endif
189# endif
190# if defined(TIOCCONS) || defined(SRIOCSREDIR) || defined(Lynx)
191#  define USE_PTY
192static int  tty_fd, pty_fd;
193static char ttydev[64], ptydev[64];
194# endif
195#endif
196
197#if (defined(SVR4) && !defined(sun)) || (defined(SYSV) && defined(i386))
198#define USE_OSM
199#include <signal.h>
200#endif
201
202#ifdef USE_PTY
203static int get_pty(int *pty, int *tty, char *ttydev, char *ptydev);
204#endif
205
206#ifdef USE_OSM
207static FILE *osm_pipe(void);
208static int child_pid;
209#endif
210
211/* Copied from xterm/ptyx.h */
212#ifndef PTYCHAR1
213#ifdef __hpux
214#define PTYCHAR1        "zyxwvutsrqp"
215#else   /* !__hpux */
216#ifdef __UNIXOS2__
217#define PTYCHAR1        "pq"
218#else
219#define PTYCHAR1        "pqrstuvwxyzPQRSTUVWXYZ"
220#endif  /* !__UNIXOS2__ */
221#endif  /* !__hpux */
222#endif  /* !PTYCHAR1 */
223
224#ifndef PTYCHAR2
225#ifdef __hpux
226#define PTYCHAR2        "fedcba9876543210"
227#else   /* !__hpux */
228#ifdef __FreeBSD__
229#define PTYCHAR2        "0123456789abcdefghijklmnopqrstuv"
230#else /* !__FreeBSD__ */
231#define PTYCHAR2        "0123456789abcdef"
232#endif /* !__FreeBSD__ */
233#endif  /* !__hpux */
234#endif  /* !PTYCHAR2 */
235
236#ifdef Lynx
237static void
238RestoreConsole(void)
239{
240    int fd;
241    if ((fd = open("/dev/con", O_RDONLY)) >= 0)
242	newconsole(fd);
243}
244#endif
245
246static void
247OpenConsole(void)
248{
249    input = 0;
250    if (app_resources.file)
251    {
252	if (!strcmp (app_resources.file, "console"))
253	{
254	    /* must be owner and have read/write permission */
255#if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(Lynx) && !defined(__UNIXOS2__)
256	    struct stat sbuf;
257# if !defined (linux)
258	    if (!stat("/dev/console", &sbuf) &&
259		(sbuf.st_uid == getuid()) &&
260		!access("/dev/console", R_OK|W_OK))
261# endif
262#endif
263	    {
264#ifdef USE_FILE
265# ifdef linux
266		if (!stat(FILE_NAME, &sbuf))
267# endif
268		input = fopen (FILE_NAME, "r");
269# ifdef __UNIXOS2__
270		if (input)
271		{
272		    ULONG arg = 1,arglen;
273		    APIRET rc;
274		    if ((rc=DosDevIOCtl(fileno(input), 0x76,0x4d,
275			&arg, sizeof(arg), &arglen,
276			NULL, 0, NULL)) != 0)
277		    {
278			fclose(input);
279			input = 0;
280		    }
281		}
282# endif
283#endif
284
285#ifdef USE_PTY
286		if (!input && get_pty (&pty_fd, &tty_fd, ttydev, ptydev) == 0)
287		{
288# ifdef TIOCCONS
289		    int on = 1;
290		    if (ioctl (tty_fd, TIOCCONS, (char *) &on) != -1)
291			input = fdopen (pty_fd, "r");
292# else
293#  ifndef Lynx
294		    int consfd = open("/dev/console", O_RDONLY);
295		    if (consfd >= 0)
296		    {
297			if (ioctl(consfd, SRIOCSREDIR, tty_fd) != -1)
298			    input = fdopen (pty_fd, "r");
299			close(consfd);
300		    }
301#  else
302		    if (newconsole(tty_fd) < 0)
303			perror("newconsole");
304		    else
305		    {
306			input = fdopen (pty_fd, "r");
307			atexit(RestoreConsole);
308		    }
309#  endif
310# endif
311		}
312#endif /* USE_PTY */
313	    }
314#ifdef USE_OSM
315	    /* Don't have to be owner of /dev/console when using /dev/osm. */
316	    if (!input)
317		input = osm_pipe();
318#endif
319	    if (input && app_resources.verbose)
320	    {
321		char	*hostname;
322		TextAppend (text, "Console log for ", 16);
323		hostname = mit_console_name + MIT_CONSOLE_LEN;
324		TextAppend (text, hostname, strlen (hostname));
325		TextAppend (text, "\n", 1);
326	    }
327	}
328	else
329	{
330	    struct stat sbuf;
331
332	    regularFile = FALSE;
333	    if (access(app_resources.file, R_OK) == 0)
334	    {
335		input = fopen (app_resources.file, "r");
336		if (input)
337		    if (!stat(app_resources.file, &sbuf) &&
338			S_ISREG( sbuf.st_mode ) )
339			regularFile = TRUE;
340	    }
341	}
342	if (!input)
343	{
344	    if (app_resources.exitOnFail)
345		exit(0);
346	    TextAppend (text, "Couldn't open ", 14);
347	    TextAppend (text, app_resources.file, strlen (app_resources.file));
348	    TextAppend (text, "\n", 1);
349	}
350    }
351    else
352	input = stdin;
353
354    if (input)
355    {
356	input_id = XtAddInput (fileno (input), (XtPointer) XtInputReadMask,
357			       inputReady, (XtPointer) text);
358    }
359}
360
361static void
362CloseConsole (void)
363{
364    if (input)
365    {
366	XtRemoveInput (input_id);
367	fclose (input);
368    }
369#ifdef USE_PTY
370    close (tty_fd);
371#endif
372}
373
374#ifdef USE_OSM
375static void
376KillChild(int sig)
377{
378    if (child_pid > 0)
379	kill(child_pid, SIGTERM);
380    _exit(0);
381}
382#endif
383
384/*ARGSUSED*/
385static void
386Quit(Widget widget, XEvent *event, String *params, Cardinal *num_params)
387{
388#ifdef USE_OSM
389    if (child_pid > 0)
390	kill(child_pid, SIGTERM);
391#endif
392    exit (0);
393}
394
395#ifdef USE_OSM
396static int (*ioerror)(Display *);
397
398static int
399IOError(Display *dpy)
400{
401    if (child_pid > 0)
402	kill(child_pid, SIGTERM);
403    return (*ioerror)(dpy);
404}
405#endif
406
407static void
408Notify(void)
409{
410    Arg	    arglist[1];
411    char    *oldName;
412    char    *newName;
413
414    if (!iconified || !app_resources.notify || notified)
415	return;
416    XtSetArg (arglist[0], XtNiconName, &oldName);
417    XtGetValues (top, arglist, 1);
418    newName = malloc (strlen (oldName) + 3);
419    if (!newName)
420	return;
421    sprintf (newName, "%s *", oldName);
422    XtSetArg (arglist[0], XtNiconName, newName);
423    XtSetValues (top, arglist, 1);
424    free (newName);
425    notified = True;
426}
427
428/*ARGSUSED*/
429static void
430Deiconified(Widget widget, XEvent *event, String *params, Cardinal *num_params)
431{
432    Arg	    arglist[1];
433    char    *oldName;
434    char    *newName;
435    int	    oldlen;
436
437    iconified = False;
438    if (!app_resources.notify || !notified)
439	return;
440    XtSetArg (arglist[0], XtNiconName, &oldName);
441    XtGetValues (top, arglist, 1);
442    oldlen = strlen (oldName);
443    if (oldlen >= 2)
444    {
445	newName = malloc (oldlen - 1);
446	if (!newName)
447	    return;
448	strncpy (newName, oldName, oldlen - 2);
449	newName[oldlen - 2] = '\0';
450	XtSetArg (arglist[0], XtNiconName, newName);
451	XtSetValues (top, arglist, 1);
452	free (newName);
453    }
454    notified = False;
455}
456
457/*ARGSUSED*/
458static void
459Iconified(Widget widget, XEvent *event, String *params, Cardinal *num_params)
460{
461    iconified = True;
462}
463
464/*ARGSUSED*/
465static void
466Clear(Widget widget, XEvent *event, String *params, Cardinal *num_params)
467{
468    long	    last;
469    XawTextBlock    block;
470
471    last = TextLength (text);
472    block.ptr = "";
473    block.firstPos = 0;
474    block.length = 0;
475    block.format = FMT8BIT;
476    TextReplace (text, 0, last, &block);
477}
478
479static XtActionsRec actions[] = {
480    { "Quit",		Quit },
481    { "Iconified",	Iconified },
482    { "Deiconified",	Deiconified },
483    { "Clear",		Clear },
484};
485
486static void
487stripNonprint(char *b)
488{
489    char    *c;
490
491    c = b;
492    while (*b)
493    {
494	if (isprint (*b) || (isspace (*b) && *b != '\r'))
495	{
496	    if (c != b)
497		*c = *b;
498	    ++c;
499	}
500	++b;
501    }
502    *c = '\0';
503}
504
505static void
506inputReady(XtPointer w, int *source, XtInputId *id)
507{
508    char    buffer[1025];
509    int	    n;
510
511    n = read (*source, buffer, sizeof (buffer) - 1);
512    if (n <= 0)
513    {
514	if (app_resources.file && regularFile && n == 0)
515	{
516	    if (XPending(XtDisplay(w)))
517		return;
518
519	    sleep(1);
520	    return;
521	}
522
523	fclose (input);
524	XtRemoveInput (*id);
525
526	/* try to reopen if pipe; this can be caused by syslog restart */
527	if (app_resources.file && !regularFile && n == 0)
528	{
529	    OpenConsole();
530	}
531    } else {
532	Notify();
533	buffer[n] = '\0';
534	if (app_resources.stripNonprint)
535	{
536	    stripNonprint (buffer);
537	    n = strlen (buffer);
538	}
539
540	TextAppend ((Widget) text, buffer, n);
541    }
542}
543
544static Boolean
545ConvertSelection(Widget w, Atom *selection, Atom *target, Atom *type,
546		 XtPointer *value, unsigned long *length, int *format)
547{
548    Display* d = XtDisplay(w);
549    XSelectionRequestEvent* req =
550	XtGetSelectionRequest(w, *selection, (XtRequestId)NULL);
551
552    if (*target == XA_TARGETS(d))
553    {
554	Atom* targetP;
555	Atom* std_targets;
556	unsigned long std_length;
557	XmuConvertStandardSelection(w, req->time, selection, target, type,
558				    (XPointer *)&std_targets, &std_length,
559				    format);
560	*value = (XtPointer)XtMalloc(sizeof(Atom)*(std_length + 5));
561	targetP = *(Atom**)value;
562	*targetP++ = XA_STRING;
563	*targetP++ = XA_TEXT(d);
564	*targetP++ = XA_LENGTH(d);
565	*targetP++ = XA_LIST_LENGTH(d);
566	*targetP++ = XA_CHARACTER_POSITION(d);
567	*length = std_length + (targetP - (*(Atom **) value));
568	memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length);
569	XtFree((char*)std_targets);
570	*type = XA_ATOM;
571	*format = 32;
572	return True;
573    }
574
575    if (*target == XA_LIST_LENGTH(d) ||
576	*target == XA_LENGTH(d))
577    {
578	long * temp;
579
580	temp = (long *) XtMalloc(sizeof(long));
581	if (*target == XA_LIST_LENGTH(d))
582	  *temp = 1L;
583	else			/* *target == XA_LENGTH(d) */
584	  *temp = (long) TextLength (text);
585
586	*value = (XtPointer) temp;
587	*type = XA_INTEGER;
588	*length = 1L;
589	*format = 32;
590	return True;
591    }
592
593    if (*target == XA_CHARACTER_POSITION(d))
594    {
595	long * temp;
596
597	temp = (long *) XtMalloc(2 * sizeof(long));
598	temp[0] = (long) 0;
599	temp[1] = TextLength (text);
600	*value = (XtPointer) temp;
601	*type = XA_SPAN(d);
602	*length = 2L;
603	*format = 32;
604	return True;
605    }
606
607    if (*target == XA_STRING ||
608      *target == XA_TEXT(d) ||
609      *target == XA_COMPOUND_TEXT(d))
610    {
611	if (*target == XA_COMPOUND_TEXT(d))
612	    *type = *target;
613	else
614	    *type = XA_STRING;
615	*length = TextLength (text);
616	*value = (XtPointer)_XawTextGetSTRING((TextWidget) text, 0, *length);
617	*format = 8;
618	/*
619	 * Drop our connection to the file; the new console program
620	 * will open as soon as it receives the selection contents; there
621	 * is a small window where console output will not be redirected,
622	 * but I see no way of avoiding that without having two programs
623	 * attempt to redirect console output at the same time, which seems
624	 * worse
625	 */
626	CloseConsole ();
627	return True;
628    }
629
630    if (XmuConvertStandardSelection(w, req->time, selection, target, type,
631				    (XPointer *)value, length, format))
632	return True;
633
634    return False;
635}
636
637static void
638LoseSelection(Widget w, Atom *selection)
639{
640    Quit (w, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL);
641}
642
643/*ARGSUSED*/
644static void
645InsertSelection(Widget w, XtPointer client_data, Atom *selection, Atom *type,
646		XtPointer value, unsigned long *length, int *format)
647{
648    if (*type != XT_CONVERT_FAIL)
649	TextInsert (text, (char *) value, *length);
650    XtOwnSelection(top, mit_console, CurrentTime,
651		   ConvertSelection, LoseSelection, NULL);
652    OpenConsole ();
653}
654
655int
656main(int argc, char *argv[])
657{
658    Arg arglist[10];
659    Cardinal num_args;
660
661    XtSetLanguageProc(NULL,NULL,NULL);
662    top = XtInitialize ("xconsole", "XConsole", options, XtNumber (options),
663			&argc, argv);
664    XtGetApplicationResources (top, (XtPointer)&app_resources, resources,
665			       XtNumber (resources), NULL, 0);
666
667    if (app_resources.daemon)
668	if (fork ()) exit (0);
669    XtAddActions (actions, XtNumber (actions));
670
671    text = XtCreateManagedWidget ("text", asciiTextWidgetClass,
672				  top, NULL, 0);
673
674    XtRealizeWidget (top);
675    num_args = 0;
676    XtSetArg(arglist[num_args], XtNiconic, &iconified); num_args++;
677    XtGetValues(top, arglist, num_args);
678    if (iconified)
679	Iconified((Widget)NULL, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL);
680    else
681	Deiconified((Widget)NULL,(XEvent*)NULL,(String*)NULL,(Cardinal*)NULL);
682    wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW",
683				   False);
684    (void) XSetWMProtocols (XtDisplay(top), XtWindow(top),
685                            &wm_delete_window, 1);
686
687    XmuGetHostname (mit_console_name + MIT_CONSOLE_LEN, 255);
688
689    mit_console = XInternAtom(XtDisplay(top), mit_console_name, False);
690
691    if (XGetSelectionOwner (XtDisplay (top), mit_console))
692    {
693	XtGetSelectionValue(top, mit_console, XA_STRING, InsertSelection,
694			    NULL, CurrentTime);
695    }
696    else
697    {
698	XtOwnSelection(top, mit_console, CurrentTime,
699		       ConvertSelection, LoseSelection, NULL);
700	OpenConsole ();
701    }
702#ifdef USE_OSM
703    ioerror = XSetIOErrorHandler(IOError);
704#endif
705    XtMainLoop ();
706    return 0;
707}
708
709static long
710TextLength(Widget w)
711{
712    return XawTextSourceScan (XawTextGetSource (w),
713			      (XawTextPosition) 0,
714			      XawstAll, XawsdRight, 1, TRUE);
715}
716
717static void
718TextReplace(Widget w, int start, int end, XawTextBlock *block)
719{
720    Arg		    arg;
721    Widget	    source;
722    XawTextEditType edit_mode;
723
724    source = XawTextGetSource (w);
725    XtSetArg (arg, XtNeditType, &edit_mode);
726    XtGetValues (source, &arg, ONE);
727    XtSetArg (arg, XtNeditType, XawtextEdit);
728    XtSetValues (source, &arg, ONE);
729    XawTextReplace (w, start, end, block);
730    XtSetArg (arg, XtNeditType, edit_mode);
731    XtSetValues (source, &arg, ONE);
732}
733
734static void
735TextAppend(Widget w, char *s, int len)
736{
737    long	    last, current;
738    XawTextBlock    block;
739
740    current = XawTextGetInsertionPoint (w);
741    last = TextLength (w);
742    block.ptr = s;
743    block.firstPos = 0;
744    block.length = len;
745    block.format = FMT8BIT;
746    /*
747     * If saveLines is 1, just replace the entire contents of the widget
748     * each time, so the test in ExceededMaxLines() isn't fooled.
749     */
750    if (app_resources.saveLines == 1)
751	TextReplace (w, 0, last, &block);
752    else
753	TextReplace (w, last, last, &block);
754    if (current == last)
755	XawTextSetInsertionPoint (w, last + block.length);
756    if (ExceededMaxLines(w))
757	ScrollLine(w);
758}
759
760static void
761TextInsert(Widget w, char *s, int len)
762{
763    XawTextBlock    block;
764    long	    current;
765
766    current = XawTextGetInsertionPoint (w);
767    block.ptr = s;
768    block.firstPos = 0;
769    block.length = len;
770    block.format = FMT8BIT;
771    TextReplace (w, 0, 0, &block);
772    if (current == 0)
773	XawTextSetInsertionPoint (w, len);
774    if (ExceededMaxLines(w))
775	ScrollLine(w);
776}
777
778static Bool
779ExceededMaxLines(Widget w)
780{
781    XawTextPosition end_of_last_line;
782    Bool retval = False;
783
784    if (app_resources.saveLines > 0)
785    {
786    /*
787     * XawTextSourceScan() will return the end of the widget if it cannot
788     * find what it is searching for.
789     */
790	end_of_last_line = XawTextSourceScan (XawTextGetSource (w),
791					      (XawTextPosition) 0,
792					      XawstEOL, XawsdRight,
793					      app_resources.saveLines, TRUE);
794	if (TextLength(w) > end_of_last_line)
795	    retval = True;
796	else
797	    retval = False;
798    }
799    else
800	retval = False;
801    return retval;
802}
803
804static void
805ScrollLine(Widget w)
806{
807    XawTextPosition firstnewline;
808    XawTextBlock    block;
809
810    /*
811     * This is pretty inefficient but should work well enough unless the
812     * console device is getting totally spammed.  Generally, new lines
813     * only come in one at a time anyway.
814     */
815    firstnewline = XawTextSourceScan (XawTextGetSource (w),
816				      (XawTextPosition) 0,
817				      XawstEOL, XawsdRight, 1, TRUE);
818    block.ptr = "";
819    block.firstPos = 0;
820    block.length = 0;
821    block.format = FMT8BIT;
822    TextReplace (w, 0, firstnewline, &block);
823}
824
825#ifdef USE_PTY
826/*
827 * This function opens up a pty master and stuffs its value into pty.
828 * If it finds one, it returns a value of 0.  If it does not find one,
829 * it returns a value of !0.  This routine is designed to be re-entrant,
830 * so that if a pty master is found and later, we find that the slave
831 * has problems, we can re-enter this function and get another one.
832 */
833
834static int
835get_pty(int *pty, int *tty, char *ttydev, char *ptydev)
836{
837#ifdef HAS_OPENPTY
838	if (openpty(pty, tty, NULL, NULL, NULL) == -1) {
839		return 1;
840	}
841	return 0;
842#elif defined (SVR4) || defined (USE_PTS)
843#if defined (_AIX)
844	if ((*pty = open ("/dev/ptc", O_RDWR)) < 0)
845#else
846	if ((*pty = open ("/dev/ptmx", O_RDWR)) < 0)
847#endif
848	    return 1;
849	grantpt(*pty);
850	unlockpt(*pty);
851	strcpy(ttydev, (char *)ptsname(*pty));
852	if ((*tty = open(ttydev, O_RDWR)) >= 0)
853	{
854	    (void)ioctl(*tty, I_PUSH, "ttcompat");
855	    return 0;
856	}
857	if (*pty >= 0)
858	    close (*pty);
859#else /* !SVR4, need lots of code */
860#ifdef USE_GET_PSEUDOTTY
861	if ((*pty = getpseudotty (&ttydev, &ptydev)) >= 0 &&
862	    (*tty = open (ttydev, O_RDWR)) >= 0)
863	    return 0;
864	if (*pty >= 0)
865	    close (*pty);
866#else
867	static int devindex, letter = 0;
868
869#if defined(umips) && defined (SYSTYPE_SYSV)
870	struct stat fstat_buf;
871
872	*pty = open ("/dev/ptc", O_RDWR);
873	if (*pty < 0 || (fstat (*pty, &fstat_buf)) < 0)
874	{
875	  return(1);
876	}
877	sprintf (ttydev, "/dev/ttyq%d", minor(fstat_buf.st_rdev));
878	sprintf (ptydev, "/dev/ptyq%d", minor(fstat_buf.st_rdev));
879	if ((*tty = open (ttydev, O_RDWR)) >= 0)
880	{
881	    /* got one! */
882	    return(0);
883	}
884	close (*pty);
885#else /* not (umips && SYSTYPE_SYSV) */
886#ifdef CRAY
887	for (; devindex < 256; devindex++) {
888	    sprintf (ttydev, "/dev/ttyp%03d", devindex);
889	    sprintf (ptydev, "/dev/pty/%03d", devindex);
890
891	    if ((*pty = open (ptydev, O_RDWR)) >= 0 &&
892		(*tty = open (ttydev, O_RDWR)) >= 0)
893	    {
894		/*
895		 * We need to set things up for our next entry
896		 * into this function!
897		 */
898		(void) devindex++;
899		return(0);
900	    }
901	    if (*pty >= 0)
902		close (*pty);
903	}
904#else /* !CRAY */
905#ifdef sgi
906	{
907	    char *slave;
908	    slave = _getpty (pty, O_RDWR, 0622, 0);
909	    if ((*tty = open (slave, O_RDWR)) != -1)
910		return 0;
911	}
912#else
913#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
914	return openpty(pty, tty, ttydev, NULL, NULL);
915#else
916	strcpy (ttydev, "/dev/ttyxx");
917	strcpy (ptydev, "/dev/ptyxx");
918	while (PTYCHAR1[letter]) {
919	    ttydev [strlen(ttydev) - 2]  = ptydev [strlen(ptydev) - 2] =
920		    PTYCHAR1 [letter];
921
922	    while (PTYCHAR2[devindex]) {
923		ttydev [strlen(ttydev) - 1] = ptydev [strlen(ptydev) - 1] =
924			PTYCHAR2 [devindex];
925		if ((*pty = open (ptydev, O_RDWR)) >= 0 &&
926		    (*tty = open (ttydev, O_RDWR)) >= 0)
927		{
928			/*
929			 * We need to set things up for our next entry
930			 * into this function!
931			 */
932			(void) devindex++;
933			return(0);
934		}
935		if (*pty >= 0)
936		    close (*pty);
937		devindex++;
938	    }
939	    devindex = 0;
940	    (void) letter++;
941	}
942#endif /* BSD4_4 else not BSD4_4 */
943#endif /* sgi else not sgi */
944#endif /* CRAY else not CRAY */
945#endif /* umips && SYSTYPE_SYSV */
946#endif /* USE_GET_PSEUDOTTY */
947#endif /* SVR4 */
948	/*
949	 * We were unable to allocate a pty master!  Return an error
950	 * condition and let our caller terminate cleanly.
951	 */
952	return(1);
953}
954#endif
955
956#ifdef USE_OSM
957/*
958 * On SYSV386 there is a special device, /dev/osm, where system messages
959 * are sent.  Problem is that we can't perform a select(2) on this device.
960 * So this routine creates a streams-pty where one end reads the device and
961 * sends the output to xconsole.
962 */
963
964#ifdef SCO325
965#define	OSM_DEVICE	"/dev/error"
966#else
967#ifdef __UNIXWARE__
968#define OSM_DEVICE	"/dev/osm2"
969#define NO_READAHEAD
970#else
971#define	OSM_DEVICE	"/dev/osm"
972#endif
973#endif
974
975#ifdef ISC
976#define NO_READAHEAD
977#endif
978
979static FILE *
980osm_pipe(void)
981{
982    int tty;
983    char ttydev[64];
984
985    if (access(OSM_DEVICE, R_OK) < 0)
986	return NULL;
987#if defined (_AIX)
988    if ((tty = open("/dev/ptc", O_RDWR)) < 0)
989#else
990    if ((tty = open("/dev/ptmx", O_RDWR)) < 0)
991#endif
992	return NULL;
993
994    grantpt(tty);
995    unlockpt(tty);
996    strcpy(ttydev, (char *)ptsname(tty));
997
998    if ((child_pid = fork()) == 0)
999    {
1000	int pty, osm, nbytes, skip;
1001	char cbuf[128];
1002
1003	skip = 0;
1004#ifndef NO_READAHEAD
1005	osm = open(OSM_DEVICE, O_RDONLY);
1006	if (osm >= 0)
1007	{
1008	    while ((nbytes = read(osm, cbuf, sizeof(cbuf))) > 0)
1009		skip += nbytes;
1010	    close(osm);
1011	}
1012#endif
1013	pty = open(ttydev, O_RDWR);
1014	if (pty < 0)
1015	    exit(1);
1016	osm = open(OSM_DEVICE, O_RDONLY);
1017	if (osm < 0)
1018	    exit(1);
1019	for (nbytes = 0; skip > 0 && nbytes >= 0; skip -= nbytes)
1020	{
1021	    nbytes = skip;
1022	    if (nbytes > sizeof(cbuf))
1023		nbytes = sizeof(cbuf);
1024	    nbytes = read(osm, cbuf, nbytes);
1025	}
1026	while ((nbytes = read(osm, cbuf, sizeof(cbuf))) >= 0)
1027	    write(pty, cbuf, nbytes);
1028	exit(0);
1029    }
1030    signal(SIGHUP, KillChild);
1031    signal(SIGINT, KillChild);
1032    signal(SIGTERM, KillChild);
1033    return fdopen(tty, "r");
1034}
1035#endif  /* USE_OSM */
1036