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