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