xref: /titanic_54/usr/src/lib/libtecla/common/getline.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * All rights reserved.
5*7c478bd9Sstevel@tonic-gate  *
6*7c478bd9Sstevel@tonic-gate  * Permission is hereby granted, free of charge, to any person obtaining a
7*7c478bd9Sstevel@tonic-gate  * copy of this software and associated documentation files (the
8*7c478bd9Sstevel@tonic-gate  * "Software"), to deal in the Software without restriction, including
9*7c478bd9Sstevel@tonic-gate  * without limitation the rights to use, copy, modify, merge, publish,
10*7c478bd9Sstevel@tonic-gate  * distribute, and/or sell copies of the Software, and to permit persons
11*7c478bd9Sstevel@tonic-gate  * to whom the Software is furnished to do so, provided that the above
12*7c478bd9Sstevel@tonic-gate  * copyright notice(s) and this permission notice appear in all copies of
13*7c478bd9Sstevel@tonic-gate  * the Software and that both the above copyright notice(s) and this
14*7c478bd9Sstevel@tonic-gate  * permission notice appear in supporting documentation.
15*7c478bd9Sstevel@tonic-gate  *
16*7c478bd9Sstevel@tonic-gate  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17*7c478bd9Sstevel@tonic-gate  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*7c478bd9Sstevel@tonic-gate  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19*7c478bd9Sstevel@tonic-gate  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20*7c478bd9Sstevel@tonic-gate  * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21*7c478bd9Sstevel@tonic-gate  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22*7c478bd9Sstevel@tonic-gate  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23*7c478bd9Sstevel@tonic-gate  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24*7c478bd9Sstevel@tonic-gate  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25*7c478bd9Sstevel@tonic-gate  *
26*7c478bd9Sstevel@tonic-gate  * Except as contained in this notice, the name of a copyright holder
27*7c478bd9Sstevel@tonic-gate  * shall not be used in advertising or otherwise to promote the sale, use
28*7c478bd9Sstevel@tonic-gate  * or other dealings in this Software without prior written authorization
29*7c478bd9Sstevel@tonic-gate  * of the copyright holder.
30*7c478bd9Sstevel@tonic-gate  */
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate /*
33*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
34*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
35*7c478bd9Sstevel@tonic-gate  */
36*7c478bd9Sstevel@tonic-gate 
37*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
38*7c478bd9Sstevel@tonic-gate 
39*7c478bd9Sstevel@tonic-gate /*
40*7c478bd9Sstevel@tonic-gate  * Standard headers.
41*7c478bd9Sstevel@tonic-gate  */
42*7c478bd9Sstevel@tonic-gate #include <stdio.h>
43*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
44*7c478bd9Sstevel@tonic-gate #include <signal.h>
45*7c478bd9Sstevel@tonic-gate #include <string.h>
46*7c478bd9Sstevel@tonic-gate #include <errno.h>
47*7c478bd9Sstevel@tonic-gate #include <ctype.h>
48*7c478bd9Sstevel@tonic-gate #include <setjmp.h>
49*7c478bd9Sstevel@tonic-gate #include <stdarg.h>
50*7c478bd9Sstevel@tonic-gate 
51*7c478bd9Sstevel@tonic-gate /*
52*7c478bd9Sstevel@tonic-gate  * UNIX headers.
53*7c478bd9Sstevel@tonic-gate  */
54*7c478bd9Sstevel@tonic-gate #include <sys/ioctl.h>
55*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
56*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SYS_SELECT_H
57*7c478bd9Sstevel@tonic-gate #include <sys/select.h>
58*7c478bd9Sstevel@tonic-gate #endif
59*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
60*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
61*7c478bd9Sstevel@tonic-gate #endif
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate /*
64*7c478bd9Sstevel@tonic-gate  * Handle the different sources of terminal control string and size
65*7c478bd9Sstevel@tonic-gate  * information. Note that if no terminal information database is available,
66*7c478bd9Sstevel@tonic-gate  * ANSI VT100 control sequences are used.
67*7c478bd9Sstevel@tonic-gate  */
68*7c478bd9Sstevel@tonic-gate #if defined(USE_TERMINFO) || defined(USE_TERMCAP)
69*7c478bd9Sstevel@tonic-gate /*
70*7c478bd9Sstevel@tonic-gate  * Include curses.h or ncurses/curses.h depending on which is available.
71*7c478bd9Sstevel@tonic-gate  */
72*7c478bd9Sstevel@tonic-gate #ifdef HAVE_CURSES_H
73*7c478bd9Sstevel@tonic-gate #include <curses.h>
74*7c478bd9Sstevel@tonic-gate #elif defined(HAVE_NCURSES_CURSES_H)
75*7c478bd9Sstevel@tonic-gate #include <ncurses/curses.h>
76*7c478bd9Sstevel@tonic-gate #endif
77*7c478bd9Sstevel@tonic-gate /*
78*7c478bd9Sstevel@tonic-gate  * Include term.h where available.
79*7c478bd9Sstevel@tonic-gate  */
80*7c478bd9Sstevel@tonic-gate #if defined(HAVE_TERM_H)
81*7c478bd9Sstevel@tonic-gate #include <term.h>
82*7c478bd9Sstevel@tonic-gate #elif defined(HAVE_NCURSES_TERM_H)
83*7c478bd9Sstevel@tonic-gate #include <ncurses/term.h>
84*7c478bd9Sstevel@tonic-gate #endif
85*7c478bd9Sstevel@tonic-gate /*
86*7c478bd9Sstevel@tonic-gate  * When using termcap, include termcap.h on systems that have it.
87*7c478bd9Sstevel@tonic-gate  * Otherwise assume that all prototypes are provided by curses.h.
88*7c478bd9Sstevel@tonic-gate  */
89*7c478bd9Sstevel@tonic-gate #if defined(USE_TERMCAP) && defined(HAVE_TERMCAP_H)
90*7c478bd9Sstevel@tonic-gate #include <termcap.h>
91*7c478bd9Sstevel@tonic-gate #endif
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate /*
94*7c478bd9Sstevel@tonic-gate  * Under Solaris default Curses the output function that tputs takes is
95*7c478bd9Sstevel@tonic-gate  * declared to have a char argument. On all other systems and on Solaris
96*7c478bd9Sstevel@tonic-gate  * X/Open Curses (Issue 4, Version 2) it expects an int argument (using
97*7c478bd9Sstevel@tonic-gate  * c89 or options -I /usr/xpg4/include -L /usr/xpg4/lib -R /usr/xpg4/lib
98*7c478bd9Sstevel@tonic-gate  * selects XPG4v2 Curses on Solaris 2.6 and later).
99*7c478bd9Sstevel@tonic-gate  *
100*7c478bd9Sstevel@tonic-gate  * Similarly, under Mac OS X, the return value of the tputs output
101*7c478bd9Sstevel@tonic-gate  * function is declared as void, whereas it is declared as int on
102*7c478bd9Sstevel@tonic-gate  * other systems.
103*7c478bd9Sstevel@tonic-gate  */
104*7c478bd9Sstevel@tonic-gate #if defined __sun && defined __SVR4 && !defined _XOPEN_CURSES
105*7c478bd9Sstevel@tonic-gate typedef int TputsRetType;
106*7c478bd9Sstevel@tonic-gate typedef char TputsArgType;              /* int tputs(char c, FILE *fp) */
107*7c478bd9Sstevel@tonic-gate #define TPUTS_RETURNS_VALUE 1
108*7c478bd9Sstevel@tonic-gate #elif defined(__APPLE__) && defined(__MACH__)
109*7c478bd9Sstevel@tonic-gate typedef void TputsRetType;
110*7c478bd9Sstevel@tonic-gate typedef int TputsArgType;               /* void tputs(int c, FILE *fp) */
111*7c478bd9Sstevel@tonic-gate #define TPUTS_RETURNS_VALUE 0
112*7c478bd9Sstevel@tonic-gate #else
113*7c478bd9Sstevel@tonic-gate typedef int TputsRetType;
114*7c478bd9Sstevel@tonic-gate typedef int TputsArgType;               /* int tputs(int c, FILE *fp) */
115*7c478bd9Sstevel@tonic-gate #define TPUTS_RETURNS_VALUE 1
116*7c478bd9Sstevel@tonic-gate #endif
117*7c478bd9Sstevel@tonic-gate 
118*7c478bd9Sstevel@tonic-gate /*
119*7c478bd9Sstevel@tonic-gate  * Use the above specifications to prototype our tputs callback function.
120*7c478bd9Sstevel@tonic-gate  */
121*7c478bd9Sstevel@tonic-gate static TputsRetType gl_tputs_putchar(TputsArgType c);
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate #endif  /* defined(USE_TERMINFO) || defined(USE_TERMCAP) */
124*7c478bd9Sstevel@tonic-gate 
125*7c478bd9Sstevel@tonic-gate /*
126*7c478bd9Sstevel@tonic-gate  * If the library is being compiled without filesystem access facilities,
127*7c478bd9Sstevel@tonic-gate  * ensure that none of the action functions that normally do access the
128*7c478bd9Sstevel@tonic-gate  * filesystem are bound by default, and that it they do get bound, that
129*7c478bd9Sstevel@tonic-gate  * they don't do anything.
130*7c478bd9Sstevel@tonic-gate  */
131*7c478bd9Sstevel@tonic-gate #if WITHOUT_FILE_SYSTEM
132*7c478bd9Sstevel@tonic-gate #define HIDE_FILE_SYSTEM
133*7c478bd9Sstevel@tonic-gate #endif
134*7c478bd9Sstevel@tonic-gate 
135*7c478bd9Sstevel@tonic-gate /*
136*7c478bd9Sstevel@tonic-gate  * POSIX headers.
137*7c478bd9Sstevel@tonic-gate  */
138*7c478bd9Sstevel@tonic-gate #include <unistd.h>
139*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
140*7c478bd9Sstevel@tonic-gate #include <termios.h>
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate /*
143*7c478bd9Sstevel@tonic-gate  * Provide typedefs for standard POSIX structures.
144*7c478bd9Sstevel@tonic-gate  */
145*7c478bd9Sstevel@tonic-gate typedef struct sigaction SigAction;
146*7c478bd9Sstevel@tonic-gate typedef struct termios Termios;
147*7c478bd9Sstevel@tonic-gate 
148*7c478bd9Sstevel@tonic-gate /*
149*7c478bd9Sstevel@tonic-gate  * Which flag is used to select non-blocking I/O with fcntl()?
150*7c478bd9Sstevel@tonic-gate  */
151*7c478bd9Sstevel@tonic-gate #undef NON_BLOCKING_FLAG
152*7c478bd9Sstevel@tonic-gate #if defined(O_NONBLOCK)
153*7c478bd9Sstevel@tonic-gate #define NON_BLOCKING_FLAG (O_NONBLOCK)
154*7c478bd9Sstevel@tonic-gate #elif defined(O_NDELAY)
155*7c478bd9Sstevel@tonic-gate #define NON_BLOCKING_FLAG (O_NDELAY)
156*7c478bd9Sstevel@tonic-gate #endif
157*7c478bd9Sstevel@tonic-gate 
158*7c478bd9Sstevel@tonic-gate /*
159*7c478bd9Sstevel@tonic-gate  * What value should we give errno if I/O blocks when it shouldn't.
160*7c478bd9Sstevel@tonic-gate  */
161*7c478bd9Sstevel@tonic-gate #undef BLOCKED_ERRNO
162*7c478bd9Sstevel@tonic-gate #if defined(EAGAIN)
163*7c478bd9Sstevel@tonic-gate #define BLOCKED_ERRNO (EAGAIN)
164*7c478bd9Sstevel@tonic-gate #elif defined(EWOULDBLOCK)
165*7c478bd9Sstevel@tonic-gate #define BLOCKED_ERRNO (EWOULDBLOCK)
166*7c478bd9Sstevel@tonic-gate #elif defined(EIO)
167*7c478bd9Sstevel@tonic-gate #define BLOCKED_ERRNO (EIO)
168*7c478bd9Sstevel@tonic-gate #else
169*7c478bd9Sstevel@tonic-gate #define BLOCKED_ERRNO 0
170*7c478bd9Sstevel@tonic-gate #endif
171*7c478bd9Sstevel@tonic-gate 
172*7c478bd9Sstevel@tonic-gate /*
173*7c478bd9Sstevel@tonic-gate  * Local headers.
174*7c478bd9Sstevel@tonic-gate  */
175*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
176*7c478bd9Sstevel@tonic-gate #include "pathutil.h"
177*7c478bd9Sstevel@tonic-gate #endif
178*7c478bd9Sstevel@tonic-gate #include "libtecla.h"
179*7c478bd9Sstevel@tonic-gate #include "keytab.h"
180*7c478bd9Sstevel@tonic-gate #include "getline.h"
181*7c478bd9Sstevel@tonic-gate #include "ioutil.h"
182*7c478bd9Sstevel@tonic-gate #include "history.h"
183*7c478bd9Sstevel@tonic-gate #include "freelist.h"
184*7c478bd9Sstevel@tonic-gate #include "stringrp.h"
185*7c478bd9Sstevel@tonic-gate #include "chrqueue.h"
186*7c478bd9Sstevel@tonic-gate #include "cplmatch.h"
187*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
188*7c478bd9Sstevel@tonic-gate #include "expand.h"
189*7c478bd9Sstevel@tonic-gate #endif
190*7c478bd9Sstevel@tonic-gate #include "errmsg.h"
191*7c478bd9Sstevel@tonic-gate 
192*7c478bd9Sstevel@tonic-gate /*
193*7c478bd9Sstevel@tonic-gate  * Enumerate the available editing styles.
194*7c478bd9Sstevel@tonic-gate  */
195*7c478bd9Sstevel@tonic-gate typedef enum {
196*7c478bd9Sstevel@tonic-gate   GL_EMACS_MODE,   /* Emacs style editing */
197*7c478bd9Sstevel@tonic-gate   GL_VI_MODE,      /* Vi style editing */
198*7c478bd9Sstevel@tonic-gate   GL_NO_EDITOR     /* Fall back to the basic OS-provided editing */
199*7c478bd9Sstevel@tonic-gate } GlEditor;
200*7c478bd9Sstevel@tonic-gate 
201*7c478bd9Sstevel@tonic-gate /*
202*7c478bd9Sstevel@tonic-gate  * Set the largest key-sequence that can be handled.
203*7c478bd9Sstevel@tonic-gate  */
204*7c478bd9Sstevel@tonic-gate #define GL_KEY_MAX 64
205*7c478bd9Sstevel@tonic-gate 
206*7c478bd9Sstevel@tonic-gate /*
207*7c478bd9Sstevel@tonic-gate  * In vi mode, the following datatype is used to implement the
208*7c478bd9Sstevel@tonic-gate  * undo command. It records a copy of the input line from before
209*7c478bd9Sstevel@tonic-gate  * the command-mode action which edited the input line.
210*7c478bd9Sstevel@tonic-gate  */
211*7c478bd9Sstevel@tonic-gate typedef struct {
212*7c478bd9Sstevel@tonic-gate   char *line;        /* A historical copy of the input line */
213*7c478bd9Sstevel@tonic-gate   int buff_curpos;   /* The historical location of the cursor in */
214*7c478bd9Sstevel@tonic-gate                      /*  line[] when the line was modified. */
215*7c478bd9Sstevel@tonic-gate   int ntotal;        /* The number of characters in line[] */
216*7c478bd9Sstevel@tonic-gate   int saved;         /* True once a line has been saved after the */
217*7c478bd9Sstevel@tonic-gate                      /*  last call to gl_interpret_char(). */
218*7c478bd9Sstevel@tonic-gate } ViUndo;
219*7c478bd9Sstevel@tonic-gate 
220*7c478bd9Sstevel@tonic-gate /*
221*7c478bd9Sstevel@tonic-gate  * In vi mode, the following datatype is used to record information
222*7c478bd9Sstevel@tonic-gate  * needed by the vi-repeat-change command.
223*7c478bd9Sstevel@tonic-gate  */
224*7c478bd9Sstevel@tonic-gate typedef struct {
225*7c478bd9Sstevel@tonic-gate   KtAction action;           /* The last action function that made a */
226*7c478bd9Sstevel@tonic-gate                              /*  change to the line. */
227*7c478bd9Sstevel@tonic-gate   int count;                 /* The repeat count that was passed to the */
228*7c478bd9Sstevel@tonic-gate                              /*  above command. */
229*7c478bd9Sstevel@tonic-gate   int input_curpos;          /* Whenever vi command mode is entered, the */
230*7c478bd9Sstevel@tonic-gate                              /*  the position at which it was first left */
231*7c478bd9Sstevel@tonic-gate                              /*  is recorded here. */
232*7c478bd9Sstevel@tonic-gate   int command_curpos;        /* Whenever vi command mode is entered, the */
233*7c478bd9Sstevel@tonic-gate                              /*  the location of the cursor is recorded */
234*7c478bd9Sstevel@tonic-gate                              /*  here. */
235*7c478bd9Sstevel@tonic-gate   char input_char;           /* Commands that call gl_read_terminal() */
236*7c478bd9Sstevel@tonic-gate                              /*  record the character here, so that it can */
237*7c478bd9Sstevel@tonic-gate                              /*  used on repeating the function. */
238*7c478bd9Sstevel@tonic-gate   int saved;                 /* True if a function has been saved since the */
239*7c478bd9Sstevel@tonic-gate                              /*  last call to gl_interpret_char(). */
240*7c478bd9Sstevel@tonic-gate   int active;                /* True while a function is being repeated. */
241*7c478bd9Sstevel@tonic-gate } ViRepeat;
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate /*
244*7c478bd9Sstevel@tonic-gate  * The following datatype is used to encapsulate information specific
245*7c478bd9Sstevel@tonic-gate  * to vi mode.
246*7c478bd9Sstevel@tonic-gate  */
247*7c478bd9Sstevel@tonic-gate typedef struct {
248*7c478bd9Sstevel@tonic-gate   ViUndo undo;               /* Information needed to implement the vi */
249*7c478bd9Sstevel@tonic-gate                              /*  undo command. */
250*7c478bd9Sstevel@tonic-gate   ViRepeat repeat;           /* Information needed to implement the vi */
251*7c478bd9Sstevel@tonic-gate                              /*  repeat command. */
252*7c478bd9Sstevel@tonic-gate   int command;               /* True in vi command-mode */
253*7c478bd9Sstevel@tonic-gate   int find_forward;          /* True if the last character search was in the */
254*7c478bd9Sstevel@tonic-gate                              /*  forward direction. */
255*7c478bd9Sstevel@tonic-gate   int find_onto;             /* True if the last character search left the */
256*7c478bd9Sstevel@tonic-gate                              /*  on top of the located character, as opposed */
257*7c478bd9Sstevel@tonic-gate                              /*  to just before or after it. */
258*7c478bd9Sstevel@tonic-gate   char find_char;            /* The last character sought, or '\0' if no */
259*7c478bd9Sstevel@tonic-gate                              /*  searches have been performed yet. */
260*7c478bd9Sstevel@tonic-gate } ViMode;
261*7c478bd9Sstevel@tonic-gate 
262*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
263*7c478bd9Sstevel@tonic-gate /*
264*7c478bd9Sstevel@tonic-gate  * Define a type for recording a file-descriptor callback and its associated
265*7c478bd9Sstevel@tonic-gate  * data.
266*7c478bd9Sstevel@tonic-gate  */
267*7c478bd9Sstevel@tonic-gate typedef struct {
268*7c478bd9Sstevel@tonic-gate   GlFdEventFn *fn;   /* The callback function */
269*7c478bd9Sstevel@tonic-gate   void *data;        /* Anonymous data to pass to the callback function */
270*7c478bd9Sstevel@tonic-gate } GlFdHandler;
271*7c478bd9Sstevel@tonic-gate 
272*7c478bd9Sstevel@tonic-gate /*
273*7c478bd9Sstevel@tonic-gate  * A list of nodes of the following type is used to record file-activity
274*7c478bd9Sstevel@tonic-gate  * event handlers, but only on systems that have the select() system call.
275*7c478bd9Sstevel@tonic-gate  */
276*7c478bd9Sstevel@tonic-gate typedef struct GlFdNode GlFdNode;
277*7c478bd9Sstevel@tonic-gate struct GlFdNode {
278*7c478bd9Sstevel@tonic-gate   GlFdNode *next;    /* The next in the list of nodes */
279*7c478bd9Sstevel@tonic-gate   int fd;            /* The file descriptor being watched */
280*7c478bd9Sstevel@tonic-gate   GlFdHandler rd;    /* The callback to call when fd is readable */
281*7c478bd9Sstevel@tonic-gate   GlFdHandler wr;    /* The callback to call when fd is writable */
282*7c478bd9Sstevel@tonic-gate   GlFdHandler ur;    /* The callback to call when fd has urgent data */
283*7c478bd9Sstevel@tonic-gate };
284*7c478bd9Sstevel@tonic-gate 
285*7c478bd9Sstevel@tonic-gate /*
286*7c478bd9Sstevel@tonic-gate  * Set the number of the above structures to allocate every time that
287*7c478bd9Sstevel@tonic-gate  * the freelist of GlFdNode's becomes exhausted.
288*7c478bd9Sstevel@tonic-gate  */
289*7c478bd9Sstevel@tonic-gate #define GLFD_FREELIST_BLOCKING 10
290*7c478bd9Sstevel@tonic-gate 
291*7c478bd9Sstevel@tonic-gate 
292*7c478bd9Sstevel@tonic-gate static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd,
293*7c478bd9Sstevel@tonic-gate 			      GlFdEvent event);
294*7c478bd9Sstevel@tonic-gate 
295*7c478bd9Sstevel@tonic-gate static int gl_call_timeout_handler(GetLine *gl);
296*7c478bd9Sstevel@tonic-gate 
297*7c478bd9Sstevel@tonic-gate #endif
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate /*
300*7c478bd9Sstevel@tonic-gate  * Each signal that gl_get_line() traps is described by a list node
301*7c478bd9Sstevel@tonic-gate  * of the following type.
302*7c478bd9Sstevel@tonic-gate  */
303*7c478bd9Sstevel@tonic-gate typedef struct GlSignalNode GlSignalNode;
304*7c478bd9Sstevel@tonic-gate struct GlSignalNode {
305*7c478bd9Sstevel@tonic-gate   GlSignalNode *next;  /* The next signal in the list */
306*7c478bd9Sstevel@tonic-gate   int signo;           /* The number of the signal */
307*7c478bd9Sstevel@tonic-gate   sigset_t proc_mask;  /* A process mask which only includes signo */
308*7c478bd9Sstevel@tonic-gate   SigAction original;  /* The signal disposition of the calling program */
309*7c478bd9Sstevel@tonic-gate                        /*  for this signal. */
310*7c478bd9Sstevel@tonic-gate   unsigned flags;      /* A bitwise union of GlSignalFlags enumerators */
311*7c478bd9Sstevel@tonic-gate   GlAfterSignal after; /* What to do after the signal has been handled */
312*7c478bd9Sstevel@tonic-gate   int errno_value;     /* What to set errno to */
313*7c478bd9Sstevel@tonic-gate };
314*7c478bd9Sstevel@tonic-gate 
315*7c478bd9Sstevel@tonic-gate /*
316*7c478bd9Sstevel@tonic-gate  * Set the number of the above structures to allocate every time that
317*7c478bd9Sstevel@tonic-gate  * the freelist of GlSignalNode's becomes exhausted.
318*7c478bd9Sstevel@tonic-gate  */
319*7c478bd9Sstevel@tonic-gate #define GLS_FREELIST_BLOCKING 30
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate /*
322*7c478bd9Sstevel@tonic-gate  * Completion handlers and their callback data are recorded in
323*7c478bd9Sstevel@tonic-gate  * nodes of the following type.
324*7c478bd9Sstevel@tonic-gate  */
325*7c478bd9Sstevel@tonic-gate typedef struct GlCplCallback GlCplCallback;
326*7c478bd9Sstevel@tonic-gate struct GlCplCallback {
327*7c478bd9Sstevel@tonic-gate   CplMatchFn *fn;            /* The completion callback function */
328*7c478bd9Sstevel@tonic-gate   void *data;                /* Arbitrary callback data */
329*7c478bd9Sstevel@tonic-gate };
330*7c478bd9Sstevel@tonic-gate 
331*7c478bd9Sstevel@tonic-gate /*
332*7c478bd9Sstevel@tonic-gate  * The following function is used as the default completion handler when
333*7c478bd9Sstevel@tonic-gate  * the filesystem is to be hidden. It simply reports no completions.
334*7c478bd9Sstevel@tonic-gate  */
335*7c478bd9Sstevel@tonic-gate #ifdef HIDE_FILE_SYSTEM
336*7c478bd9Sstevel@tonic-gate static CPL_MATCH_FN(gl_no_completions);
337*7c478bd9Sstevel@tonic-gate #endif
338*7c478bd9Sstevel@tonic-gate 
339*7c478bd9Sstevel@tonic-gate /*
340*7c478bd9Sstevel@tonic-gate  * Specify how many GlCplCallback nodes are added to the GlCplCallback freelist
341*7c478bd9Sstevel@tonic-gate  * whenever it becomes exhausted.
342*7c478bd9Sstevel@tonic-gate  */
343*7c478bd9Sstevel@tonic-gate #define GL_CPL_FREELIST_BLOCKING 10
344*7c478bd9Sstevel@tonic-gate 
345*7c478bd9Sstevel@tonic-gate /*
346*7c478bd9Sstevel@tonic-gate  * External action functions and their callback data are recorded in
347*7c478bd9Sstevel@tonic-gate  * nodes of the following type.
348*7c478bd9Sstevel@tonic-gate  */
349*7c478bd9Sstevel@tonic-gate typedef struct GlExternalAction GlExternalAction;
350*7c478bd9Sstevel@tonic-gate struct GlExternalAction {
351*7c478bd9Sstevel@tonic-gate   GlActionFn *fn;          /* The function which implements the action */
352*7c478bd9Sstevel@tonic-gate   void *data;              /* Arbitrary callback data */
353*7c478bd9Sstevel@tonic-gate };
354*7c478bd9Sstevel@tonic-gate 
355*7c478bd9Sstevel@tonic-gate /*
356*7c478bd9Sstevel@tonic-gate  * Specify how many GlExternalAction nodes are added to the
357*7c478bd9Sstevel@tonic-gate  * GlExternalAction freelist whenever it becomes exhausted.
358*7c478bd9Sstevel@tonic-gate  */
359*7c478bd9Sstevel@tonic-gate #define GL_EXT_ACT_FREELIST_BLOCKING 10
360*7c478bd9Sstevel@tonic-gate 
361*7c478bd9Sstevel@tonic-gate /*
362*7c478bd9Sstevel@tonic-gate  * Define the contents of the GetLine object.
363*7c478bd9Sstevel@tonic-gate  * Note that the typedef for this object can be found in libtecla.h.
364*7c478bd9Sstevel@tonic-gate  */
365*7c478bd9Sstevel@tonic-gate struct GetLine {
366*7c478bd9Sstevel@tonic-gate   ErrMsg *err;               /* The error-reporting buffer */
367*7c478bd9Sstevel@tonic-gate   GlHistory *glh;            /* The line-history buffer */
368*7c478bd9Sstevel@tonic-gate   WordCompletion *cpl;       /* String completion resource object */
369*7c478bd9Sstevel@tonic-gate   GlCplCallback cplfn;       /* The completion callback */
370*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
371*7c478bd9Sstevel@tonic-gate   ExpandFile *ef;            /* ~user/, $envvar and wildcard expansion */
372*7c478bd9Sstevel@tonic-gate                              /*  resource object. */
373*7c478bd9Sstevel@tonic-gate #endif
374*7c478bd9Sstevel@tonic-gate   StringGroup *capmem;       /* Memory for recording terminal capability */
375*7c478bd9Sstevel@tonic-gate                              /*  strings. */
376*7c478bd9Sstevel@tonic-gate   GlCharQueue *cq;           /* The terminal output character queue */
377*7c478bd9Sstevel@tonic-gate   int input_fd;              /* The file descriptor to read on */
378*7c478bd9Sstevel@tonic-gate   int output_fd;             /* The file descriptor to write to */
379*7c478bd9Sstevel@tonic-gate   FILE *input_fp;            /* A stream wrapper around input_fd */
380*7c478bd9Sstevel@tonic-gate   FILE *output_fp;           /* A stream wrapper around output_fd */
381*7c478bd9Sstevel@tonic-gate   FILE *file_fp;             /* When input is being temporarily taken from */
382*7c478bd9Sstevel@tonic-gate                              /*  a file, this is its file-pointer. Otherwise */
383*7c478bd9Sstevel@tonic-gate                              /*  it is NULL. */
384*7c478bd9Sstevel@tonic-gate   char *term;                /* The terminal type specified on the last call */
385*7c478bd9Sstevel@tonic-gate                              /*  to gl_change_terminal(). */
386*7c478bd9Sstevel@tonic-gate   int is_term;               /* True if stdin is a terminal */
387*7c478bd9Sstevel@tonic-gate   GlWriteFn *flush_fn;       /* The function to call to write to the terminal */
388*7c478bd9Sstevel@tonic-gate   GlIOMode io_mode;          /* The I/O mode established by gl_io_mode() */
389*7c478bd9Sstevel@tonic-gate   int raw_mode;              /* True while the terminal is in raw mode */
390*7c478bd9Sstevel@tonic-gate   GlPendingIO pending_io;    /* The type of I/O that is currently pending */
391*7c478bd9Sstevel@tonic-gate   GlReturnStatus rtn_status; /* The reason why gl_get_line() returned */
392*7c478bd9Sstevel@tonic-gate   int rtn_errno;             /* THe value of errno associated with rtn_status */
393*7c478bd9Sstevel@tonic-gate   size_t linelen;            /* The max number of characters per line */
394*7c478bd9Sstevel@tonic-gate   char *line;                /* A line-input buffer of allocated size */
395*7c478bd9Sstevel@tonic-gate                              /*  linelen+2. The extra 2 characters are */
396*7c478bd9Sstevel@tonic-gate                              /*  reserved for "\n\0". */
397*7c478bd9Sstevel@tonic-gate   char *cutbuf;              /* A cut-buffer of the same size as line[] */
398*7c478bd9Sstevel@tonic-gate   char *prompt;              /* The current prompt string */
399*7c478bd9Sstevel@tonic-gate   int prompt_len;            /* The length of the prompt string */
400*7c478bd9Sstevel@tonic-gate   int prompt_changed;        /* True after a callback changes the prompt */
401*7c478bd9Sstevel@tonic-gate   int prompt_style;          /* How the prompt string is displayed */
402*7c478bd9Sstevel@tonic-gate   FreeList *cpl_mem;         /* Memory for GlCplCallback objects */
403*7c478bd9Sstevel@tonic-gate   FreeList *ext_act_mem;     /* Memory for GlExternalAction objects */
404*7c478bd9Sstevel@tonic-gate   FreeList *sig_mem;         /* Memory for nodes of the signal list */
405*7c478bd9Sstevel@tonic-gate   GlSignalNode *sigs;        /* The head of the list of signals */
406*7c478bd9Sstevel@tonic-gate   int signals_masked;        /* True between calls to gl_mask_signals() and */
407*7c478bd9Sstevel@tonic-gate                              /*  gl_unmask_signals() */
408*7c478bd9Sstevel@tonic-gate   int signals_overriden;     /* True between calls to gl_override_signals() */
409*7c478bd9Sstevel@tonic-gate                              /*  and gl_restore_signals() */
410*7c478bd9Sstevel@tonic-gate   sigset_t all_signal_set;   /* The set of all signals that we are trapping */
411*7c478bd9Sstevel@tonic-gate   sigset_t old_signal_set;   /* The set of blocked signals on entry to */
412*7c478bd9Sstevel@tonic-gate                              /*  gl_get_line(). */
413*7c478bd9Sstevel@tonic-gate   sigset_t use_signal_set;   /* The subset of all_signal_set to unblock */
414*7c478bd9Sstevel@tonic-gate                              /*  while waiting for key-strokes */
415*7c478bd9Sstevel@tonic-gate   Termios oldattr;           /* Saved terminal attributes. */
416*7c478bd9Sstevel@tonic-gate   KeyTab *bindings;          /* A table of key-bindings */
417*7c478bd9Sstevel@tonic-gate   int ntotal;                /* The number of characters in gl->line[] */
418*7c478bd9Sstevel@tonic-gate   int buff_curpos;           /* The cursor position within gl->line[] */
419*7c478bd9Sstevel@tonic-gate   int term_curpos;           /* The cursor position on the terminal */
420*7c478bd9Sstevel@tonic-gate   int term_len;              /* The number of terminal characters used to */
421*7c478bd9Sstevel@tonic-gate                              /*  display the current input line. */
422*7c478bd9Sstevel@tonic-gate   int buff_mark;             /* A marker location in the buffer */
423*7c478bd9Sstevel@tonic-gate   int insert_curpos;         /* The cursor position at start of insert */
424*7c478bd9Sstevel@tonic-gate   int insert;                /* True in insert mode */
425*7c478bd9Sstevel@tonic-gate   int number;                /* If >= 0, a numeric argument is being read */
426*7c478bd9Sstevel@tonic-gate   int endline;               /* True to tell gl_get_input_line() to return */
427*7c478bd9Sstevel@tonic-gate                              /*  the current contents of gl->line[] */
428*7c478bd9Sstevel@tonic-gate   int displayed;             /* True if an input line is currently displayed */
429*7c478bd9Sstevel@tonic-gate   int redisplay;             /* If true, the input line will be redrawn */
430*7c478bd9Sstevel@tonic-gate                              /*  either after the current action function */
431*7c478bd9Sstevel@tonic-gate                              /*  returns, or when gl_get_input_line() */
432*7c478bd9Sstevel@tonic-gate                              /*  is next called. */
433*7c478bd9Sstevel@tonic-gate   int postpone;              /* _gl_normal_io() sets this flag, to */
434*7c478bd9Sstevel@tonic-gate                              /*  postpone any redisplays until */
435*7c478bd9Sstevel@tonic-gate                              /*  is next called, to resume line editing. */
436*7c478bd9Sstevel@tonic-gate   char keybuf[GL_KEY_MAX+1]; /* A buffer of currently unprocessed key presses */
437*7c478bd9Sstevel@tonic-gate   int nbuf;                  /* The number of characters in keybuf[] */
438*7c478bd9Sstevel@tonic-gate   int nread;                 /* The number of characters read from keybuf[] */
439*7c478bd9Sstevel@tonic-gate   KtAction current_action;   /* The action function that is being invoked */
440*7c478bd9Sstevel@tonic-gate   int current_count;         /* The repeat count passed to */
441*7c478bd9Sstevel@tonic-gate                              /*  current_acction.fn() */
442*7c478bd9Sstevel@tonic-gate   GlhLineID preload_id;      /* When not zero, this should be the ID of a */
443*7c478bd9Sstevel@tonic-gate                              /*  line in the history buffer for potential */
444*7c478bd9Sstevel@tonic-gate                              /*  recall. */
445*7c478bd9Sstevel@tonic-gate   int preload_history;       /* If true, preload the above history line when */
446*7c478bd9Sstevel@tonic-gate                              /*  gl_get_input_line() is next called. */
447*7c478bd9Sstevel@tonic-gate   long keyseq_count;         /* The number of key sequences entered by the */
448*7c478bd9Sstevel@tonic-gate                              /*  the user since new_GetLine() was called. */
449*7c478bd9Sstevel@tonic-gate   long last_search;          /* The value of keyseq_count during the last */
450*7c478bd9Sstevel@tonic-gate                              /*  history search operation. */
451*7c478bd9Sstevel@tonic-gate   GlEditor editor;           /* The style of editing, (eg. vi or emacs) */
452*7c478bd9Sstevel@tonic-gate   int silence_bell;          /* True if gl_ring_bell() should do nothing. */
453*7c478bd9Sstevel@tonic-gate   int automatic_history;     /* True to automatically archive entered lines */
454*7c478bd9Sstevel@tonic-gate                              /*  in the history list. */
455*7c478bd9Sstevel@tonic-gate   ViMode vi;                 /* Parameters used when editing in vi mode */
456*7c478bd9Sstevel@tonic-gate   const char *left;          /* The string that moves the cursor 1 character */
457*7c478bd9Sstevel@tonic-gate                              /*  left. */
458*7c478bd9Sstevel@tonic-gate   const char *right;         /* The string that moves the cursor 1 character */
459*7c478bd9Sstevel@tonic-gate                              /*  right. */
460*7c478bd9Sstevel@tonic-gate   const char *up;            /* The string that moves the cursor 1 character */
461*7c478bd9Sstevel@tonic-gate                              /*  up. */
462*7c478bd9Sstevel@tonic-gate   const char *down;          /* The string that moves the cursor 1 character */
463*7c478bd9Sstevel@tonic-gate                              /*  down. */
464*7c478bd9Sstevel@tonic-gate   const char *home;          /* The string that moves the cursor home */
465*7c478bd9Sstevel@tonic-gate   const char *bol;           /* Move cursor to beginning of line */
466*7c478bd9Sstevel@tonic-gate   const char *clear_eol;     /* The string that clears from the cursor to */
467*7c478bd9Sstevel@tonic-gate                              /*  the end of the line. */
468*7c478bd9Sstevel@tonic-gate   const char *clear_eod;     /* The string that clears from the cursor to */
469*7c478bd9Sstevel@tonic-gate                              /*  the end of the display. */
470*7c478bd9Sstevel@tonic-gate   const char *u_arrow;       /* The string returned by the up-arrow key */
471*7c478bd9Sstevel@tonic-gate   const char *d_arrow;       /* The string returned by the down-arrow key */
472*7c478bd9Sstevel@tonic-gate   const char *l_arrow;       /* The string returned by the left-arrow key */
473*7c478bd9Sstevel@tonic-gate   const char *r_arrow;       /* The string returned by the right-arrow key */
474*7c478bd9Sstevel@tonic-gate   const char *sound_bell;    /* The string needed to ring the terminal bell */
475*7c478bd9Sstevel@tonic-gate   const char *bold;          /* Switch to the bold font */
476*7c478bd9Sstevel@tonic-gate   const char *underline;     /* Underline subsequent characters */
477*7c478bd9Sstevel@tonic-gate   const char *standout;      /* Turn on standout mode */
478*7c478bd9Sstevel@tonic-gate   const char *dim;           /* Switch to a dim font */
479*7c478bd9Sstevel@tonic-gate   const char *reverse;       /* Turn on reverse video */
480*7c478bd9Sstevel@tonic-gate   const char *blink;         /* Switch to a blinking font */
481*7c478bd9Sstevel@tonic-gate   const char *text_attr_off; /* Turn off all text attributes */
482*7c478bd9Sstevel@tonic-gate   int nline;                 /* The height of the terminal in lines */
483*7c478bd9Sstevel@tonic-gate   int ncolumn;               /* The width of the terminal in columns */
484*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMCAP
485*7c478bd9Sstevel@tonic-gate   char *tgetent_buf;         /* The buffer that is used by tgetent() to */
486*7c478bd9Sstevel@tonic-gate                              /*  store a terminal description. */
487*7c478bd9Sstevel@tonic-gate   char *tgetstr_buf;         /* The buffer that is used by tgetstr() to */
488*7c478bd9Sstevel@tonic-gate                              /*  store terminal capabilities. */
489*7c478bd9Sstevel@tonic-gate #endif
490*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
491*7c478bd9Sstevel@tonic-gate   const char *left_n;        /* The parameter string that moves the cursor */
492*7c478bd9Sstevel@tonic-gate                              /*  n characters left. */
493*7c478bd9Sstevel@tonic-gate   const char *right_n;       /* The parameter string that moves the cursor */
494*7c478bd9Sstevel@tonic-gate                              /*  n characters right. */
495*7c478bd9Sstevel@tonic-gate #endif
496*7c478bd9Sstevel@tonic-gate   char *app_file;            /* The pathname of the application-specific */
497*7c478bd9Sstevel@tonic-gate                              /*  .teclarc configuration file, or NULL. */
498*7c478bd9Sstevel@tonic-gate   char *user_file;           /* The pathname of the user-specific */
499*7c478bd9Sstevel@tonic-gate                              /*  .teclarc configuration file, or NULL. */
500*7c478bd9Sstevel@tonic-gate   int configured;            /* True as soon as any teclarc configuration */
501*7c478bd9Sstevel@tonic-gate                              /*  file has been read. */
502*7c478bd9Sstevel@tonic-gate   int echo;                  /* True to display the line as it is being */
503*7c478bd9Sstevel@tonic-gate                              /*  entered. If 0, only the prompt will be */
504*7c478bd9Sstevel@tonic-gate                              /*  displayed, and the line will not be */
505*7c478bd9Sstevel@tonic-gate                              /*  archived in the history list. */
506*7c478bd9Sstevel@tonic-gate   int last_signal;           /* The last signal that was caught by */
507*7c478bd9Sstevel@tonic-gate                              /*  the last call to gl_get_line(), or -1 */
508*7c478bd9Sstevel@tonic-gate                              /*  if no signal has been caught yet. */
509*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
510*7c478bd9Sstevel@tonic-gate   FreeList *fd_node_mem;     /* A freelist of GlFdNode structures */
511*7c478bd9Sstevel@tonic-gate   GlFdNode *fd_nodes;        /* The list of fd event descriptions */
512*7c478bd9Sstevel@tonic-gate   fd_set rfds;               /* The set of fds to watch for readability */
513*7c478bd9Sstevel@tonic-gate   fd_set wfds;               /* The set of fds to watch for writability */
514*7c478bd9Sstevel@tonic-gate   fd_set ufds;               /* The set of fds to watch for urgent data */
515*7c478bd9Sstevel@tonic-gate   int max_fd;                /* The maximum file-descriptor being watched */
516*7c478bd9Sstevel@tonic-gate   struct {                   /* Inactivity timeout related data */
517*7c478bd9Sstevel@tonic-gate     struct timeval dt;       /* The inactivity timeout when timer.fn() */
518*7c478bd9Sstevel@tonic-gate                              /*  isn't 0 */
519*7c478bd9Sstevel@tonic-gate     GlTimeoutFn *fn;         /* The application callback to call when */
520*7c478bd9Sstevel@tonic-gate                              /*  the inactivity timer expires, or 0 if */
521*7c478bd9Sstevel@tonic-gate                              /*  timeouts are not required. */
522*7c478bd9Sstevel@tonic-gate     void *data;              /* Application provided data to be passed to */
523*7c478bd9Sstevel@tonic-gate                              /*  timer.fn(). */
524*7c478bd9Sstevel@tonic-gate   } timer;
525*7c478bd9Sstevel@tonic-gate #endif
526*7c478bd9Sstevel@tonic-gate };
527*7c478bd9Sstevel@tonic-gate 
528*7c478bd9Sstevel@tonic-gate /*
529*7c478bd9Sstevel@tonic-gate  * Define the max amount of space needed to store a termcap terminal
530*7c478bd9Sstevel@tonic-gate  * description. Unfortunately this has to be done by guesswork, so
531*7c478bd9Sstevel@tonic-gate  * there is the potential for buffer overflows if we guess too small.
532*7c478bd9Sstevel@tonic-gate  * Fortunately termcap has been replaced by terminfo on most
533*7c478bd9Sstevel@tonic-gate  * platforms, and with terminfo this isn't an issue. The value that I
534*7c478bd9Sstevel@tonic-gate  * am using here is the conventional value, as recommended by certain
535*7c478bd9Sstevel@tonic-gate  * web references.
536*7c478bd9Sstevel@tonic-gate  */
537*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMCAP
538*7c478bd9Sstevel@tonic-gate #define TERMCAP_BUF_SIZE 2048
539*7c478bd9Sstevel@tonic-gate #endif
540*7c478bd9Sstevel@tonic-gate 
541*7c478bd9Sstevel@tonic-gate /*
542*7c478bd9Sstevel@tonic-gate  * Set the size of the string segments used to store terminal capability
543*7c478bd9Sstevel@tonic-gate  * strings.
544*7c478bd9Sstevel@tonic-gate  */
545*7c478bd9Sstevel@tonic-gate #define CAPMEM_SEGMENT_SIZE 512
546*7c478bd9Sstevel@tonic-gate 
547*7c478bd9Sstevel@tonic-gate /*
548*7c478bd9Sstevel@tonic-gate  * If no terminal size information is available, substitute the
549*7c478bd9Sstevel@tonic-gate  * following vt100 default sizes.
550*7c478bd9Sstevel@tonic-gate  */
551*7c478bd9Sstevel@tonic-gate #define GL_DEF_NLINE 24
552*7c478bd9Sstevel@tonic-gate #define GL_DEF_NCOLUMN 80
553*7c478bd9Sstevel@tonic-gate 
554*7c478bd9Sstevel@tonic-gate /*
555*7c478bd9Sstevel@tonic-gate  * Enumerate the attributes needed to classify different types of
556*7c478bd9Sstevel@tonic-gate  * signals. These attributes reflect the standard default
557*7c478bd9Sstevel@tonic-gate  * characteristics of these signals (according to Richard Steven's
558*7c478bd9Sstevel@tonic-gate  * Advanced Programming in the UNIX Environment). Note that these values
559*7c478bd9Sstevel@tonic-gate  * are all powers of 2, so that they can be combined in a bitwise union.
560*7c478bd9Sstevel@tonic-gate  */
561*7c478bd9Sstevel@tonic-gate typedef enum {
562*7c478bd9Sstevel@tonic-gate   GLSA_TERM=1,   /* A signal that terminates processes */
563*7c478bd9Sstevel@tonic-gate   GLSA_SUSP=2,   /* A signal that suspends processes */
564*7c478bd9Sstevel@tonic-gate   GLSA_CONT=4,   /* A signal that is sent when suspended processes resume */
565*7c478bd9Sstevel@tonic-gate   GLSA_IGN=8,    /* A signal that is ignored */
566*7c478bd9Sstevel@tonic-gate   GLSA_CORE=16,  /* A signal that generates a core dump */
567*7c478bd9Sstevel@tonic-gate   GLSA_HARD=32,  /* A signal generated by a hardware exception */
568*7c478bd9Sstevel@tonic-gate   GLSA_SIZE=64   /* A signal indicating terminal size changes */
569*7c478bd9Sstevel@tonic-gate } GlSigAttr;
570*7c478bd9Sstevel@tonic-gate 
571*7c478bd9Sstevel@tonic-gate /*
572*7c478bd9Sstevel@tonic-gate  * List the signals that we need to catch. In general these are
573*7c478bd9Sstevel@tonic-gate  * those that by default terminate or suspend the process, since
574*7c478bd9Sstevel@tonic-gate  * in such cases we need to restore terminal settings.
575*7c478bd9Sstevel@tonic-gate  */
576*7c478bd9Sstevel@tonic-gate static const struct GlDefSignal {
577*7c478bd9Sstevel@tonic-gate   int signo;            /* The number of the signal */
578*7c478bd9Sstevel@tonic-gate   unsigned flags;       /* A bitwise union of GlSignalFlags enumerators */
579*7c478bd9Sstevel@tonic-gate   GlAfterSignal after;  /* What to do after the signal has been delivered */
580*7c478bd9Sstevel@tonic-gate   int attr;             /* The default attributes of this signal, expressed */
581*7c478bd9Sstevel@tonic-gate                         /* as a bitwise union of GlSigAttr enumerators */
582*7c478bd9Sstevel@tonic-gate   int errno_value;      /* What to set errno to */
583*7c478bd9Sstevel@tonic-gate } gl_signal_list[] = {
584*7c478bd9Sstevel@tonic-gate   {SIGABRT,   GLS_SUSPEND_INPUT,    GLS_ABORT, GLSA_TERM|GLSA_CORE, EINTR},
585*7c478bd9Sstevel@tonic-gate   {SIGALRM,   GLS_RESTORE_ENV,   GLS_CONTINUE, GLSA_TERM,           0},
586*7c478bd9Sstevel@tonic-gate   {SIGCONT,   GLS_RESTORE_ENV,   GLS_CONTINUE, GLSA_CONT|GLSA_IGN,  0},
587*7c478bd9Sstevel@tonic-gate #if defined(SIGHUP)
588*7c478bd9Sstevel@tonic-gate #ifdef ENOTTY
589*7c478bd9Sstevel@tonic-gate   {SIGHUP,    GLS_SUSPEND_INPUT,    GLS_ABORT, GLSA_TERM,           ENOTTY},
590*7c478bd9Sstevel@tonic-gate #else
591*7c478bd9Sstevel@tonic-gate   {SIGHUP,    GLS_SUSPEND_INPUT,    GLS_ABORT, GLSA_TERM,           EINTR},
592*7c478bd9Sstevel@tonic-gate #endif
593*7c478bd9Sstevel@tonic-gate #endif
594*7c478bd9Sstevel@tonic-gate   {SIGINT,    GLS_SUSPEND_INPUT,    GLS_ABORT, GLSA_TERM,           EINTR},
595*7c478bd9Sstevel@tonic-gate #if defined(SIGPIPE)
596*7c478bd9Sstevel@tonic-gate #ifdef EPIPE
597*7c478bd9Sstevel@tonic-gate   {SIGPIPE,   GLS_SUSPEND_INPUT,    GLS_ABORT, GLSA_TERM,           EPIPE},
598*7c478bd9Sstevel@tonic-gate #else
599*7c478bd9Sstevel@tonic-gate   {SIGPIPE,   GLS_SUSPEND_INPUT,    GLS_ABORT, GLSA_TERM,           EINTR},
600*7c478bd9Sstevel@tonic-gate #endif
601*7c478bd9Sstevel@tonic-gate #endif
602*7c478bd9Sstevel@tonic-gate #ifdef SIGPOLL
603*7c478bd9Sstevel@tonic-gate   {SIGPOLL,   GLS_SUSPEND_INPUT,    GLS_ABORT, GLSA_TERM,           EINTR},
604*7c478bd9Sstevel@tonic-gate #endif
605*7c478bd9Sstevel@tonic-gate #ifdef SIGPWR
606*7c478bd9Sstevel@tonic-gate   {SIGPWR,    GLS_RESTORE_ENV,   GLS_CONTINUE, GLSA_IGN,            0},
607*7c478bd9Sstevel@tonic-gate #endif
608*7c478bd9Sstevel@tonic-gate #ifdef SIGQUIT
609*7c478bd9Sstevel@tonic-gate   {SIGQUIT,   GLS_SUSPEND_INPUT,    GLS_ABORT, GLSA_TERM|GLSA_CORE, EINTR},
610*7c478bd9Sstevel@tonic-gate #endif
611*7c478bd9Sstevel@tonic-gate   {SIGTERM,   GLS_SUSPEND_INPUT,    GLS_ABORT, GLSA_TERM,           EINTR},
612*7c478bd9Sstevel@tonic-gate #ifdef SIGTSTP
613*7c478bd9Sstevel@tonic-gate   {SIGTSTP,   GLS_SUSPEND_INPUT, GLS_CONTINUE, GLSA_SUSP,           0},
614*7c478bd9Sstevel@tonic-gate #endif
615*7c478bd9Sstevel@tonic-gate #ifdef SIGTTIN
616*7c478bd9Sstevel@tonic-gate   {SIGTTIN,   GLS_SUSPEND_INPUT, GLS_CONTINUE, GLSA_SUSP,           0},
617*7c478bd9Sstevel@tonic-gate #endif
618*7c478bd9Sstevel@tonic-gate #ifdef SIGTTOU
619*7c478bd9Sstevel@tonic-gate   {SIGTTOU,   GLS_SUSPEND_INPUT, GLS_CONTINUE, GLSA_SUSP,           0},
620*7c478bd9Sstevel@tonic-gate #endif
621*7c478bd9Sstevel@tonic-gate #ifdef SIGUSR1
622*7c478bd9Sstevel@tonic-gate   {SIGUSR1,   GLS_RESTORE_ENV,   GLS_CONTINUE, GLSA_TERM,           0},
623*7c478bd9Sstevel@tonic-gate #endif
624*7c478bd9Sstevel@tonic-gate #ifdef SIGUSR2
625*7c478bd9Sstevel@tonic-gate   {SIGUSR2,   GLS_RESTORE_ENV,   GLS_CONTINUE, GLSA_TERM,           0},
626*7c478bd9Sstevel@tonic-gate #endif
627*7c478bd9Sstevel@tonic-gate #ifdef SIGVTALRM
628*7c478bd9Sstevel@tonic-gate   {SIGVTALRM, GLS_RESTORE_ENV,   GLS_CONTINUE, GLSA_TERM,           0},
629*7c478bd9Sstevel@tonic-gate #endif
630*7c478bd9Sstevel@tonic-gate #ifdef SIGWINCH
631*7c478bd9Sstevel@tonic-gate   {SIGWINCH,  GLS_RESTORE_ENV,   GLS_CONTINUE, GLSA_SIZE|GLSA_IGN,  0},
632*7c478bd9Sstevel@tonic-gate #endif
633*7c478bd9Sstevel@tonic-gate #ifdef SIGXCPU
634*7c478bd9Sstevel@tonic-gate   {SIGXCPU,   GLS_RESTORE_ENV,   GLS_CONTINUE, GLSA_TERM|GLSA_CORE, 0},
635*7c478bd9Sstevel@tonic-gate #endif
636*7c478bd9Sstevel@tonic-gate #ifdef SIGXFSZ
637*7c478bd9Sstevel@tonic-gate   {SIGXFSZ,   GLS_RESTORE_ENV,   GLS_CONTINUE, GLSA_TERM|GLSA_CORE, 0},
638*7c478bd9Sstevel@tonic-gate #endif
639*7c478bd9Sstevel@tonic-gate };
640*7c478bd9Sstevel@tonic-gate 
641*7c478bd9Sstevel@tonic-gate /*
642*7c478bd9Sstevel@tonic-gate  * Define file-scope variables for use in signal handlers.
643*7c478bd9Sstevel@tonic-gate  */
644*7c478bd9Sstevel@tonic-gate static volatile sig_atomic_t gl_pending_signal = -1;
645*7c478bd9Sstevel@tonic-gate static sigjmp_buf gl_setjmp_buffer;
646*7c478bd9Sstevel@tonic-gate 
647*7c478bd9Sstevel@tonic-gate static void gl_signal_handler(int signo);
648*7c478bd9Sstevel@tonic-gate 
649*7c478bd9Sstevel@tonic-gate static int gl_check_caught_signal(GetLine *gl);
650*7c478bd9Sstevel@tonic-gate 
651*7c478bd9Sstevel@tonic-gate /*
652*7c478bd9Sstevel@tonic-gate  * Respond to an externally caught process suspension or
653*7c478bd9Sstevel@tonic-gate  * termination signal.
654*7c478bd9Sstevel@tonic-gate  */
655*7c478bd9Sstevel@tonic-gate static void gl_suspend_process(int signo, GetLine *gl, int ngl);
656*7c478bd9Sstevel@tonic-gate 
657*7c478bd9Sstevel@tonic-gate /* Return the default attributes of a given signal */
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate static int gl_classify_signal(int signo);
660*7c478bd9Sstevel@tonic-gate 
661*7c478bd9Sstevel@tonic-gate /*
662*7c478bd9Sstevel@tonic-gate  * Unfortunately both terminfo and termcap require one to use the tputs()
663*7c478bd9Sstevel@tonic-gate  * function to output terminal control characters, and this function
664*7c478bd9Sstevel@tonic-gate  * doesn't allow one to specify a file stream. As a result, the following
665*7c478bd9Sstevel@tonic-gate  * file-scope variable is used to pass the current output file stream.
666*7c478bd9Sstevel@tonic-gate  * This is bad, but there doesn't seem to be any alternative.
667*7c478bd9Sstevel@tonic-gate  */
668*7c478bd9Sstevel@tonic-gate static GetLine *tputs_gl = NULL;
669*7c478bd9Sstevel@tonic-gate 
670*7c478bd9Sstevel@tonic-gate /*
671*7c478bd9Sstevel@tonic-gate  * Define a tab to be a string of 8 spaces.
672*7c478bd9Sstevel@tonic-gate  */
673*7c478bd9Sstevel@tonic-gate #define TAB_WIDTH 8
674*7c478bd9Sstevel@tonic-gate 
675*7c478bd9Sstevel@tonic-gate /*
676*7c478bd9Sstevel@tonic-gate  * Lookup the current size of the terminal.
677*7c478bd9Sstevel@tonic-gate  */
678*7c478bd9Sstevel@tonic-gate static void gl_query_size(GetLine *gl, int *ncolumn, int *nline);
679*7c478bd9Sstevel@tonic-gate 
680*7c478bd9Sstevel@tonic-gate /*
681*7c478bd9Sstevel@tonic-gate  * Getline calls this to temporarily override certain signal handlers
682*7c478bd9Sstevel@tonic-gate  * of the calling program.
683*7c478bd9Sstevel@tonic-gate  */
684*7c478bd9Sstevel@tonic-gate static int gl_override_signal_handlers(GetLine *gl);
685*7c478bd9Sstevel@tonic-gate 
686*7c478bd9Sstevel@tonic-gate /*
687*7c478bd9Sstevel@tonic-gate  * Getline calls this to restore the signal handlers of the calling
688*7c478bd9Sstevel@tonic-gate  * program.
689*7c478bd9Sstevel@tonic-gate  */
690*7c478bd9Sstevel@tonic-gate static int gl_restore_signal_handlers(GetLine *gl);
691*7c478bd9Sstevel@tonic-gate 
692*7c478bd9Sstevel@tonic-gate /*
693*7c478bd9Sstevel@tonic-gate  * Temporarily block the delivery of all signals that gl_get_line()
694*7c478bd9Sstevel@tonic-gate  * is currently configured to trap.
695*7c478bd9Sstevel@tonic-gate  */
696*7c478bd9Sstevel@tonic-gate static int gl_mask_signals(GetLine *gl, sigset_t *oldset);
697*7c478bd9Sstevel@tonic-gate 
698*7c478bd9Sstevel@tonic-gate /*
699*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask that was overriden by a previous
700*7c478bd9Sstevel@tonic-gate  * call to gl_mask_signals().
701*7c478bd9Sstevel@tonic-gate  */
702*7c478bd9Sstevel@tonic-gate static int gl_unmask_signals(GetLine *gl, sigset_t *oldset);
703*7c478bd9Sstevel@tonic-gate 
704*7c478bd9Sstevel@tonic-gate /*
705*7c478bd9Sstevel@tonic-gate  * Unblock the signals that gl_get_line() has been configured to catch.
706*7c478bd9Sstevel@tonic-gate  */
707*7c478bd9Sstevel@tonic-gate static int gl_catch_signals(GetLine *gl);
708*7c478bd9Sstevel@tonic-gate 
709*7c478bd9Sstevel@tonic-gate /*
710*7c478bd9Sstevel@tonic-gate  * Return the set of all trappable signals.
711*7c478bd9Sstevel@tonic-gate  */
712*7c478bd9Sstevel@tonic-gate static void gl_list_trappable_signals(sigset_t *signals);
713*7c478bd9Sstevel@tonic-gate 
714*7c478bd9Sstevel@tonic-gate /*
715*7c478bd9Sstevel@tonic-gate  * Put the terminal into raw input mode, after saving the original
716*7c478bd9Sstevel@tonic-gate  * terminal attributes in gl->oldattr.
717*7c478bd9Sstevel@tonic-gate  */
718*7c478bd9Sstevel@tonic-gate static int gl_raw_terminal_mode(GetLine *gl);
719*7c478bd9Sstevel@tonic-gate 
720*7c478bd9Sstevel@tonic-gate /*
721*7c478bd9Sstevel@tonic-gate  * Restore the terminal attributes from gl->oldattr.
722*7c478bd9Sstevel@tonic-gate  */
723*7c478bd9Sstevel@tonic-gate static int gl_restore_terminal_attributes(GetLine *gl);
724*7c478bd9Sstevel@tonic-gate 
725*7c478bd9Sstevel@tonic-gate /*
726*7c478bd9Sstevel@tonic-gate  * Switch to non-blocking I/O if possible.
727*7c478bd9Sstevel@tonic-gate  */
728*7c478bd9Sstevel@tonic-gate static int gl_nonblocking_io(GetLine *gl, int fd);
729*7c478bd9Sstevel@tonic-gate 
730*7c478bd9Sstevel@tonic-gate /*
731*7c478bd9Sstevel@tonic-gate  * Switch to blocking I/O if possible.
732*7c478bd9Sstevel@tonic-gate  */
733*7c478bd9Sstevel@tonic-gate static int gl_blocking_io(GetLine *gl, int fd);
734*7c478bd9Sstevel@tonic-gate 
735*7c478bd9Sstevel@tonic-gate /*
736*7c478bd9Sstevel@tonic-gate  * Read a line from the user in raw mode.
737*7c478bd9Sstevel@tonic-gate  */
738*7c478bd9Sstevel@tonic-gate static int gl_get_input_line(GetLine *gl, const char *prompt,
739*7c478bd9Sstevel@tonic-gate 			     const char *start_line, int start_pos);
740*7c478bd9Sstevel@tonic-gate 
741*7c478bd9Sstevel@tonic-gate /*
742*7c478bd9Sstevel@tonic-gate  * Query the user for a single character.
743*7c478bd9Sstevel@tonic-gate  */
744*7c478bd9Sstevel@tonic-gate static int gl_get_query_char(GetLine *gl, const char *prompt, int defchar);
745*7c478bd9Sstevel@tonic-gate 
746*7c478bd9Sstevel@tonic-gate /*
747*7c478bd9Sstevel@tonic-gate  * Read input from a non-interactive input stream.
748*7c478bd9Sstevel@tonic-gate  */
749*7c478bd9Sstevel@tonic-gate static int gl_read_stream_line(GetLine *gl);
750*7c478bd9Sstevel@tonic-gate 
751*7c478bd9Sstevel@tonic-gate /*
752*7c478bd9Sstevel@tonic-gate  * Read a single character from a non-interactive input stream.
753*7c478bd9Sstevel@tonic-gate  */
754*7c478bd9Sstevel@tonic-gate static int gl_read_stream_char(GetLine *gl);
755*7c478bd9Sstevel@tonic-gate 
756*7c478bd9Sstevel@tonic-gate /*
757*7c478bd9Sstevel@tonic-gate  * Prepare to edit a new line.
758*7c478bd9Sstevel@tonic-gate  */
759*7c478bd9Sstevel@tonic-gate static int gl_present_line(GetLine *gl, const char *prompt,
760*7c478bd9Sstevel@tonic-gate 			   const char *start_line, int start_pos);
761*7c478bd9Sstevel@tonic-gate 
762*7c478bd9Sstevel@tonic-gate /*
763*7c478bd9Sstevel@tonic-gate  * Reset all line input parameters for a new input line.
764*7c478bd9Sstevel@tonic-gate  */
765*7c478bd9Sstevel@tonic-gate static void gl_reset_input_line(GetLine *gl);
766*7c478bd9Sstevel@tonic-gate 
767*7c478bd9Sstevel@tonic-gate /*
768*7c478bd9Sstevel@tonic-gate  * Handle the receipt of the potential start of a new key-sequence from
769*7c478bd9Sstevel@tonic-gate  * the user.
770*7c478bd9Sstevel@tonic-gate  */
771*7c478bd9Sstevel@tonic-gate static int gl_interpret_char(GetLine *gl, char c);
772*7c478bd9Sstevel@tonic-gate 
773*7c478bd9Sstevel@tonic-gate /*
774*7c478bd9Sstevel@tonic-gate  * Bind a single control or meta character to an action.
775*7c478bd9Sstevel@tonic-gate  */
776*7c478bd9Sstevel@tonic-gate static int gl_bind_control_char(GetLine *gl, KtBinder binder,
777*7c478bd9Sstevel@tonic-gate 				char c, const char *action);
778*7c478bd9Sstevel@tonic-gate 
779*7c478bd9Sstevel@tonic-gate /*
780*7c478bd9Sstevel@tonic-gate  * Set up terminal-specific key bindings.
781*7c478bd9Sstevel@tonic-gate  */
782*7c478bd9Sstevel@tonic-gate static int gl_bind_terminal_keys(GetLine *gl);
783*7c478bd9Sstevel@tonic-gate 
784*7c478bd9Sstevel@tonic-gate /*
785*7c478bd9Sstevel@tonic-gate  * Lookup terminal control string and size information.
786*7c478bd9Sstevel@tonic-gate  */
787*7c478bd9Sstevel@tonic-gate static int gl_control_strings(GetLine *gl, const char *term);
788*7c478bd9Sstevel@tonic-gate 
789*7c478bd9Sstevel@tonic-gate /*
790*7c478bd9Sstevel@tonic-gate  * Wrappers around the terminfo and termcap functions that lookup
791*7c478bd9Sstevel@tonic-gate  * strings in the terminal information databases.
792*7c478bd9Sstevel@tonic-gate  */
793*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
794*7c478bd9Sstevel@tonic-gate static const char *gl_tigetstr(GetLine *gl, const char *name);
795*7c478bd9Sstevel@tonic-gate #elif defined(USE_TERMCAP)
796*7c478bd9Sstevel@tonic-gate static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr);
797*7c478bd9Sstevel@tonic-gate #endif
798*7c478bd9Sstevel@tonic-gate 
799*7c478bd9Sstevel@tonic-gate /*
800*7c478bd9Sstevel@tonic-gate  * Output a binary string directly to the terminal.
801*7c478bd9Sstevel@tonic-gate  */
802*7c478bd9Sstevel@tonic-gate static int gl_print_raw_string(GetLine *gl, int buffered,
803*7c478bd9Sstevel@tonic-gate 			       const char *string, int n);
804*7c478bd9Sstevel@tonic-gate 
805*7c478bd9Sstevel@tonic-gate /*
806*7c478bd9Sstevel@tonic-gate  * Print an informational message, starting and finishing on new lines.
807*7c478bd9Sstevel@tonic-gate  * After the list of strings to be printed, the last argument MUST be
808*7c478bd9Sstevel@tonic-gate  * GL_END_INFO.
809*7c478bd9Sstevel@tonic-gate  */
810*7c478bd9Sstevel@tonic-gate static int gl_print_info(GetLine *gl, ...);
811*7c478bd9Sstevel@tonic-gate #define GL_END_INFO ((const char *)0)
812*7c478bd9Sstevel@tonic-gate 
813*7c478bd9Sstevel@tonic-gate /*
814*7c478bd9Sstevel@tonic-gate  * Start a newline and place the cursor at its start.
815*7c478bd9Sstevel@tonic-gate  */
816*7c478bd9Sstevel@tonic-gate static int gl_start_newline(GetLine *gl, int buffered);
817*7c478bd9Sstevel@tonic-gate 
818*7c478bd9Sstevel@tonic-gate /*
819*7c478bd9Sstevel@tonic-gate  * Output a terminal control sequence.
820*7c478bd9Sstevel@tonic-gate  */
821*7c478bd9Sstevel@tonic-gate static int gl_print_control_sequence(GetLine *gl, int nline,
822*7c478bd9Sstevel@tonic-gate 				     const char *string);
823*7c478bd9Sstevel@tonic-gate 
824*7c478bd9Sstevel@tonic-gate /*
825*7c478bd9Sstevel@tonic-gate  * Output a character or string to the terminal after converting tabs
826*7c478bd9Sstevel@tonic-gate  * to spaces and control characters to a caret followed by the modified
827*7c478bd9Sstevel@tonic-gate  * character.
828*7c478bd9Sstevel@tonic-gate  */
829*7c478bd9Sstevel@tonic-gate static int gl_print_char(GetLine *gl, char c, char pad);
830*7c478bd9Sstevel@tonic-gate static int gl_print_string(GetLine *gl, const char *string, char pad);
831*7c478bd9Sstevel@tonic-gate 
832*7c478bd9Sstevel@tonic-gate /*
833*7c478bd9Sstevel@tonic-gate  * Delete nc characters starting from the one under the cursor.
834*7c478bd9Sstevel@tonic-gate  * Optionally copy the deleted characters to the cut buffer.
835*7c478bd9Sstevel@tonic-gate  */
836*7c478bd9Sstevel@tonic-gate static int gl_delete_chars(GetLine *gl, int nc, int cut);
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate /*
839*7c478bd9Sstevel@tonic-gate  * Add a character to the line buffer at the current cursor position,
840*7c478bd9Sstevel@tonic-gate  * inserting or overwriting according the current mode.
841*7c478bd9Sstevel@tonic-gate  */
842*7c478bd9Sstevel@tonic-gate static int gl_add_char_to_line(GetLine *gl, char c);
843*7c478bd9Sstevel@tonic-gate 
844*7c478bd9Sstevel@tonic-gate /*
845*7c478bd9Sstevel@tonic-gate  * Insert/append a string to the line buffer and terminal at the current
846*7c478bd9Sstevel@tonic-gate  * cursor position.
847*7c478bd9Sstevel@tonic-gate  */
848*7c478bd9Sstevel@tonic-gate static int gl_add_string_to_line(GetLine *gl, const char *s);
849*7c478bd9Sstevel@tonic-gate 
850*7c478bd9Sstevel@tonic-gate /*
851*7c478bd9Sstevel@tonic-gate  * Record a new character in the input-line buffer.
852*7c478bd9Sstevel@tonic-gate  */
853*7c478bd9Sstevel@tonic-gate static int gl_buffer_char(GetLine *gl, char c, int bufpos);
854*7c478bd9Sstevel@tonic-gate 
855*7c478bd9Sstevel@tonic-gate /*
856*7c478bd9Sstevel@tonic-gate  * Record a string in the input-line buffer.
857*7c478bd9Sstevel@tonic-gate  */
858*7c478bd9Sstevel@tonic-gate static int gl_buffer_string(GetLine *gl, const char *s, int n, int bufpos);
859*7c478bd9Sstevel@tonic-gate 
860*7c478bd9Sstevel@tonic-gate /*
861*7c478bd9Sstevel@tonic-gate  * Make way to insert a string in the input-line buffer.
862*7c478bd9Sstevel@tonic-gate  */
863*7c478bd9Sstevel@tonic-gate static int gl_make_gap_in_buffer(GetLine *gl, int start, int n);
864*7c478bd9Sstevel@tonic-gate 
865*7c478bd9Sstevel@tonic-gate /*
866*7c478bd9Sstevel@tonic-gate  * Remove characters from the input-line buffer, and move any characters
867*7c478bd9Sstevel@tonic-gate  * that followed them to the start of the vacated space.
868*7c478bd9Sstevel@tonic-gate  */
869*7c478bd9Sstevel@tonic-gate static void gl_remove_from_buffer(GetLine *gl, int start, int n);
870*7c478bd9Sstevel@tonic-gate 
871*7c478bd9Sstevel@tonic-gate /*
872*7c478bd9Sstevel@tonic-gate  * Terminate the input-line buffer after a specified number of characters.
873*7c478bd9Sstevel@tonic-gate  */
874*7c478bd9Sstevel@tonic-gate static int gl_truncate_buffer(GetLine *gl, int n);
875*7c478bd9Sstevel@tonic-gate 
876*7c478bd9Sstevel@tonic-gate /*
877*7c478bd9Sstevel@tonic-gate  * Delete the displayed part of the input line that follows the current
878*7c478bd9Sstevel@tonic-gate  * terminal cursor position.
879*7c478bd9Sstevel@tonic-gate  */
880*7c478bd9Sstevel@tonic-gate static int gl_truncate_display(GetLine *gl);
881*7c478bd9Sstevel@tonic-gate 
882*7c478bd9Sstevel@tonic-gate /*
883*7c478bd9Sstevel@tonic-gate  * Accomodate changes to the contents of the input line buffer
884*7c478bd9Sstevel@tonic-gate  * that weren't made by the above gl_*buffer functions.
885*7c478bd9Sstevel@tonic-gate  */
886*7c478bd9Sstevel@tonic-gate static void gl_update_buffer(GetLine *gl);
887*7c478bd9Sstevel@tonic-gate 
888*7c478bd9Sstevel@tonic-gate /*
889*7c478bd9Sstevel@tonic-gate  * Read a single character from the terminal.
890*7c478bd9Sstevel@tonic-gate  */
891*7c478bd9Sstevel@tonic-gate static int gl_read_terminal(GetLine *gl, int keep, char *c);
892*7c478bd9Sstevel@tonic-gate 
893*7c478bd9Sstevel@tonic-gate /*
894*7c478bd9Sstevel@tonic-gate  * Discard processed characters from the key-press lookahead buffer.
895*7c478bd9Sstevel@tonic-gate  */
896*7c478bd9Sstevel@tonic-gate static void gl_discard_chars(GetLine *gl, int nused);
897*7c478bd9Sstevel@tonic-gate 
898*7c478bd9Sstevel@tonic-gate /*
899*7c478bd9Sstevel@tonic-gate  * Move the terminal cursor n positions to the left or right.
900*7c478bd9Sstevel@tonic-gate  */
901*7c478bd9Sstevel@tonic-gate static int gl_terminal_move_cursor(GetLine *gl, int n);
902*7c478bd9Sstevel@tonic-gate 
903*7c478bd9Sstevel@tonic-gate /*
904*7c478bd9Sstevel@tonic-gate  * Move the terminal cursor to a given position.
905*7c478bd9Sstevel@tonic-gate  */
906*7c478bd9Sstevel@tonic-gate static int gl_set_term_curpos(GetLine *gl, int term_curpos);
907*7c478bd9Sstevel@tonic-gate 
908*7c478bd9Sstevel@tonic-gate /*
909*7c478bd9Sstevel@tonic-gate  * Set the position of the cursor both in the line input buffer and on the
910*7c478bd9Sstevel@tonic-gate  * terminal.
911*7c478bd9Sstevel@tonic-gate  */
912*7c478bd9Sstevel@tonic-gate static int gl_place_cursor(GetLine *gl, int buff_curpos);
913*7c478bd9Sstevel@tonic-gate 
914*7c478bd9Sstevel@tonic-gate /*
915*7c478bd9Sstevel@tonic-gate  * How many characters are needed to write a number as an octal string?
916*7c478bd9Sstevel@tonic-gate  */
917*7c478bd9Sstevel@tonic-gate static int gl_octal_width(unsigned num);
918*7c478bd9Sstevel@tonic-gate 
919*7c478bd9Sstevel@tonic-gate /*
920*7c478bd9Sstevel@tonic-gate  * Return the number of spaces needed to display a tab character at
921*7c478bd9Sstevel@tonic-gate  * a given location of the terminal.
922*7c478bd9Sstevel@tonic-gate  */
923*7c478bd9Sstevel@tonic-gate static int gl_displayed_tab_width(GetLine *gl, int term_curpos);
924*7c478bd9Sstevel@tonic-gate 
925*7c478bd9Sstevel@tonic-gate /*
926*7c478bd9Sstevel@tonic-gate  * Return the number of terminal characters needed to display a
927*7c478bd9Sstevel@tonic-gate  * given raw character.
928*7c478bd9Sstevel@tonic-gate  */
929*7c478bd9Sstevel@tonic-gate static int gl_displayed_char_width(GetLine *gl, char c, int term_curpos);
930*7c478bd9Sstevel@tonic-gate 
931*7c478bd9Sstevel@tonic-gate /*
932*7c478bd9Sstevel@tonic-gate  * Return the number of terminal characters needed to display a
933*7c478bd9Sstevel@tonic-gate  * given substring.
934*7c478bd9Sstevel@tonic-gate  */
935*7c478bd9Sstevel@tonic-gate static int gl_displayed_string_width(GetLine *gl, const char *string, int nc,
936*7c478bd9Sstevel@tonic-gate 				     int term_curpos);
937*7c478bd9Sstevel@tonic-gate 
938*7c478bd9Sstevel@tonic-gate /*
939*7c478bd9Sstevel@tonic-gate  * Return non-zero if 'c' is to be considered part of a word.
940*7c478bd9Sstevel@tonic-gate  */
941*7c478bd9Sstevel@tonic-gate static int gl_is_word_char(int c);
942*7c478bd9Sstevel@tonic-gate 
943*7c478bd9Sstevel@tonic-gate /*
944*7c478bd9Sstevel@tonic-gate  * Read a tecla configuration file.
945*7c478bd9Sstevel@tonic-gate  */
946*7c478bd9Sstevel@tonic-gate static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who);
947*7c478bd9Sstevel@tonic-gate 
948*7c478bd9Sstevel@tonic-gate /*
949*7c478bd9Sstevel@tonic-gate  * Read a tecla configuration string.
950*7c478bd9Sstevel@tonic-gate  */
951*7c478bd9Sstevel@tonic-gate static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who);
952*7c478bd9Sstevel@tonic-gate 
953*7c478bd9Sstevel@tonic-gate /*
954*7c478bd9Sstevel@tonic-gate  * Define the callback function used by _gl_parse_config_line() to
955*7c478bd9Sstevel@tonic-gate  * read the next character of a configuration stream.
956*7c478bd9Sstevel@tonic-gate  */
957*7c478bd9Sstevel@tonic-gate #define GLC_GETC_FN(fn) int (fn)(void *stream)
958*7c478bd9Sstevel@tonic-gate typedef GLC_GETC_FN(GlcGetcFn);
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate static GLC_GETC_FN(glc_file_getc);  /* Read from a file */
961*7c478bd9Sstevel@tonic-gate static GLC_GETC_FN(glc_buff_getc);  /* Read from a string */
962*7c478bd9Sstevel@tonic-gate 
963*7c478bd9Sstevel@tonic-gate /*
964*7c478bd9Sstevel@tonic-gate  * Parse a single configuration command line.
965*7c478bd9Sstevel@tonic-gate  */
966*7c478bd9Sstevel@tonic-gate static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn,
967*7c478bd9Sstevel@tonic-gate 				 const char *origin, KtBinder who, int *lineno);
968*7c478bd9Sstevel@tonic-gate static int gl_report_config_error(GetLine *gl, const char *origin, int lineno,
969*7c478bd9Sstevel@tonic-gate 				  const char *errmsg);
970*7c478bd9Sstevel@tonic-gate 
971*7c478bd9Sstevel@tonic-gate /*
972*7c478bd9Sstevel@tonic-gate  * Bind the actual arrow key bindings to match those of the symbolic
973*7c478bd9Sstevel@tonic-gate  * arrow-key bindings.
974*7c478bd9Sstevel@tonic-gate  */
975*7c478bd9Sstevel@tonic-gate static int _gl_bind_arrow_keys(GetLine *gl);
976*7c478bd9Sstevel@tonic-gate 
977*7c478bd9Sstevel@tonic-gate /*
978*7c478bd9Sstevel@tonic-gate  * Copy the binding of the specified symbolic arrow-key binding to
979*7c478bd9Sstevel@tonic-gate  * the terminal specific, and default arrow-key key-sequences.
980*7c478bd9Sstevel@tonic-gate  */
981*7c478bd9Sstevel@tonic-gate static int _gl_rebind_arrow_key(GetLine *gl, const char *name,
982*7c478bd9Sstevel@tonic-gate 				const char *term_seq,
983*7c478bd9Sstevel@tonic-gate 				const char *def_seq1,
984*7c478bd9Sstevel@tonic-gate 				const char *def_seq2);
985*7c478bd9Sstevel@tonic-gate 
986*7c478bd9Sstevel@tonic-gate /*
987*7c478bd9Sstevel@tonic-gate  * After the gl_read_from_file() action has been used to tell gl_get_line()
988*7c478bd9Sstevel@tonic-gate  * to temporarily read input from a file, gl_revert_input() arranges
989*7c478bd9Sstevel@tonic-gate  * for input to be reverted to the input stream last registered with
990*7c478bd9Sstevel@tonic-gate  * gl_change_terminal().
991*7c478bd9Sstevel@tonic-gate  */
992*7c478bd9Sstevel@tonic-gate static void gl_revert_input(GetLine *gl);
993*7c478bd9Sstevel@tonic-gate 
994*7c478bd9Sstevel@tonic-gate /*
995*7c478bd9Sstevel@tonic-gate  * Flush unwritten characters to the terminal.
996*7c478bd9Sstevel@tonic-gate  */
997*7c478bd9Sstevel@tonic-gate static int gl_flush_output(GetLine *gl);
998*7c478bd9Sstevel@tonic-gate 
999*7c478bd9Sstevel@tonic-gate /*
1000*7c478bd9Sstevel@tonic-gate  * The callback through which all terminal output is routed.
1001*7c478bd9Sstevel@tonic-gate  * This simply appends characters to a queue buffer, which is
1002*7c478bd9Sstevel@tonic-gate  * subsequently flushed to the output channel by gl_flush_output().
1003*7c478bd9Sstevel@tonic-gate  */
1004*7c478bd9Sstevel@tonic-gate static GL_WRITE_FN(gl_write_fn);
1005*7c478bd9Sstevel@tonic-gate 
1006*7c478bd9Sstevel@tonic-gate /*
1007*7c478bd9Sstevel@tonic-gate  * The callback function which the output character queue object
1008*7c478bd9Sstevel@tonic-gate  * calls to transfer characters to the output channel.
1009*7c478bd9Sstevel@tonic-gate  */
1010*7c478bd9Sstevel@tonic-gate static GL_WRITE_FN(gl_flush_terminal);
1011*7c478bd9Sstevel@tonic-gate 
1012*7c478bd9Sstevel@tonic-gate /*
1013*7c478bd9Sstevel@tonic-gate  * Enumerate the possible return statuses of gl_read_input().
1014*7c478bd9Sstevel@tonic-gate  */
1015*7c478bd9Sstevel@tonic-gate typedef enum {
1016*7c478bd9Sstevel@tonic-gate   GL_READ_OK,      /* A character was read successfully */
1017*7c478bd9Sstevel@tonic-gate   GL_READ_ERROR,   /* A read-error occurred */
1018*7c478bd9Sstevel@tonic-gate   GL_READ_BLOCKED, /* The read would have blocked the caller */
1019*7c478bd9Sstevel@tonic-gate   GL_READ_EOF      /* The end of the current input file was reached */
1020*7c478bd9Sstevel@tonic-gate } GlReadStatus;
1021*7c478bd9Sstevel@tonic-gate 
1022*7c478bd9Sstevel@tonic-gate static GlReadStatus gl_read_input(GetLine *gl, char *c);
1023*7c478bd9Sstevel@tonic-gate /*
1024*7c478bd9Sstevel@tonic-gate  * Private functions of gl_read_input().
1025*7c478bd9Sstevel@tonic-gate  */
1026*7c478bd9Sstevel@tonic-gate static int gl_event_handler(GetLine *gl, int fd);
1027*7c478bd9Sstevel@tonic-gate static int gl_read_unmasked(GetLine *gl, int fd, char *c);
1028*7c478bd9Sstevel@tonic-gate 
1029*7c478bd9Sstevel@tonic-gate 
1030*7c478bd9Sstevel@tonic-gate /*
1031*7c478bd9Sstevel@tonic-gate  * A private function of gl_tty_signals().
1032*7c478bd9Sstevel@tonic-gate  */
1033*7c478bd9Sstevel@tonic-gate static int gl_set_tty_signal(int signo, void (*handler)(int));
1034*7c478bd9Sstevel@tonic-gate 
1035*7c478bd9Sstevel@tonic-gate /*
1036*7c478bd9Sstevel@tonic-gate  * Change the editor style being emulated.
1037*7c478bd9Sstevel@tonic-gate  */
1038*7c478bd9Sstevel@tonic-gate static int gl_change_editor(GetLine *gl, GlEditor editor);
1039*7c478bd9Sstevel@tonic-gate 
1040*7c478bd9Sstevel@tonic-gate /*
1041*7c478bd9Sstevel@tonic-gate  * Searching in a given direction, return the index of a given (or
1042*7c478bd9Sstevel@tonic-gate  * read) character in the input line, or the character that precedes
1043*7c478bd9Sstevel@tonic-gate  * it in the specified search direction. Return -1 if not found.
1044*7c478bd9Sstevel@tonic-gate  */
1045*7c478bd9Sstevel@tonic-gate static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c);
1046*7c478bd9Sstevel@tonic-gate 
1047*7c478bd9Sstevel@tonic-gate /*
1048*7c478bd9Sstevel@tonic-gate  * Return the buffer index of the nth word ending after the cursor.
1049*7c478bd9Sstevel@tonic-gate  */
1050*7c478bd9Sstevel@tonic-gate static int gl_nth_word_end_forward(GetLine *gl, int n);
1051*7c478bd9Sstevel@tonic-gate 
1052*7c478bd9Sstevel@tonic-gate /*
1053*7c478bd9Sstevel@tonic-gate  * Return the buffer index of the nth word start after the cursor.
1054*7c478bd9Sstevel@tonic-gate  */
1055*7c478bd9Sstevel@tonic-gate static int gl_nth_word_start_forward(GetLine *gl, int n);
1056*7c478bd9Sstevel@tonic-gate 
1057*7c478bd9Sstevel@tonic-gate /*
1058*7c478bd9Sstevel@tonic-gate  * Return the buffer index of the nth word start before the cursor.
1059*7c478bd9Sstevel@tonic-gate  */
1060*7c478bd9Sstevel@tonic-gate static int gl_nth_word_start_backward(GetLine *gl, int n);
1061*7c478bd9Sstevel@tonic-gate 
1062*7c478bd9Sstevel@tonic-gate /*
1063*7c478bd9Sstevel@tonic-gate  * When called when vi command mode is enabled, this function saves the
1064*7c478bd9Sstevel@tonic-gate  * current line and cursor position for potential restoration later
1065*7c478bd9Sstevel@tonic-gate  * by the vi undo command.
1066*7c478bd9Sstevel@tonic-gate  */
1067*7c478bd9Sstevel@tonic-gate static void gl_save_for_undo(GetLine *gl);
1068*7c478bd9Sstevel@tonic-gate 
1069*7c478bd9Sstevel@tonic-gate /*
1070*7c478bd9Sstevel@tonic-gate  * If in vi mode, switch to vi command mode.
1071*7c478bd9Sstevel@tonic-gate  */
1072*7c478bd9Sstevel@tonic-gate static void gl_vi_command_mode(GetLine *gl);
1073*7c478bd9Sstevel@tonic-gate 
1074*7c478bd9Sstevel@tonic-gate /*
1075*7c478bd9Sstevel@tonic-gate  * In vi mode this is used to delete up to or onto a given or read
1076*7c478bd9Sstevel@tonic-gate  * character in the input line. Also switch to insert mode if requested
1077*7c478bd9Sstevel@tonic-gate  * after the deletion.
1078*7c478bd9Sstevel@tonic-gate  */
1079*7c478bd9Sstevel@tonic-gate static int gl_delete_find(GetLine *gl, int count, char c, int forward,
1080*7c478bd9Sstevel@tonic-gate 			  int onto, int change);
1081*7c478bd9Sstevel@tonic-gate 
1082*7c478bd9Sstevel@tonic-gate /*
1083*7c478bd9Sstevel@tonic-gate  * Copy the characters between the cursor and the count'th instance of
1084*7c478bd9Sstevel@tonic-gate  * a specified (or read) character in the input line, into the cut buffer.
1085*7c478bd9Sstevel@tonic-gate  */
1086*7c478bd9Sstevel@tonic-gate static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto);
1087*7c478bd9Sstevel@tonic-gate 
1088*7c478bd9Sstevel@tonic-gate /*
1089*7c478bd9Sstevel@tonic-gate  * Return the line index of the parenthesis that either matches the one under
1090*7c478bd9Sstevel@tonic-gate  * the cursor, or not over a parenthesis character, the index of the next
1091*7c478bd9Sstevel@tonic-gate  * close parenthesis. Return -1 if not found.
1092*7c478bd9Sstevel@tonic-gate  */
1093*7c478bd9Sstevel@tonic-gate static int gl_index_of_matching_paren(GetLine *gl);
1094*7c478bd9Sstevel@tonic-gate 
1095*7c478bd9Sstevel@tonic-gate /*
1096*7c478bd9Sstevel@tonic-gate  * Replace a malloc'd string (or NULL), with another malloc'd copy of
1097*7c478bd9Sstevel@tonic-gate  * a string (or NULL).
1098*7c478bd9Sstevel@tonic-gate  */
1099*7c478bd9Sstevel@tonic-gate static int gl_record_string(char **sptr, const char *string);
1100*7c478bd9Sstevel@tonic-gate 
1101*7c478bd9Sstevel@tonic-gate /*
1102*7c478bd9Sstevel@tonic-gate  * Enumerate text display attributes as powers of two, suitable for
1103*7c478bd9Sstevel@tonic-gate  * use in a bit-mask.
1104*7c478bd9Sstevel@tonic-gate  */
1105*7c478bd9Sstevel@tonic-gate typedef enum {
1106*7c478bd9Sstevel@tonic-gate   GL_TXT_STANDOUT=1,   /* Display text highlighted */
1107*7c478bd9Sstevel@tonic-gate   GL_TXT_UNDERLINE=2,  /* Display text underlined */
1108*7c478bd9Sstevel@tonic-gate   GL_TXT_REVERSE=4,    /* Display text with reverse video */
1109*7c478bd9Sstevel@tonic-gate   GL_TXT_BLINK=8,      /* Display blinking text */
1110*7c478bd9Sstevel@tonic-gate   GL_TXT_DIM=16,       /* Display text in a dim font */
1111*7c478bd9Sstevel@tonic-gate   GL_TXT_BOLD=32       /* Display text using a bold font */
1112*7c478bd9Sstevel@tonic-gate } GlTextAttr;
1113*7c478bd9Sstevel@tonic-gate 
1114*7c478bd9Sstevel@tonic-gate /*
1115*7c478bd9Sstevel@tonic-gate  * Display the prompt regardless of the current visibility mode.
1116*7c478bd9Sstevel@tonic-gate  */
1117*7c478bd9Sstevel@tonic-gate static int gl_display_prompt(GetLine *gl);
1118*7c478bd9Sstevel@tonic-gate 
1119*7c478bd9Sstevel@tonic-gate /*
1120*7c478bd9Sstevel@tonic-gate  * Return the number of characters used by the prompt on the terminal.
1121*7c478bd9Sstevel@tonic-gate  */
1122*7c478bd9Sstevel@tonic-gate static int gl_displayed_prompt_width(GetLine *gl);
1123*7c478bd9Sstevel@tonic-gate 
1124*7c478bd9Sstevel@tonic-gate /*
1125*7c478bd9Sstevel@tonic-gate  * Prepare to return the current input line to the caller of gl_get_line().
1126*7c478bd9Sstevel@tonic-gate  */
1127*7c478bd9Sstevel@tonic-gate static int gl_line_ended(GetLine *gl, int newline_char);
1128*7c478bd9Sstevel@tonic-gate 
1129*7c478bd9Sstevel@tonic-gate /*
1130*7c478bd9Sstevel@tonic-gate  * Arrange for the input line to be redisplayed when the current contents
1131*7c478bd9Sstevel@tonic-gate  * of the output queue have been flushed.
1132*7c478bd9Sstevel@tonic-gate  */
1133*7c478bd9Sstevel@tonic-gate static void gl_queue_redisplay(GetLine *gl);
1134*7c478bd9Sstevel@tonic-gate 
1135*7c478bd9Sstevel@tonic-gate /*
1136*7c478bd9Sstevel@tonic-gate  * Erase the displayed representation of the input line, without
1137*7c478bd9Sstevel@tonic-gate  * touching the buffered copy.
1138*7c478bd9Sstevel@tonic-gate  */
1139*7c478bd9Sstevel@tonic-gate static int gl_erase_line(GetLine *gl);
1140*7c478bd9Sstevel@tonic-gate 
1141*7c478bd9Sstevel@tonic-gate /*
1142*7c478bd9Sstevel@tonic-gate  * This function is called whenever the input line has been erased.
1143*7c478bd9Sstevel@tonic-gate  */
1144*7c478bd9Sstevel@tonic-gate static void gl_line_erased(GetLine *gl);
1145*7c478bd9Sstevel@tonic-gate 
1146*7c478bd9Sstevel@tonic-gate /*
1147*7c478bd9Sstevel@tonic-gate  * Arrange for the current input line to be discarded.
1148*7c478bd9Sstevel@tonic-gate  */
1149*7c478bd9Sstevel@tonic-gate void _gl_abandon_line(GetLine *gl);
1150*7c478bd9Sstevel@tonic-gate 
1151*7c478bd9Sstevel@tonic-gate /*
1152*7c478bd9Sstevel@tonic-gate  * The following are private internally callable versions of pertinent
1153*7c478bd9Sstevel@tonic-gate  * public functions. Unlike their public wrapper functions, they don't
1154*7c478bd9Sstevel@tonic-gate  * block signals while running, and assume that their arguments are valid.
1155*7c478bd9Sstevel@tonic-gate  * They are designed to be called from places where signals are already
1156*7c478bd9Sstevel@tonic-gate  * blocked, and where simple sanity checks have already been applied to
1157*7c478bd9Sstevel@tonic-gate  * their arguments.
1158*7c478bd9Sstevel@tonic-gate  */
1159*7c478bd9Sstevel@tonic-gate static char *_gl_get_line(GetLine *gl, const char *prompt,
1160*7c478bd9Sstevel@tonic-gate 			  const char *start_line, int start_pos);
1161*7c478bd9Sstevel@tonic-gate static int _gl_query_char(GetLine *gl, const char *prompt, char defchar);
1162*7c478bd9Sstevel@tonic-gate static int _gl_read_char(GetLine *gl);
1163*7c478bd9Sstevel@tonic-gate static int _gl_update_size(GetLine *gl);
1164*7c478bd9Sstevel@tonic-gate /*
1165*7c478bd9Sstevel@tonic-gate  * Redraw the current input line to account for a change in the terminal
1166*7c478bd9Sstevel@tonic-gate  * size. Also install the new size in gl.
1167*7c478bd9Sstevel@tonic-gate  */
1168*7c478bd9Sstevel@tonic-gate static int gl_handle_tty_resize(GetLine *gl, int ncolumn, int nline);
1169*7c478bd9Sstevel@tonic-gate 
1170*7c478bd9Sstevel@tonic-gate static int _gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp,
1171*7c478bd9Sstevel@tonic-gate 			       const char *term);
1172*7c478bd9Sstevel@tonic-gate static int _gl_configure_getline(GetLine *gl, const char *app_string,
1173*7c478bd9Sstevel@tonic-gate 				 const char *app_file, const char *user_file);
1174*7c478bd9Sstevel@tonic-gate static int _gl_save_history(GetLine *gl, const char *filename,
1175*7c478bd9Sstevel@tonic-gate 			    const char *comment, int max_lines);
1176*7c478bd9Sstevel@tonic-gate static int _gl_load_history(GetLine *gl, const char *filename,
1177*7c478bd9Sstevel@tonic-gate 			    const char *comment);
1178*7c478bd9Sstevel@tonic-gate static int _gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
1179*7c478bd9Sstevel@tonic-gate 			GlFdEventFn *callback, void *data);
1180*7c478bd9Sstevel@tonic-gate static void _gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline,
1181*7c478bd9Sstevel@tonic-gate 			      GlTerminalSize *size);
1182*7c478bd9Sstevel@tonic-gate static void _gl_replace_prompt(GetLine *gl, const char *prompt);
1183*7c478bd9Sstevel@tonic-gate static int _gl_trap_signal(GetLine *gl, int signo, unsigned flags,
1184*7c478bd9Sstevel@tonic-gate 			   GlAfterSignal after, int errno_value);
1185*7c478bd9Sstevel@tonic-gate static int _gl_raw_io(GetLine *gl, int redisplay);
1186*7c478bd9Sstevel@tonic-gate static int _gl_normal_io(GetLine *gl);
1187*7c478bd9Sstevel@tonic-gate static int _gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn,
1188*7c478bd9Sstevel@tonic-gate 				 int list_only, const char *name,
1189*7c478bd9Sstevel@tonic-gate 				 const char *keyseq);
1190*7c478bd9Sstevel@tonic-gate static int _gl_register_action(GetLine *gl, void *data, GlActionFn *fn,
1191*7c478bd9Sstevel@tonic-gate 			       const char *name, const char *keyseq);
1192*7c478bd9Sstevel@tonic-gate static int _gl_io_mode(GetLine *gl, GlIOMode mode);
1193*7c478bd9Sstevel@tonic-gate static int _gl_set_term_size(GetLine *gl, int ncolumn, int nline);
1194*7c478bd9Sstevel@tonic-gate static int _gl_append_history(GetLine *gl, const char *line);
1195*7c478bd9Sstevel@tonic-gate 
1196*7c478bd9Sstevel@tonic-gate /*
1197*7c478bd9Sstevel@tonic-gate  * Reset the completion status and associated errno value in
1198*7c478bd9Sstevel@tonic-gate  * gl->rtn_status and gl->rtn_errno.
1199*7c478bd9Sstevel@tonic-gate  */
1200*7c478bd9Sstevel@tonic-gate static void gl_clear_status(GetLine *gl);
1201*7c478bd9Sstevel@tonic-gate 
1202*7c478bd9Sstevel@tonic-gate /*
1203*7c478bd9Sstevel@tonic-gate  * Record a completion status, unless a previous abnormal completion
1204*7c478bd9Sstevel@tonic-gate  * status has already been recorded for the current call.
1205*7c478bd9Sstevel@tonic-gate  */
1206*7c478bd9Sstevel@tonic-gate static void gl_record_status(GetLine *gl, GlReturnStatus rtn_status,
1207*7c478bd9Sstevel@tonic-gate 			     int rtn_errno);
1208*7c478bd9Sstevel@tonic-gate 
1209*7c478bd9Sstevel@tonic-gate /*
1210*7c478bd9Sstevel@tonic-gate  * Set the maximum length of a line in a user's tecla configuration
1211*7c478bd9Sstevel@tonic-gate  * file (not counting comments).
1212*7c478bd9Sstevel@tonic-gate  */
1213*7c478bd9Sstevel@tonic-gate #define GL_CONF_BUFLEN 100
1214*7c478bd9Sstevel@tonic-gate 
1215*7c478bd9Sstevel@tonic-gate /*
1216*7c478bd9Sstevel@tonic-gate  * Set the maximum number of arguments supported by individual commands
1217*7c478bd9Sstevel@tonic-gate  * in tecla configuration files.
1218*7c478bd9Sstevel@tonic-gate  */
1219*7c478bd9Sstevel@tonic-gate #define GL_CONF_MAXARG 10
1220*7c478bd9Sstevel@tonic-gate 
1221*7c478bd9Sstevel@tonic-gate /*
1222*7c478bd9Sstevel@tonic-gate  * Prototype the available action functions.
1223*7c478bd9Sstevel@tonic-gate  */
1224*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_user_interrupt);
1225*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_abort);
1226*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_suspend);
1227*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_stop_output);
1228*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_start_output);
1229*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_literal_next);
1230*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_cursor_left);
1231*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_cursor_right);
1232*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_insert_mode);
1233*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_beginning_of_line);
1234*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_end_of_line);
1235*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_line);
1236*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_kill_line);
1237*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_word);
1238*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_word);
1239*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_delete_char);
1240*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_delete_char);
1241*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_delete_word);
1242*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_delete_word);
1243*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_refind);
1244*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_invert_refind);
1245*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_to_column);
1246*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_to_parenthesis);
1247*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_delete_find);
1248*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_delete_find);
1249*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_delete_to);
1250*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_delete_to);
1251*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_upcase_word);
1252*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_downcase_word);
1253*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_capitalize_word);
1254*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_redisplay);
1255*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_clear_screen);
1256*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_transpose_chars);
1257*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_set_mark);
1258*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_exchange_point_and_mark);
1259*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_kill_region);
1260*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_region_as_kill);
1261*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_yank);
1262*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_up_history);
1263*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_down_history);
1264*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_history_search_backward);
1265*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_history_re_search_backward);
1266*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_history_search_forward);
1267*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_history_re_search_forward);
1268*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_complete_word);
1269*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
1270*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_expand_filename);
1271*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_read_from_file);
1272*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_read_init_files);
1273*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_list_glob);
1274*7c478bd9Sstevel@tonic-gate #endif
1275*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_del_char_or_list_or_eof);
1276*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_list_or_eof);
1277*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_beginning_of_history);
1278*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_end_of_history);
1279*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_digit_argument);
1280*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_newline);
1281*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_repeat_history);
1282*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_insert);
1283*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_overwrite);
1284*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_change_case);
1285*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_insert_at_bol);
1286*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_append_at_eol);
1287*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_append);
1288*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_kill_line);
1289*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_goto_column);
1290*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_to_word);
1291*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_replace_char);
1292*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_rest_of_line);
1293*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_line);
1294*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_to_bol);
1295*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_refind);
1296*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_invert_refind);
1297*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_to_column);
1298*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_to_parenthesis);
1299*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_forward_change_word);
1300*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_backward_change_word);
1301*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_forward_change_find);
1302*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_backward_change_find);
1303*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_forward_change_to);
1304*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_backward_change_to);
1305*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_forward_change_char);
1306*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_backward_change_char);
1307*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_copy_char);
1308*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_copy_char);
1309*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_find_char);
1310*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_find_char);
1311*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_to_char);
1312*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_to_char);
1313*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_repeat_find_char);
1314*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_invert_refind_char);
1315*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_append_yank);
1316*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_copy_word);
1317*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_copy_word);
1318*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_to_bol);
1319*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_refind);
1320*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_invert_refind);
1321*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_to_column);
1322*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_to_parenthesis);
1323*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_rest_of_line);
1324*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_line);
1325*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_copy_find);
1326*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_copy_find);
1327*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_copy_to);
1328*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_copy_to);
1329*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_undo);
1330*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_emacs_editing_mode);
1331*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_editing_mode);
1332*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_ring_bell);
1333*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_repeat_change);
1334*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_find_parenthesis);
1335*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_list_history);
1336*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_list_completions);
1337*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_run_external_action);
1338*7c478bd9Sstevel@tonic-gate 
1339*7c478bd9Sstevel@tonic-gate /*
1340*7c478bd9Sstevel@tonic-gate  * Name the available action functions.
1341*7c478bd9Sstevel@tonic-gate  */
1342*7c478bd9Sstevel@tonic-gate static const struct {const char *name; KT_KEY_FN(*fn);} gl_actions[] = {
1343*7c478bd9Sstevel@tonic-gate   {"user-interrupt",             gl_user_interrupt},
1344*7c478bd9Sstevel@tonic-gate   {"abort",                      gl_abort},
1345*7c478bd9Sstevel@tonic-gate   {"suspend",                    gl_suspend},
1346*7c478bd9Sstevel@tonic-gate   {"stop-output",                gl_stop_output},
1347*7c478bd9Sstevel@tonic-gate   {"start-output",               gl_start_output},
1348*7c478bd9Sstevel@tonic-gate   {"literal-next",               gl_literal_next},
1349*7c478bd9Sstevel@tonic-gate   {"cursor-right",               gl_cursor_right},
1350*7c478bd9Sstevel@tonic-gate   {"cursor-left",                gl_cursor_left},
1351*7c478bd9Sstevel@tonic-gate   {"insert-mode",                gl_insert_mode},
1352*7c478bd9Sstevel@tonic-gate   {"beginning-of-line",          gl_beginning_of_line},
1353*7c478bd9Sstevel@tonic-gate   {"end-of-line",                gl_end_of_line},
1354*7c478bd9Sstevel@tonic-gate   {"delete-line",                gl_delete_line},
1355*7c478bd9Sstevel@tonic-gate   {"kill-line",                  gl_kill_line},
1356*7c478bd9Sstevel@tonic-gate   {"forward-word",               gl_forward_word},
1357*7c478bd9Sstevel@tonic-gate   {"backward-word",              gl_backward_word},
1358*7c478bd9Sstevel@tonic-gate   {"forward-delete-char",        gl_forward_delete_char},
1359*7c478bd9Sstevel@tonic-gate   {"backward-delete-char",       gl_backward_delete_char},
1360*7c478bd9Sstevel@tonic-gate   {"forward-delete-word",        gl_forward_delete_word},
1361*7c478bd9Sstevel@tonic-gate   {"backward-delete-word",       gl_backward_delete_word},
1362*7c478bd9Sstevel@tonic-gate   {"delete-refind",              gl_delete_refind},
1363*7c478bd9Sstevel@tonic-gate   {"delete-invert-refind",       gl_delete_invert_refind},
1364*7c478bd9Sstevel@tonic-gate   {"delete-to-column",           gl_delete_to_column},
1365*7c478bd9Sstevel@tonic-gate   {"delete-to-parenthesis",      gl_delete_to_parenthesis},
1366*7c478bd9Sstevel@tonic-gate   {"forward-delete-find",        gl_forward_delete_find},
1367*7c478bd9Sstevel@tonic-gate   {"backward-delete-find",       gl_backward_delete_find},
1368*7c478bd9Sstevel@tonic-gate   {"forward-delete-to",          gl_forward_delete_to},
1369*7c478bd9Sstevel@tonic-gate   {"backward-delete-to",         gl_backward_delete_to},
1370*7c478bd9Sstevel@tonic-gate   {"upcase-word",                gl_upcase_word},
1371*7c478bd9Sstevel@tonic-gate   {"downcase-word",              gl_downcase_word},
1372*7c478bd9Sstevel@tonic-gate   {"capitalize-word",            gl_capitalize_word},
1373*7c478bd9Sstevel@tonic-gate   {"redisplay",                  gl_redisplay},
1374*7c478bd9Sstevel@tonic-gate   {"clear-screen",               gl_clear_screen},
1375*7c478bd9Sstevel@tonic-gate   {"transpose-chars",            gl_transpose_chars},
1376*7c478bd9Sstevel@tonic-gate   {"set-mark",                   gl_set_mark},
1377*7c478bd9Sstevel@tonic-gate   {"exchange-point-and-mark",    gl_exchange_point_and_mark},
1378*7c478bd9Sstevel@tonic-gate   {"kill-region",                gl_kill_region},
1379*7c478bd9Sstevel@tonic-gate   {"copy-region-as-kill",        gl_copy_region_as_kill},
1380*7c478bd9Sstevel@tonic-gate   {"yank",                       gl_yank},
1381*7c478bd9Sstevel@tonic-gate   {"up-history",                 gl_up_history},
1382*7c478bd9Sstevel@tonic-gate   {"down-history",               gl_down_history},
1383*7c478bd9Sstevel@tonic-gate   {"history-search-backward",    gl_history_search_backward},
1384*7c478bd9Sstevel@tonic-gate   {"history-re-search-backward", gl_history_re_search_backward},
1385*7c478bd9Sstevel@tonic-gate   {"history-search-forward",     gl_history_search_forward},
1386*7c478bd9Sstevel@tonic-gate   {"history-re-search-forward",  gl_history_re_search_forward},
1387*7c478bd9Sstevel@tonic-gate   {"complete-word",              gl_complete_word},
1388*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
1389*7c478bd9Sstevel@tonic-gate   {"expand-filename",            gl_expand_filename},
1390*7c478bd9Sstevel@tonic-gate   {"read-from-file",             gl_read_from_file},
1391*7c478bd9Sstevel@tonic-gate   {"read-init-files",            gl_read_init_files},
1392*7c478bd9Sstevel@tonic-gate   {"list-glob",                  gl_list_glob},
1393*7c478bd9Sstevel@tonic-gate #endif
1394*7c478bd9Sstevel@tonic-gate   {"del-char-or-list-or-eof",    gl_del_char_or_list_or_eof},
1395*7c478bd9Sstevel@tonic-gate   {"beginning-of-history",       gl_beginning_of_history},
1396*7c478bd9Sstevel@tonic-gate   {"end-of-history",             gl_end_of_history},
1397*7c478bd9Sstevel@tonic-gate   {"digit-argument",             gl_digit_argument},
1398*7c478bd9Sstevel@tonic-gate   {"newline",                    gl_newline},
1399*7c478bd9Sstevel@tonic-gate   {"repeat-history",             gl_repeat_history},
1400*7c478bd9Sstevel@tonic-gate   {"vi-insert",                  gl_vi_insert},
1401*7c478bd9Sstevel@tonic-gate   {"vi-overwrite",               gl_vi_overwrite},
1402*7c478bd9Sstevel@tonic-gate   {"vi-insert-at-bol",           gl_vi_insert_at_bol},
1403*7c478bd9Sstevel@tonic-gate   {"vi-append-at-eol",           gl_vi_append_at_eol},
1404*7c478bd9Sstevel@tonic-gate   {"vi-append",                  gl_vi_append},
1405*7c478bd9Sstevel@tonic-gate   {"change-case",                gl_change_case},
1406*7c478bd9Sstevel@tonic-gate   {"backward-kill-line",         gl_backward_kill_line},
1407*7c478bd9Sstevel@tonic-gate   {"goto-column",                gl_goto_column},
1408*7c478bd9Sstevel@tonic-gate   {"forward-to-word",            gl_forward_to_word},
1409*7c478bd9Sstevel@tonic-gate   {"vi-replace-char",            gl_vi_replace_char},
1410*7c478bd9Sstevel@tonic-gate   {"vi-change-rest-of-line",     gl_vi_change_rest_of_line},
1411*7c478bd9Sstevel@tonic-gate   {"vi-change-line",             gl_vi_change_line},
1412*7c478bd9Sstevel@tonic-gate   {"vi-change-to-bol",           gl_vi_change_to_bol},
1413*7c478bd9Sstevel@tonic-gate   {"vi-change-refind",           gl_vi_change_refind},
1414*7c478bd9Sstevel@tonic-gate   {"vi-change-invert-refind",    gl_vi_change_invert_refind},
1415*7c478bd9Sstevel@tonic-gate   {"vi-change-to-column",        gl_vi_change_to_column},
1416*7c478bd9Sstevel@tonic-gate   {"vi-change-to-parenthesis",   gl_vi_change_to_parenthesis},
1417*7c478bd9Sstevel@tonic-gate   {"forward-copy-char",          gl_forward_copy_char},
1418*7c478bd9Sstevel@tonic-gate   {"backward-copy-char",         gl_backward_copy_char},
1419*7c478bd9Sstevel@tonic-gate   {"forward-find-char",          gl_forward_find_char},
1420*7c478bd9Sstevel@tonic-gate   {"backward-find-char",         gl_backward_find_char},
1421*7c478bd9Sstevel@tonic-gate   {"forward-to-char",            gl_forward_to_char},
1422*7c478bd9Sstevel@tonic-gate   {"backward-to-char",           gl_backward_to_char},
1423*7c478bd9Sstevel@tonic-gate   {"repeat-find-char",           gl_repeat_find_char},
1424*7c478bd9Sstevel@tonic-gate   {"invert-refind-char",         gl_invert_refind_char},
1425*7c478bd9Sstevel@tonic-gate   {"append-yank",                gl_append_yank},
1426*7c478bd9Sstevel@tonic-gate   {"backward-copy-word",         gl_backward_copy_word},
1427*7c478bd9Sstevel@tonic-gate   {"forward-copy-word",          gl_forward_copy_word},
1428*7c478bd9Sstevel@tonic-gate   {"copy-to-bol",                gl_copy_to_bol},
1429*7c478bd9Sstevel@tonic-gate   {"copy-refind",                gl_copy_refind},
1430*7c478bd9Sstevel@tonic-gate   {"copy-invert-refind",         gl_copy_invert_refind},
1431*7c478bd9Sstevel@tonic-gate   {"copy-to-column",             gl_copy_to_column},
1432*7c478bd9Sstevel@tonic-gate   {"copy-to-parenthesis",        gl_copy_to_parenthesis},
1433*7c478bd9Sstevel@tonic-gate   {"copy-rest-of-line",          gl_copy_rest_of_line},
1434*7c478bd9Sstevel@tonic-gate   {"copy-line",                  gl_copy_line},
1435*7c478bd9Sstevel@tonic-gate   {"backward-copy-find",         gl_backward_copy_find},
1436*7c478bd9Sstevel@tonic-gate   {"forward-copy-find",          gl_forward_copy_find},
1437*7c478bd9Sstevel@tonic-gate   {"backward-copy-to",           gl_backward_copy_to},
1438*7c478bd9Sstevel@tonic-gate   {"forward-copy-to",            gl_forward_copy_to},
1439*7c478bd9Sstevel@tonic-gate   {"list-or-eof",                gl_list_or_eof},
1440*7c478bd9Sstevel@tonic-gate   {"vi-undo",                    gl_vi_undo},
1441*7c478bd9Sstevel@tonic-gate   {"vi-backward-change-word",    gl_vi_backward_change_word},
1442*7c478bd9Sstevel@tonic-gate   {"vi-forward-change-word",     gl_vi_forward_change_word},
1443*7c478bd9Sstevel@tonic-gate   {"vi-backward-change-find",    gl_vi_backward_change_find},
1444*7c478bd9Sstevel@tonic-gate   {"vi-forward-change-find",     gl_vi_forward_change_find},
1445*7c478bd9Sstevel@tonic-gate   {"vi-backward-change-to",      gl_vi_backward_change_to},
1446*7c478bd9Sstevel@tonic-gate   {"vi-forward-change-to",       gl_vi_forward_change_to},
1447*7c478bd9Sstevel@tonic-gate   {"vi-backward-change-char",    gl_vi_backward_change_char},
1448*7c478bd9Sstevel@tonic-gate   {"vi-forward-change-char",     gl_vi_forward_change_char},
1449*7c478bd9Sstevel@tonic-gate   {"emacs-mode",                 gl_emacs_editing_mode},
1450*7c478bd9Sstevel@tonic-gate   {"vi-mode",                    gl_vi_editing_mode},
1451*7c478bd9Sstevel@tonic-gate   {"ring-bell",                  gl_ring_bell},
1452*7c478bd9Sstevel@tonic-gate   {"vi-repeat-change",           gl_vi_repeat_change},
1453*7c478bd9Sstevel@tonic-gate   {"find-parenthesis",           gl_find_parenthesis},
1454*7c478bd9Sstevel@tonic-gate   {"list-history",               gl_list_history},
1455*7c478bd9Sstevel@tonic-gate };
1456*7c478bd9Sstevel@tonic-gate 
1457*7c478bd9Sstevel@tonic-gate /*
1458*7c478bd9Sstevel@tonic-gate  * Define the default key-bindings in emacs mode.
1459*7c478bd9Sstevel@tonic-gate  */
1460*7c478bd9Sstevel@tonic-gate static const KtKeyBinding gl_emacs_bindings[] = {
1461*7c478bd9Sstevel@tonic-gate   {"right",        "cursor-right"},
1462*7c478bd9Sstevel@tonic-gate   {"^F",           "cursor-right"},
1463*7c478bd9Sstevel@tonic-gate   {"left",         "cursor-left"},
1464*7c478bd9Sstevel@tonic-gate   {"^B",           "cursor-left"},
1465*7c478bd9Sstevel@tonic-gate   {"M-i",          "insert-mode"},
1466*7c478bd9Sstevel@tonic-gate   {"M-I",          "insert-mode"},
1467*7c478bd9Sstevel@tonic-gate   {"^A",           "beginning-of-line"},
1468*7c478bd9Sstevel@tonic-gate   {"^E",           "end-of-line"},
1469*7c478bd9Sstevel@tonic-gate   {"^U",           "delete-line"},
1470*7c478bd9Sstevel@tonic-gate   {"^K",           "kill-line"},
1471*7c478bd9Sstevel@tonic-gate   {"M-f",          "forward-word"},
1472*7c478bd9Sstevel@tonic-gate   {"M-F",          "forward-word"},
1473*7c478bd9Sstevel@tonic-gate   {"M-b",          "backward-word"},
1474*7c478bd9Sstevel@tonic-gate   {"M-B",          "backward-word"},
1475*7c478bd9Sstevel@tonic-gate   {"^D",           "del-char-or-list-or-eof"},
1476*7c478bd9Sstevel@tonic-gate   {"^H",           "backward-delete-char"},
1477*7c478bd9Sstevel@tonic-gate   {"^?",           "backward-delete-char"},
1478*7c478bd9Sstevel@tonic-gate   {"M-d",          "forward-delete-word"},
1479*7c478bd9Sstevel@tonic-gate   {"M-D",          "forward-delete-word"},
1480*7c478bd9Sstevel@tonic-gate   {"M-^H",         "backward-delete-word"},
1481*7c478bd9Sstevel@tonic-gate   {"M-^?",         "backward-delete-word"},
1482*7c478bd9Sstevel@tonic-gate   {"M-u",          "upcase-word"},
1483*7c478bd9Sstevel@tonic-gate   {"M-U",          "upcase-word"},
1484*7c478bd9Sstevel@tonic-gate   {"M-l",          "downcase-word"},
1485*7c478bd9Sstevel@tonic-gate   {"M-L",          "downcase-word"},
1486*7c478bd9Sstevel@tonic-gate   {"M-c",          "capitalize-word"},
1487*7c478bd9Sstevel@tonic-gate   {"M-C",          "capitalize-word"},
1488*7c478bd9Sstevel@tonic-gate   {"^R",           "redisplay"},
1489*7c478bd9Sstevel@tonic-gate   {"^L",           "clear-screen"},
1490*7c478bd9Sstevel@tonic-gate   {"^T",           "transpose-chars"},
1491*7c478bd9Sstevel@tonic-gate   {"^@",           "set-mark"},
1492*7c478bd9Sstevel@tonic-gate   {"^X^X",         "exchange-point-and-mark"},
1493*7c478bd9Sstevel@tonic-gate   {"^W",           "kill-region"},
1494*7c478bd9Sstevel@tonic-gate   {"M-w",          "copy-region-as-kill"},
1495*7c478bd9Sstevel@tonic-gate   {"M-W",          "copy-region-as-kill"},
1496*7c478bd9Sstevel@tonic-gate   {"^Y",           "yank"},
1497*7c478bd9Sstevel@tonic-gate   {"^P",           "up-history"},
1498*7c478bd9Sstevel@tonic-gate   {"up",           "up-history"},
1499*7c478bd9Sstevel@tonic-gate   {"^N",           "down-history"},
1500*7c478bd9Sstevel@tonic-gate   {"down",         "down-history"},
1501*7c478bd9Sstevel@tonic-gate   {"M-p",          "history-search-backward"},
1502*7c478bd9Sstevel@tonic-gate   {"M-P",          "history-search-backward"},
1503*7c478bd9Sstevel@tonic-gate   {"M-n",          "history-search-forward"},
1504*7c478bd9Sstevel@tonic-gate   {"M-N",          "history-search-forward"},
1505*7c478bd9Sstevel@tonic-gate   {"\t",           "complete-word"},
1506*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
1507*7c478bd9Sstevel@tonic-gate   {"^X*",          "expand-filename"},
1508*7c478bd9Sstevel@tonic-gate   {"^X^F",         "read-from-file"},
1509*7c478bd9Sstevel@tonic-gate   {"^X^R",         "read-init-files"},
1510*7c478bd9Sstevel@tonic-gate   {"^Xg",          "list-glob"},
1511*7c478bd9Sstevel@tonic-gate   {"^XG",          "list-glob"},
1512*7c478bd9Sstevel@tonic-gate #endif
1513*7c478bd9Sstevel@tonic-gate   {"^Xh",          "list-history"},
1514*7c478bd9Sstevel@tonic-gate   {"^XH",          "list-history"},
1515*7c478bd9Sstevel@tonic-gate   {"M-<",          "beginning-of-history"},
1516*7c478bd9Sstevel@tonic-gate   {"M->",          "end-of-history"},
1517*7c478bd9Sstevel@tonic-gate   {"M-0",          "digit-argument"},
1518*7c478bd9Sstevel@tonic-gate   {"M-1",          "digit-argument"},
1519*7c478bd9Sstevel@tonic-gate   {"M-2",          "digit-argument"},
1520*7c478bd9Sstevel@tonic-gate   {"M-3",          "digit-argument"},
1521*7c478bd9Sstevel@tonic-gate   {"M-4",          "digit-argument"},
1522*7c478bd9Sstevel@tonic-gate   {"M-5",          "digit-argument"},
1523*7c478bd9Sstevel@tonic-gate   {"M-6",          "digit-argument"},
1524*7c478bd9Sstevel@tonic-gate   {"M-7",          "digit-argument"},
1525*7c478bd9Sstevel@tonic-gate   {"M-8",          "digit-argument"},
1526*7c478bd9Sstevel@tonic-gate   {"M-9",          "digit-argument"},
1527*7c478bd9Sstevel@tonic-gate   {"\r",           "newline"},
1528*7c478bd9Sstevel@tonic-gate   {"\n",           "newline"},
1529*7c478bd9Sstevel@tonic-gate   {"M-o",          "repeat-history"},
1530*7c478bd9Sstevel@tonic-gate   {"M-C-v",        "vi-mode"},
1531*7c478bd9Sstevel@tonic-gate };
1532*7c478bd9Sstevel@tonic-gate 
1533*7c478bd9Sstevel@tonic-gate /*
1534*7c478bd9Sstevel@tonic-gate  * Define the default key-bindings in vi mode. Note that in vi-mode
1535*7c478bd9Sstevel@tonic-gate  * meta-key bindings are command-mode bindings. For example M-i first
1536*7c478bd9Sstevel@tonic-gate  * switches to command mode if not already in that mode, then moves
1537*7c478bd9Sstevel@tonic-gate  * the cursor one position right, as in vi.
1538*7c478bd9Sstevel@tonic-gate  */
1539*7c478bd9Sstevel@tonic-gate static const KtKeyBinding gl_vi_bindings[] = {
1540*7c478bd9Sstevel@tonic-gate   {"^D",           "list-or-eof"},
1541*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
1542*7c478bd9Sstevel@tonic-gate   {"^G",           "list-glob"},
1543*7c478bd9Sstevel@tonic-gate #endif
1544*7c478bd9Sstevel@tonic-gate   {"^H",           "backward-delete-char"},
1545*7c478bd9Sstevel@tonic-gate   {"\t",           "complete-word"},
1546*7c478bd9Sstevel@tonic-gate   {"\r",           "newline"},
1547*7c478bd9Sstevel@tonic-gate   {"\n",           "newline"},
1548*7c478bd9Sstevel@tonic-gate   {"^L",           "clear-screen"},
1549*7c478bd9Sstevel@tonic-gate   {"^N",           "down-history"},
1550*7c478bd9Sstevel@tonic-gate   {"^P",           "up-history"},
1551*7c478bd9Sstevel@tonic-gate   {"^R",           "redisplay"},
1552*7c478bd9Sstevel@tonic-gate   {"^U",           "backward-kill-line"},
1553*7c478bd9Sstevel@tonic-gate   {"^W",           "backward-delete-word"},
1554*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
1555*7c478bd9Sstevel@tonic-gate   {"^X^F",         "read-from-file"},
1556*7c478bd9Sstevel@tonic-gate   {"^X^R",         "read-init-files"},
1557*7c478bd9Sstevel@tonic-gate   {"^X*",          "expand-filename"},
1558*7c478bd9Sstevel@tonic-gate #endif
1559*7c478bd9Sstevel@tonic-gate   {"^?",           "backward-delete-char"},
1560*7c478bd9Sstevel@tonic-gate   {"M- ",          "cursor-right"},
1561*7c478bd9Sstevel@tonic-gate   {"M-$",          "end-of-line"},
1562*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
1563*7c478bd9Sstevel@tonic-gate   {"M-*",          "expand-filename"},
1564*7c478bd9Sstevel@tonic-gate #endif
1565*7c478bd9Sstevel@tonic-gate   {"M-+",          "down-history"},
1566*7c478bd9Sstevel@tonic-gate   {"M--",          "up-history"},
1567*7c478bd9Sstevel@tonic-gate   {"M-<",          "beginning-of-history"},
1568*7c478bd9Sstevel@tonic-gate   {"M->",          "end-of-history"},
1569*7c478bd9Sstevel@tonic-gate   {"M-^",          "beginning-of-line"},
1570*7c478bd9Sstevel@tonic-gate   {"M-;",          "repeat-find-char"},
1571*7c478bd9Sstevel@tonic-gate   {"M-,",          "invert-refind-char"},
1572*7c478bd9Sstevel@tonic-gate   {"M-|",          "goto-column"},
1573*7c478bd9Sstevel@tonic-gate   {"M-~",          "change-case"},
1574*7c478bd9Sstevel@tonic-gate   {"M-.",          "vi-repeat-change"},
1575*7c478bd9Sstevel@tonic-gate   {"M-%",          "find-parenthesis"},
1576*7c478bd9Sstevel@tonic-gate   {"M-0",          "digit-argument"},
1577*7c478bd9Sstevel@tonic-gate   {"M-1",          "digit-argument"},
1578*7c478bd9Sstevel@tonic-gate   {"M-2",          "digit-argument"},
1579*7c478bd9Sstevel@tonic-gate   {"M-3",          "digit-argument"},
1580*7c478bd9Sstevel@tonic-gate   {"M-4",          "digit-argument"},
1581*7c478bd9Sstevel@tonic-gate   {"M-5",          "digit-argument"},
1582*7c478bd9Sstevel@tonic-gate   {"M-6",          "digit-argument"},
1583*7c478bd9Sstevel@tonic-gate   {"M-7",          "digit-argument"},
1584*7c478bd9Sstevel@tonic-gate   {"M-8",          "digit-argument"},
1585*7c478bd9Sstevel@tonic-gate   {"M-9",          "digit-argument"},
1586*7c478bd9Sstevel@tonic-gate   {"M-a",          "vi-append"},
1587*7c478bd9Sstevel@tonic-gate   {"M-A",          "vi-append-at-eol"},
1588*7c478bd9Sstevel@tonic-gate   {"M-b",          "backward-word"},
1589*7c478bd9Sstevel@tonic-gate   {"M-B",          "backward-word"},
1590*7c478bd9Sstevel@tonic-gate   {"M-C",          "vi-change-rest-of-line"},
1591*7c478bd9Sstevel@tonic-gate   {"M-cb",         "vi-backward-change-word"},
1592*7c478bd9Sstevel@tonic-gate   {"M-cB",         "vi-backward-change-word"},
1593*7c478bd9Sstevel@tonic-gate   {"M-cc",         "vi-change-line"},
1594*7c478bd9Sstevel@tonic-gate   {"M-ce",         "vi-forward-change-word"},
1595*7c478bd9Sstevel@tonic-gate   {"M-cE",         "vi-forward-change-word"},
1596*7c478bd9Sstevel@tonic-gate   {"M-cw",         "vi-forward-change-word"},
1597*7c478bd9Sstevel@tonic-gate   {"M-cW",         "vi-forward-change-word"},
1598*7c478bd9Sstevel@tonic-gate   {"M-cF",         "vi-backward-change-find"},
1599*7c478bd9Sstevel@tonic-gate   {"M-cf",         "vi-forward-change-find"},
1600*7c478bd9Sstevel@tonic-gate   {"M-cT",         "vi-backward-change-to"},
1601*7c478bd9Sstevel@tonic-gate   {"M-ct",         "vi-forward-change-to"},
1602*7c478bd9Sstevel@tonic-gate   {"M-c;",         "vi-change-refind"},
1603*7c478bd9Sstevel@tonic-gate   {"M-c,",         "vi-change-invert-refind"},
1604*7c478bd9Sstevel@tonic-gate   {"M-ch",         "vi-backward-change-char"},
1605*7c478bd9Sstevel@tonic-gate   {"M-c^H",        "vi-backward-change-char"},
1606*7c478bd9Sstevel@tonic-gate   {"M-c^?",        "vi-backward-change-char"},
1607*7c478bd9Sstevel@tonic-gate   {"M-cl",         "vi-forward-change-char"},
1608*7c478bd9Sstevel@tonic-gate   {"M-c ",         "vi-forward-change-char"},
1609*7c478bd9Sstevel@tonic-gate   {"M-c^",         "vi-change-to-bol"},
1610*7c478bd9Sstevel@tonic-gate   {"M-c0",         "vi-change-to-bol"},
1611*7c478bd9Sstevel@tonic-gate   {"M-c$",         "vi-change-rest-of-line"},
1612*7c478bd9Sstevel@tonic-gate   {"M-c|",         "vi-change-to-column"},
1613*7c478bd9Sstevel@tonic-gate   {"M-c%",         "vi-change-to-parenthesis"},
1614*7c478bd9Sstevel@tonic-gate   {"M-dh",         "backward-delete-char"},
1615*7c478bd9Sstevel@tonic-gate   {"M-d^H",        "backward-delete-char"},
1616*7c478bd9Sstevel@tonic-gate   {"M-d^?",        "backward-delete-char"},
1617*7c478bd9Sstevel@tonic-gate   {"M-dl",         "forward-delete-char"},
1618*7c478bd9Sstevel@tonic-gate   {"M-d ",         "forward-delete-char"},
1619*7c478bd9Sstevel@tonic-gate   {"M-dd",         "delete-line"},
1620*7c478bd9Sstevel@tonic-gate   {"M-db",         "backward-delete-word"},
1621*7c478bd9Sstevel@tonic-gate   {"M-dB",         "backward-delete-word"},
1622*7c478bd9Sstevel@tonic-gate   {"M-de",         "forward-delete-word"},
1623*7c478bd9Sstevel@tonic-gate   {"M-dE",         "forward-delete-word"},
1624*7c478bd9Sstevel@tonic-gate   {"M-dw",         "forward-delete-word"},
1625*7c478bd9Sstevel@tonic-gate   {"M-dW",         "forward-delete-word"},
1626*7c478bd9Sstevel@tonic-gate   {"M-dF",         "backward-delete-find"},
1627*7c478bd9Sstevel@tonic-gate   {"M-df",         "forward-delete-find"},
1628*7c478bd9Sstevel@tonic-gate   {"M-dT",         "backward-delete-to"},
1629*7c478bd9Sstevel@tonic-gate   {"M-dt",         "forward-delete-to"},
1630*7c478bd9Sstevel@tonic-gate   {"M-d;",         "delete-refind"},
1631*7c478bd9Sstevel@tonic-gate   {"M-d,",         "delete-invert-refind"},
1632*7c478bd9Sstevel@tonic-gate   {"M-d^",         "backward-kill-line"},
1633*7c478bd9Sstevel@tonic-gate   {"M-d0",         "backward-kill-line"},
1634*7c478bd9Sstevel@tonic-gate   {"M-d$",         "kill-line"},
1635*7c478bd9Sstevel@tonic-gate   {"M-D",          "kill-line"},
1636*7c478bd9Sstevel@tonic-gate   {"M-d|",         "delete-to-column"},
1637*7c478bd9Sstevel@tonic-gate   {"M-d%",         "delete-to-parenthesis"},
1638*7c478bd9Sstevel@tonic-gate   {"M-e",          "forward-word"},
1639*7c478bd9Sstevel@tonic-gate   {"M-E",          "forward-word"},
1640*7c478bd9Sstevel@tonic-gate   {"M-f",          "forward-find-char"},
1641*7c478bd9Sstevel@tonic-gate   {"M-F",          "backward-find-char"},
1642*7c478bd9Sstevel@tonic-gate   {"M--",          "up-history"},
1643*7c478bd9Sstevel@tonic-gate   {"M-h",          "cursor-left"},
1644*7c478bd9Sstevel@tonic-gate   {"M-H",          "beginning-of-history"},
1645*7c478bd9Sstevel@tonic-gate   {"M-i",          "vi-insert"},
1646*7c478bd9Sstevel@tonic-gate   {"M-I",          "vi-insert-at-bol"},
1647*7c478bd9Sstevel@tonic-gate   {"M-j",          "down-history"},
1648*7c478bd9Sstevel@tonic-gate   {"M-J",          "history-search-forward"},
1649*7c478bd9Sstevel@tonic-gate   {"M-k",          "up-history"},
1650*7c478bd9Sstevel@tonic-gate   {"M-K",          "history-search-backward"},
1651*7c478bd9Sstevel@tonic-gate   {"M-l",          "cursor-right"},
1652*7c478bd9Sstevel@tonic-gate   {"M-L",          "end-of-history"},
1653*7c478bd9Sstevel@tonic-gate   {"M-n",          "history-re-search-forward"},
1654*7c478bd9Sstevel@tonic-gate   {"M-N",          "history-re-search-backward"},
1655*7c478bd9Sstevel@tonic-gate   {"M-p",          "append-yank"},
1656*7c478bd9Sstevel@tonic-gate   {"M-P",          "yank"},
1657*7c478bd9Sstevel@tonic-gate   {"M-r",          "vi-replace-char"},
1658*7c478bd9Sstevel@tonic-gate   {"M-R",          "vi-overwrite"},
1659*7c478bd9Sstevel@tonic-gate   {"M-s",          "vi-forward-change-char"},
1660*7c478bd9Sstevel@tonic-gate   {"M-S",          "vi-change-line"},
1661*7c478bd9Sstevel@tonic-gate   {"M-t",          "forward-to-char"},
1662*7c478bd9Sstevel@tonic-gate   {"M-T",          "backward-to-char"},
1663*7c478bd9Sstevel@tonic-gate   {"M-u",          "vi-undo"},
1664*7c478bd9Sstevel@tonic-gate   {"M-w",          "forward-to-word"},
1665*7c478bd9Sstevel@tonic-gate   {"M-W",          "forward-to-word"},
1666*7c478bd9Sstevel@tonic-gate   {"M-x",          "forward-delete-char"},
1667*7c478bd9Sstevel@tonic-gate   {"M-X",          "backward-delete-char"},
1668*7c478bd9Sstevel@tonic-gate   {"M-yh",         "backward-copy-char"},
1669*7c478bd9Sstevel@tonic-gate   {"M-y^H",        "backward-copy-char"},
1670*7c478bd9Sstevel@tonic-gate   {"M-y^?",        "backward-copy-char"},
1671*7c478bd9Sstevel@tonic-gate   {"M-yl",         "forward-copy-char"},
1672*7c478bd9Sstevel@tonic-gate   {"M-y ",         "forward-copy-char"},
1673*7c478bd9Sstevel@tonic-gate   {"M-ye",         "forward-copy-word"},
1674*7c478bd9Sstevel@tonic-gate   {"M-yE",         "forward-copy-word"},
1675*7c478bd9Sstevel@tonic-gate   {"M-yw",         "forward-copy-word"},
1676*7c478bd9Sstevel@tonic-gate   {"M-yW",         "forward-copy-word"},
1677*7c478bd9Sstevel@tonic-gate   {"M-yb",         "backward-copy-word"},
1678*7c478bd9Sstevel@tonic-gate   {"M-yB",         "backward-copy-word"},
1679*7c478bd9Sstevel@tonic-gate   {"M-yf",         "forward-copy-find"},
1680*7c478bd9Sstevel@tonic-gate   {"M-yF",         "backward-copy-find"},
1681*7c478bd9Sstevel@tonic-gate   {"M-yt",         "forward-copy-to"},
1682*7c478bd9Sstevel@tonic-gate   {"M-yT",         "backward-copy-to"},
1683*7c478bd9Sstevel@tonic-gate   {"M-y;",         "copy-refind"},
1684*7c478bd9Sstevel@tonic-gate   {"M-y,",         "copy-invert-refind"},
1685*7c478bd9Sstevel@tonic-gate   {"M-y^",         "copy-to-bol"},
1686*7c478bd9Sstevel@tonic-gate   {"M-y0",         "copy-to-bol"},
1687*7c478bd9Sstevel@tonic-gate   {"M-y$",         "copy-rest-of-line"},
1688*7c478bd9Sstevel@tonic-gate   {"M-yy",         "copy-line"},
1689*7c478bd9Sstevel@tonic-gate   {"M-Y",          "copy-line"},
1690*7c478bd9Sstevel@tonic-gate   {"M-y|",         "copy-to-column"},
1691*7c478bd9Sstevel@tonic-gate   {"M-y%",         "copy-to-parenthesis"},
1692*7c478bd9Sstevel@tonic-gate   {"M-^E",         "emacs-mode"},
1693*7c478bd9Sstevel@tonic-gate   {"M-^H",         "cursor-left"},
1694*7c478bd9Sstevel@tonic-gate   {"M-^?",         "cursor-left"},
1695*7c478bd9Sstevel@tonic-gate   {"M-^L",         "clear-screen"},
1696*7c478bd9Sstevel@tonic-gate   {"M-^N",         "down-history"},
1697*7c478bd9Sstevel@tonic-gate   {"M-^P",         "up-history"},
1698*7c478bd9Sstevel@tonic-gate   {"M-^R",         "redisplay"},
1699*7c478bd9Sstevel@tonic-gate   {"M-^D",         "list-or-eof"},
1700*7c478bd9Sstevel@tonic-gate   {"M-\r",         "newline"},
1701*7c478bd9Sstevel@tonic-gate   {"M-\t",         "complete-word"},
1702*7c478bd9Sstevel@tonic-gate   {"M-\n",         "newline"},
1703*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
1704*7c478bd9Sstevel@tonic-gate   {"M-^X^R",       "read-init-files"},
1705*7c478bd9Sstevel@tonic-gate #endif
1706*7c478bd9Sstevel@tonic-gate   {"M-^Xh",        "list-history"},
1707*7c478bd9Sstevel@tonic-gate   {"M-^XH",        "list-history"},
1708*7c478bd9Sstevel@tonic-gate   {"down",         "down-history"},
1709*7c478bd9Sstevel@tonic-gate   {"up",           "up-history"},
1710*7c478bd9Sstevel@tonic-gate   {"left",         "cursor-left"},
1711*7c478bd9Sstevel@tonic-gate   {"right",        "cursor-right"},
1712*7c478bd9Sstevel@tonic-gate };
1713*7c478bd9Sstevel@tonic-gate 
1714*7c478bd9Sstevel@tonic-gate /*.......................................................................
1715*7c478bd9Sstevel@tonic-gate  * Create a new GetLine object.
1716*7c478bd9Sstevel@tonic-gate  *
1717*7c478bd9Sstevel@tonic-gate  * Input:
1718*7c478bd9Sstevel@tonic-gate  *  linelen  size_t    The maximum line length to allow for.
1719*7c478bd9Sstevel@tonic-gate  *  histlen  size_t    The number of bytes to allocate for recording
1720*7c478bd9Sstevel@tonic-gate  *                     a circular buffer of history lines.
1721*7c478bd9Sstevel@tonic-gate  * Output:
1722*7c478bd9Sstevel@tonic-gate  *  return  GetLine *  The new object, or NULL on error.
1723*7c478bd9Sstevel@tonic-gate  */
1724*7c478bd9Sstevel@tonic-gate GetLine *new_GetLine(size_t linelen, size_t histlen)
1725*7c478bd9Sstevel@tonic-gate {
1726*7c478bd9Sstevel@tonic-gate   GetLine *gl;  /* The object to be returned */
1727*7c478bd9Sstevel@tonic-gate   int i;
1728*7c478bd9Sstevel@tonic-gate /*
1729*7c478bd9Sstevel@tonic-gate  * Check the arguments.
1730*7c478bd9Sstevel@tonic-gate  */
1731*7c478bd9Sstevel@tonic-gate   if(linelen < 10) {
1732*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1733*7c478bd9Sstevel@tonic-gate     return NULL;
1734*7c478bd9Sstevel@tonic-gate   };
1735*7c478bd9Sstevel@tonic-gate /*
1736*7c478bd9Sstevel@tonic-gate  * Allocate the container.
1737*7c478bd9Sstevel@tonic-gate  */
1738*7c478bd9Sstevel@tonic-gate   gl = (GetLine *) malloc(sizeof(GetLine));
1739*7c478bd9Sstevel@tonic-gate   if(!gl) {
1740*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1741*7c478bd9Sstevel@tonic-gate     return NULL;
1742*7c478bd9Sstevel@tonic-gate   };
1743*7c478bd9Sstevel@tonic-gate /*
1744*7c478bd9Sstevel@tonic-gate  * Before attempting any operation that might fail, initialize the
1745*7c478bd9Sstevel@tonic-gate  * container at least up to the point at which it can safely be passed
1746*7c478bd9Sstevel@tonic-gate  * to del_GetLine().
1747*7c478bd9Sstevel@tonic-gate  */
1748*7c478bd9Sstevel@tonic-gate   gl->err = NULL;
1749*7c478bd9Sstevel@tonic-gate   gl->glh = NULL;
1750*7c478bd9Sstevel@tonic-gate   gl->cpl = NULL;
1751*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
1752*7c478bd9Sstevel@tonic-gate   gl->cplfn.fn = cpl_file_completions;
1753*7c478bd9Sstevel@tonic-gate #else
1754*7c478bd9Sstevel@tonic-gate   gl->cplfn.fn = gl_no_completions;
1755*7c478bd9Sstevel@tonic-gate #endif
1756*7c478bd9Sstevel@tonic-gate   gl->cplfn.data = NULL;
1757*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
1758*7c478bd9Sstevel@tonic-gate   gl->ef = NULL;
1759*7c478bd9Sstevel@tonic-gate #endif
1760*7c478bd9Sstevel@tonic-gate   gl->capmem = NULL;
1761*7c478bd9Sstevel@tonic-gate   gl->cq = NULL;
1762*7c478bd9Sstevel@tonic-gate   gl->input_fd = -1;
1763*7c478bd9Sstevel@tonic-gate   gl->output_fd = -1;
1764*7c478bd9Sstevel@tonic-gate   gl->input_fp = NULL;
1765*7c478bd9Sstevel@tonic-gate   gl->output_fp = NULL;
1766*7c478bd9Sstevel@tonic-gate   gl->file_fp = NULL;
1767*7c478bd9Sstevel@tonic-gate   gl->term = NULL;
1768*7c478bd9Sstevel@tonic-gate   gl->is_term = 0;
1769*7c478bd9Sstevel@tonic-gate   gl->flush_fn = gl_flush_terminal;
1770*7c478bd9Sstevel@tonic-gate   gl->io_mode = GL_NORMAL_MODE;
1771*7c478bd9Sstevel@tonic-gate   gl->raw_mode = 0;
1772*7c478bd9Sstevel@tonic-gate   gl->pending_io = GLP_WRITE;  /* We will start by writing the prompt */
1773*7c478bd9Sstevel@tonic-gate   gl_clear_status(gl);
1774*7c478bd9Sstevel@tonic-gate   gl->linelen = linelen;
1775*7c478bd9Sstevel@tonic-gate   gl->line = NULL;
1776*7c478bd9Sstevel@tonic-gate   gl->cutbuf = NULL;
1777*7c478bd9Sstevel@tonic-gate   gl->prompt = NULL;
1778*7c478bd9Sstevel@tonic-gate   gl->prompt_len = 0;
1779*7c478bd9Sstevel@tonic-gate   gl->prompt_changed = 0;
1780*7c478bd9Sstevel@tonic-gate   gl->prompt_style = GL_LITERAL_PROMPT;
1781*7c478bd9Sstevel@tonic-gate   gl->cpl_mem = NULL;
1782*7c478bd9Sstevel@tonic-gate   gl->ext_act_mem = NULL;
1783*7c478bd9Sstevel@tonic-gate   gl->sig_mem = NULL;
1784*7c478bd9Sstevel@tonic-gate   gl->sigs = NULL;
1785*7c478bd9Sstevel@tonic-gate   gl->signals_masked = 0;
1786*7c478bd9Sstevel@tonic-gate   gl->signals_overriden = 0;
1787*7c478bd9Sstevel@tonic-gate   sigemptyset(&gl->all_signal_set);
1788*7c478bd9Sstevel@tonic-gate   sigemptyset(&gl->old_signal_set);
1789*7c478bd9Sstevel@tonic-gate   sigemptyset(&gl->use_signal_set);
1790*7c478bd9Sstevel@tonic-gate   gl->bindings = NULL;
1791*7c478bd9Sstevel@tonic-gate   gl->ntotal = 0;
1792*7c478bd9Sstevel@tonic-gate   gl->buff_curpos = 0;
1793*7c478bd9Sstevel@tonic-gate   gl->term_curpos = 0;
1794*7c478bd9Sstevel@tonic-gate   gl->term_len = 0;
1795*7c478bd9Sstevel@tonic-gate   gl->buff_mark = 0;
1796*7c478bd9Sstevel@tonic-gate   gl->insert_curpos = 0;
1797*7c478bd9Sstevel@tonic-gate   gl->insert = 1;
1798*7c478bd9Sstevel@tonic-gate   gl->number = -1;
1799*7c478bd9Sstevel@tonic-gate   gl->endline = 1;
1800*7c478bd9Sstevel@tonic-gate   gl->displayed = 0;
1801*7c478bd9Sstevel@tonic-gate   gl->redisplay = 0;
1802*7c478bd9Sstevel@tonic-gate   gl->postpone = 0;
1803*7c478bd9Sstevel@tonic-gate   gl->keybuf[0]='\0';
1804*7c478bd9Sstevel@tonic-gate   gl->nbuf = 0;
1805*7c478bd9Sstevel@tonic-gate   gl->nread = 0;
1806*7c478bd9Sstevel@tonic-gate   gl->current_action.fn = 0;
1807*7c478bd9Sstevel@tonic-gate   gl->current_action.data = NULL;
1808*7c478bd9Sstevel@tonic-gate   gl->current_count = 0;
1809*7c478bd9Sstevel@tonic-gate   gl->preload_id = 0;
1810*7c478bd9Sstevel@tonic-gate   gl->preload_history = 0;
1811*7c478bd9Sstevel@tonic-gate   gl->keyseq_count = 0;
1812*7c478bd9Sstevel@tonic-gate   gl->last_search = -1;
1813*7c478bd9Sstevel@tonic-gate   gl->editor = GL_EMACS_MODE;
1814*7c478bd9Sstevel@tonic-gate   gl->silence_bell = 0;
1815*7c478bd9Sstevel@tonic-gate   gl->automatic_history = 1;
1816*7c478bd9Sstevel@tonic-gate   gl->vi.undo.line = NULL;
1817*7c478bd9Sstevel@tonic-gate   gl->vi.undo.buff_curpos = 0;
1818*7c478bd9Sstevel@tonic-gate   gl->vi.undo.ntotal = 0;
1819*7c478bd9Sstevel@tonic-gate   gl->vi.undo.saved = 0;
1820*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.action.fn = 0;
1821*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.action.data = 0;
1822*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.count = 0;
1823*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.input_curpos = 0;
1824*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.command_curpos = 0;
1825*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.input_char = '\0';
1826*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.saved = 0;
1827*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.active = 0;
1828*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;
1829*7c478bd9Sstevel@tonic-gate   gl->vi.find_forward = 0;
1830*7c478bd9Sstevel@tonic-gate   gl->vi.find_onto = 0;
1831*7c478bd9Sstevel@tonic-gate   gl->vi.find_char = '\0';
1832*7c478bd9Sstevel@tonic-gate   gl->left = NULL;
1833*7c478bd9Sstevel@tonic-gate   gl->right = NULL;
1834*7c478bd9Sstevel@tonic-gate   gl->up = NULL;
1835*7c478bd9Sstevel@tonic-gate   gl->down = NULL;
1836*7c478bd9Sstevel@tonic-gate   gl->home = NULL;
1837*7c478bd9Sstevel@tonic-gate   gl->bol = 0;
1838*7c478bd9Sstevel@tonic-gate   gl->clear_eol = NULL;
1839*7c478bd9Sstevel@tonic-gate   gl->clear_eod = NULL;
1840*7c478bd9Sstevel@tonic-gate   gl->u_arrow = NULL;
1841*7c478bd9Sstevel@tonic-gate   gl->d_arrow = NULL;
1842*7c478bd9Sstevel@tonic-gate   gl->l_arrow = NULL;
1843*7c478bd9Sstevel@tonic-gate   gl->r_arrow = NULL;
1844*7c478bd9Sstevel@tonic-gate   gl->sound_bell = NULL;
1845*7c478bd9Sstevel@tonic-gate   gl->bold = NULL;
1846*7c478bd9Sstevel@tonic-gate   gl->underline = NULL;
1847*7c478bd9Sstevel@tonic-gate   gl->standout = NULL;
1848*7c478bd9Sstevel@tonic-gate   gl->dim = NULL;
1849*7c478bd9Sstevel@tonic-gate   gl->reverse = NULL;
1850*7c478bd9Sstevel@tonic-gate   gl->blink = NULL;
1851*7c478bd9Sstevel@tonic-gate   gl->text_attr_off = NULL;
1852*7c478bd9Sstevel@tonic-gate   gl->nline = 0;
1853*7c478bd9Sstevel@tonic-gate   gl->ncolumn = 0;
1854*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
1855*7c478bd9Sstevel@tonic-gate   gl->left_n = NULL;
1856*7c478bd9Sstevel@tonic-gate   gl->right_n = NULL;
1857*7c478bd9Sstevel@tonic-gate #elif defined(USE_TERMCAP)
1858*7c478bd9Sstevel@tonic-gate   gl->tgetent_buf = NULL;
1859*7c478bd9Sstevel@tonic-gate   gl->tgetstr_buf = NULL;
1860*7c478bd9Sstevel@tonic-gate #endif
1861*7c478bd9Sstevel@tonic-gate   gl->app_file = NULL;
1862*7c478bd9Sstevel@tonic-gate   gl->user_file = NULL;
1863*7c478bd9Sstevel@tonic-gate   gl->configured = 0;
1864*7c478bd9Sstevel@tonic-gate   gl->echo = 1;
1865*7c478bd9Sstevel@tonic-gate   gl->last_signal = -1;
1866*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
1867*7c478bd9Sstevel@tonic-gate   gl->fd_node_mem = NULL;
1868*7c478bd9Sstevel@tonic-gate   gl->fd_nodes = NULL;
1869*7c478bd9Sstevel@tonic-gate   FD_ZERO(&gl->rfds);
1870*7c478bd9Sstevel@tonic-gate   FD_ZERO(&gl->wfds);
1871*7c478bd9Sstevel@tonic-gate   FD_ZERO(&gl->ufds);
1872*7c478bd9Sstevel@tonic-gate   gl->max_fd = 0;
1873*7c478bd9Sstevel@tonic-gate   gl->timer.dt.tv_sec = 0;
1874*7c478bd9Sstevel@tonic-gate   gl->timer.dt.tv_usec = 0;
1875*7c478bd9Sstevel@tonic-gate   gl->timer.fn = 0;
1876*7c478bd9Sstevel@tonic-gate   gl->timer.data = NULL;
1877*7c478bd9Sstevel@tonic-gate #endif
1878*7c478bd9Sstevel@tonic-gate /*
1879*7c478bd9Sstevel@tonic-gate  * Allocate an error reporting buffer.
1880*7c478bd9Sstevel@tonic-gate  */
1881*7c478bd9Sstevel@tonic-gate   gl->err = _new_ErrMsg();
1882*7c478bd9Sstevel@tonic-gate   if(!gl->err)
1883*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1884*7c478bd9Sstevel@tonic-gate /*
1885*7c478bd9Sstevel@tonic-gate  * Allocate the history buffer.
1886*7c478bd9Sstevel@tonic-gate  */
1887*7c478bd9Sstevel@tonic-gate   gl->glh = _new_GlHistory(histlen);
1888*7c478bd9Sstevel@tonic-gate   if(!gl->glh)
1889*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1890*7c478bd9Sstevel@tonic-gate /*
1891*7c478bd9Sstevel@tonic-gate  * Allocate the resource object for file-completion.
1892*7c478bd9Sstevel@tonic-gate  */
1893*7c478bd9Sstevel@tonic-gate   gl->cpl = new_WordCompletion();
1894*7c478bd9Sstevel@tonic-gate   if(!gl->cpl)
1895*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1896*7c478bd9Sstevel@tonic-gate /*
1897*7c478bd9Sstevel@tonic-gate  * Allocate the resource object for file-completion.
1898*7c478bd9Sstevel@tonic-gate  */
1899*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
1900*7c478bd9Sstevel@tonic-gate   gl->ef = new_ExpandFile();
1901*7c478bd9Sstevel@tonic-gate   if(!gl->ef)
1902*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1903*7c478bd9Sstevel@tonic-gate #endif
1904*7c478bd9Sstevel@tonic-gate /*
1905*7c478bd9Sstevel@tonic-gate  * Allocate a string-segment memory allocator for use in storing terminal
1906*7c478bd9Sstevel@tonic-gate  * capablity strings.
1907*7c478bd9Sstevel@tonic-gate  */
1908*7c478bd9Sstevel@tonic-gate   gl->capmem = _new_StringGroup(CAPMEM_SEGMENT_SIZE);
1909*7c478bd9Sstevel@tonic-gate   if(!gl->capmem)
1910*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1911*7c478bd9Sstevel@tonic-gate /*
1912*7c478bd9Sstevel@tonic-gate  * Allocate the character queue that is used to buffer terminal output.
1913*7c478bd9Sstevel@tonic-gate  */
1914*7c478bd9Sstevel@tonic-gate   gl->cq = _new_GlCharQueue();
1915*7c478bd9Sstevel@tonic-gate   if(!gl->cq)
1916*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1917*7c478bd9Sstevel@tonic-gate /*
1918*7c478bd9Sstevel@tonic-gate  * Allocate a line buffer, leaving 2 extra characters for the terminating
1919*7c478bd9Sstevel@tonic-gate  * '\n' and '\0' characters
1920*7c478bd9Sstevel@tonic-gate  */
1921*7c478bd9Sstevel@tonic-gate   gl->line = (char *) malloc(linelen + 2);
1922*7c478bd9Sstevel@tonic-gate   if(!gl->line) {
1923*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1924*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1925*7c478bd9Sstevel@tonic-gate   };
1926*7c478bd9Sstevel@tonic-gate /*
1927*7c478bd9Sstevel@tonic-gate  * Start with an empty input line.
1928*7c478bd9Sstevel@tonic-gate  */
1929*7c478bd9Sstevel@tonic-gate   gl_truncate_buffer(gl, 0);
1930*7c478bd9Sstevel@tonic-gate /*
1931*7c478bd9Sstevel@tonic-gate  * Allocate a cut buffer.
1932*7c478bd9Sstevel@tonic-gate  */
1933*7c478bd9Sstevel@tonic-gate   gl->cutbuf = (char *) malloc(linelen + 2);
1934*7c478bd9Sstevel@tonic-gate   if(!gl->cutbuf) {
1935*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1936*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1937*7c478bd9Sstevel@tonic-gate   };
1938*7c478bd9Sstevel@tonic-gate   gl->cutbuf[0] = '\0';
1939*7c478bd9Sstevel@tonic-gate /*
1940*7c478bd9Sstevel@tonic-gate  * Allocate an initial empty prompt.
1941*7c478bd9Sstevel@tonic-gate  */
1942*7c478bd9Sstevel@tonic-gate   _gl_replace_prompt(gl, NULL);
1943*7c478bd9Sstevel@tonic-gate   if(!gl->prompt) {
1944*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1945*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1946*7c478bd9Sstevel@tonic-gate   };
1947*7c478bd9Sstevel@tonic-gate /*
1948*7c478bd9Sstevel@tonic-gate  * Allocate a vi undo buffer.
1949*7c478bd9Sstevel@tonic-gate  */
1950*7c478bd9Sstevel@tonic-gate   gl->vi.undo.line = (char *) malloc(linelen + 2);
1951*7c478bd9Sstevel@tonic-gate   if(!gl->vi.undo.line) {
1952*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
1953*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1954*7c478bd9Sstevel@tonic-gate   };
1955*7c478bd9Sstevel@tonic-gate   gl->vi.undo.line[0] = '\0';
1956*7c478bd9Sstevel@tonic-gate /*
1957*7c478bd9Sstevel@tonic-gate  * Allocate a freelist from which to allocate nodes for the list
1958*7c478bd9Sstevel@tonic-gate  * of completion functions.
1959*7c478bd9Sstevel@tonic-gate  */
1960*7c478bd9Sstevel@tonic-gate   gl->cpl_mem = _new_FreeList(sizeof(GlCplCallback), GL_CPL_FREELIST_BLOCKING);
1961*7c478bd9Sstevel@tonic-gate   if(!gl->cpl_mem)
1962*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1963*7c478bd9Sstevel@tonic-gate /*
1964*7c478bd9Sstevel@tonic-gate  * Allocate a freelist from which to allocate nodes for the list
1965*7c478bd9Sstevel@tonic-gate  * of external action functions.
1966*7c478bd9Sstevel@tonic-gate  */
1967*7c478bd9Sstevel@tonic-gate   gl->ext_act_mem = _new_FreeList(sizeof(GlExternalAction),
1968*7c478bd9Sstevel@tonic-gate 				  GL_EXT_ACT_FREELIST_BLOCKING);
1969*7c478bd9Sstevel@tonic-gate   if(!gl->ext_act_mem)
1970*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1971*7c478bd9Sstevel@tonic-gate /*
1972*7c478bd9Sstevel@tonic-gate  * Allocate a freelist from which to allocate nodes for the list
1973*7c478bd9Sstevel@tonic-gate  * of signals.
1974*7c478bd9Sstevel@tonic-gate  */
1975*7c478bd9Sstevel@tonic-gate   gl->sig_mem = _new_FreeList(sizeof(GlSignalNode), GLS_FREELIST_BLOCKING);
1976*7c478bd9Sstevel@tonic-gate   if(!gl->sig_mem)
1977*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1978*7c478bd9Sstevel@tonic-gate /*
1979*7c478bd9Sstevel@tonic-gate  * Install initial dispositions for the default list of signals that
1980*7c478bd9Sstevel@tonic-gate  * gl_get_line() traps.
1981*7c478bd9Sstevel@tonic-gate  */
1982*7c478bd9Sstevel@tonic-gate   for(i=0; i<sizeof(gl_signal_list)/sizeof(gl_signal_list[0]); i++) {
1983*7c478bd9Sstevel@tonic-gate     const struct GlDefSignal *sig = gl_signal_list + i;
1984*7c478bd9Sstevel@tonic-gate     if(_gl_trap_signal(gl, sig->signo, sig->flags, sig->after,
1985*7c478bd9Sstevel@tonic-gate 		       sig->errno_value))
1986*7c478bd9Sstevel@tonic-gate       return del_GetLine(gl);
1987*7c478bd9Sstevel@tonic-gate   };
1988*7c478bd9Sstevel@tonic-gate /*
1989*7c478bd9Sstevel@tonic-gate  * Allocate an empty table of key bindings.
1990*7c478bd9Sstevel@tonic-gate  */
1991*7c478bd9Sstevel@tonic-gate   gl->bindings = _new_KeyTab();
1992*7c478bd9Sstevel@tonic-gate   if(!gl->bindings)
1993*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
1994*7c478bd9Sstevel@tonic-gate /*
1995*7c478bd9Sstevel@tonic-gate  * Define the available actions that can be bound to key sequences.
1996*7c478bd9Sstevel@tonic-gate  */
1997*7c478bd9Sstevel@tonic-gate   for(i=0; i<sizeof(gl_actions)/sizeof(gl_actions[0]); i++) {
1998*7c478bd9Sstevel@tonic-gate     if(_kt_set_action(gl->bindings, gl_actions[i].name, gl_actions[i].fn, NULL))
1999*7c478bd9Sstevel@tonic-gate       return del_GetLine(gl);
2000*7c478bd9Sstevel@tonic-gate   };
2001*7c478bd9Sstevel@tonic-gate /*
2002*7c478bd9Sstevel@tonic-gate  * Set up the default bindings.
2003*7c478bd9Sstevel@tonic-gate  */
2004*7c478bd9Sstevel@tonic-gate   if(gl_change_editor(gl, gl->editor))
2005*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
2006*7c478bd9Sstevel@tonic-gate /*
2007*7c478bd9Sstevel@tonic-gate  * Allocate termcap buffers.
2008*7c478bd9Sstevel@tonic-gate  */
2009*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMCAP
2010*7c478bd9Sstevel@tonic-gate   gl->tgetent_buf = (char *) malloc(TERMCAP_BUF_SIZE);
2011*7c478bd9Sstevel@tonic-gate   gl->tgetstr_buf = (char *) malloc(TERMCAP_BUF_SIZE);
2012*7c478bd9Sstevel@tonic-gate   if(!gl->tgetent_buf || !gl->tgetstr_buf) {
2013*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
2014*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
2015*7c478bd9Sstevel@tonic-gate   };
2016*7c478bd9Sstevel@tonic-gate #endif
2017*7c478bd9Sstevel@tonic-gate /*
2018*7c478bd9Sstevel@tonic-gate  * Set up for I/O assuming stdin and stdout.
2019*7c478bd9Sstevel@tonic-gate  */
2020*7c478bd9Sstevel@tonic-gate   if(_gl_change_terminal(gl, stdin, stdout, getenv("TERM")))
2021*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
2022*7c478bd9Sstevel@tonic-gate /*
2023*7c478bd9Sstevel@tonic-gate  * Create a freelist for use in allocating GlFdNode list nodes.
2024*7c478bd9Sstevel@tonic-gate  */
2025*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
2026*7c478bd9Sstevel@tonic-gate   gl->fd_node_mem = _new_FreeList(sizeof(GlFdNode), GLFD_FREELIST_BLOCKING);
2027*7c478bd9Sstevel@tonic-gate   if(!gl->fd_node_mem)
2028*7c478bd9Sstevel@tonic-gate     return del_GetLine(gl);
2029*7c478bd9Sstevel@tonic-gate #endif
2030*7c478bd9Sstevel@tonic-gate /*
2031*7c478bd9Sstevel@tonic-gate  * We are done for now.
2032*7c478bd9Sstevel@tonic-gate  */
2033*7c478bd9Sstevel@tonic-gate   return gl;
2034*7c478bd9Sstevel@tonic-gate }
2035*7c478bd9Sstevel@tonic-gate 
2036*7c478bd9Sstevel@tonic-gate /*.......................................................................
2037*7c478bd9Sstevel@tonic-gate  * Delete a GetLine object.
2038*7c478bd9Sstevel@tonic-gate  *
2039*7c478bd9Sstevel@tonic-gate  * Input:
2040*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  The object to be deleted.
2041*7c478bd9Sstevel@tonic-gate  * Output:
2042*7c478bd9Sstevel@tonic-gate  *  return GetLine *  The deleted object (always NULL).
2043*7c478bd9Sstevel@tonic-gate  */
2044*7c478bd9Sstevel@tonic-gate GetLine *del_GetLine(GetLine *gl)
2045*7c478bd9Sstevel@tonic-gate {
2046*7c478bd9Sstevel@tonic-gate   if(gl) {
2047*7c478bd9Sstevel@tonic-gate /*
2048*7c478bd9Sstevel@tonic-gate  * If the terminal is in raw server mode, reset it.
2049*7c478bd9Sstevel@tonic-gate  */
2050*7c478bd9Sstevel@tonic-gate     _gl_normal_io(gl);
2051*7c478bd9Sstevel@tonic-gate /*
2052*7c478bd9Sstevel@tonic-gate  * Deallocate all objects contained by gl.
2053*7c478bd9Sstevel@tonic-gate  */
2054*7c478bd9Sstevel@tonic-gate     gl->err = _del_ErrMsg(gl->err);
2055*7c478bd9Sstevel@tonic-gate     gl->glh = _del_GlHistory(gl->glh);
2056*7c478bd9Sstevel@tonic-gate     gl->cpl = del_WordCompletion(gl->cpl);
2057*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
2058*7c478bd9Sstevel@tonic-gate     gl->ef = del_ExpandFile(gl->ef);
2059*7c478bd9Sstevel@tonic-gate #endif
2060*7c478bd9Sstevel@tonic-gate     gl->capmem = _del_StringGroup(gl->capmem);
2061*7c478bd9Sstevel@tonic-gate     gl->cq = _del_GlCharQueue(gl->cq);
2062*7c478bd9Sstevel@tonic-gate     if(gl->file_fp)
2063*7c478bd9Sstevel@tonic-gate       fclose(gl->file_fp);
2064*7c478bd9Sstevel@tonic-gate     if(gl->term)
2065*7c478bd9Sstevel@tonic-gate       free(gl->term);
2066*7c478bd9Sstevel@tonic-gate     if(gl->line)
2067*7c478bd9Sstevel@tonic-gate       free(gl->line);
2068*7c478bd9Sstevel@tonic-gate     if(gl->cutbuf)
2069*7c478bd9Sstevel@tonic-gate       free(gl->cutbuf);
2070*7c478bd9Sstevel@tonic-gate     if(gl->prompt)
2071*7c478bd9Sstevel@tonic-gate       free(gl->prompt);
2072*7c478bd9Sstevel@tonic-gate     gl->cpl_mem = _del_FreeList(gl->cpl_mem, 1);
2073*7c478bd9Sstevel@tonic-gate     gl->ext_act_mem = _del_FreeList(gl->ext_act_mem, 1);
2074*7c478bd9Sstevel@tonic-gate     gl->sig_mem = _del_FreeList(gl->sig_mem, 1);
2075*7c478bd9Sstevel@tonic-gate     gl->sigs = NULL;       /* Already freed by freeing sig_mem */
2076*7c478bd9Sstevel@tonic-gate     gl->bindings = _del_KeyTab(gl->bindings);
2077*7c478bd9Sstevel@tonic-gate     if(gl->vi.undo.line)
2078*7c478bd9Sstevel@tonic-gate       free(gl->vi.undo.line);
2079*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMCAP
2080*7c478bd9Sstevel@tonic-gate     if(gl->tgetent_buf)
2081*7c478bd9Sstevel@tonic-gate       free(gl->tgetent_buf);
2082*7c478bd9Sstevel@tonic-gate     if(gl->tgetstr_buf)
2083*7c478bd9Sstevel@tonic-gate       free(gl->tgetstr_buf);
2084*7c478bd9Sstevel@tonic-gate #endif
2085*7c478bd9Sstevel@tonic-gate     if(gl->app_file)
2086*7c478bd9Sstevel@tonic-gate       free(gl->app_file);
2087*7c478bd9Sstevel@tonic-gate     if(gl->user_file)
2088*7c478bd9Sstevel@tonic-gate       free(gl->user_file);
2089*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
2090*7c478bd9Sstevel@tonic-gate     gl->fd_node_mem = _del_FreeList(gl->fd_node_mem, 1);
2091*7c478bd9Sstevel@tonic-gate     gl->fd_nodes = NULL;  /* Already freed by freeing gl->fd_node_mem */
2092*7c478bd9Sstevel@tonic-gate #endif
2093*7c478bd9Sstevel@tonic-gate /*
2094*7c478bd9Sstevel@tonic-gate  * Delete the now empty container.
2095*7c478bd9Sstevel@tonic-gate  */
2096*7c478bd9Sstevel@tonic-gate     free(gl);
2097*7c478bd9Sstevel@tonic-gate   };
2098*7c478bd9Sstevel@tonic-gate   return NULL;
2099*7c478bd9Sstevel@tonic-gate }
2100*7c478bd9Sstevel@tonic-gate 
2101*7c478bd9Sstevel@tonic-gate /*.......................................................................
2102*7c478bd9Sstevel@tonic-gate  * Bind a control or meta character to an action.
2103*7c478bd9Sstevel@tonic-gate  *
2104*7c478bd9Sstevel@tonic-gate  * Input:
2105*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of this program.
2106*7c478bd9Sstevel@tonic-gate  *  binder    KtBinder    The source of the binding.
2107*7c478bd9Sstevel@tonic-gate  *  c             char    The control or meta character.
2108*7c478bd9Sstevel@tonic-gate  *                        If this is '\0', the call is ignored.
2109*7c478bd9Sstevel@tonic-gate  *  action  const char *  The action name to bind the key to.
2110*7c478bd9Sstevel@tonic-gate  * Output:
2111*7c478bd9Sstevel@tonic-gate  *  return         int    0 - OK.
2112*7c478bd9Sstevel@tonic-gate  *                        1 - Error.
2113*7c478bd9Sstevel@tonic-gate  */
2114*7c478bd9Sstevel@tonic-gate static int gl_bind_control_char(GetLine *gl, KtBinder binder, char c,
2115*7c478bd9Sstevel@tonic-gate 				const char *action)
2116*7c478bd9Sstevel@tonic-gate {
2117*7c478bd9Sstevel@tonic-gate   char keyseq[2];
2118*7c478bd9Sstevel@tonic-gate /*
2119*7c478bd9Sstevel@tonic-gate  * Quietly reject binding to the NUL control character, since this
2120*7c478bd9Sstevel@tonic-gate  * is an ambiguous prefix of all bindings.
2121*7c478bd9Sstevel@tonic-gate  */
2122*7c478bd9Sstevel@tonic-gate   if(c == '\0')
2123*7c478bd9Sstevel@tonic-gate     return 0;
2124*7c478bd9Sstevel@tonic-gate /*
2125*7c478bd9Sstevel@tonic-gate  * Making sure not to bind characters which aren't either control or
2126*7c478bd9Sstevel@tonic-gate  * meta characters.
2127*7c478bd9Sstevel@tonic-gate  */
2128*7c478bd9Sstevel@tonic-gate   if(IS_CTRL_CHAR(c) || IS_META_CHAR(c)) {
2129*7c478bd9Sstevel@tonic-gate     keyseq[0] = c;
2130*7c478bd9Sstevel@tonic-gate     keyseq[1] = '\0';
2131*7c478bd9Sstevel@tonic-gate   } else {
2132*7c478bd9Sstevel@tonic-gate     return 0;
2133*7c478bd9Sstevel@tonic-gate   };
2134*7c478bd9Sstevel@tonic-gate /*
2135*7c478bd9Sstevel@tonic-gate  * Install the binding.
2136*7c478bd9Sstevel@tonic-gate  */
2137*7c478bd9Sstevel@tonic-gate   if(_kt_set_keybinding(gl->bindings, binder, keyseq, action)) {
2138*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG);
2139*7c478bd9Sstevel@tonic-gate     return 1;
2140*7c478bd9Sstevel@tonic-gate   };
2141*7c478bd9Sstevel@tonic-gate   return 0;
2142*7c478bd9Sstevel@tonic-gate }
2143*7c478bd9Sstevel@tonic-gate 
2144*7c478bd9Sstevel@tonic-gate /*.......................................................................
2145*7c478bd9Sstevel@tonic-gate  * Read a line from the user.
2146*7c478bd9Sstevel@tonic-gate  *
2147*7c478bd9Sstevel@tonic-gate  * Input:
2148*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  A resource object returned by new_GetLine().
2149*7c478bd9Sstevel@tonic-gate  *  prompt      char *  The prompt to prefix the line with.
2150*7c478bd9Sstevel@tonic-gate  *  start_line  char *  The initial contents of the input line, or NULL
2151*7c478bd9Sstevel@tonic-gate  *                      if it should start out empty.
2152*7c478bd9Sstevel@tonic-gate  *  start_pos    int    If start_line isn't NULL, this specifies the
2153*7c478bd9Sstevel@tonic-gate  *                      index of the character over which the cursor
2154*7c478bd9Sstevel@tonic-gate  *                      should initially be positioned within the line.
2155*7c478bd9Sstevel@tonic-gate  *                      If you just want it to follow the last character
2156*7c478bd9Sstevel@tonic-gate  *                      of the line, send -1.
2157*7c478bd9Sstevel@tonic-gate  * Output:
2158*7c478bd9Sstevel@tonic-gate  *  return      char *  An internal buffer containing the input line, or
2159*7c478bd9Sstevel@tonic-gate  *                      NULL at the end of input. If the line fitted in
2160*7c478bd9Sstevel@tonic-gate  *                      the buffer there will be a '\n' newline character
2161*7c478bd9Sstevel@tonic-gate  *                      before the terminating '\0'. If it was truncated
2162*7c478bd9Sstevel@tonic-gate  *                      there will be no newline character, and the remains
2163*7c478bd9Sstevel@tonic-gate  *                      of the line should be retrieved via further calls
2164*7c478bd9Sstevel@tonic-gate  *                      to this function.
2165*7c478bd9Sstevel@tonic-gate  */
2166*7c478bd9Sstevel@tonic-gate char *gl_get_line(GetLine *gl, const char *prompt,
2167*7c478bd9Sstevel@tonic-gate 		  const char *start_line, int start_pos)
2168*7c478bd9Sstevel@tonic-gate {
2169*7c478bd9Sstevel@tonic-gate   char *retval;   /* The return value of _gl_get_line() */
2170*7c478bd9Sstevel@tonic-gate /*
2171*7c478bd9Sstevel@tonic-gate  * Check the arguments.
2172*7c478bd9Sstevel@tonic-gate  */
2173*7c478bd9Sstevel@tonic-gate   if(!gl) {
2174*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
2175*7c478bd9Sstevel@tonic-gate     return NULL;
2176*7c478bd9Sstevel@tonic-gate   };
2177*7c478bd9Sstevel@tonic-gate /*
2178*7c478bd9Sstevel@tonic-gate  * Temporarily block all of the signals that we have been asked to trap.
2179*7c478bd9Sstevel@tonic-gate  */
2180*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &gl->old_signal_set))
2181*7c478bd9Sstevel@tonic-gate     return NULL;
2182*7c478bd9Sstevel@tonic-gate /*
2183*7c478bd9Sstevel@tonic-gate  * Perform the command-line editing task.
2184*7c478bd9Sstevel@tonic-gate  */
2185*7c478bd9Sstevel@tonic-gate   retval = _gl_get_line(gl, prompt, start_line, start_pos);
2186*7c478bd9Sstevel@tonic-gate /*
2187*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask to how it was when this function was
2188*7c478bd9Sstevel@tonic-gate  * first called.
2189*7c478bd9Sstevel@tonic-gate  */
2190*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &gl->old_signal_set);
2191*7c478bd9Sstevel@tonic-gate   return retval;
2192*7c478bd9Sstevel@tonic-gate }
2193*7c478bd9Sstevel@tonic-gate 
2194*7c478bd9Sstevel@tonic-gate 
2195*7c478bd9Sstevel@tonic-gate /*.......................................................................
2196*7c478bd9Sstevel@tonic-gate  * This is the main body of the public function gl_get_line().
2197*7c478bd9Sstevel@tonic-gate  */
2198*7c478bd9Sstevel@tonic-gate static char *_gl_get_line(GetLine *gl, const char *prompt,
2199*7c478bd9Sstevel@tonic-gate 			  const char *start_line, int start_pos)
2200*7c478bd9Sstevel@tonic-gate {
2201*7c478bd9Sstevel@tonic-gate   int waserr = 0;    /* True if an error occurs */
2202*7c478bd9Sstevel@tonic-gate /*
2203*7c478bd9Sstevel@tonic-gate  * Assume that this call will successfully complete the input
2204*7c478bd9Sstevel@tonic-gate  * line until proven otherwise.
2205*7c478bd9Sstevel@tonic-gate  */
2206*7c478bd9Sstevel@tonic-gate   gl_clear_status(gl);
2207*7c478bd9Sstevel@tonic-gate /*
2208*7c478bd9Sstevel@tonic-gate  * If this is the first call to this function since new_GetLine(),
2209*7c478bd9Sstevel@tonic-gate  * complete any postponed configuration.
2210*7c478bd9Sstevel@tonic-gate  */
2211*7c478bd9Sstevel@tonic-gate   if(!gl->configured) {
2212*7c478bd9Sstevel@tonic-gate     (void) _gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE);
2213*7c478bd9Sstevel@tonic-gate     gl->configured = 1;
2214*7c478bd9Sstevel@tonic-gate   };
2215*7c478bd9Sstevel@tonic-gate /*
2216*7c478bd9Sstevel@tonic-gate  * Before installing our signal handler functions, record the fact
2217*7c478bd9Sstevel@tonic-gate  * that there are no pending signals.
2218*7c478bd9Sstevel@tonic-gate  */
2219*7c478bd9Sstevel@tonic-gate   gl_pending_signal = -1;
2220*7c478bd9Sstevel@tonic-gate /*
2221*7c478bd9Sstevel@tonic-gate  * Temporarily override the signal handlers of the calling program,
2222*7c478bd9Sstevel@tonic-gate  * so that we can intercept signals that would leave the terminal
2223*7c478bd9Sstevel@tonic-gate  * in a bad state.
2224*7c478bd9Sstevel@tonic-gate  */
2225*7c478bd9Sstevel@tonic-gate   waserr = gl_override_signal_handlers(gl);
2226*7c478bd9Sstevel@tonic-gate /*
2227*7c478bd9Sstevel@tonic-gate  * After recording the current terminal settings, switch the terminal
2228*7c478bd9Sstevel@tonic-gate  * into raw input mode.
2229*7c478bd9Sstevel@tonic-gate  */
2230*7c478bd9Sstevel@tonic-gate   waserr = waserr || _gl_raw_io(gl, 1);
2231*7c478bd9Sstevel@tonic-gate /*
2232*7c478bd9Sstevel@tonic-gate  * Attempt to read the line. This will require more than one attempt if
2233*7c478bd9Sstevel@tonic-gate  * either a current temporary input file is opened by gl_get_input_line()
2234*7c478bd9Sstevel@tonic-gate  * or the end of a temporary input file is reached by gl_read_stream_line().
2235*7c478bd9Sstevel@tonic-gate  */
2236*7c478bd9Sstevel@tonic-gate   while(!waserr) {
2237*7c478bd9Sstevel@tonic-gate /*
2238*7c478bd9Sstevel@tonic-gate  * Read a line from a non-interactive stream?
2239*7c478bd9Sstevel@tonic-gate  */
2240*7c478bd9Sstevel@tonic-gate     if(gl->file_fp || !gl->is_term) {
2241*7c478bd9Sstevel@tonic-gate       if(gl_read_stream_line(gl)==0) {
2242*7c478bd9Sstevel@tonic-gate 	break;
2243*7c478bd9Sstevel@tonic-gate       } else if(gl->file_fp) {
2244*7c478bd9Sstevel@tonic-gate 	gl_revert_input(gl);
2245*7c478bd9Sstevel@tonic-gate 	gl_record_status(gl, GLR_NEWLINE, 0);
2246*7c478bd9Sstevel@tonic-gate       } else {
2247*7c478bd9Sstevel@tonic-gate 	waserr = 1;
2248*7c478bd9Sstevel@tonic-gate 	break;
2249*7c478bd9Sstevel@tonic-gate       };
2250*7c478bd9Sstevel@tonic-gate     };
2251*7c478bd9Sstevel@tonic-gate /*
2252*7c478bd9Sstevel@tonic-gate  * Read from the terminal? Note that the above if() block may have
2253*7c478bd9Sstevel@tonic-gate  * changed gl->file_fp, so it is necessary to retest it here, rather
2254*7c478bd9Sstevel@tonic-gate  * than using an else statement.
2255*7c478bd9Sstevel@tonic-gate  */
2256*7c478bd9Sstevel@tonic-gate     if(!gl->file_fp && gl->is_term) {
2257*7c478bd9Sstevel@tonic-gate       if(gl_get_input_line(gl, prompt, start_line, start_pos))
2258*7c478bd9Sstevel@tonic-gate 	waserr = 1;
2259*7c478bd9Sstevel@tonic-gate       else
2260*7c478bd9Sstevel@tonic-gate 	break;
2261*7c478bd9Sstevel@tonic-gate     };
2262*7c478bd9Sstevel@tonic-gate   };
2263*7c478bd9Sstevel@tonic-gate /*
2264*7c478bd9Sstevel@tonic-gate  * If an error occurred, but gl->rtn_status is still set to
2265*7c478bd9Sstevel@tonic-gate  * GLR_NEWLINE, change the status to GLR_ERROR. Otherwise
2266*7c478bd9Sstevel@tonic-gate  * leave it at whatever specific value was assigned by the function
2267*7c478bd9Sstevel@tonic-gate  * that aborted input. This means that only functions that trap
2268*7c478bd9Sstevel@tonic-gate  * non-generic errors have to remember to update gl->rtn_status
2269*7c478bd9Sstevel@tonic-gate  * themselves.
2270*7c478bd9Sstevel@tonic-gate  */
2271*7c478bd9Sstevel@tonic-gate   if(waserr && gl->rtn_status == GLR_NEWLINE)
2272*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_ERROR, errno);
2273*7c478bd9Sstevel@tonic-gate /*
2274*7c478bd9Sstevel@tonic-gate  * Restore terminal settings.
2275*7c478bd9Sstevel@tonic-gate  */
2276*7c478bd9Sstevel@tonic-gate   if(gl->io_mode != GL_SERVER_MODE)
2277*7c478bd9Sstevel@tonic-gate     _gl_normal_io(gl);
2278*7c478bd9Sstevel@tonic-gate /*
2279*7c478bd9Sstevel@tonic-gate  * Restore the signal handlers.
2280*7c478bd9Sstevel@tonic-gate  */
2281*7c478bd9Sstevel@tonic-gate   gl_restore_signal_handlers(gl);
2282*7c478bd9Sstevel@tonic-gate /*
2283*7c478bd9Sstevel@tonic-gate  * If gl_get_line() gets aborted early, the errno value associated
2284*7c478bd9Sstevel@tonic-gate  * with the event that caused this to happen is recorded in
2285*7c478bd9Sstevel@tonic-gate  * gl->rtn_errno. Since errno may have been overwritten by cleanup
2286*7c478bd9Sstevel@tonic-gate  * functions after this, restore its value to the value that it had
2287*7c478bd9Sstevel@tonic-gate  * when the error condition occured, so that the caller can examine it
2288*7c478bd9Sstevel@tonic-gate  * to find out what happened.
2289*7c478bd9Sstevel@tonic-gate  */
2290*7c478bd9Sstevel@tonic-gate   errno = gl->rtn_errno;
2291*7c478bd9Sstevel@tonic-gate /*
2292*7c478bd9Sstevel@tonic-gate  * Check the completion status to see how to return.
2293*7c478bd9Sstevel@tonic-gate  */
2294*7c478bd9Sstevel@tonic-gate   switch(gl->rtn_status) {
2295*7c478bd9Sstevel@tonic-gate   case GLR_NEWLINE:    /* Success */
2296*7c478bd9Sstevel@tonic-gate     return gl->line;
2297*7c478bd9Sstevel@tonic-gate   case GLR_BLOCKED:    /* These events abort the current input line, */
2298*7c478bd9Sstevel@tonic-gate   case GLR_SIGNAL:     /*  when in normal blocking I/O mode, but only */
2299*7c478bd9Sstevel@tonic-gate   case GLR_TIMEOUT:    /*  temporarily pause line editing when in */
2300*7c478bd9Sstevel@tonic-gate   case GLR_FDABORT:    /*  non-blocking server I/O mode. */
2301*7c478bd9Sstevel@tonic-gate     if(gl->io_mode != GL_SERVER_MODE)
2302*7c478bd9Sstevel@tonic-gate       _gl_abandon_line(gl);
2303*7c478bd9Sstevel@tonic-gate     return NULL;
2304*7c478bd9Sstevel@tonic-gate   case GLR_ERROR:      /* Unrecoverable errors abort the input line, */
2305*7c478bd9Sstevel@tonic-gate   case GLR_EOF:        /*  regardless of the I/O mode. */
2306*7c478bd9Sstevel@tonic-gate   default:
2307*7c478bd9Sstevel@tonic-gate     _gl_abandon_line(gl);
2308*7c478bd9Sstevel@tonic-gate     return NULL;
2309*7c478bd9Sstevel@tonic-gate   };
2310*7c478bd9Sstevel@tonic-gate }
2311*7c478bd9Sstevel@tonic-gate 
2312*7c478bd9Sstevel@tonic-gate /*.......................................................................
2313*7c478bd9Sstevel@tonic-gate  * Read a single character from the user.
2314*7c478bd9Sstevel@tonic-gate  *
2315*7c478bd9Sstevel@tonic-gate  * Input:
2316*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  A resource object returned by new_GetLine().
2317*7c478bd9Sstevel@tonic-gate  *  prompt      char *  The prompt to prefix the line with, or NULL if
2318*7c478bd9Sstevel@tonic-gate  *                      no prompt is required.
2319*7c478bd9Sstevel@tonic-gate  *  defchar     char    The character to substitute if the
2320*7c478bd9Sstevel@tonic-gate  *                      user simply hits return, or '\n' if you don't
2321*7c478bd9Sstevel@tonic-gate  *                      need to substitute anything.
2322*7c478bd9Sstevel@tonic-gate  * Output:
2323*7c478bd9Sstevel@tonic-gate  *  return       int    The character that was read, or EOF if the read
2324*7c478bd9Sstevel@tonic-gate  *                      had to be aborted (in which case you can call
2325*7c478bd9Sstevel@tonic-gate  *                      gl_return_status() to find out why).
2326*7c478bd9Sstevel@tonic-gate  */
2327*7c478bd9Sstevel@tonic-gate int gl_query_char(GetLine *gl, const char *prompt, char defchar)
2328*7c478bd9Sstevel@tonic-gate {
2329*7c478bd9Sstevel@tonic-gate   int retval;   /* The return value of _gl_query_char() */
2330*7c478bd9Sstevel@tonic-gate /*
2331*7c478bd9Sstevel@tonic-gate  * Check the arguments.
2332*7c478bd9Sstevel@tonic-gate  */
2333*7c478bd9Sstevel@tonic-gate   if(!gl) {
2334*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
2335*7c478bd9Sstevel@tonic-gate     return EOF;
2336*7c478bd9Sstevel@tonic-gate   };
2337*7c478bd9Sstevel@tonic-gate /*
2338*7c478bd9Sstevel@tonic-gate  * Temporarily block all of the signals that we have been asked to trap.
2339*7c478bd9Sstevel@tonic-gate  */
2340*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &gl->old_signal_set))
2341*7c478bd9Sstevel@tonic-gate     return EOF;
2342*7c478bd9Sstevel@tonic-gate /*
2343*7c478bd9Sstevel@tonic-gate  * Perform the character reading task.
2344*7c478bd9Sstevel@tonic-gate  */
2345*7c478bd9Sstevel@tonic-gate   retval = _gl_query_char(gl, prompt, defchar);
2346*7c478bd9Sstevel@tonic-gate /*
2347*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask to how it was when this function was
2348*7c478bd9Sstevel@tonic-gate  * first called.
2349*7c478bd9Sstevel@tonic-gate  */
2350*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &gl->old_signal_set);
2351*7c478bd9Sstevel@tonic-gate   return retval;
2352*7c478bd9Sstevel@tonic-gate }
2353*7c478bd9Sstevel@tonic-gate 
2354*7c478bd9Sstevel@tonic-gate /*.......................................................................
2355*7c478bd9Sstevel@tonic-gate  * This is the main body of the public function gl_query_char().
2356*7c478bd9Sstevel@tonic-gate  */
2357*7c478bd9Sstevel@tonic-gate static int _gl_query_char(GetLine *gl, const char *prompt, char defchar)
2358*7c478bd9Sstevel@tonic-gate {
2359*7c478bd9Sstevel@tonic-gate   int c = EOF;       /* The character to be returned */
2360*7c478bd9Sstevel@tonic-gate   int waserr = 0;    /* True if an error occurs */
2361*7c478bd9Sstevel@tonic-gate /*
2362*7c478bd9Sstevel@tonic-gate  * Assume that this call will successfully complete the input operation
2363*7c478bd9Sstevel@tonic-gate  * until proven otherwise.
2364*7c478bd9Sstevel@tonic-gate  */
2365*7c478bd9Sstevel@tonic-gate   gl_clear_status(gl);
2366*7c478bd9Sstevel@tonic-gate /*
2367*7c478bd9Sstevel@tonic-gate  * If this is the first call to this function or gl_get_line(),
2368*7c478bd9Sstevel@tonic-gate  * since new_GetLine(), complete any postponed configuration.
2369*7c478bd9Sstevel@tonic-gate  */
2370*7c478bd9Sstevel@tonic-gate   if(!gl->configured) {
2371*7c478bd9Sstevel@tonic-gate     (void) _gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE);
2372*7c478bd9Sstevel@tonic-gate     gl->configured = 1;
2373*7c478bd9Sstevel@tonic-gate   };
2374*7c478bd9Sstevel@tonic-gate /*
2375*7c478bd9Sstevel@tonic-gate  * Before installing our signal handler functions, record the fact
2376*7c478bd9Sstevel@tonic-gate  * that there are no pending signals.
2377*7c478bd9Sstevel@tonic-gate  */
2378*7c478bd9Sstevel@tonic-gate   gl_pending_signal = -1;
2379*7c478bd9Sstevel@tonic-gate /*
2380*7c478bd9Sstevel@tonic-gate  * Temporarily override the signal handlers of the calling program,
2381*7c478bd9Sstevel@tonic-gate  * so that we can intercept signals that would leave the terminal
2382*7c478bd9Sstevel@tonic-gate  * in a bad state.
2383*7c478bd9Sstevel@tonic-gate  */
2384*7c478bd9Sstevel@tonic-gate   waserr = gl_override_signal_handlers(gl);
2385*7c478bd9Sstevel@tonic-gate /*
2386*7c478bd9Sstevel@tonic-gate  * After recording the current terminal settings, switch the terminal
2387*7c478bd9Sstevel@tonic-gate  * into raw input mode without redisplaying any partially entered
2388*7c478bd9Sstevel@tonic-gate  * input line.
2389*7c478bd9Sstevel@tonic-gate  */
2390*7c478bd9Sstevel@tonic-gate   waserr = waserr || _gl_raw_io(gl, 0);
2391*7c478bd9Sstevel@tonic-gate /*
2392*7c478bd9Sstevel@tonic-gate  * Attempt to read the line. This will require more than one attempt if
2393*7c478bd9Sstevel@tonic-gate  * either a current temporary input file is opened by gl_get_input_line()
2394*7c478bd9Sstevel@tonic-gate  * or the end of a temporary input file is reached by gl_read_stream_line().
2395*7c478bd9Sstevel@tonic-gate  */
2396*7c478bd9Sstevel@tonic-gate   while(!waserr) {
2397*7c478bd9Sstevel@tonic-gate /*
2398*7c478bd9Sstevel@tonic-gate  * Read a line from a non-interactive stream?
2399*7c478bd9Sstevel@tonic-gate  */
2400*7c478bd9Sstevel@tonic-gate     if(gl->file_fp || !gl->is_term) {
2401*7c478bd9Sstevel@tonic-gate       c = gl_read_stream_char(gl);
2402*7c478bd9Sstevel@tonic-gate       if(c != EOF) {            /* Success? */
2403*7c478bd9Sstevel@tonic-gate 	if(c=='\n') c = defchar;
2404*7c478bd9Sstevel@tonic-gate 	break;
2405*7c478bd9Sstevel@tonic-gate       } else if(gl->file_fp) {  /* End of temporary input file? */
2406*7c478bd9Sstevel@tonic-gate 	gl_revert_input(gl);
2407*7c478bd9Sstevel@tonic-gate 	gl_record_status(gl, GLR_NEWLINE, 0);
2408*7c478bd9Sstevel@tonic-gate       } else {                  /* An error? */
2409*7c478bd9Sstevel@tonic-gate 	waserr = 1;
2410*7c478bd9Sstevel@tonic-gate 	break;
2411*7c478bd9Sstevel@tonic-gate       };
2412*7c478bd9Sstevel@tonic-gate     };
2413*7c478bd9Sstevel@tonic-gate /*
2414*7c478bd9Sstevel@tonic-gate  * Read from the terminal? Note that the above if() block may have
2415*7c478bd9Sstevel@tonic-gate  * changed gl->file_fp, so it is necessary to retest it here, rather
2416*7c478bd9Sstevel@tonic-gate  * than using an else statement.
2417*7c478bd9Sstevel@tonic-gate  */
2418*7c478bd9Sstevel@tonic-gate     if(!gl->file_fp && gl->is_term) {
2419*7c478bd9Sstevel@tonic-gate       c = gl_get_query_char(gl, prompt, defchar);
2420*7c478bd9Sstevel@tonic-gate       if(c==EOF)
2421*7c478bd9Sstevel@tonic-gate 	waserr = 1;
2422*7c478bd9Sstevel@tonic-gate       else
2423*7c478bd9Sstevel@tonic-gate 	break;
2424*7c478bd9Sstevel@tonic-gate     };
2425*7c478bd9Sstevel@tonic-gate   };
2426*7c478bd9Sstevel@tonic-gate /*
2427*7c478bd9Sstevel@tonic-gate  * If an error occurred, but gl->rtn_status is still set to
2428*7c478bd9Sstevel@tonic-gate  * GLR_NEWLINE, change the status to GLR_ERROR. Otherwise
2429*7c478bd9Sstevel@tonic-gate  * leave it at whatever specific value was assigned by the function
2430*7c478bd9Sstevel@tonic-gate  * that aborted input. This means that only functions that trap
2431*7c478bd9Sstevel@tonic-gate  * non-generic errors have to remember to update gl->rtn_status
2432*7c478bd9Sstevel@tonic-gate  * themselves.
2433*7c478bd9Sstevel@tonic-gate  */
2434*7c478bd9Sstevel@tonic-gate   if(waserr && gl->rtn_status == GLR_NEWLINE)
2435*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_ERROR, errno);
2436*7c478bd9Sstevel@tonic-gate /*
2437*7c478bd9Sstevel@tonic-gate  * Restore terminal settings.
2438*7c478bd9Sstevel@tonic-gate  */
2439*7c478bd9Sstevel@tonic-gate   if(gl->io_mode != GL_SERVER_MODE)
2440*7c478bd9Sstevel@tonic-gate     _gl_normal_io(gl);
2441*7c478bd9Sstevel@tonic-gate /*
2442*7c478bd9Sstevel@tonic-gate  * Restore the signal handlers.
2443*7c478bd9Sstevel@tonic-gate  */
2444*7c478bd9Sstevel@tonic-gate   gl_restore_signal_handlers(gl);
2445*7c478bd9Sstevel@tonic-gate /*
2446*7c478bd9Sstevel@tonic-gate  * If this function gets aborted early, the errno value associated
2447*7c478bd9Sstevel@tonic-gate  * with the event that caused this to happen is recorded in
2448*7c478bd9Sstevel@tonic-gate  * gl->rtn_errno. Since errno may have been overwritten by cleanup
2449*7c478bd9Sstevel@tonic-gate  * functions after this, restore its value to the value that it had
2450*7c478bd9Sstevel@tonic-gate  * when the error condition occured, so that the caller can examine it
2451*7c478bd9Sstevel@tonic-gate  * to find out what happened.
2452*7c478bd9Sstevel@tonic-gate  */
2453*7c478bd9Sstevel@tonic-gate   errno = gl->rtn_errno;
2454*7c478bd9Sstevel@tonic-gate /*
2455*7c478bd9Sstevel@tonic-gate  * Error conditions are signalled to the caller, by setting the returned
2456*7c478bd9Sstevel@tonic-gate  * character to EOF.
2457*7c478bd9Sstevel@tonic-gate  */
2458*7c478bd9Sstevel@tonic-gate   if(gl->rtn_status != GLR_NEWLINE)
2459*7c478bd9Sstevel@tonic-gate     c = EOF;
2460*7c478bd9Sstevel@tonic-gate /*
2461*7c478bd9Sstevel@tonic-gate  * In this mode, every character that is read is a completed
2462*7c478bd9Sstevel@tonic-gate  * transaction, just like reading a completed input line, so prepare
2463*7c478bd9Sstevel@tonic-gate  * for the next input line or character.
2464*7c478bd9Sstevel@tonic-gate  */
2465*7c478bd9Sstevel@tonic-gate   _gl_abandon_line(gl);
2466*7c478bd9Sstevel@tonic-gate /*
2467*7c478bd9Sstevel@tonic-gate  * Return the acquired character.
2468*7c478bd9Sstevel@tonic-gate  */
2469*7c478bd9Sstevel@tonic-gate   return c;
2470*7c478bd9Sstevel@tonic-gate }
2471*7c478bd9Sstevel@tonic-gate 
2472*7c478bd9Sstevel@tonic-gate /*.......................................................................
2473*7c478bd9Sstevel@tonic-gate  * Record of the signal handlers of the calling program, so that they
2474*7c478bd9Sstevel@tonic-gate  * can be restored later.
2475*7c478bd9Sstevel@tonic-gate  *
2476*7c478bd9Sstevel@tonic-gate  * Input:
2477*7c478bd9Sstevel@tonic-gate  *  gl    GetLine *   The resource object of this library.
2478*7c478bd9Sstevel@tonic-gate  * Output:
2479*7c478bd9Sstevel@tonic-gate  *  return    int     0 - OK.
2480*7c478bd9Sstevel@tonic-gate  *                    1 - Error.
2481*7c478bd9Sstevel@tonic-gate  */
2482*7c478bd9Sstevel@tonic-gate static int gl_override_signal_handlers(GetLine *gl)
2483*7c478bd9Sstevel@tonic-gate {
2484*7c478bd9Sstevel@tonic-gate   GlSignalNode *sig;   /* A node in the list of signals to be caught */
2485*7c478bd9Sstevel@tonic-gate /*
2486*7c478bd9Sstevel@tonic-gate  * Set up our signal handler.
2487*7c478bd9Sstevel@tonic-gate  */
2488*7c478bd9Sstevel@tonic-gate   SigAction act;
2489*7c478bd9Sstevel@tonic-gate   act.sa_handler = gl_signal_handler;
2490*7c478bd9Sstevel@tonic-gate   memcpy(&act.sa_mask, &gl->all_signal_set, sizeof(sigset_t));
2491*7c478bd9Sstevel@tonic-gate   act.sa_flags = 0;
2492*7c478bd9Sstevel@tonic-gate /*
2493*7c478bd9Sstevel@tonic-gate  * Get the subset of the signals that we are supposed to trap that
2494*7c478bd9Sstevel@tonic-gate  * should actually be trapped.
2495*7c478bd9Sstevel@tonic-gate  */
2496*7c478bd9Sstevel@tonic-gate   sigemptyset(&gl->use_signal_set);
2497*7c478bd9Sstevel@tonic-gate   for(sig=gl->sigs; sig; sig=sig->next) {
2498*7c478bd9Sstevel@tonic-gate /*
2499*7c478bd9Sstevel@tonic-gate  * Trap this signal? If it is blocked by the calling program and we
2500*7c478bd9Sstevel@tonic-gate  * haven't been told to unblock it, don't arrange to trap this signal.
2501*7c478bd9Sstevel@tonic-gate  */
2502*7c478bd9Sstevel@tonic-gate     if(sig->flags & GLS_UNBLOCK_SIG ||
2503*7c478bd9Sstevel@tonic-gate        !sigismember(&gl->old_signal_set, sig->signo)) {
2504*7c478bd9Sstevel@tonic-gate       if(sigaddset(&gl->use_signal_set, sig->signo) == -1) {
2505*7c478bd9Sstevel@tonic-gate 	_err_record_msg(gl->err, "sigaddset error", END_ERR_MSG);
2506*7c478bd9Sstevel@tonic-gate 	return 1;
2507*7c478bd9Sstevel@tonic-gate       };
2508*7c478bd9Sstevel@tonic-gate     };
2509*7c478bd9Sstevel@tonic-gate   };
2510*7c478bd9Sstevel@tonic-gate /*
2511*7c478bd9Sstevel@tonic-gate  * Override the actions of the signals that we are trapping.
2512*7c478bd9Sstevel@tonic-gate  */
2513*7c478bd9Sstevel@tonic-gate   for(sig=gl->sigs; sig; sig=sig->next) {
2514*7c478bd9Sstevel@tonic-gate     if(sigismember(&gl->use_signal_set, sig->signo)) {
2515*7c478bd9Sstevel@tonic-gate       sigdelset(&act.sa_mask, sig->signo);
2516*7c478bd9Sstevel@tonic-gate       if(sigaction(sig->signo, &act, &sig->original)) {
2517*7c478bd9Sstevel@tonic-gate 	_err_record_msg(gl->err, "sigaction error", END_ERR_MSG);
2518*7c478bd9Sstevel@tonic-gate 	return 1;
2519*7c478bd9Sstevel@tonic-gate       };
2520*7c478bd9Sstevel@tonic-gate       sigaddset(&act.sa_mask, sig->signo);
2521*7c478bd9Sstevel@tonic-gate     };
2522*7c478bd9Sstevel@tonic-gate   };
2523*7c478bd9Sstevel@tonic-gate /*
2524*7c478bd9Sstevel@tonic-gate  * Record the fact that the application's signal handlers have now
2525*7c478bd9Sstevel@tonic-gate  * been overriden.
2526*7c478bd9Sstevel@tonic-gate  */
2527*7c478bd9Sstevel@tonic-gate   gl->signals_overriden = 1;
2528*7c478bd9Sstevel@tonic-gate /*
2529*7c478bd9Sstevel@tonic-gate  * Just in case a SIGWINCH signal was sent to the process while our
2530*7c478bd9Sstevel@tonic-gate  * SIGWINCH signal handler wasn't in place, check to see if the terminal
2531*7c478bd9Sstevel@tonic-gate  * size needs updating.
2532*7c478bd9Sstevel@tonic-gate  */
2533*7c478bd9Sstevel@tonic-gate   if(_gl_update_size(gl))
2534*7c478bd9Sstevel@tonic-gate     return 1;
2535*7c478bd9Sstevel@tonic-gate   return 0;
2536*7c478bd9Sstevel@tonic-gate }
2537*7c478bd9Sstevel@tonic-gate 
2538*7c478bd9Sstevel@tonic-gate /*.......................................................................
2539*7c478bd9Sstevel@tonic-gate  * Restore the signal handlers of the calling program.
2540*7c478bd9Sstevel@tonic-gate  *
2541*7c478bd9Sstevel@tonic-gate  * Input:
2542*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  The resource object of this library.
2543*7c478bd9Sstevel@tonic-gate  * Output:
2544*7c478bd9Sstevel@tonic-gate  *  return     int    0 - OK.
2545*7c478bd9Sstevel@tonic-gate  *                    1 - Error.
2546*7c478bd9Sstevel@tonic-gate  */
2547*7c478bd9Sstevel@tonic-gate static int gl_restore_signal_handlers(GetLine *gl)
2548*7c478bd9Sstevel@tonic-gate {
2549*7c478bd9Sstevel@tonic-gate   GlSignalNode *sig;   /* A node in the list of signals to be caught */
2550*7c478bd9Sstevel@tonic-gate /*
2551*7c478bd9Sstevel@tonic-gate  * Restore application signal handlers that were overriden
2552*7c478bd9Sstevel@tonic-gate  * by gl_override_signal_handlers().
2553*7c478bd9Sstevel@tonic-gate  */
2554*7c478bd9Sstevel@tonic-gate   for(sig=gl->sigs; sig; sig=sig->next) {
2555*7c478bd9Sstevel@tonic-gate     if(sigismember(&gl->use_signal_set, sig->signo) &&
2556*7c478bd9Sstevel@tonic-gate        sigaction(sig->signo, &sig->original, NULL)) {
2557*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "sigaction error", END_ERR_MSG);
2558*7c478bd9Sstevel@tonic-gate       return 1;
2559*7c478bd9Sstevel@tonic-gate     };
2560*7c478bd9Sstevel@tonic-gate   };
2561*7c478bd9Sstevel@tonic-gate /*
2562*7c478bd9Sstevel@tonic-gate  * Record the fact that the application's signal handlers have now
2563*7c478bd9Sstevel@tonic-gate  * been restored.
2564*7c478bd9Sstevel@tonic-gate  */
2565*7c478bd9Sstevel@tonic-gate   gl->signals_overriden = 0;
2566*7c478bd9Sstevel@tonic-gate   return 0;
2567*7c478bd9Sstevel@tonic-gate }
2568*7c478bd9Sstevel@tonic-gate 
2569*7c478bd9Sstevel@tonic-gate /*.......................................................................
2570*7c478bd9Sstevel@tonic-gate  * This signal handler simply records the fact that a given signal was
2571*7c478bd9Sstevel@tonic-gate  * caught in the file-scope gl_pending_signal variable.
2572*7c478bd9Sstevel@tonic-gate  */
2573*7c478bd9Sstevel@tonic-gate static void gl_signal_handler(int signo)
2574*7c478bd9Sstevel@tonic-gate {
2575*7c478bd9Sstevel@tonic-gate   gl_pending_signal = signo;
2576*7c478bd9Sstevel@tonic-gate   siglongjmp(gl_setjmp_buffer, 1);
2577*7c478bd9Sstevel@tonic-gate }
2578*7c478bd9Sstevel@tonic-gate 
2579*7c478bd9Sstevel@tonic-gate /*.......................................................................
2580*7c478bd9Sstevel@tonic-gate  * Switch the terminal into raw mode after storing the previous terminal
2581*7c478bd9Sstevel@tonic-gate  * settings in gl->attributes.
2582*7c478bd9Sstevel@tonic-gate  *
2583*7c478bd9Sstevel@tonic-gate  * Input:
2584*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *   The resource object of this program.
2585*7c478bd9Sstevel@tonic-gate  * Output:
2586*7c478bd9Sstevel@tonic-gate  *  return     int     0 - OK.
2587*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
2588*7c478bd9Sstevel@tonic-gate  */
2589*7c478bd9Sstevel@tonic-gate static int gl_raw_terminal_mode(GetLine *gl)
2590*7c478bd9Sstevel@tonic-gate {
2591*7c478bd9Sstevel@tonic-gate   Termios newattr;   /* The new terminal attributes */
2592*7c478bd9Sstevel@tonic-gate /*
2593*7c478bd9Sstevel@tonic-gate  * If the terminal is already in raw mode, do nothing.
2594*7c478bd9Sstevel@tonic-gate  */
2595*7c478bd9Sstevel@tonic-gate   if(gl->raw_mode)
2596*7c478bd9Sstevel@tonic-gate     return 0;
2597*7c478bd9Sstevel@tonic-gate /*
2598*7c478bd9Sstevel@tonic-gate  * Record the current terminal attributes.
2599*7c478bd9Sstevel@tonic-gate  */
2600*7c478bd9Sstevel@tonic-gate   if(tcgetattr(gl->input_fd, &gl->oldattr)) {
2601*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG);
2602*7c478bd9Sstevel@tonic-gate     return 1;
2603*7c478bd9Sstevel@tonic-gate   };
2604*7c478bd9Sstevel@tonic-gate /*
2605*7c478bd9Sstevel@tonic-gate  * This function shouldn't do anything but record the current terminal
2606*7c478bd9Sstevel@tonic-gate  * attritubes if editing has been disabled.
2607*7c478bd9Sstevel@tonic-gate  */
2608*7c478bd9Sstevel@tonic-gate   if(gl->editor == GL_NO_EDITOR)
2609*7c478bd9Sstevel@tonic-gate     return 0;
2610*7c478bd9Sstevel@tonic-gate /*
2611*7c478bd9Sstevel@tonic-gate  * Modify the existing attributes.
2612*7c478bd9Sstevel@tonic-gate  */
2613*7c478bd9Sstevel@tonic-gate   newattr = gl->oldattr;
2614*7c478bd9Sstevel@tonic-gate /*
2615*7c478bd9Sstevel@tonic-gate  * Turn off local echo, canonical input mode and extended input processing.
2616*7c478bd9Sstevel@tonic-gate  */
2617*7c478bd9Sstevel@tonic-gate   newattr.c_lflag &= ~(ECHO | ICANON | IEXTEN);
2618*7c478bd9Sstevel@tonic-gate /*
2619*7c478bd9Sstevel@tonic-gate  * Don't translate carriage return to newline, turn off input parity
2620*7c478bd9Sstevel@tonic-gate  * checking, don't strip off 8th bit, turn off output flow control.
2621*7c478bd9Sstevel@tonic-gate  */
2622*7c478bd9Sstevel@tonic-gate   newattr.c_iflag &= ~(ICRNL | INPCK | ISTRIP);
2623*7c478bd9Sstevel@tonic-gate /*
2624*7c478bd9Sstevel@tonic-gate  * Clear size bits, turn off parity checking, and allow 8-bit characters.
2625*7c478bd9Sstevel@tonic-gate  */
2626*7c478bd9Sstevel@tonic-gate   newattr.c_cflag &= ~(CSIZE | PARENB);
2627*7c478bd9Sstevel@tonic-gate   newattr.c_cflag |= CS8;
2628*7c478bd9Sstevel@tonic-gate /*
2629*7c478bd9Sstevel@tonic-gate  * Turn off output processing.
2630*7c478bd9Sstevel@tonic-gate  */
2631*7c478bd9Sstevel@tonic-gate   newattr.c_oflag &= ~(OPOST);
2632*7c478bd9Sstevel@tonic-gate /*
2633*7c478bd9Sstevel@tonic-gate  * Request one byte at a time, without waiting.
2634*7c478bd9Sstevel@tonic-gate  */
2635*7c478bd9Sstevel@tonic-gate   newattr.c_cc[VMIN] = gl->io_mode==GL_SERVER_MODE ? 0:1;
2636*7c478bd9Sstevel@tonic-gate   newattr.c_cc[VTIME] = 0;
2637*7c478bd9Sstevel@tonic-gate /*
2638*7c478bd9Sstevel@tonic-gate  * Install the new terminal modes.
2639*7c478bd9Sstevel@tonic-gate  */
2640*7c478bd9Sstevel@tonic-gate   while(tcsetattr(gl->input_fd, TCSADRAIN, &newattr)) {
2641*7c478bd9Sstevel@tonic-gate     if(errno != EINTR) {
2642*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG);
2643*7c478bd9Sstevel@tonic-gate       return 1;
2644*7c478bd9Sstevel@tonic-gate     };
2645*7c478bd9Sstevel@tonic-gate   };
2646*7c478bd9Sstevel@tonic-gate /*
2647*7c478bd9Sstevel@tonic-gate  * Record the new terminal mode.
2648*7c478bd9Sstevel@tonic-gate  */
2649*7c478bd9Sstevel@tonic-gate   gl->raw_mode = 1;
2650*7c478bd9Sstevel@tonic-gate   return 0;
2651*7c478bd9Sstevel@tonic-gate }
2652*7c478bd9Sstevel@tonic-gate 
2653*7c478bd9Sstevel@tonic-gate /*.......................................................................
2654*7c478bd9Sstevel@tonic-gate  * Restore the terminal attributes recorded in gl->oldattr.
2655*7c478bd9Sstevel@tonic-gate  *
2656*7c478bd9Sstevel@tonic-gate  * Input:
2657*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *   The resource object of this library.
2658*7c478bd9Sstevel@tonic-gate  * Output:
2659*7c478bd9Sstevel@tonic-gate  *  return     int     0 - OK.
2660*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
2661*7c478bd9Sstevel@tonic-gate  */
2662*7c478bd9Sstevel@tonic-gate static int gl_restore_terminal_attributes(GetLine *gl)
2663*7c478bd9Sstevel@tonic-gate {
2664*7c478bd9Sstevel@tonic-gate   int waserr = 0;
2665*7c478bd9Sstevel@tonic-gate /*
2666*7c478bd9Sstevel@tonic-gate  * If not in raw mode, do nothing.
2667*7c478bd9Sstevel@tonic-gate  */
2668*7c478bd9Sstevel@tonic-gate   if(!gl->raw_mode)
2669*7c478bd9Sstevel@tonic-gate     return 0;
2670*7c478bd9Sstevel@tonic-gate /*
2671*7c478bd9Sstevel@tonic-gate  * Before changing the terminal attributes, make sure that all output
2672*7c478bd9Sstevel@tonic-gate  * has been passed to the terminal.
2673*7c478bd9Sstevel@tonic-gate  */
2674*7c478bd9Sstevel@tonic-gate   if(gl_flush_output(gl))
2675*7c478bd9Sstevel@tonic-gate     waserr = 1;
2676*7c478bd9Sstevel@tonic-gate /*
2677*7c478bd9Sstevel@tonic-gate  * Reset the terminal attributes to the values that they had on
2678*7c478bd9Sstevel@tonic-gate  * entry to gl_get_line().
2679*7c478bd9Sstevel@tonic-gate  */
2680*7c478bd9Sstevel@tonic-gate   while(tcsetattr(gl->input_fd, TCSADRAIN, &gl->oldattr)) {
2681*7c478bd9Sstevel@tonic-gate     if(errno != EINTR) {
2682*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG);
2683*7c478bd9Sstevel@tonic-gate       waserr = 1;
2684*7c478bd9Sstevel@tonic-gate       break;
2685*7c478bd9Sstevel@tonic-gate     };
2686*7c478bd9Sstevel@tonic-gate   };
2687*7c478bd9Sstevel@tonic-gate /*
2688*7c478bd9Sstevel@tonic-gate  * Record the new terminal mode.
2689*7c478bd9Sstevel@tonic-gate  */
2690*7c478bd9Sstevel@tonic-gate   gl->raw_mode = 0;
2691*7c478bd9Sstevel@tonic-gate   return waserr;
2692*7c478bd9Sstevel@tonic-gate }
2693*7c478bd9Sstevel@tonic-gate 
2694*7c478bd9Sstevel@tonic-gate /*.......................................................................
2695*7c478bd9Sstevel@tonic-gate  * Switch the terminal file descriptor to use non-blocking I/O.
2696*7c478bd9Sstevel@tonic-gate  *
2697*7c478bd9Sstevel@tonic-gate  * Input:
2698*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
2699*7c478bd9Sstevel@tonic-gate  *  fd             int    The file descriptor to make non-blocking.
2700*7c478bd9Sstevel@tonic-gate  */
2701*7c478bd9Sstevel@tonic-gate static int gl_nonblocking_io(GetLine *gl, int fd)
2702*7c478bd9Sstevel@tonic-gate {
2703*7c478bd9Sstevel@tonic-gate   int fcntl_flags;   /* The new file-descriptor control flags */
2704*7c478bd9Sstevel@tonic-gate /*
2705*7c478bd9Sstevel@tonic-gate  * Is non-blocking I/O supported on this system?  Note that even
2706*7c478bd9Sstevel@tonic-gate  * without non-blocking I/O, the terminal will probably still act as
2707*7c478bd9Sstevel@tonic-gate  * though it was non-blocking, because we also set the terminal
2708*7c478bd9Sstevel@tonic-gate  * attributes to return immediately if no input is available and we
2709*7c478bd9Sstevel@tonic-gate  * use select() to wait to be able to write. If select() also isn't
2710*7c478bd9Sstevel@tonic-gate  * available, then input will probably remain fine, but output could
2711*7c478bd9Sstevel@tonic-gate  * block, depending on the behaviour of the terminal driver.
2712*7c478bd9Sstevel@tonic-gate  */
2713*7c478bd9Sstevel@tonic-gate #if defined(NON_BLOCKING_FLAG)
2714*7c478bd9Sstevel@tonic-gate /*
2715*7c478bd9Sstevel@tonic-gate  * Query the current file-control flags, and add the
2716*7c478bd9Sstevel@tonic-gate  * non-blocking I/O flag.
2717*7c478bd9Sstevel@tonic-gate  */
2718*7c478bd9Sstevel@tonic-gate   fcntl_flags = fcntl(fd, F_GETFL) | NON_BLOCKING_FLAG;
2719*7c478bd9Sstevel@tonic-gate /*
2720*7c478bd9Sstevel@tonic-gate  * Install the new control flags.
2721*7c478bd9Sstevel@tonic-gate  */
2722*7c478bd9Sstevel@tonic-gate   if(fcntl(fd, F_SETFL, fcntl_flags) == -1) {
2723*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "fcntl error", END_ERR_MSG);
2724*7c478bd9Sstevel@tonic-gate     return 1;
2725*7c478bd9Sstevel@tonic-gate   };
2726*7c478bd9Sstevel@tonic-gate #endif
2727*7c478bd9Sstevel@tonic-gate   return 0;
2728*7c478bd9Sstevel@tonic-gate }
2729*7c478bd9Sstevel@tonic-gate 
2730*7c478bd9Sstevel@tonic-gate /*.......................................................................
2731*7c478bd9Sstevel@tonic-gate  * Switch to blocking terminal I/O.
2732*7c478bd9Sstevel@tonic-gate  *
2733*7c478bd9Sstevel@tonic-gate  * Input:
2734*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
2735*7c478bd9Sstevel@tonic-gate  *  fd             int    The file descriptor to make blocking.
2736*7c478bd9Sstevel@tonic-gate  */
2737*7c478bd9Sstevel@tonic-gate static int gl_blocking_io(GetLine *gl, int fd)
2738*7c478bd9Sstevel@tonic-gate {
2739*7c478bd9Sstevel@tonic-gate   int fcntl_flags;   /* The new file-descriptor control flags */
2740*7c478bd9Sstevel@tonic-gate /*
2741*7c478bd9Sstevel@tonic-gate  * Is non-blocking I/O implemented on this system?
2742*7c478bd9Sstevel@tonic-gate  */
2743*7c478bd9Sstevel@tonic-gate #if defined(NON_BLOCKING_FLAG)
2744*7c478bd9Sstevel@tonic-gate /*
2745*7c478bd9Sstevel@tonic-gate  * Query the current file control flags and remove the non-blocking
2746*7c478bd9Sstevel@tonic-gate  * I/O flag.
2747*7c478bd9Sstevel@tonic-gate  */
2748*7c478bd9Sstevel@tonic-gate   fcntl_flags = fcntl(fd, F_GETFL) & ~NON_BLOCKING_FLAG;
2749*7c478bd9Sstevel@tonic-gate /*
2750*7c478bd9Sstevel@tonic-gate  * Install the modified control flags.
2751*7c478bd9Sstevel@tonic-gate  */
2752*7c478bd9Sstevel@tonic-gate   if(fcntl(fd, F_SETFL, fcntl_flags) == -1) {
2753*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "fcntl error", END_ERR_MSG);
2754*7c478bd9Sstevel@tonic-gate     return 1;
2755*7c478bd9Sstevel@tonic-gate   };
2756*7c478bd9Sstevel@tonic-gate #endif
2757*7c478bd9Sstevel@tonic-gate   return 0;
2758*7c478bd9Sstevel@tonic-gate }
2759*7c478bd9Sstevel@tonic-gate 
2760*7c478bd9Sstevel@tonic-gate /*.......................................................................
2761*7c478bd9Sstevel@tonic-gate  * Read a new input line from the user.
2762*7c478bd9Sstevel@tonic-gate  *
2763*7c478bd9Sstevel@tonic-gate  * Input:
2764*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of this library.
2765*7c478bd9Sstevel@tonic-gate  *  prompt        char *  The prompt to prefix the line with, or NULL to
2766*7c478bd9Sstevel@tonic-gate  *                        use the same prompt that was used by the previous
2767*7c478bd9Sstevel@tonic-gate  *                        line.
2768*7c478bd9Sstevel@tonic-gate  *  start_line    char *  The initial contents of the input line, or NULL
2769*7c478bd9Sstevel@tonic-gate  *                        if it should start out empty.
2770*7c478bd9Sstevel@tonic-gate  *  start_pos      int    If start_line isn't NULL, this specifies the
2771*7c478bd9Sstevel@tonic-gate  *                        index of the character over which the cursor
2772*7c478bd9Sstevel@tonic-gate  *                        should initially be positioned within the line.
2773*7c478bd9Sstevel@tonic-gate  *                        If you just want it to follow the last character
2774*7c478bd9Sstevel@tonic-gate  *                        of the line, send -1.
2775*7c478bd9Sstevel@tonic-gate  * Output:
2776*7c478bd9Sstevel@tonic-gate  *  return    int    0 - OK.
2777*7c478bd9Sstevel@tonic-gate  *                   1 - Error.
2778*7c478bd9Sstevel@tonic-gate  */
2779*7c478bd9Sstevel@tonic-gate static int gl_get_input_line(GetLine *gl, const char *prompt,
2780*7c478bd9Sstevel@tonic-gate 			     const char *start_line, int start_pos)
2781*7c478bd9Sstevel@tonic-gate {
2782*7c478bd9Sstevel@tonic-gate   char c;               /* The character being read */
2783*7c478bd9Sstevel@tonic-gate /*
2784*7c478bd9Sstevel@tonic-gate  * Flush any pending output to the terminal.
2785*7c478bd9Sstevel@tonic-gate  */
2786*7c478bd9Sstevel@tonic-gate   if(_glq_char_count(gl->cq) > 0 && gl_flush_output(gl))
2787*7c478bd9Sstevel@tonic-gate     return 1;
2788*7c478bd9Sstevel@tonic-gate /*
2789*7c478bd9Sstevel@tonic-gate  * Are we starting a new line?
2790*7c478bd9Sstevel@tonic-gate  */
2791*7c478bd9Sstevel@tonic-gate   if(gl->endline) {
2792*7c478bd9Sstevel@tonic-gate /*
2793*7c478bd9Sstevel@tonic-gate  * Delete any incompletely enterred line.
2794*7c478bd9Sstevel@tonic-gate  */
2795*7c478bd9Sstevel@tonic-gate     if(gl_erase_line(gl))
2796*7c478bd9Sstevel@tonic-gate       return 1;
2797*7c478bd9Sstevel@tonic-gate /*
2798*7c478bd9Sstevel@tonic-gate  * Display the new line to be edited.
2799*7c478bd9Sstevel@tonic-gate  */
2800*7c478bd9Sstevel@tonic-gate     if(gl_present_line(gl, prompt, start_line, start_pos))
2801*7c478bd9Sstevel@tonic-gate       return 1;
2802*7c478bd9Sstevel@tonic-gate   };
2803*7c478bd9Sstevel@tonic-gate /*
2804*7c478bd9Sstevel@tonic-gate  * Read one character at a time.
2805*7c478bd9Sstevel@tonic-gate  */
2806*7c478bd9Sstevel@tonic-gate   while(gl_read_terminal(gl, 1, &c) == 0) {
2807*7c478bd9Sstevel@tonic-gate /*
2808*7c478bd9Sstevel@tonic-gate  * Increment the count of the number of key sequences entered.
2809*7c478bd9Sstevel@tonic-gate  */
2810*7c478bd9Sstevel@tonic-gate     gl->keyseq_count++;
2811*7c478bd9Sstevel@tonic-gate /*
2812*7c478bd9Sstevel@tonic-gate  * Interpret the character either as the start of a new key-sequence,
2813*7c478bd9Sstevel@tonic-gate  * as a continuation of a repeat count, or as a printable character
2814*7c478bd9Sstevel@tonic-gate  * to be added to the line.
2815*7c478bd9Sstevel@tonic-gate  */
2816*7c478bd9Sstevel@tonic-gate     if(gl_interpret_char(gl, c))
2817*7c478bd9Sstevel@tonic-gate       break;
2818*7c478bd9Sstevel@tonic-gate /*
2819*7c478bd9Sstevel@tonic-gate  * If we just ran an action function which temporarily asked for
2820*7c478bd9Sstevel@tonic-gate  * input to be taken from a file, abort this call.
2821*7c478bd9Sstevel@tonic-gate  */
2822*7c478bd9Sstevel@tonic-gate     if(gl->file_fp)
2823*7c478bd9Sstevel@tonic-gate       return 0;
2824*7c478bd9Sstevel@tonic-gate /*
2825*7c478bd9Sstevel@tonic-gate  * Has the line been completed?
2826*7c478bd9Sstevel@tonic-gate  */
2827*7c478bd9Sstevel@tonic-gate     if(gl->endline)
2828*7c478bd9Sstevel@tonic-gate       return gl_line_ended(gl, c);
2829*7c478bd9Sstevel@tonic-gate   };
2830*7c478bd9Sstevel@tonic-gate /*
2831*7c478bd9Sstevel@tonic-gate  * To get here, gl_read_terminal() must have returned non-zero. See
2832*7c478bd9Sstevel@tonic-gate  * whether a signal was caught that requested that the current line
2833*7c478bd9Sstevel@tonic-gate  * be returned.
2834*7c478bd9Sstevel@tonic-gate  */
2835*7c478bd9Sstevel@tonic-gate   if(gl->endline)
2836*7c478bd9Sstevel@tonic-gate     return gl_line_ended(gl, '\n');
2837*7c478bd9Sstevel@tonic-gate /*
2838*7c478bd9Sstevel@tonic-gate  * If I/O blocked while attempting to get the latest character
2839*7c478bd9Sstevel@tonic-gate  * of the key sequence, rewind the key buffer to allow interpretation of
2840*7c478bd9Sstevel@tonic-gate  * the current key sequence to be restarted on the next call to this
2841*7c478bd9Sstevel@tonic-gate  * function.
2842*7c478bd9Sstevel@tonic-gate  */
2843*7c478bd9Sstevel@tonic-gate   if(gl->rtn_status == GLR_BLOCKED && gl->pending_io == GLP_READ)
2844*7c478bd9Sstevel@tonic-gate     gl->nread = 0;
2845*7c478bd9Sstevel@tonic-gate   return 1;
2846*7c478bd9Sstevel@tonic-gate }
2847*7c478bd9Sstevel@tonic-gate 
2848*7c478bd9Sstevel@tonic-gate /*.......................................................................
2849*7c478bd9Sstevel@tonic-gate  * This is the private function of gl_query_char() that handles
2850*7c478bd9Sstevel@tonic-gate  * prompting the user, reading a character from the terminal, and
2851*7c478bd9Sstevel@tonic-gate  * displaying what the user entered.
2852*7c478bd9Sstevel@tonic-gate  *
2853*7c478bd9Sstevel@tonic-gate  * Input:
2854*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of this library.
2855*7c478bd9Sstevel@tonic-gate  *  prompt        char *  The prompt to prefix the line with.
2856*7c478bd9Sstevel@tonic-gate  *  defchar       char    The character to substitute if the
2857*7c478bd9Sstevel@tonic-gate  *                        user simply hits return, or '\n' if you don't
2858*7c478bd9Sstevel@tonic-gate  *                        need to substitute anything.
2859*7c478bd9Sstevel@tonic-gate  * Output:
2860*7c478bd9Sstevel@tonic-gate  *  return         int    The character that was read, or EOF if something
2861*7c478bd9Sstevel@tonic-gate  *                        prevented a character from being read.
2862*7c478bd9Sstevel@tonic-gate  */
2863*7c478bd9Sstevel@tonic-gate static int gl_get_query_char(GetLine *gl, const char *prompt, int defchar)
2864*7c478bd9Sstevel@tonic-gate {
2865*7c478bd9Sstevel@tonic-gate   char c;               /* The character being read */
2866*7c478bd9Sstevel@tonic-gate   int retval;           /* The return value of this function */
2867*7c478bd9Sstevel@tonic-gate /*
2868*7c478bd9Sstevel@tonic-gate  * Flush any pending output to the terminal.
2869*7c478bd9Sstevel@tonic-gate  */
2870*7c478bd9Sstevel@tonic-gate   if(_glq_char_count(gl->cq) > 0 && gl_flush_output(gl))
2871*7c478bd9Sstevel@tonic-gate     return EOF;
2872*7c478bd9Sstevel@tonic-gate /*
2873*7c478bd9Sstevel@tonic-gate  * Delete any incompletely entered line.
2874*7c478bd9Sstevel@tonic-gate  */
2875*7c478bd9Sstevel@tonic-gate   if(gl_erase_line(gl))
2876*7c478bd9Sstevel@tonic-gate     return EOF;
2877*7c478bd9Sstevel@tonic-gate /*
2878*7c478bd9Sstevel@tonic-gate  * Reset the line input parameters and display the prompt, if any.
2879*7c478bd9Sstevel@tonic-gate  */
2880*7c478bd9Sstevel@tonic-gate   if(gl_present_line(gl, prompt, NULL, 0))
2881*7c478bd9Sstevel@tonic-gate     return EOF;
2882*7c478bd9Sstevel@tonic-gate /*
2883*7c478bd9Sstevel@tonic-gate  * Read one character.
2884*7c478bd9Sstevel@tonic-gate  */
2885*7c478bd9Sstevel@tonic-gate   if(gl_read_terminal(gl, 1, &c) == 0) {
2886*7c478bd9Sstevel@tonic-gate /*
2887*7c478bd9Sstevel@tonic-gate  * In this mode, count each character as being a new key-sequence.
2888*7c478bd9Sstevel@tonic-gate  */
2889*7c478bd9Sstevel@tonic-gate     gl->keyseq_count++;
2890*7c478bd9Sstevel@tonic-gate /*
2891*7c478bd9Sstevel@tonic-gate  * Delete the character that was read, from the key-press buffer.
2892*7c478bd9Sstevel@tonic-gate  */
2893*7c478bd9Sstevel@tonic-gate     gl_discard_chars(gl, gl->nread);
2894*7c478bd9Sstevel@tonic-gate /*
2895*7c478bd9Sstevel@tonic-gate  * Convert carriage returns to newlines.
2896*7c478bd9Sstevel@tonic-gate  */
2897*7c478bd9Sstevel@tonic-gate     if(c == '\r')
2898*7c478bd9Sstevel@tonic-gate       c = '\n';
2899*7c478bd9Sstevel@tonic-gate /*
2900*7c478bd9Sstevel@tonic-gate  * If the user just hit return, subsitute the default character.
2901*7c478bd9Sstevel@tonic-gate  */
2902*7c478bd9Sstevel@tonic-gate     if(c == '\n')
2903*7c478bd9Sstevel@tonic-gate       c = defchar;
2904*7c478bd9Sstevel@tonic-gate /*
2905*7c478bd9Sstevel@tonic-gate  * Display the entered character to the right of the prompt.
2906*7c478bd9Sstevel@tonic-gate  */
2907*7c478bd9Sstevel@tonic-gate     if(c!='\n') {
2908*7c478bd9Sstevel@tonic-gate       if(gl_end_of_line(gl, 1, NULL)==0)
2909*7c478bd9Sstevel@tonic-gate 	gl_print_char(gl, c, ' ');
2910*7c478bd9Sstevel@tonic-gate     };
2911*7c478bd9Sstevel@tonic-gate /*
2912*7c478bd9Sstevel@tonic-gate  * Record the return character, and mark the call as successful.
2913*7c478bd9Sstevel@tonic-gate  */
2914*7c478bd9Sstevel@tonic-gate     retval = c;
2915*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_NEWLINE, 0);
2916*7c478bd9Sstevel@tonic-gate /*
2917*7c478bd9Sstevel@tonic-gate  * Was a signal caught whose disposition is to cause the current input
2918*7c478bd9Sstevel@tonic-gate  * line to be returned? If so return a newline character.
2919*7c478bd9Sstevel@tonic-gate  */
2920*7c478bd9Sstevel@tonic-gate   } else if(gl->endline) {
2921*7c478bd9Sstevel@tonic-gate     retval = '\n';
2922*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_NEWLINE, 0);
2923*7c478bd9Sstevel@tonic-gate   } else {
2924*7c478bd9Sstevel@tonic-gate     retval = EOF;
2925*7c478bd9Sstevel@tonic-gate   };
2926*7c478bd9Sstevel@tonic-gate /*
2927*7c478bd9Sstevel@tonic-gate  * Start a new line.
2928*7c478bd9Sstevel@tonic-gate  */
2929*7c478bd9Sstevel@tonic-gate   if(gl_start_newline(gl, 1))
2930*7c478bd9Sstevel@tonic-gate     return EOF;
2931*7c478bd9Sstevel@tonic-gate /*
2932*7c478bd9Sstevel@tonic-gate  * Attempt to flush any pending output.
2933*7c478bd9Sstevel@tonic-gate  */
2934*7c478bd9Sstevel@tonic-gate   (void) gl_flush_output(gl);
2935*7c478bd9Sstevel@tonic-gate /*
2936*7c478bd9Sstevel@tonic-gate  * Return either the character that was read, or EOF if an error occurred.
2937*7c478bd9Sstevel@tonic-gate  */
2938*7c478bd9Sstevel@tonic-gate   return retval;
2939*7c478bd9Sstevel@tonic-gate }
2940*7c478bd9Sstevel@tonic-gate 
2941*7c478bd9Sstevel@tonic-gate /*.......................................................................
2942*7c478bd9Sstevel@tonic-gate  * Add a character to the line buffer at the current cursor position,
2943*7c478bd9Sstevel@tonic-gate  * inserting or overwriting according the current mode.
2944*7c478bd9Sstevel@tonic-gate  *
2945*7c478bd9Sstevel@tonic-gate  * Input:
2946*7c478bd9Sstevel@tonic-gate  *  gl   GetLine *   The resource object of this library.
2947*7c478bd9Sstevel@tonic-gate  *  c       char     The character to be added.
2948*7c478bd9Sstevel@tonic-gate  * Output:
2949*7c478bd9Sstevel@tonic-gate  *  return   int     0 - OK.
2950*7c478bd9Sstevel@tonic-gate  *                   1 - Insufficient room.
2951*7c478bd9Sstevel@tonic-gate  */
2952*7c478bd9Sstevel@tonic-gate static int gl_add_char_to_line(GetLine *gl, char c)
2953*7c478bd9Sstevel@tonic-gate {
2954*7c478bd9Sstevel@tonic-gate /*
2955*7c478bd9Sstevel@tonic-gate  * Keep a record of the current cursor position.
2956*7c478bd9Sstevel@tonic-gate  */
2957*7c478bd9Sstevel@tonic-gate   int buff_curpos = gl->buff_curpos;
2958*7c478bd9Sstevel@tonic-gate   int term_curpos = gl->term_curpos;
2959*7c478bd9Sstevel@tonic-gate /*
2960*7c478bd9Sstevel@tonic-gate  * Work out the displayed width of the new character.
2961*7c478bd9Sstevel@tonic-gate  */
2962*7c478bd9Sstevel@tonic-gate   int width = gl_displayed_char_width(gl, c, term_curpos);
2963*7c478bd9Sstevel@tonic-gate /*
2964*7c478bd9Sstevel@tonic-gate  * If we are in insert mode, or at the end of the line,
2965*7c478bd9Sstevel@tonic-gate  * check that we can accomodate a new character in the buffer.
2966*7c478bd9Sstevel@tonic-gate  * If not, simply return, leaving it up to the calling program
2967*7c478bd9Sstevel@tonic-gate  * to check for the absence of a newline character.
2968*7c478bd9Sstevel@tonic-gate  */
2969*7c478bd9Sstevel@tonic-gate   if((gl->insert || buff_curpos >= gl->ntotal) && gl->ntotal >= gl->linelen)
2970*7c478bd9Sstevel@tonic-gate     return 0;
2971*7c478bd9Sstevel@tonic-gate /*
2972*7c478bd9Sstevel@tonic-gate  * Are we adding characters to the line (ie. inserting or appending)?
2973*7c478bd9Sstevel@tonic-gate  */
2974*7c478bd9Sstevel@tonic-gate   if(gl->insert || buff_curpos >= gl->ntotal) {
2975*7c478bd9Sstevel@tonic-gate /*
2976*7c478bd9Sstevel@tonic-gate  * If inserting, make room for the new character.
2977*7c478bd9Sstevel@tonic-gate  */
2978*7c478bd9Sstevel@tonic-gate     if(buff_curpos < gl->ntotal)
2979*7c478bd9Sstevel@tonic-gate       gl_make_gap_in_buffer(gl, buff_curpos, 1);
2980*7c478bd9Sstevel@tonic-gate /*
2981*7c478bd9Sstevel@tonic-gate  * Copy the character into the buffer.
2982*7c478bd9Sstevel@tonic-gate  */
2983*7c478bd9Sstevel@tonic-gate     gl_buffer_char(gl, c, buff_curpos);
2984*7c478bd9Sstevel@tonic-gate     gl->buff_curpos++;
2985*7c478bd9Sstevel@tonic-gate /*
2986*7c478bd9Sstevel@tonic-gate  * Redraw the line from the cursor position to the end of the line,
2987*7c478bd9Sstevel@tonic-gate  * and move the cursor to just after the added character.
2988*7c478bd9Sstevel@tonic-gate  */
2989*7c478bd9Sstevel@tonic-gate     if(gl_print_string(gl, gl->line + buff_curpos, '\0') ||
2990*7c478bd9Sstevel@tonic-gate        gl_set_term_curpos(gl, term_curpos + width))
2991*7c478bd9Sstevel@tonic-gate       return 1;
2992*7c478bd9Sstevel@tonic-gate /*
2993*7c478bd9Sstevel@tonic-gate  * Are we overwriting an existing character?
2994*7c478bd9Sstevel@tonic-gate  */
2995*7c478bd9Sstevel@tonic-gate   } else {
2996*7c478bd9Sstevel@tonic-gate /*
2997*7c478bd9Sstevel@tonic-gate  * Get the width of the character being overwritten.
2998*7c478bd9Sstevel@tonic-gate  */
2999*7c478bd9Sstevel@tonic-gate     int old_width = gl_displayed_char_width(gl, gl->line[buff_curpos],
3000*7c478bd9Sstevel@tonic-gate 					    term_curpos);
3001*7c478bd9Sstevel@tonic-gate /*
3002*7c478bd9Sstevel@tonic-gate  * Overwrite the character in the buffer.
3003*7c478bd9Sstevel@tonic-gate  */
3004*7c478bd9Sstevel@tonic-gate     gl_buffer_char(gl, c, buff_curpos);
3005*7c478bd9Sstevel@tonic-gate /*
3006*7c478bd9Sstevel@tonic-gate  * If we are replacing with a narrower character, we need to
3007*7c478bd9Sstevel@tonic-gate  * redraw the terminal string to the end of the line, then
3008*7c478bd9Sstevel@tonic-gate  * overwrite the trailing old_width - width characters
3009*7c478bd9Sstevel@tonic-gate  * with spaces.
3010*7c478bd9Sstevel@tonic-gate  */
3011*7c478bd9Sstevel@tonic-gate     if(old_width > width) {
3012*7c478bd9Sstevel@tonic-gate       if(gl_print_string(gl, gl->line + buff_curpos, '\0'))
3013*7c478bd9Sstevel@tonic-gate 	return 1;
3014*7c478bd9Sstevel@tonic-gate /*
3015*7c478bd9Sstevel@tonic-gate  * Clear to the end of the terminal.
3016*7c478bd9Sstevel@tonic-gate  */
3017*7c478bd9Sstevel@tonic-gate       if(gl_truncate_display(gl))
3018*7c478bd9Sstevel@tonic-gate 	return 1;
3019*7c478bd9Sstevel@tonic-gate /*
3020*7c478bd9Sstevel@tonic-gate  * Move the cursor to the end of the new character.
3021*7c478bd9Sstevel@tonic-gate  */
3022*7c478bd9Sstevel@tonic-gate       if(gl_set_term_curpos(gl, term_curpos + width))
3023*7c478bd9Sstevel@tonic-gate 	return 1;
3024*7c478bd9Sstevel@tonic-gate       gl->buff_curpos++;
3025*7c478bd9Sstevel@tonic-gate /*
3026*7c478bd9Sstevel@tonic-gate  * If we are replacing with a wider character, then we will be
3027*7c478bd9Sstevel@tonic-gate  * inserting new characters, and thus extending the line.
3028*7c478bd9Sstevel@tonic-gate  */
3029*7c478bd9Sstevel@tonic-gate     } else if(width > old_width) {
3030*7c478bd9Sstevel@tonic-gate /*
3031*7c478bd9Sstevel@tonic-gate  * Redraw the line from the cursor position to the end of the line,
3032*7c478bd9Sstevel@tonic-gate  * and move the cursor to just after the added character.
3033*7c478bd9Sstevel@tonic-gate  */
3034*7c478bd9Sstevel@tonic-gate       if(gl_print_string(gl, gl->line + buff_curpos, '\0') ||
3035*7c478bd9Sstevel@tonic-gate 	 gl_set_term_curpos(gl, term_curpos + width))
3036*7c478bd9Sstevel@tonic-gate 	return 1;
3037*7c478bd9Sstevel@tonic-gate       gl->buff_curpos++;
3038*7c478bd9Sstevel@tonic-gate /*
3039*7c478bd9Sstevel@tonic-gate  * The original and replacement characters have the same width,
3040*7c478bd9Sstevel@tonic-gate  * so simply overwrite.
3041*7c478bd9Sstevel@tonic-gate  */
3042*7c478bd9Sstevel@tonic-gate     } else {
3043*7c478bd9Sstevel@tonic-gate /*
3044*7c478bd9Sstevel@tonic-gate  * Copy the character into the buffer.
3045*7c478bd9Sstevel@tonic-gate  */
3046*7c478bd9Sstevel@tonic-gate       gl_buffer_char(gl, c, buff_curpos);
3047*7c478bd9Sstevel@tonic-gate       gl->buff_curpos++;
3048*7c478bd9Sstevel@tonic-gate /*
3049*7c478bd9Sstevel@tonic-gate  * Overwrite the original character.
3050*7c478bd9Sstevel@tonic-gate  */
3051*7c478bd9Sstevel@tonic-gate       if(gl_print_char(gl, c, gl->line[gl->buff_curpos]))
3052*7c478bd9Sstevel@tonic-gate 	return 1;
3053*7c478bd9Sstevel@tonic-gate     };
3054*7c478bd9Sstevel@tonic-gate   };
3055*7c478bd9Sstevel@tonic-gate   return 0;
3056*7c478bd9Sstevel@tonic-gate }
3057*7c478bd9Sstevel@tonic-gate 
3058*7c478bd9Sstevel@tonic-gate /*.......................................................................
3059*7c478bd9Sstevel@tonic-gate  * Insert/append a string to the line buffer and terminal at the current
3060*7c478bd9Sstevel@tonic-gate  * cursor position.
3061*7c478bd9Sstevel@tonic-gate  *
3062*7c478bd9Sstevel@tonic-gate  * Input:
3063*7c478bd9Sstevel@tonic-gate  *  gl   GetLine *   The resource object of this library.
3064*7c478bd9Sstevel@tonic-gate  *  s       char *   The string to be added.
3065*7c478bd9Sstevel@tonic-gate  * Output:
3066*7c478bd9Sstevel@tonic-gate  *  return   int     0 - OK.
3067*7c478bd9Sstevel@tonic-gate  *                   1 - Insufficient room.
3068*7c478bd9Sstevel@tonic-gate  */
3069*7c478bd9Sstevel@tonic-gate static int gl_add_string_to_line(GetLine *gl, const char *s)
3070*7c478bd9Sstevel@tonic-gate {
3071*7c478bd9Sstevel@tonic-gate   int buff_slen;   /* The length of the string being added to line[] */
3072*7c478bd9Sstevel@tonic-gate   int term_slen;   /* The length of the string being written to the terminal */
3073*7c478bd9Sstevel@tonic-gate   int buff_curpos; /* The original value of gl->buff_curpos */
3074*7c478bd9Sstevel@tonic-gate   int term_curpos; /* The original value of gl->term_curpos */
3075*7c478bd9Sstevel@tonic-gate /*
3076*7c478bd9Sstevel@tonic-gate  * Keep a record of the current cursor position.
3077*7c478bd9Sstevel@tonic-gate  */
3078*7c478bd9Sstevel@tonic-gate   buff_curpos = gl->buff_curpos;
3079*7c478bd9Sstevel@tonic-gate   term_curpos = gl->term_curpos;
3080*7c478bd9Sstevel@tonic-gate /*
3081*7c478bd9Sstevel@tonic-gate  * How long is the string to be added?
3082*7c478bd9Sstevel@tonic-gate  */
3083*7c478bd9Sstevel@tonic-gate   buff_slen = strlen(s);
3084*7c478bd9Sstevel@tonic-gate   term_slen = gl_displayed_string_width(gl, s, buff_slen, term_curpos);
3085*7c478bd9Sstevel@tonic-gate /*
3086*7c478bd9Sstevel@tonic-gate  * Check that we can accomodate the string in the buffer.
3087*7c478bd9Sstevel@tonic-gate  * If not, simply return, leaving it up to the calling program
3088*7c478bd9Sstevel@tonic-gate  * to check for the absence of a newline character.
3089*7c478bd9Sstevel@tonic-gate  */
3090*7c478bd9Sstevel@tonic-gate   if(gl->ntotal + buff_slen > gl->linelen)
3091*7c478bd9Sstevel@tonic-gate     return 0;
3092*7c478bd9Sstevel@tonic-gate /*
3093*7c478bd9Sstevel@tonic-gate  * Move the characters that follow the cursor in the buffer by
3094*7c478bd9Sstevel@tonic-gate  * buff_slen characters to the right.
3095*7c478bd9Sstevel@tonic-gate  */
3096*7c478bd9Sstevel@tonic-gate   if(gl->ntotal > gl->buff_curpos)
3097*7c478bd9Sstevel@tonic-gate     gl_make_gap_in_buffer(gl, gl->buff_curpos, buff_slen);
3098*7c478bd9Sstevel@tonic-gate /*
3099*7c478bd9Sstevel@tonic-gate  * Copy the string into the buffer.
3100*7c478bd9Sstevel@tonic-gate  */
3101*7c478bd9Sstevel@tonic-gate   gl_buffer_string(gl, s, buff_slen, gl->buff_curpos);
3102*7c478bd9Sstevel@tonic-gate   gl->buff_curpos += buff_slen;
3103*7c478bd9Sstevel@tonic-gate /*
3104*7c478bd9Sstevel@tonic-gate  * Write the modified part of the line to the terminal, then move
3105*7c478bd9Sstevel@tonic-gate  * the terminal cursor to the end of the displayed input string.
3106*7c478bd9Sstevel@tonic-gate  */
3107*7c478bd9Sstevel@tonic-gate   if(gl_print_string(gl, gl->line + buff_curpos, '\0') ||
3108*7c478bd9Sstevel@tonic-gate      gl_set_term_curpos(gl, term_curpos + term_slen))
3109*7c478bd9Sstevel@tonic-gate     return 1;
3110*7c478bd9Sstevel@tonic-gate   return 0;
3111*7c478bd9Sstevel@tonic-gate }
3112*7c478bd9Sstevel@tonic-gate 
3113*7c478bd9Sstevel@tonic-gate /*.......................................................................
3114*7c478bd9Sstevel@tonic-gate  * Read a single character from the terminal.
3115*7c478bd9Sstevel@tonic-gate  *
3116*7c478bd9Sstevel@tonic-gate  * Input:
3117*7c478bd9Sstevel@tonic-gate  *  gl    GetLine *   The resource object of this library.
3118*7c478bd9Sstevel@tonic-gate  *  keep      int     If true, the returned character will be kept in
3119*7c478bd9Sstevel@tonic-gate  *                    the input buffer, for potential replays. It should
3120*7c478bd9Sstevel@tonic-gate  *                    subsequently be removed from the buffer when the
3121*7c478bd9Sstevel@tonic-gate  *                    key sequence that it belongs to has been fully
3122*7c478bd9Sstevel@tonic-gate  *                    processed, by calling gl_discard_chars().
3123*7c478bd9Sstevel@tonic-gate  * Input/Output:
3124*7c478bd9Sstevel@tonic-gate  *  c        char *   The character that is read, is assigned to *c.
3125*7c478bd9Sstevel@tonic-gate  * Output:
3126*7c478bd9Sstevel@tonic-gate  *  return    int     0 - OK.
3127*7c478bd9Sstevel@tonic-gate  *                    1 - Either an I/O error occurred, or a signal was
3128*7c478bd9Sstevel@tonic-gate  *                        caught who's disposition is to abort gl_get_line()
3129*7c478bd9Sstevel@tonic-gate  *                        or to have gl_get_line() return the current line
3130*7c478bd9Sstevel@tonic-gate  *                        as though the user had pressed return. In the
3131*7c478bd9Sstevel@tonic-gate  *                        latter case gl->endline will be non-zero.
3132*7c478bd9Sstevel@tonic-gate  */
3133*7c478bd9Sstevel@tonic-gate static int gl_read_terminal(GetLine *gl, int keep, char *c)
3134*7c478bd9Sstevel@tonic-gate {
3135*7c478bd9Sstevel@tonic-gate /*
3136*7c478bd9Sstevel@tonic-gate  * Before waiting for a new character to be input, flush unwritten
3137*7c478bd9Sstevel@tonic-gate  * characters to the terminal.
3138*7c478bd9Sstevel@tonic-gate  */
3139*7c478bd9Sstevel@tonic-gate   if(gl_flush_output(gl))
3140*7c478bd9Sstevel@tonic-gate     return 1;
3141*7c478bd9Sstevel@tonic-gate /*
3142*7c478bd9Sstevel@tonic-gate  * Record the fact that we are about to read from the terminal.
3143*7c478bd9Sstevel@tonic-gate  */
3144*7c478bd9Sstevel@tonic-gate   gl->pending_io = GLP_READ;
3145*7c478bd9Sstevel@tonic-gate /*
3146*7c478bd9Sstevel@tonic-gate  * If there is already an unread character in the buffer,
3147*7c478bd9Sstevel@tonic-gate  * return it.
3148*7c478bd9Sstevel@tonic-gate  */
3149*7c478bd9Sstevel@tonic-gate   if(gl->nread < gl->nbuf) {
3150*7c478bd9Sstevel@tonic-gate     *c = gl->keybuf[gl->nread];
3151*7c478bd9Sstevel@tonic-gate /*
3152*7c478bd9Sstevel@tonic-gate  * Retain the character in the key buffer, but mark it as having been read?
3153*7c478bd9Sstevel@tonic-gate  */
3154*7c478bd9Sstevel@tonic-gate     if(keep) {
3155*7c478bd9Sstevel@tonic-gate       gl->nread++;
3156*7c478bd9Sstevel@tonic-gate /*
3157*7c478bd9Sstevel@tonic-gate  * Completely remove the character from the key buffer?
3158*7c478bd9Sstevel@tonic-gate  */
3159*7c478bd9Sstevel@tonic-gate     } else {
3160*7c478bd9Sstevel@tonic-gate       memmove(gl->keybuf + gl->nread, gl->keybuf + gl->nread + 1,
3161*7c478bd9Sstevel@tonic-gate 	      gl->nbuf - gl->nread - 1);
3162*7c478bd9Sstevel@tonic-gate     };
3163*7c478bd9Sstevel@tonic-gate     return 0;
3164*7c478bd9Sstevel@tonic-gate   };
3165*7c478bd9Sstevel@tonic-gate /*
3166*7c478bd9Sstevel@tonic-gate  * Make sure that there is space in the key buffer for one more character.
3167*7c478bd9Sstevel@tonic-gate  * This should always be true if gl_interpret_char() is called for each
3168*7c478bd9Sstevel@tonic-gate  * new character added, since it will clear the buffer once it has recognized
3169*7c478bd9Sstevel@tonic-gate  * or rejected a key sequence.
3170*7c478bd9Sstevel@tonic-gate  */
3171*7c478bd9Sstevel@tonic-gate   if(gl->nbuf + 1 > GL_KEY_MAX) {
3172*7c478bd9Sstevel@tonic-gate     gl_print_info(gl, "gl_read_terminal: Buffer overflow avoided.",
3173*7c478bd9Sstevel@tonic-gate 		  GL_END_INFO);
3174*7c478bd9Sstevel@tonic-gate     errno = EIO;
3175*7c478bd9Sstevel@tonic-gate     return 1;
3176*7c478bd9Sstevel@tonic-gate   };
3177*7c478bd9Sstevel@tonic-gate /*
3178*7c478bd9Sstevel@tonic-gate  * Read one character from the terminal.
3179*7c478bd9Sstevel@tonic-gate  */
3180*7c478bd9Sstevel@tonic-gate   switch(gl_read_input(gl, c)) {
3181*7c478bd9Sstevel@tonic-gate   case GL_READ_OK:
3182*7c478bd9Sstevel@tonic-gate     break;
3183*7c478bd9Sstevel@tonic-gate   case GL_READ_BLOCKED:
3184*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO);
3185*7c478bd9Sstevel@tonic-gate     return 1;
3186*7c478bd9Sstevel@tonic-gate     break;
3187*7c478bd9Sstevel@tonic-gate   default:
3188*7c478bd9Sstevel@tonic-gate     return 1;
3189*7c478bd9Sstevel@tonic-gate     break;
3190*7c478bd9Sstevel@tonic-gate   };
3191*7c478bd9Sstevel@tonic-gate /*
3192*7c478bd9Sstevel@tonic-gate  * Append the character to the key buffer?
3193*7c478bd9Sstevel@tonic-gate  */
3194*7c478bd9Sstevel@tonic-gate   if(keep) {
3195*7c478bd9Sstevel@tonic-gate     gl->keybuf[gl->nbuf] = *c;
3196*7c478bd9Sstevel@tonic-gate     gl->nread = ++gl->nbuf;
3197*7c478bd9Sstevel@tonic-gate   };
3198*7c478bd9Sstevel@tonic-gate   return 0;
3199*7c478bd9Sstevel@tonic-gate }
3200*7c478bd9Sstevel@tonic-gate 
3201*7c478bd9Sstevel@tonic-gate /*.......................................................................
3202*7c478bd9Sstevel@tonic-gate  * Read one or more keypresses from the terminal of an input stream.
3203*7c478bd9Sstevel@tonic-gate  *
3204*7c478bd9Sstevel@tonic-gate  * Input:
3205*7c478bd9Sstevel@tonic-gate  *  gl           GetLine *  The resource object of this module.
3206*7c478bd9Sstevel@tonic-gate  *  c               char *  The character that was read is assigned to *c.
3207*7c478bd9Sstevel@tonic-gate  * Output:
3208*7c478bd9Sstevel@tonic-gate  *  return  GlReadStatus    The completion status of the read operation.
3209*7c478bd9Sstevel@tonic-gate  */
3210*7c478bd9Sstevel@tonic-gate static GlReadStatus gl_read_input(GetLine *gl, char *c)
3211*7c478bd9Sstevel@tonic-gate {
3212*7c478bd9Sstevel@tonic-gate /*
3213*7c478bd9Sstevel@tonic-gate  * We may have to repeat the read if window change signals are received.
3214*7c478bd9Sstevel@tonic-gate  */
3215*7c478bd9Sstevel@tonic-gate   for(;;) {
3216*7c478bd9Sstevel@tonic-gate /*
3217*7c478bd9Sstevel@tonic-gate  * Which file descriptor should we read from? Mark this volatile, so
3218*7c478bd9Sstevel@tonic-gate  * that siglongjmp() can't clobber it.
3219*7c478bd9Sstevel@tonic-gate  */
3220*7c478bd9Sstevel@tonic-gate     volatile int fd = gl->file_fp ? fileno(gl->file_fp) : gl->input_fd;
3221*7c478bd9Sstevel@tonic-gate /*
3222*7c478bd9Sstevel@tonic-gate  * If the endline flag becomes set, don't wait for another character.
3223*7c478bd9Sstevel@tonic-gate  */
3224*7c478bd9Sstevel@tonic-gate     if(gl->endline)
3225*7c478bd9Sstevel@tonic-gate       return GL_READ_ERROR;
3226*7c478bd9Sstevel@tonic-gate /*
3227*7c478bd9Sstevel@tonic-gate  * Since the code in this function can block, trap signals.
3228*7c478bd9Sstevel@tonic-gate  */
3229*7c478bd9Sstevel@tonic-gate     if(sigsetjmp(gl_setjmp_buffer, 1)==0) {
3230*7c478bd9Sstevel@tonic-gate /*
3231*7c478bd9Sstevel@tonic-gate  * Handle the different I/O modes.
3232*7c478bd9Sstevel@tonic-gate  */
3233*7c478bd9Sstevel@tonic-gate       switch(gl->io_mode) {
3234*7c478bd9Sstevel@tonic-gate /*
3235*7c478bd9Sstevel@tonic-gate  * In normal I/O mode, we call the event handler before attempting
3236*7c478bd9Sstevel@tonic-gate  * to read, since read() blocks.
3237*7c478bd9Sstevel@tonic-gate  */
3238*7c478bd9Sstevel@tonic-gate       case GL_NORMAL_MODE:
3239*7c478bd9Sstevel@tonic-gate 	if(gl_event_handler(gl, fd))
3240*7c478bd9Sstevel@tonic-gate 	  return GL_READ_ERROR;
3241*7c478bd9Sstevel@tonic-gate 	return gl_read_unmasked(gl, fd, c);  /* Read one character */
3242*7c478bd9Sstevel@tonic-gate 	break;
3243*7c478bd9Sstevel@tonic-gate /*
3244*7c478bd9Sstevel@tonic-gate  * In non-blocking server I/O mode, we attempt to read a character,
3245*7c478bd9Sstevel@tonic-gate  * and only if this fails, call the event handler to wait for a any
3246*7c478bd9Sstevel@tonic-gate  * user-configured timeout and any other user-configured events.  In
3247*7c478bd9Sstevel@tonic-gate  * addition, we turn off the fcntl() non-blocking flag when reading
3248*7c478bd9Sstevel@tonic-gate  * from the terminal, to work around a bug in Solaris. We can do this
3249*7c478bd9Sstevel@tonic-gate  * without causing the read() to block, because when in non-blocking
3250*7c478bd9Sstevel@tonic-gate  * server-I/O mode, gl_raw_io() sets the VMIN terminal attribute to 0,
3251*7c478bd9Sstevel@tonic-gate  * which tells the terminal driver to return immediately if no
3252*7c478bd9Sstevel@tonic-gate  * characters are available to be read.
3253*7c478bd9Sstevel@tonic-gate  */
3254*7c478bd9Sstevel@tonic-gate       case GL_SERVER_MODE:
3255*7c478bd9Sstevel@tonic-gate 	{
3256*7c478bd9Sstevel@tonic-gate 	  GlReadStatus status;        /* The return status */
3257*7c478bd9Sstevel@tonic-gate 	  if(isatty(fd))              /* If we reading from a terminal, */
3258*7c478bd9Sstevel@tonic-gate 	     gl_blocking_io(gl, fd);  /* switch to blocking I/O */
3259*7c478bd9Sstevel@tonic-gate 	  status = gl_read_unmasked(gl, fd, c); /* Try reading */
3260*7c478bd9Sstevel@tonic-gate 	  if(status == GL_READ_BLOCKED) {       /* Nothing readable yet */
3261*7c478bd9Sstevel@tonic-gate 	    if(gl_event_handler(gl, fd))        /* Wait for input */
3262*7c478bd9Sstevel@tonic-gate 	      status = GL_READ_ERROR;
3263*7c478bd9Sstevel@tonic-gate 	    else
3264*7c478bd9Sstevel@tonic-gate 	      status = gl_read_unmasked(gl, fd, c); /* Try reading again */
3265*7c478bd9Sstevel@tonic-gate 	  };
3266*7c478bd9Sstevel@tonic-gate 	  gl_nonblocking_io(gl, fd); /* Restore non-blocking I/O */
3267*7c478bd9Sstevel@tonic-gate 	  return status;
3268*7c478bd9Sstevel@tonic-gate 	};
3269*7c478bd9Sstevel@tonic-gate 	break;
3270*7c478bd9Sstevel@tonic-gate       };
3271*7c478bd9Sstevel@tonic-gate     };
3272*7c478bd9Sstevel@tonic-gate /*
3273*7c478bd9Sstevel@tonic-gate  * To get here, one of the signals that we are trapping must have
3274*7c478bd9Sstevel@tonic-gate  * been received. Note that by using sigsetjmp() instead of setjmp()
3275*7c478bd9Sstevel@tonic-gate  * the signal mask that was blocking these signals will have been
3276*7c478bd9Sstevel@tonic-gate  * reinstated, so we can be sure that no more of these signals will
3277*7c478bd9Sstevel@tonic-gate  * be received until we explicitly unblock them again.
3278*7c478bd9Sstevel@tonic-gate  *
3279*7c478bd9Sstevel@tonic-gate  * First, if non-blocking I/O was temporarily disabled, reinstate it.
3280*7c478bd9Sstevel@tonic-gate  */
3281*7c478bd9Sstevel@tonic-gate     if(gl->io_mode == GL_SERVER_MODE)
3282*7c478bd9Sstevel@tonic-gate       gl_nonblocking_io(gl, fd);
3283*7c478bd9Sstevel@tonic-gate /*
3284*7c478bd9Sstevel@tonic-gate  * Now respond to the signal that was caught.
3285*7c478bd9Sstevel@tonic-gate  */
3286*7c478bd9Sstevel@tonic-gate     if(gl_check_caught_signal(gl))
3287*7c478bd9Sstevel@tonic-gate       return GL_READ_ERROR;
3288*7c478bd9Sstevel@tonic-gate   };
3289*7c478bd9Sstevel@tonic-gate }
3290*7c478bd9Sstevel@tonic-gate 
3291*7c478bd9Sstevel@tonic-gate /*.......................................................................
3292*7c478bd9Sstevel@tonic-gate  * This is a private function of gl_read_input(), which unblocks signals
3293*7c478bd9Sstevel@tonic-gate  * temporarily while it reads a single character from the specified file
3294*7c478bd9Sstevel@tonic-gate  * descriptor.
3295*7c478bd9Sstevel@tonic-gate  *
3296*7c478bd9Sstevel@tonic-gate  * Input:
3297*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *  The resource object of this module.
3298*7c478bd9Sstevel@tonic-gate  *  fd              int    The file descriptor to read from.
3299*7c478bd9Sstevel@tonic-gate  *  c              char *  The character that was read is assigned to *c.
3300*7c478bd9Sstevel@tonic-gate  * Output:
3301*7c478bd9Sstevel@tonic-gate  *  return GlReadStatus    The completion status of the read.
3302*7c478bd9Sstevel@tonic-gate  */
3303*7c478bd9Sstevel@tonic-gate static int gl_read_unmasked(GetLine *gl, int fd, char *c)
3304*7c478bd9Sstevel@tonic-gate {
3305*7c478bd9Sstevel@tonic-gate   int nread;  /* The return value of read() */
3306*7c478bd9Sstevel@tonic-gate /*
3307*7c478bd9Sstevel@tonic-gate  * Unblock the signals that we are trapping, while waiting for I/O.
3308*7c478bd9Sstevel@tonic-gate  */
3309*7c478bd9Sstevel@tonic-gate   gl_catch_signals(gl);
3310*7c478bd9Sstevel@tonic-gate /*
3311*7c478bd9Sstevel@tonic-gate  * Attempt to read one character from the terminal, restarting the read
3312*7c478bd9Sstevel@tonic-gate  * if any signals that we aren't trapping, are received.
3313*7c478bd9Sstevel@tonic-gate  */
3314*7c478bd9Sstevel@tonic-gate   do {
3315*7c478bd9Sstevel@tonic-gate     errno = 0;
3316*7c478bd9Sstevel@tonic-gate     nread = read(fd, c, 1);
3317*7c478bd9Sstevel@tonic-gate   } while(nread < 0 && errno==EINTR);
3318*7c478bd9Sstevel@tonic-gate /*
3319*7c478bd9Sstevel@tonic-gate  * Block all of the signals that we are trapping.
3320*7c478bd9Sstevel@tonic-gate  */
3321*7c478bd9Sstevel@tonic-gate   gl_mask_signals(gl, NULL);
3322*7c478bd9Sstevel@tonic-gate /*
3323*7c478bd9Sstevel@tonic-gate  * Check the completion status of the read.
3324*7c478bd9Sstevel@tonic-gate  */
3325*7c478bd9Sstevel@tonic-gate   switch(nread) {
3326*7c478bd9Sstevel@tonic-gate   case 1:
3327*7c478bd9Sstevel@tonic-gate     return GL_READ_OK;
3328*7c478bd9Sstevel@tonic-gate   case 0:
3329*7c478bd9Sstevel@tonic-gate     return (isatty(fd) || errno != 0) ? GL_READ_BLOCKED : GL_READ_EOF;
3330*7c478bd9Sstevel@tonic-gate   default:
3331*7c478bd9Sstevel@tonic-gate     return GL_READ_ERROR;
3332*7c478bd9Sstevel@tonic-gate   };
3333*7c478bd9Sstevel@tonic-gate }
3334*7c478bd9Sstevel@tonic-gate 
3335*7c478bd9Sstevel@tonic-gate /*.......................................................................
3336*7c478bd9Sstevel@tonic-gate  * Remove a specified number of characters from the start of the
3337*7c478bd9Sstevel@tonic-gate  * key-press lookahead buffer, gl->keybuf[], and arrange for the next
3338*7c478bd9Sstevel@tonic-gate  * read to start from the character at the start of the shifted buffer.
3339*7c478bd9Sstevel@tonic-gate  *
3340*7c478bd9Sstevel@tonic-gate  * Input:
3341*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The resource object of this module.
3342*7c478bd9Sstevel@tonic-gate  *  nused       int    The number of characters to discard from the start
3343*7c478bd9Sstevel@tonic-gate  *                     of the buffer.
3344*7c478bd9Sstevel@tonic-gate  */
3345*7c478bd9Sstevel@tonic-gate static void gl_discard_chars(GetLine *gl, int nused)
3346*7c478bd9Sstevel@tonic-gate {
3347*7c478bd9Sstevel@tonic-gate   int nkeep = gl->nbuf - nused;
3348*7c478bd9Sstevel@tonic-gate   if(nkeep > 0) {
3349*7c478bd9Sstevel@tonic-gate     memmove(gl->keybuf, gl->keybuf + nused, nkeep);
3350*7c478bd9Sstevel@tonic-gate     gl->nbuf = nkeep;
3351*7c478bd9Sstevel@tonic-gate     gl->nread = 0;
3352*7c478bd9Sstevel@tonic-gate   } else {
3353*7c478bd9Sstevel@tonic-gate     gl->nbuf = gl->nread = 0;
3354*7c478bd9Sstevel@tonic-gate   };
3355*7c478bd9Sstevel@tonic-gate }
3356*7c478bd9Sstevel@tonic-gate 
3357*7c478bd9Sstevel@tonic-gate /*.......................................................................
3358*7c478bd9Sstevel@tonic-gate  * This function is called to handle signals caught between calls to
3359*7c478bd9Sstevel@tonic-gate  * sigsetjmp() and siglongjmp().
3360*7c478bd9Sstevel@tonic-gate  *
3361*7c478bd9Sstevel@tonic-gate  * Input:
3362*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *   The resource object of this library.
3363*7c478bd9Sstevel@tonic-gate  * Output:
3364*7c478bd9Sstevel@tonic-gate  *  return      int     0 - Signal handled internally.
3365*7c478bd9Sstevel@tonic-gate  *                      1 - Signal requires gl_get_line() to abort.
3366*7c478bd9Sstevel@tonic-gate  */
3367*7c478bd9Sstevel@tonic-gate static int gl_check_caught_signal(GetLine *gl)
3368*7c478bd9Sstevel@tonic-gate {
3369*7c478bd9Sstevel@tonic-gate   GlSignalNode *sig;      /* The signal disposition */
3370*7c478bd9Sstevel@tonic-gate   SigAction keep_action;  /* The signal disposition of tecla signal handlers */
3371*7c478bd9Sstevel@tonic-gate   unsigned flags;         /* The signal processing flags to use */
3372*7c478bd9Sstevel@tonic-gate   int signo;              /* The signal to be handled */
3373*7c478bd9Sstevel@tonic-gate /*
3374*7c478bd9Sstevel@tonic-gate  * Was no signal caught?
3375*7c478bd9Sstevel@tonic-gate  */
3376*7c478bd9Sstevel@tonic-gate   if(gl_pending_signal == -1)
3377*7c478bd9Sstevel@tonic-gate     return 0;
3378*7c478bd9Sstevel@tonic-gate /*
3379*7c478bd9Sstevel@tonic-gate  * Get the signal to be handled.
3380*7c478bd9Sstevel@tonic-gate  */
3381*7c478bd9Sstevel@tonic-gate   signo = gl_pending_signal;
3382*7c478bd9Sstevel@tonic-gate /*
3383*7c478bd9Sstevel@tonic-gate  * Mark the signal as handled. Note that at this point, all of
3384*7c478bd9Sstevel@tonic-gate  * the signals that we are trapping are blocked from delivery.
3385*7c478bd9Sstevel@tonic-gate  */
3386*7c478bd9Sstevel@tonic-gate   gl_pending_signal = -1;
3387*7c478bd9Sstevel@tonic-gate /*
3388*7c478bd9Sstevel@tonic-gate  * Record the signal that was caught, so that the user can query it later.
3389*7c478bd9Sstevel@tonic-gate  */
3390*7c478bd9Sstevel@tonic-gate   gl->last_signal = signo;
3391*7c478bd9Sstevel@tonic-gate /*
3392*7c478bd9Sstevel@tonic-gate  * In non-blocking server mode, the application is responsible for
3393*7c478bd9Sstevel@tonic-gate  * responding to terminal signals, and we don't want gl_get_line()s
3394*7c478bd9Sstevel@tonic-gate  * normal signal handling to clash with this, so whenever a signal
3395*7c478bd9Sstevel@tonic-gate  * is caught, we arrange for gl_get_line() to abort and requeue the
3396*7c478bd9Sstevel@tonic-gate  * signal while signals are still blocked. If the application
3397*7c478bd9Sstevel@tonic-gate  * had the signal unblocked when gl_get_line() was called, the signal
3398*7c478bd9Sstevel@tonic-gate  * will be delivered again as soon as gl_get_line() restores the
3399*7c478bd9Sstevel@tonic-gate  * process signal mask, just before returning to the application.
3400*7c478bd9Sstevel@tonic-gate  * Note that the caller of this function should set gl->pending_io
3401*7c478bd9Sstevel@tonic-gate  * to the appropriate choice of GLP_READ and GLP_WRITE, before returning.
3402*7c478bd9Sstevel@tonic-gate  */
3403*7c478bd9Sstevel@tonic-gate   if(gl->io_mode==GL_SERVER_MODE) {
3404*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_SIGNAL, EINTR);
3405*7c478bd9Sstevel@tonic-gate     raise(signo);
3406*7c478bd9Sstevel@tonic-gate     return 1;
3407*7c478bd9Sstevel@tonic-gate   };
3408*7c478bd9Sstevel@tonic-gate /*
3409*7c478bd9Sstevel@tonic-gate  * Lookup the requested disposition of this signal.
3410*7c478bd9Sstevel@tonic-gate  */
3411*7c478bd9Sstevel@tonic-gate   for(sig=gl->sigs; sig && sig->signo != signo; sig=sig->next)
3412*7c478bd9Sstevel@tonic-gate     ;
3413*7c478bd9Sstevel@tonic-gate   if(!sig)
3414*7c478bd9Sstevel@tonic-gate     return 0;
3415*7c478bd9Sstevel@tonic-gate /*
3416*7c478bd9Sstevel@tonic-gate  * Get the signal response flags for this signal.
3417*7c478bd9Sstevel@tonic-gate  */
3418*7c478bd9Sstevel@tonic-gate   flags = sig->flags;
3419*7c478bd9Sstevel@tonic-gate /*
3420*7c478bd9Sstevel@tonic-gate  * Did we receive a terminal size signal?
3421*7c478bd9Sstevel@tonic-gate  */
3422*7c478bd9Sstevel@tonic-gate #ifdef SIGWINCH
3423*7c478bd9Sstevel@tonic-gate   if(signo == SIGWINCH && _gl_update_size(gl))
3424*7c478bd9Sstevel@tonic-gate     return 1;
3425*7c478bd9Sstevel@tonic-gate #endif
3426*7c478bd9Sstevel@tonic-gate /*
3427*7c478bd9Sstevel@tonic-gate  * Start a fresh line?
3428*7c478bd9Sstevel@tonic-gate  */
3429*7c478bd9Sstevel@tonic-gate   if(flags & GLS_RESTORE_LINE) {
3430*7c478bd9Sstevel@tonic-gate     if(gl_start_newline(gl, 0))
3431*7c478bd9Sstevel@tonic-gate       return 1;
3432*7c478bd9Sstevel@tonic-gate   };
3433*7c478bd9Sstevel@tonic-gate /*
3434*7c478bd9Sstevel@tonic-gate  * Restore terminal settings to how they were before gl_get_line() was
3435*7c478bd9Sstevel@tonic-gate  * called?
3436*7c478bd9Sstevel@tonic-gate  */
3437*7c478bd9Sstevel@tonic-gate   if(flags & GLS_RESTORE_TTY)
3438*7c478bd9Sstevel@tonic-gate     gl_restore_terminal_attributes(gl);
3439*7c478bd9Sstevel@tonic-gate /*
3440*7c478bd9Sstevel@tonic-gate  * Restore signal handlers to how they were before gl_get_line() was
3441*7c478bd9Sstevel@tonic-gate  * called? If this hasn't been requested, only reinstate the signal
3442*7c478bd9Sstevel@tonic-gate  * handler of the signal that we are handling.
3443*7c478bd9Sstevel@tonic-gate  */
3444*7c478bd9Sstevel@tonic-gate   if(flags & GLS_RESTORE_SIG) {
3445*7c478bd9Sstevel@tonic-gate     gl_restore_signal_handlers(gl);
3446*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &gl->old_signal_set);
3447*7c478bd9Sstevel@tonic-gate   } else {
3448*7c478bd9Sstevel@tonic-gate     (void) sigaction(sig->signo, &sig->original, &keep_action);
3449*7c478bd9Sstevel@tonic-gate     (void) sigprocmask(SIG_UNBLOCK, &sig->proc_mask, NULL);
3450*7c478bd9Sstevel@tonic-gate   };
3451*7c478bd9Sstevel@tonic-gate /*
3452*7c478bd9Sstevel@tonic-gate  * Forward the signal to the application's signal handler.
3453*7c478bd9Sstevel@tonic-gate  */
3454*7c478bd9Sstevel@tonic-gate   if(!(flags & GLS_DONT_FORWARD))
3455*7c478bd9Sstevel@tonic-gate     raise(signo);
3456*7c478bd9Sstevel@tonic-gate /*
3457*7c478bd9Sstevel@tonic-gate  * Reinstate our signal handlers.
3458*7c478bd9Sstevel@tonic-gate  */
3459*7c478bd9Sstevel@tonic-gate   if(flags & GLS_RESTORE_SIG) {
3460*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, NULL);
3461*7c478bd9Sstevel@tonic-gate     gl_override_signal_handlers(gl);
3462*7c478bd9Sstevel@tonic-gate   } else {
3463*7c478bd9Sstevel@tonic-gate     (void) sigaction(sig->signo, &keep_action, NULL);
3464*7c478bd9Sstevel@tonic-gate     (void) sigprocmask(SIG_BLOCK, &sig->proc_mask, NULL);
3465*7c478bd9Sstevel@tonic-gate   };
3466*7c478bd9Sstevel@tonic-gate /*
3467*7c478bd9Sstevel@tonic-gate  * Do we need to reinstate our terminal settings?
3468*7c478bd9Sstevel@tonic-gate  */
3469*7c478bd9Sstevel@tonic-gate   if(flags & GLS_RESTORE_TTY)
3470*7c478bd9Sstevel@tonic-gate     gl_raw_terminal_mode(gl);
3471*7c478bd9Sstevel@tonic-gate /*
3472*7c478bd9Sstevel@tonic-gate  * Redraw the line?
3473*7c478bd9Sstevel@tonic-gate  */
3474*7c478bd9Sstevel@tonic-gate   if(flags & GLS_REDRAW_LINE)
3475*7c478bd9Sstevel@tonic-gate     gl_queue_redisplay(gl);
3476*7c478bd9Sstevel@tonic-gate /*
3477*7c478bd9Sstevel@tonic-gate  * What next?
3478*7c478bd9Sstevel@tonic-gate  */
3479*7c478bd9Sstevel@tonic-gate   switch(sig->after) {
3480*7c478bd9Sstevel@tonic-gate   case GLS_RETURN:
3481*7c478bd9Sstevel@tonic-gate     gl_newline(gl, 1, NULL);
3482*7c478bd9Sstevel@tonic-gate     return gl_flush_output(gl);
3483*7c478bd9Sstevel@tonic-gate     break;
3484*7c478bd9Sstevel@tonic-gate   case GLS_ABORT:
3485*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_SIGNAL, sig->errno_value);
3486*7c478bd9Sstevel@tonic-gate     return 1;
3487*7c478bd9Sstevel@tonic-gate     break;
3488*7c478bd9Sstevel@tonic-gate   case GLS_CONTINUE:
3489*7c478bd9Sstevel@tonic-gate     return gl_flush_output(gl);
3490*7c478bd9Sstevel@tonic-gate     break;
3491*7c478bd9Sstevel@tonic-gate   };
3492*7c478bd9Sstevel@tonic-gate   return 0;
3493*7c478bd9Sstevel@tonic-gate }
3494*7c478bd9Sstevel@tonic-gate 
3495*7c478bd9Sstevel@tonic-gate /*.......................................................................
3496*7c478bd9Sstevel@tonic-gate  * Get pertinent terminal control strings and the initial terminal size.
3497*7c478bd9Sstevel@tonic-gate  *
3498*7c478bd9Sstevel@tonic-gate  * Input:
3499*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  The resource object of this library.
3500*7c478bd9Sstevel@tonic-gate  *  term      char *  The type of the terminal.
3501*7c478bd9Sstevel@tonic-gate  * Output:
3502*7c478bd9Sstevel@tonic-gate  *  return     int    0 - OK.
3503*7c478bd9Sstevel@tonic-gate  *                    1 - Error.
3504*7c478bd9Sstevel@tonic-gate  */
3505*7c478bd9Sstevel@tonic-gate static int gl_control_strings(GetLine *gl, const char *term)
3506*7c478bd9Sstevel@tonic-gate {
3507*7c478bd9Sstevel@tonic-gate   int bad_term = 0;   /* True if term is unusable */
3508*7c478bd9Sstevel@tonic-gate /*
3509*7c478bd9Sstevel@tonic-gate  * Discard any existing control strings from a previous terminal.
3510*7c478bd9Sstevel@tonic-gate  */
3511*7c478bd9Sstevel@tonic-gate   gl->left = NULL;
3512*7c478bd9Sstevel@tonic-gate   gl->right = NULL;
3513*7c478bd9Sstevel@tonic-gate   gl->up = NULL;
3514*7c478bd9Sstevel@tonic-gate   gl->down = NULL;
3515*7c478bd9Sstevel@tonic-gate   gl->home = NULL;
3516*7c478bd9Sstevel@tonic-gate   gl->bol = 0;
3517*7c478bd9Sstevel@tonic-gate   gl->clear_eol = NULL;
3518*7c478bd9Sstevel@tonic-gate   gl->clear_eod = NULL;
3519*7c478bd9Sstevel@tonic-gate   gl->u_arrow = NULL;
3520*7c478bd9Sstevel@tonic-gate   gl->d_arrow = NULL;
3521*7c478bd9Sstevel@tonic-gate   gl->l_arrow = NULL;
3522*7c478bd9Sstevel@tonic-gate   gl->r_arrow = NULL;
3523*7c478bd9Sstevel@tonic-gate   gl->sound_bell = NULL;
3524*7c478bd9Sstevel@tonic-gate   gl->bold = NULL;
3525*7c478bd9Sstevel@tonic-gate   gl->underline = NULL;
3526*7c478bd9Sstevel@tonic-gate   gl->standout = NULL;
3527*7c478bd9Sstevel@tonic-gate   gl->dim = NULL;
3528*7c478bd9Sstevel@tonic-gate   gl->reverse = NULL;
3529*7c478bd9Sstevel@tonic-gate   gl->blink = NULL;
3530*7c478bd9Sstevel@tonic-gate   gl->text_attr_off = NULL;
3531*7c478bd9Sstevel@tonic-gate   gl->nline = 0;
3532*7c478bd9Sstevel@tonic-gate   gl->ncolumn = 0;
3533*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
3534*7c478bd9Sstevel@tonic-gate   gl->left_n = NULL;
3535*7c478bd9Sstevel@tonic-gate   gl->right_n = NULL;
3536*7c478bd9Sstevel@tonic-gate #endif
3537*7c478bd9Sstevel@tonic-gate /*
3538*7c478bd9Sstevel@tonic-gate  * If possible lookup the information in a terminal information
3539*7c478bd9Sstevel@tonic-gate  * database.
3540*7c478bd9Sstevel@tonic-gate  */
3541*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
3542*7c478bd9Sstevel@tonic-gate   if(!term || setupterm((char *)term, gl->input_fd, NULL) == ERR) {
3543*7c478bd9Sstevel@tonic-gate     bad_term = 1;
3544*7c478bd9Sstevel@tonic-gate   } else {
3545*7c478bd9Sstevel@tonic-gate     _clr_StringGroup(gl->capmem);
3546*7c478bd9Sstevel@tonic-gate     gl->left = gl_tigetstr(gl, "cub1");
3547*7c478bd9Sstevel@tonic-gate     gl->right = gl_tigetstr(gl, "cuf1");
3548*7c478bd9Sstevel@tonic-gate     gl->up = gl_tigetstr(gl, "cuu1");
3549*7c478bd9Sstevel@tonic-gate     gl->down = gl_tigetstr(gl, "cud1");
3550*7c478bd9Sstevel@tonic-gate     gl->home = gl_tigetstr(gl, "home");
3551*7c478bd9Sstevel@tonic-gate     gl->clear_eol = gl_tigetstr(gl, "el");
3552*7c478bd9Sstevel@tonic-gate     gl->clear_eod = gl_tigetstr(gl, "ed");
3553*7c478bd9Sstevel@tonic-gate     gl->u_arrow = gl_tigetstr(gl, "kcuu1");
3554*7c478bd9Sstevel@tonic-gate     gl->d_arrow = gl_tigetstr(gl, "kcud1");
3555*7c478bd9Sstevel@tonic-gate     gl->l_arrow = gl_tigetstr(gl, "kcub1");
3556*7c478bd9Sstevel@tonic-gate     gl->r_arrow = gl_tigetstr(gl, "kcuf1");
3557*7c478bd9Sstevel@tonic-gate     gl->left_n = gl_tigetstr(gl, "cub");
3558*7c478bd9Sstevel@tonic-gate     gl->right_n = gl_tigetstr(gl, "cuf");
3559*7c478bd9Sstevel@tonic-gate     gl->sound_bell = gl_tigetstr(gl, "bel");
3560*7c478bd9Sstevel@tonic-gate     gl->bold = gl_tigetstr(gl, "bold");
3561*7c478bd9Sstevel@tonic-gate     gl->underline = gl_tigetstr(gl, "smul");
3562*7c478bd9Sstevel@tonic-gate     gl->standout = gl_tigetstr(gl, "smso");
3563*7c478bd9Sstevel@tonic-gate     gl->dim = gl_tigetstr(gl, "dim");
3564*7c478bd9Sstevel@tonic-gate     gl->reverse = gl_tigetstr(gl, "rev");
3565*7c478bd9Sstevel@tonic-gate     gl->blink = gl_tigetstr(gl, "blink");
3566*7c478bd9Sstevel@tonic-gate     gl->text_attr_off = gl_tigetstr(gl, "sgr0");
3567*7c478bd9Sstevel@tonic-gate   };
3568*7c478bd9Sstevel@tonic-gate #elif defined(USE_TERMCAP)
3569*7c478bd9Sstevel@tonic-gate   if(!term || tgetent(gl->tgetent_buf, (char *)term) < 0) {
3570*7c478bd9Sstevel@tonic-gate     bad_term = 1;
3571*7c478bd9Sstevel@tonic-gate   } else {
3572*7c478bd9Sstevel@tonic-gate     char *tgetstr_buf_ptr = gl->tgetstr_buf;
3573*7c478bd9Sstevel@tonic-gate     _clr_StringGroup(gl->capmem);
3574*7c478bd9Sstevel@tonic-gate     gl->left = gl_tgetstr(gl, "le", &tgetstr_buf_ptr);
3575*7c478bd9Sstevel@tonic-gate     gl->right = gl_tgetstr(gl, "nd", &tgetstr_buf_ptr);
3576*7c478bd9Sstevel@tonic-gate     gl->up = gl_tgetstr(gl, "up", &tgetstr_buf_ptr);
3577*7c478bd9Sstevel@tonic-gate     gl->down = gl_tgetstr(gl, "do", &tgetstr_buf_ptr);
3578*7c478bd9Sstevel@tonic-gate     gl->home = gl_tgetstr(gl, "ho", &tgetstr_buf_ptr);
3579*7c478bd9Sstevel@tonic-gate     gl->clear_eol = gl_tgetstr(gl, "ce", &tgetstr_buf_ptr);
3580*7c478bd9Sstevel@tonic-gate     gl->clear_eod = gl_tgetstr(gl, "cd", &tgetstr_buf_ptr);
3581*7c478bd9Sstevel@tonic-gate     gl->u_arrow = gl_tgetstr(gl, "ku", &tgetstr_buf_ptr);
3582*7c478bd9Sstevel@tonic-gate     gl->d_arrow = gl_tgetstr(gl, "kd", &tgetstr_buf_ptr);
3583*7c478bd9Sstevel@tonic-gate     gl->l_arrow = gl_tgetstr(gl, "kl", &tgetstr_buf_ptr);
3584*7c478bd9Sstevel@tonic-gate     gl->r_arrow = gl_tgetstr(gl, "kr", &tgetstr_buf_ptr);
3585*7c478bd9Sstevel@tonic-gate     gl->sound_bell = gl_tgetstr(gl, "bl", &tgetstr_buf_ptr);
3586*7c478bd9Sstevel@tonic-gate     gl->bold = gl_tgetstr(gl, "md", &tgetstr_buf_ptr);
3587*7c478bd9Sstevel@tonic-gate     gl->underline = gl_tgetstr(gl, "us", &tgetstr_buf_ptr);
3588*7c478bd9Sstevel@tonic-gate     gl->standout = gl_tgetstr(gl, "so", &tgetstr_buf_ptr);
3589*7c478bd9Sstevel@tonic-gate     gl->dim = gl_tgetstr(gl, "mh", &tgetstr_buf_ptr);
3590*7c478bd9Sstevel@tonic-gate     gl->reverse = gl_tgetstr(gl, "mr", &tgetstr_buf_ptr);
3591*7c478bd9Sstevel@tonic-gate     gl->blink = gl_tgetstr(gl, "mb", &tgetstr_buf_ptr);
3592*7c478bd9Sstevel@tonic-gate     gl->text_attr_off = gl_tgetstr(gl, "me", &tgetstr_buf_ptr);
3593*7c478bd9Sstevel@tonic-gate   };
3594*7c478bd9Sstevel@tonic-gate #endif
3595*7c478bd9Sstevel@tonic-gate /*
3596*7c478bd9Sstevel@tonic-gate  * Report term being unusable.
3597*7c478bd9Sstevel@tonic-gate  */
3598*7c478bd9Sstevel@tonic-gate   if(bad_term) {
3599*7c478bd9Sstevel@tonic-gate     gl_print_info(gl, "Bad terminal type: \"", term ? term : "(null)",
3600*7c478bd9Sstevel@tonic-gate 		  "\". Will assume vt100.", GL_END_INFO);
3601*7c478bd9Sstevel@tonic-gate   };
3602*7c478bd9Sstevel@tonic-gate /*
3603*7c478bd9Sstevel@tonic-gate  * Fill in missing information with ANSI VT100 strings.
3604*7c478bd9Sstevel@tonic-gate  */
3605*7c478bd9Sstevel@tonic-gate   if(!gl->left)
3606*7c478bd9Sstevel@tonic-gate     gl->left = "\b";    /* ^H */
3607*7c478bd9Sstevel@tonic-gate   if(!gl->right)
3608*7c478bd9Sstevel@tonic-gate     gl->right = GL_ESC_STR "[C";
3609*7c478bd9Sstevel@tonic-gate   if(!gl->up)
3610*7c478bd9Sstevel@tonic-gate     gl->up = GL_ESC_STR "[A";
3611*7c478bd9Sstevel@tonic-gate   if(!gl->down)
3612*7c478bd9Sstevel@tonic-gate     gl->down = "\n";
3613*7c478bd9Sstevel@tonic-gate   if(!gl->home)
3614*7c478bd9Sstevel@tonic-gate     gl->home = GL_ESC_STR "[H";
3615*7c478bd9Sstevel@tonic-gate   if(!gl->bol)
3616*7c478bd9Sstevel@tonic-gate     gl->bol = "\r";
3617*7c478bd9Sstevel@tonic-gate   if(!gl->clear_eol)
3618*7c478bd9Sstevel@tonic-gate     gl->clear_eol = GL_ESC_STR "[K";
3619*7c478bd9Sstevel@tonic-gate   if(!gl->clear_eod)
3620*7c478bd9Sstevel@tonic-gate     gl->clear_eod = GL_ESC_STR "[J";
3621*7c478bd9Sstevel@tonic-gate   if(!gl->u_arrow)
3622*7c478bd9Sstevel@tonic-gate     gl->u_arrow = GL_ESC_STR "[A";
3623*7c478bd9Sstevel@tonic-gate   if(!gl->d_arrow)
3624*7c478bd9Sstevel@tonic-gate     gl->d_arrow = GL_ESC_STR "[B";
3625*7c478bd9Sstevel@tonic-gate   if(!gl->l_arrow)
3626*7c478bd9Sstevel@tonic-gate     gl->l_arrow = GL_ESC_STR "[D";
3627*7c478bd9Sstevel@tonic-gate   if(!gl->r_arrow)
3628*7c478bd9Sstevel@tonic-gate     gl->r_arrow = GL_ESC_STR "[C";
3629*7c478bd9Sstevel@tonic-gate   if(!gl->sound_bell)
3630*7c478bd9Sstevel@tonic-gate     gl->sound_bell = "\a";
3631*7c478bd9Sstevel@tonic-gate   if(!gl->bold)
3632*7c478bd9Sstevel@tonic-gate     gl->bold = GL_ESC_STR "[1m";
3633*7c478bd9Sstevel@tonic-gate   if(!gl->underline)
3634*7c478bd9Sstevel@tonic-gate     gl->underline = GL_ESC_STR "[4m";
3635*7c478bd9Sstevel@tonic-gate   if(!gl->standout)
3636*7c478bd9Sstevel@tonic-gate     gl->standout = GL_ESC_STR "[1;7m";
3637*7c478bd9Sstevel@tonic-gate   if(!gl->dim)
3638*7c478bd9Sstevel@tonic-gate     gl->dim = "";  /* Not available */
3639*7c478bd9Sstevel@tonic-gate   if(!gl->reverse)
3640*7c478bd9Sstevel@tonic-gate     gl->reverse = GL_ESC_STR "[7m";
3641*7c478bd9Sstevel@tonic-gate   if(!gl->blink)
3642*7c478bd9Sstevel@tonic-gate     gl->blink = GL_ESC_STR "[5m";
3643*7c478bd9Sstevel@tonic-gate   if(!gl->text_attr_off)
3644*7c478bd9Sstevel@tonic-gate     gl->text_attr_off = GL_ESC_STR "[m";
3645*7c478bd9Sstevel@tonic-gate /*
3646*7c478bd9Sstevel@tonic-gate  * Find out the current terminal size.
3647*7c478bd9Sstevel@tonic-gate  */
3648*7c478bd9Sstevel@tonic-gate   (void) _gl_terminal_size(gl, GL_DEF_NCOLUMN, GL_DEF_NLINE, NULL);
3649*7c478bd9Sstevel@tonic-gate   return 0;
3650*7c478bd9Sstevel@tonic-gate }
3651*7c478bd9Sstevel@tonic-gate 
3652*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
3653*7c478bd9Sstevel@tonic-gate /*.......................................................................
3654*7c478bd9Sstevel@tonic-gate  * This is a private function of gl_control_strings() used to look up
3655*7c478bd9Sstevel@tonic-gate  * a termninal capability string from the terminfo database and make
3656*7c478bd9Sstevel@tonic-gate  * a private copy of it.
3657*7c478bd9Sstevel@tonic-gate  *
3658*7c478bd9Sstevel@tonic-gate  * Input:
3659*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
3660*7c478bd9Sstevel@tonic-gate  *  name    const char *  The name of the terminfo string to look up.
3661*7c478bd9Sstevel@tonic-gate  * Output:
3662*7c478bd9Sstevel@tonic-gate  *  return  const char *  The local copy of the capability, or NULL
3663*7c478bd9Sstevel@tonic-gate  *                        if not available.
3664*7c478bd9Sstevel@tonic-gate  */
3665*7c478bd9Sstevel@tonic-gate static const char *gl_tigetstr(GetLine *gl, const char *name)
3666*7c478bd9Sstevel@tonic-gate {
3667*7c478bd9Sstevel@tonic-gate   const char *value = tigetstr((char *)name);
3668*7c478bd9Sstevel@tonic-gate   if(!value || value == (char *) -1)
3669*7c478bd9Sstevel@tonic-gate     return NULL;
3670*7c478bd9Sstevel@tonic-gate   return _sg_store_string(gl->capmem, value, 0);
3671*7c478bd9Sstevel@tonic-gate }
3672*7c478bd9Sstevel@tonic-gate #elif defined(USE_TERMCAP)
3673*7c478bd9Sstevel@tonic-gate /*.......................................................................
3674*7c478bd9Sstevel@tonic-gate  * This is a private function of gl_control_strings() used to look up
3675*7c478bd9Sstevel@tonic-gate  * a termninal capability string from the termcap database and make
3676*7c478bd9Sstevel@tonic-gate  * a private copy of it. Note that some emulations of tgetstr(), such
3677*7c478bd9Sstevel@tonic-gate  * as that used by Solaris, ignores the buffer pointer that is past to
3678*7c478bd9Sstevel@tonic-gate  * it, so we can't assume that a private copy has been made that won't
3679*7c478bd9Sstevel@tonic-gate  * be trashed by another call to gl_control_strings() by another
3680*7c478bd9Sstevel@tonic-gate  * GetLine object. So we make what may be a redundant private copy
3681*7c478bd9Sstevel@tonic-gate  * of the string in gl->capmem.
3682*7c478bd9Sstevel@tonic-gate  *
3683*7c478bd9Sstevel@tonic-gate  * Input:
3684*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
3685*7c478bd9Sstevel@tonic-gate  *  name    const char *  The name of the terminfo string to look up.
3686*7c478bd9Sstevel@tonic-gate  * Input/Output:
3687*7c478bd9Sstevel@tonic-gate  *  bufptr        char ** On input *bufptr points to the location in
3688*7c478bd9Sstevel@tonic-gate  *                        gl->tgetstr_buf at which to record the
3689*7c478bd9Sstevel@tonic-gate  *                        capability string. On output *bufptr is
3690*7c478bd9Sstevel@tonic-gate  *                        incremented over the stored string.
3691*7c478bd9Sstevel@tonic-gate  * Output:
3692*7c478bd9Sstevel@tonic-gate  *  return  const char *  The local copy of the capability, or NULL
3693*7c478bd9Sstevel@tonic-gate  *                        on error.
3694*7c478bd9Sstevel@tonic-gate  */
3695*7c478bd9Sstevel@tonic-gate static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr)
3696*7c478bd9Sstevel@tonic-gate {
3697*7c478bd9Sstevel@tonic-gate   const char *value = tgetstr((char *)name, bufptr);
3698*7c478bd9Sstevel@tonic-gate   if(!value || value == (char *) -1)
3699*7c478bd9Sstevel@tonic-gate     return NULL;
3700*7c478bd9Sstevel@tonic-gate   return _sg_store_string(gl->capmem, value, 0);
3701*7c478bd9Sstevel@tonic-gate }
3702*7c478bd9Sstevel@tonic-gate #endif
3703*7c478bd9Sstevel@tonic-gate 
3704*7c478bd9Sstevel@tonic-gate /*.......................................................................
3705*7c478bd9Sstevel@tonic-gate  * This is an action function that implements a user interrupt (eg. ^C).
3706*7c478bd9Sstevel@tonic-gate  */
3707*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_user_interrupt)
3708*7c478bd9Sstevel@tonic-gate {
3709*7c478bd9Sstevel@tonic-gate   raise(SIGINT);
3710*7c478bd9Sstevel@tonic-gate   return 1;
3711*7c478bd9Sstevel@tonic-gate }
3712*7c478bd9Sstevel@tonic-gate 
3713*7c478bd9Sstevel@tonic-gate /*.......................................................................
3714*7c478bd9Sstevel@tonic-gate  * This is an action function that implements the abort signal.
3715*7c478bd9Sstevel@tonic-gate  */
3716*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_abort)
3717*7c478bd9Sstevel@tonic-gate {
3718*7c478bd9Sstevel@tonic-gate   raise(SIGABRT);
3719*7c478bd9Sstevel@tonic-gate   return 1;
3720*7c478bd9Sstevel@tonic-gate }
3721*7c478bd9Sstevel@tonic-gate 
3722*7c478bd9Sstevel@tonic-gate /*.......................................................................
3723*7c478bd9Sstevel@tonic-gate  * This is an action function that sends a suspend signal (eg. ^Z) to the
3724*7c478bd9Sstevel@tonic-gate  * the parent process.
3725*7c478bd9Sstevel@tonic-gate  */
3726*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_suspend)
3727*7c478bd9Sstevel@tonic-gate {
3728*7c478bd9Sstevel@tonic-gate   raise(SIGTSTP);
3729*7c478bd9Sstevel@tonic-gate   return 0;
3730*7c478bd9Sstevel@tonic-gate }
3731*7c478bd9Sstevel@tonic-gate 
3732*7c478bd9Sstevel@tonic-gate /*.......................................................................
3733*7c478bd9Sstevel@tonic-gate  * This is an action function that halts output to the terminal.
3734*7c478bd9Sstevel@tonic-gate  */
3735*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_stop_output)
3736*7c478bd9Sstevel@tonic-gate {
3737*7c478bd9Sstevel@tonic-gate   tcflow(gl->output_fd, TCOOFF);
3738*7c478bd9Sstevel@tonic-gate   return 0;
3739*7c478bd9Sstevel@tonic-gate }
3740*7c478bd9Sstevel@tonic-gate 
3741*7c478bd9Sstevel@tonic-gate /*.......................................................................
3742*7c478bd9Sstevel@tonic-gate  * This is an action function that resumes halted terminal output.
3743*7c478bd9Sstevel@tonic-gate  */
3744*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_start_output)
3745*7c478bd9Sstevel@tonic-gate {
3746*7c478bd9Sstevel@tonic-gate   tcflow(gl->output_fd, TCOON);
3747*7c478bd9Sstevel@tonic-gate   return 0;
3748*7c478bd9Sstevel@tonic-gate }
3749*7c478bd9Sstevel@tonic-gate 
3750*7c478bd9Sstevel@tonic-gate /*.......................................................................
3751*7c478bd9Sstevel@tonic-gate  * This is an action function that allows the next character to be accepted
3752*7c478bd9Sstevel@tonic-gate  * without any interpretation as a special character.
3753*7c478bd9Sstevel@tonic-gate  */
3754*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_literal_next)
3755*7c478bd9Sstevel@tonic-gate {
3756*7c478bd9Sstevel@tonic-gate   char c;   /* The character to be added to the line */
3757*7c478bd9Sstevel@tonic-gate   int i;
3758*7c478bd9Sstevel@tonic-gate /*
3759*7c478bd9Sstevel@tonic-gate  * Get the character to be inserted literally.
3760*7c478bd9Sstevel@tonic-gate  */
3761*7c478bd9Sstevel@tonic-gate   if(gl_read_terminal(gl, 1, &c))
3762*7c478bd9Sstevel@tonic-gate     return 1;
3763*7c478bd9Sstevel@tonic-gate /*
3764*7c478bd9Sstevel@tonic-gate  * Add the character to the line 'count' times.
3765*7c478bd9Sstevel@tonic-gate  */
3766*7c478bd9Sstevel@tonic-gate   for(i=0; i<count; i++)
3767*7c478bd9Sstevel@tonic-gate     gl_add_char_to_line(gl, c);
3768*7c478bd9Sstevel@tonic-gate   return 0;
3769*7c478bd9Sstevel@tonic-gate }
3770*7c478bd9Sstevel@tonic-gate 
3771*7c478bd9Sstevel@tonic-gate /*.......................................................................
3772*7c478bd9Sstevel@tonic-gate  * Return the width of a tab character at a given position when
3773*7c478bd9Sstevel@tonic-gate  * displayed at a given position on the terminal. This is needed
3774*7c478bd9Sstevel@tonic-gate  * because the width of tab characters depends on where they are,
3775*7c478bd9Sstevel@tonic-gate  * relative to the preceding tab stops.
3776*7c478bd9Sstevel@tonic-gate  *
3777*7c478bd9Sstevel@tonic-gate  * Input:
3778*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The resource object of this library.
3779*7c478bd9Sstevel@tonic-gate  *  term_curpos  int    The destination terminal location of the character.
3780*7c478bd9Sstevel@tonic-gate  * Output:
3781*7c478bd9Sstevel@tonic-gate  *  return       int    The number of terminal charaters needed.
3782*7c478bd9Sstevel@tonic-gate  */
3783*7c478bd9Sstevel@tonic-gate static int gl_displayed_tab_width(GetLine *gl, int term_curpos)
3784*7c478bd9Sstevel@tonic-gate {
3785*7c478bd9Sstevel@tonic-gate   return TAB_WIDTH - ((term_curpos % gl->ncolumn) % TAB_WIDTH);
3786*7c478bd9Sstevel@tonic-gate }
3787*7c478bd9Sstevel@tonic-gate 
3788*7c478bd9Sstevel@tonic-gate /*.......................................................................
3789*7c478bd9Sstevel@tonic-gate  * Return the number of characters needed to display a given character
3790*7c478bd9Sstevel@tonic-gate  * on the screen. Tab characters require eight spaces, and control
3791*7c478bd9Sstevel@tonic-gate  * characters are represented by a caret followed by the modified
3792*7c478bd9Sstevel@tonic-gate  * character.
3793*7c478bd9Sstevel@tonic-gate  *
3794*7c478bd9Sstevel@tonic-gate  * Input:
3795*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The resource object of this library.
3796*7c478bd9Sstevel@tonic-gate  *  c           char    The character to be displayed.
3797*7c478bd9Sstevel@tonic-gate  *  term_curpos  int    The destination terminal location of the character.
3798*7c478bd9Sstevel@tonic-gate  *                      This is needed because the width of tab characters
3799*7c478bd9Sstevel@tonic-gate  *                      depends on where they are, relative to the
3800*7c478bd9Sstevel@tonic-gate  *                      preceding tab stops.
3801*7c478bd9Sstevel@tonic-gate  * Output:
3802*7c478bd9Sstevel@tonic-gate  *  return       int    The number of terminal charaters needed.
3803*7c478bd9Sstevel@tonic-gate  */
3804*7c478bd9Sstevel@tonic-gate static int gl_displayed_char_width(GetLine *gl, char c, int term_curpos)
3805*7c478bd9Sstevel@tonic-gate {
3806*7c478bd9Sstevel@tonic-gate   if(c=='\t')
3807*7c478bd9Sstevel@tonic-gate     return gl_displayed_tab_width(gl, term_curpos);
3808*7c478bd9Sstevel@tonic-gate   if(IS_CTRL_CHAR(c))
3809*7c478bd9Sstevel@tonic-gate     return 2;
3810*7c478bd9Sstevel@tonic-gate   if(!isprint((int)(unsigned char) c))
3811*7c478bd9Sstevel@tonic-gate     return gl_octal_width((int)(unsigned char)c) + 1;
3812*7c478bd9Sstevel@tonic-gate   return 1;
3813*7c478bd9Sstevel@tonic-gate }
3814*7c478bd9Sstevel@tonic-gate 
3815*7c478bd9Sstevel@tonic-gate 
3816*7c478bd9Sstevel@tonic-gate /*.......................................................................
3817*7c478bd9Sstevel@tonic-gate  * Work out the length of given string of characters on the terminal.
3818*7c478bd9Sstevel@tonic-gate  *
3819*7c478bd9Sstevel@tonic-gate  * Input:
3820*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The resource object of this library.
3821*7c478bd9Sstevel@tonic-gate  *  string      char *  The string to be measured.
3822*7c478bd9Sstevel@tonic-gate  *  nc           int    The number of characters to be measured, or -1
3823*7c478bd9Sstevel@tonic-gate  *                      to measure the whole string.
3824*7c478bd9Sstevel@tonic-gate  *  term_curpos  int    The destination terminal location of the character.
3825*7c478bd9Sstevel@tonic-gate  *                      This is needed because the width of tab characters
3826*7c478bd9Sstevel@tonic-gate  *                      depends on where they are, relative to the
3827*7c478bd9Sstevel@tonic-gate  *                      preceding tab stops.
3828*7c478bd9Sstevel@tonic-gate  * Output:
3829*7c478bd9Sstevel@tonic-gate  *  return       int    The number of displayed characters.
3830*7c478bd9Sstevel@tonic-gate  */
3831*7c478bd9Sstevel@tonic-gate static int gl_displayed_string_width(GetLine *gl, const char *string, int nc,
3832*7c478bd9Sstevel@tonic-gate 				     int term_curpos)
3833*7c478bd9Sstevel@tonic-gate {
3834*7c478bd9Sstevel@tonic-gate   int slen = 0;   /* The displayed number of characters */
3835*7c478bd9Sstevel@tonic-gate   int i;
3836*7c478bd9Sstevel@tonic-gate /*
3837*7c478bd9Sstevel@tonic-gate  * How many characters are to be measured?
3838*7c478bd9Sstevel@tonic-gate  */
3839*7c478bd9Sstevel@tonic-gate   if(nc < 0)
3840*7c478bd9Sstevel@tonic-gate     nc = strlen(string);
3841*7c478bd9Sstevel@tonic-gate /*
3842*7c478bd9Sstevel@tonic-gate  * Add up the length of the displayed string.
3843*7c478bd9Sstevel@tonic-gate  */
3844*7c478bd9Sstevel@tonic-gate   for(i=0; i<nc; i++)
3845*7c478bd9Sstevel@tonic-gate     slen += gl_displayed_char_width(gl, string[i], term_curpos + slen);
3846*7c478bd9Sstevel@tonic-gate   return slen;
3847*7c478bd9Sstevel@tonic-gate }
3848*7c478bd9Sstevel@tonic-gate 
3849*7c478bd9Sstevel@tonic-gate /*.......................................................................
3850*7c478bd9Sstevel@tonic-gate  * Write a string verbatim to the current terminal or output stream.
3851*7c478bd9Sstevel@tonic-gate  *
3852*7c478bd9Sstevel@tonic-gate  * Note that when async-signal safety is required, the 'buffered'
3853*7c478bd9Sstevel@tonic-gate  * argument must be 0, and n must not be -1.
3854*7c478bd9Sstevel@tonic-gate  *
3855*7c478bd9Sstevel@tonic-gate  * Input:
3856*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of the gl_get_line().
3857*7c478bd9Sstevel@tonic-gate  *  buffered       int    If true, used buffered I/O when writing to
3858*7c478bd9Sstevel@tonic-gate  *                        the terminal. Otherwise use async-signal-safe
3859*7c478bd9Sstevel@tonic-gate  *                        unbuffered I/O.
3860*7c478bd9Sstevel@tonic-gate  *  string  const char *  The string to be written (this need not be
3861*7c478bd9Sstevel@tonic-gate  *                        '\0' terminated unless n<0).
3862*7c478bd9Sstevel@tonic-gate  *  n              int    The number of characters to write from the
3863*7c478bd9Sstevel@tonic-gate  *                        prefix of string[], or -1 to request that
3864*7c478bd9Sstevel@tonic-gate  *                        gl_print_raw_string() use strlen() to figure
3865*7c478bd9Sstevel@tonic-gate  *                        out the length.
3866*7c478bd9Sstevel@tonic-gate  * Output:
3867*7c478bd9Sstevel@tonic-gate  *  return         int    0 - OK.
3868*7c478bd9Sstevel@tonic-gate  *                        1 - Error.
3869*7c478bd9Sstevel@tonic-gate  */
3870*7c478bd9Sstevel@tonic-gate static int gl_print_raw_string(GetLine *gl, int buffered,
3871*7c478bd9Sstevel@tonic-gate 			       const char *string, int n)
3872*7c478bd9Sstevel@tonic-gate {
3873*7c478bd9Sstevel@tonic-gate   GlWriteFn *write_fn = buffered ? gl_write_fn : gl->flush_fn;
3874*7c478bd9Sstevel@tonic-gate /*
3875*7c478bd9Sstevel@tonic-gate  * Only display output when echoing is turned on.
3876*7c478bd9Sstevel@tonic-gate  */
3877*7c478bd9Sstevel@tonic-gate   if(gl->echo) {
3878*7c478bd9Sstevel@tonic-gate     int ndone = 0;   /* The number of characters written so far */
3879*7c478bd9Sstevel@tonic-gate /*
3880*7c478bd9Sstevel@tonic-gate  * When using un-buffered I/O, flush pending output first.
3881*7c478bd9Sstevel@tonic-gate  */
3882*7c478bd9Sstevel@tonic-gate     if(!buffered) {
3883*7c478bd9Sstevel@tonic-gate       if(gl_flush_output(gl))
3884*7c478bd9Sstevel@tonic-gate 	return 1;
3885*7c478bd9Sstevel@tonic-gate     };
3886*7c478bd9Sstevel@tonic-gate /*
3887*7c478bd9Sstevel@tonic-gate  * If no length has been provided, measure the length of the string.
3888*7c478bd9Sstevel@tonic-gate  */
3889*7c478bd9Sstevel@tonic-gate     if(n < 0)
3890*7c478bd9Sstevel@tonic-gate       n = strlen(string);
3891*7c478bd9Sstevel@tonic-gate /*
3892*7c478bd9Sstevel@tonic-gate  * Write the string.
3893*7c478bd9Sstevel@tonic-gate  */
3894*7c478bd9Sstevel@tonic-gate     if(write_fn(gl, string + ndone, n-ndone) != n)
3895*7c478bd9Sstevel@tonic-gate       return 1;
3896*7c478bd9Sstevel@tonic-gate   };
3897*7c478bd9Sstevel@tonic-gate   return 0;
3898*7c478bd9Sstevel@tonic-gate }
3899*7c478bd9Sstevel@tonic-gate 
3900*7c478bd9Sstevel@tonic-gate /*.......................................................................
3901*7c478bd9Sstevel@tonic-gate  * Output a terminal control sequence. When using terminfo,
3902*7c478bd9Sstevel@tonic-gate  * this must be a sequence returned by tgetstr() or tigetstr()
3903*7c478bd9Sstevel@tonic-gate  * respectively.
3904*7c478bd9Sstevel@tonic-gate  *
3905*7c478bd9Sstevel@tonic-gate  * Input:
3906*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *   The resource object of this library.
3907*7c478bd9Sstevel@tonic-gate  *  nline      int     The number of lines affected by the operation,
3908*7c478bd9Sstevel@tonic-gate  *                     or 1 if not relevant.
3909*7c478bd9Sstevel@tonic-gate  *  string    char *   The control sequence to be sent.
3910*7c478bd9Sstevel@tonic-gate  * Output:
3911*7c478bd9Sstevel@tonic-gate  *  return     int     0 - OK.
3912*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
3913*7c478bd9Sstevel@tonic-gate  */
3914*7c478bd9Sstevel@tonic-gate static int gl_print_control_sequence(GetLine *gl, int nline, const char *string)
3915*7c478bd9Sstevel@tonic-gate {
3916*7c478bd9Sstevel@tonic-gate   int waserr = 0;   /* True if an error occurs */
3917*7c478bd9Sstevel@tonic-gate /*
3918*7c478bd9Sstevel@tonic-gate  * Only write characters to the terminal when echoing is enabled.
3919*7c478bd9Sstevel@tonic-gate  */
3920*7c478bd9Sstevel@tonic-gate   if(gl->echo) {
3921*7c478bd9Sstevel@tonic-gate #if defined(USE_TERMINFO) || defined(USE_TERMCAP)
3922*7c478bd9Sstevel@tonic-gate     tputs_gl = gl;
3923*7c478bd9Sstevel@tonic-gate     errno = 0;
3924*7c478bd9Sstevel@tonic-gate     tputs((char *)string, nline, gl_tputs_putchar);
3925*7c478bd9Sstevel@tonic-gate     waserr = errno != 0;
3926*7c478bd9Sstevel@tonic-gate #else
3927*7c478bd9Sstevel@tonic-gate     waserr = gl_print_raw_string(gl, 1, string, -1);
3928*7c478bd9Sstevel@tonic-gate #endif
3929*7c478bd9Sstevel@tonic-gate   };
3930*7c478bd9Sstevel@tonic-gate   return waserr;
3931*7c478bd9Sstevel@tonic-gate }
3932*7c478bd9Sstevel@tonic-gate 
3933*7c478bd9Sstevel@tonic-gate #if defined(USE_TERMINFO) || defined(USE_TERMCAP)
3934*7c478bd9Sstevel@tonic-gate /*.......................................................................
3935*7c478bd9Sstevel@tonic-gate  * The following callback function is called by tputs() to output a raw
3936*7c478bd9Sstevel@tonic-gate  * control character to the terminal.
3937*7c478bd9Sstevel@tonic-gate  */
3938*7c478bd9Sstevel@tonic-gate static TputsRetType gl_tputs_putchar(TputsArgType c)
3939*7c478bd9Sstevel@tonic-gate {
3940*7c478bd9Sstevel@tonic-gate   char ch = c;
3941*7c478bd9Sstevel@tonic-gate #if TPUTS_RETURNS_VALUE
3942*7c478bd9Sstevel@tonic-gate   return gl_print_raw_string(tputs_gl, 1, &ch, 1);
3943*7c478bd9Sstevel@tonic-gate #else
3944*7c478bd9Sstevel@tonic-gate   (void) gl_print_raw_string(tputs_gl, 1, &ch, 1);
3945*7c478bd9Sstevel@tonic-gate #endif
3946*7c478bd9Sstevel@tonic-gate }
3947*7c478bd9Sstevel@tonic-gate #endif
3948*7c478bd9Sstevel@tonic-gate 
3949*7c478bd9Sstevel@tonic-gate /*.......................................................................
3950*7c478bd9Sstevel@tonic-gate  * Move the terminal cursor n characters to the left or right.
3951*7c478bd9Sstevel@tonic-gate  *
3952*7c478bd9Sstevel@tonic-gate  * Input:
3953*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *   The resource object of this program.
3954*7c478bd9Sstevel@tonic-gate  *  n          int     number of positions to the right (> 0) or left (< 0).
3955*7c478bd9Sstevel@tonic-gate  * Output:
3956*7c478bd9Sstevel@tonic-gate  *  return     int     0 - OK.
3957*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
3958*7c478bd9Sstevel@tonic-gate  */
3959*7c478bd9Sstevel@tonic-gate static int gl_terminal_move_cursor(GetLine *gl, int n)
3960*7c478bd9Sstevel@tonic-gate {
3961*7c478bd9Sstevel@tonic-gate   int cur_row, cur_col; /* The current terminal row and column index of */
3962*7c478bd9Sstevel@tonic-gate                         /*  the cursor wrt the start of the input line. */
3963*7c478bd9Sstevel@tonic-gate   int new_row, new_col; /* The target terminal row and column index of */
3964*7c478bd9Sstevel@tonic-gate                         /*  the cursor wrt the start of the input line. */
3965*7c478bd9Sstevel@tonic-gate /*
3966*7c478bd9Sstevel@tonic-gate  * Do nothing if the input line isn't currently displayed. In this
3967*7c478bd9Sstevel@tonic-gate  * case, the cursor will be moved to the right place when the line
3968*7c478bd9Sstevel@tonic-gate  * is next redisplayed.
3969*7c478bd9Sstevel@tonic-gate  */
3970*7c478bd9Sstevel@tonic-gate   if(!gl->displayed)
3971*7c478bd9Sstevel@tonic-gate     return 0;
3972*7c478bd9Sstevel@tonic-gate /*
3973*7c478bd9Sstevel@tonic-gate  * How far can we move left?
3974*7c478bd9Sstevel@tonic-gate  */
3975*7c478bd9Sstevel@tonic-gate   if(gl->term_curpos + n < 0)
3976*7c478bd9Sstevel@tonic-gate     n = gl->term_curpos;
3977*7c478bd9Sstevel@tonic-gate /*
3978*7c478bd9Sstevel@tonic-gate  * Break down the current and target cursor locations into rows and columns.
3979*7c478bd9Sstevel@tonic-gate  */
3980*7c478bd9Sstevel@tonic-gate   cur_row = gl->term_curpos / gl->ncolumn;
3981*7c478bd9Sstevel@tonic-gate   cur_col = gl->term_curpos % gl->ncolumn;
3982*7c478bd9Sstevel@tonic-gate   new_row = (gl->term_curpos + n) / gl->ncolumn;
3983*7c478bd9Sstevel@tonic-gate   new_col = (gl->term_curpos + n) % gl->ncolumn;
3984*7c478bd9Sstevel@tonic-gate /*
3985*7c478bd9Sstevel@tonic-gate  * Move down to the next line.
3986*7c478bd9Sstevel@tonic-gate  */
3987*7c478bd9Sstevel@tonic-gate   for(; cur_row < new_row; cur_row++) {
3988*7c478bd9Sstevel@tonic-gate     if(gl_print_control_sequence(gl, 1, gl->down))
3989*7c478bd9Sstevel@tonic-gate       return 1;
3990*7c478bd9Sstevel@tonic-gate   };
3991*7c478bd9Sstevel@tonic-gate /*
3992*7c478bd9Sstevel@tonic-gate  * Move up to the previous line.
3993*7c478bd9Sstevel@tonic-gate  */
3994*7c478bd9Sstevel@tonic-gate   for(; cur_row > new_row; cur_row--) {
3995*7c478bd9Sstevel@tonic-gate     if(gl_print_control_sequence(gl, 1, gl->up))
3996*7c478bd9Sstevel@tonic-gate       return 1;
3997*7c478bd9Sstevel@tonic-gate   };
3998*7c478bd9Sstevel@tonic-gate /*
3999*7c478bd9Sstevel@tonic-gate  * Move to the right within the target line?
4000*7c478bd9Sstevel@tonic-gate  */
4001*7c478bd9Sstevel@tonic-gate   if(cur_col < new_col) {
4002*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
4003*7c478bd9Sstevel@tonic-gate /*
4004*7c478bd9Sstevel@tonic-gate  * Use a parameterized control sequence if it generates less control
4005*7c478bd9Sstevel@tonic-gate  * characters (guess based on ANSI terminal termcap entry).
4006*7c478bd9Sstevel@tonic-gate  */
4007*7c478bd9Sstevel@tonic-gate     if(gl->right_n != NULL && new_col - cur_col > 1) {
4008*7c478bd9Sstevel@tonic-gate       if(gl_print_control_sequence(gl, 1, tparm((char *)gl->right_n,
4009*7c478bd9Sstevel@tonic-gate            (long)(new_col - cur_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l)))
4010*7c478bd9Sstevel@tonic-gate 	return 1;
4011*7c478bd9Sstevel@tonic-gate     } else
4012*7c478bd9Sstevel@tonic-gate #endif
4013*7c478bd9Sstevel@tonic-gate     {
4014*7c478bd9Sstevel@tonic-gate       for(; cur_col < new_col; cur_col++) {
4015*7c478bd9Sstevel@tonic-gate         if(gl_print_control_sequence(gl, 1, gl->right))
4016*7c478bd9Sstevel@tonic-gate           return 1;
4017*7c478bd9Sstevel@tonic-gate       };
4018*7c478bd9Sstevel@tonic-gate     };
4019*7c478bd9Sstevel@tonic-gate /*
4020*7c478bd9Sstevel@tonic-gate  * Move to the left within the target line?
4021*7c478bd9Sstevel@tonic-gate  */
4022*7c478bd9Sstevel@tonic-gate   } else if(cur_col > new_col) {
4023*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
4024*7c478bd9Sstevel@tonic-gate /*
4025*7c478bd9Sstevel@tonic-gate  * Use a parameterized control sequence if it generates less control
4026*7c478bd9Sstevel@tonic-gate  * characters (guess based on ANSI terminal termcap entry).
4027*7c478bd9Sstevel@tonic-gate  */
4028*7c478bd9Sstevel@tonic-gate     if(gl->left_n != NULL && cur_col - new_col > 3) {
4029*7c478bd9Sstevel@tonic-gate       if(gl_print_control_sequence(gl, 1, tparm((char *)gl->left_n,
4030*7c478bd9Sstevel@tonic-gate            (long)(cur_col - new_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l)))
4031*7c478bd9Sstevel@tonic-gate 	return 1;
4032*7c478bd9Sstevel@tonic-gate     } else
4033*7c478bd9Sstevel@tonic-gate #endif
4034*7c478bd9Sstevel@tonic-gate     {
4035*7c478bd9Sstevel@tonic-gate       for(; cur_col > new_col; cur_col--) {
4036*7c478bd9Sstevel@tonic-gate         if(gl_print_control_sequence(gl, 1, gl->left))
4037*7c478bd9Sstevel@tonic-gate           return 1;
4038*7c478bd9Sstevel@tonic-gate       };
4039*7c478bd9Sstevel@tonic-gate     };
4040*7c478bd9Sstevel@tonic-gate   }
4041*7c478bd9Sstevel@tonic-gate /*
4042*7c478bd9Sstevel@tonic-gate  * Update the recorded position of the terminal cursor.
4043*7c478bd9Sstevel@tonic-gate  */
4044*7c478bd9Sstevel@tonic-gate   gl->term_curpos += n;
4045*7c478bd9Sstevel@tonic-gate   return 0;
4046*7c478bd9Sstevel@tonic-gate }
4047*7c478bd9Sstevel@tonic-gate 
4048*7c478bd9Sstevel@tonic-gate /*.......................................................................
4049*7c478bd9Sstevel@tonic-gate  * Write a character to the terminal after expanding tabs and control
4050*7c478bd9Sstevel@tonic-gate  * characters to their multi-character representations.
4051*7c478bd9Sstevel@tonic-gate  *
4052*7c478bd9Sstevel@tonic-gate  * Input:
4053*7c478bd9Sstevel@tonic-gate  *  gl    GetLine *   The resource object of this program.
4054*7c478bd9Sstevel@tonic-gate  *  c        char     The character to be output.
4055*7c478bd9Sstevel@tonic-gate  *  pad      char     Many terminals have the irritating feature that
4056*7c478bd9Sstevel@tonic-gate  *                    when one writes a character in the last column of
4057*7c478bd9Sstevel@tonic-gate  *                    of the terminal, the cursor isn't wrapped to the
4058*7c478bd9Sstevel@tonic-gate  *                    start of the next line until one more character
4059*7c478bd9Sstevel@tonic-gate  *                    is written. Some terminals don't do this, so
4060*7c478bd9Sstevel@tonic-gate  *                    after such a write, we don't know where the
4061*7c478bd9Sstevel@tonic-gate  *                    terminal is unless we output an extra character.
4062*7c478bd9Sstevel@tonic-gate  *                    This argument specifies the character to write.
4063*7c478bd9Sstevel@tonic-gate  *                    If at the end of the input line send '\0' or a
4064*7c478bd9Sstevel@tonic-gate  *                    space, and a space will be written. Otherwise,
4065*7c478bd9Sstevel@tonic-gate  *                    pass the next character in the input line
4066*7c478bd9Sstevel@tonic-gate  *                    following the one being written.
4067*7c478bd9Sstevel@tonic-gate  * Output:
4068*7c478bd9Sstevel@tonic-gate  *  return    int     0 - OK.
4069*7c478bd9Sstevel@tonic-gate  */
4070*7c478bd9Sstevel@tonic-gate static int gl_print_char(GetLine *gl, char c, char pad)
4071*7c478bd9Sstevel@tonic-gate {
4072*7c478bd9Sstevel@tonic-gate   char string[TAB_WIDTH + 4]; /* A work area for composing compound strings */
4073*7c478bd9Sstevel@tonic-gate   int nchar;                  /* The number of terminal characters */
4074*7c478bd9Sstevel@tonic-gate   int i;
4075*7c478bd9Sstevel@tonic-gate /*
4076*7c478bd9Sstevel@tonic-gate  * Check for special characters.
4077*7c478bd9Sstevel@tonic-gate  */
4078*7c478bd9Sstevel@tonic-gate   if(c == '\t') {
4079*7c478bd9Sstevel@tonic-gate /*
4080*7c478bd9Sstevel@tonic-gate  * How many spaces do we need to represent a tab at the current terminal
4081*7c478bd9Sstevel@tonic-gate  * column?
4082*7c478bd9Sstevel@tonic-gate  */
4083*7c478bd9Sstevel@tonic-gate     nchar = gl_displayed_tab_width(gl, gl->term_curpos);
4084*7c478bd9Sstevel@tonic-gate /*
4085*7c478bd9Sstevel@tonic-gate  * Compose the tab string.
4086*7c478bd9Sstevel@tonic-gate  */
4087*7c478bd9Sstevel@tonic-gate     for(i=0; i<nchar; i++)
4088*7c478bd9Sstevel@tonic-gate       string[i] = ' ';
4089*7c478bd9Sstevel@tonic-gate   } else if(IS_CTRL_CHAR(c)) {
4090*7c478bd9Sstevel@tonic-gate     string[0] = '^';
4091*7c478bd9Sstevel@tonic-gate     string[1] = CTRL_TO_CHAR(c);
4092*7c478bd9Sstevel@tonic-gate     nchar = 2;
4093*7c478bd9Sstevel@tonic-gate   } else if(!isprint((int)(unsigned char) c)) {
4094*7c478bd9Sstevel@tonic-gate     snprintf(string, sizeof(string), "\\%o", (int)(unsigned char)c);
4095*7c478bd9Sstevel@tonic-gate     nchar = strlen(string);
4096*7c478bd9Sstevel@tonic-gate   } else {
4097*7c478bd9Sstevel@tonic-gate     string[0] = c;
4098*7c478bd9Sstevel@tonic-gate     nchar = 1;
4099*7c478bd9Sstevel@tonic-gate   };
4100*7c478bd9Sstevel@tonic-gate /*
4101*7c478bd9Sstevel@tonic-gate  * Terminate the string.
4102*7c478bd9Sstevel@tonic-gate  */
4103*7c478bd9Sstevel@tonic-gate   string[nchar] = '\0';
4104*7c478bd9Sstevel@tonic-gate /*
4105*7c478bd9Sstevel@tonic-gate  * Write the string to the terminal.
4106*7c478bd9Sstevel@tonic-gate  */
4107*7c478bd9Sstevel@tonic-gate   if(gl_print_raw_string(gl, 1, string, -1))
4108*7c478bd9Sstevel@tonic-gate     return 1;
4109*7c478bd9Sstevel@tonic-gate /*
4110*7c478bd9Sstevel@tonic-gate  * Except for one exception to be described in a moment, the cursor should
4111*7c478bd9Sstevel@tonic-gate  * now have been positioned after the character that was just output.
4112*7c478bd9Sstevel@tonic-gate  */
4113*7c478bd9Sstevel@tonic-gate   gl->term_curpos += nchar;
4114*7c478bd9Sstevel@tonic-gate /*
4115*7c478bd9Sstevel@tonic-gate  * Keep a record of the number of characters in the terminal version
4116*7c478bd9Sstevel@tonic-gate  * of the input line.
4117*7c478bd9Sstevel@tonic-gate  */
4118*7c478bd9Sstevel@tonic-gate   if(gl->term_curpos > gl->term_len)
4119*7c478bd9Sstevel@tonic-gate     gl->term_len = gl->term_curpos;
4120*7c478bd9Sstevel@tonic-gate /*
4121*7c478bd9Sstevel@tonic-gate  * If the new character ended exactly at the end of a line,
4122*7c478bd9Sstevel@tonic-gate  * most terminals won't move the cursor onto the next line until we
4123*7c478bd9Sstevel@tonic-gate  * have written a character on the next line, so append an extra
4124*7c478bd9Sstevel@tonic-gate  * space then move the cursor back.
4125*7c478bd9Sstevel@tonic-gate  */
4126*7c478bd9Sstevel@tonic-gate   if(gl->term_curpos % gl->ncolumn == 0) {
4127*7c478bd9Sstevel@tonic-gate     int term_curpos = gl->term_curpos;
4128*7c478bd9Sstevel@tonic-gate     if(gl_print_char(gl, pad ? pad : ' ', ' ') ||
4129*7c478bd9Sstevel@tonic-gate        gl_set_term_curpos(gl, term_curpos))
4130*7c478bd9Sstevel@tonic-gate       return 1;
4131*7c478bd9Sstevel@tonic-gate   };
4132*7c478bd9Sstevel@tonic-gate   return 0;
4133*7c478bd9Sstevel@tonic-gate }
4134*7c478bd9Sstevel@tonic-gate 
4135*7c478bd9Sstevel@tonic-gate /*.......................................................................
4136*7c478bd9Sstevel@tonic-gate  * Write a string to the terminal after expanding tabs and control
4137*7c478bd9Sstevel@tonic-gate  * characters to their multi-character representations.
4138*7c478bd9Sstevel@tonic-gate  *
4139*7c478bd9Sstevel@tonic-gate  * Input:
4140*7c478bd9Sstevel@tonic-gate  *  gl    GetLine *   The resource object of this program.
4141*7c478bd9Sstevel@tonic-gate  *  string   char *   The string to be output.
4142*7c478bd9Sstevel@tonic-gate  *  pad      char     Many terminals have the irritating feature that
4143*7c478bd9Sstevel@tonic-gate  *                    when one writes a character in the last column of
4144*7c478bd9Sstevel@tonic-gate  *                    of the terminal, the cursor isn't wrapped to the
4145*7c478bd9Sstevel@tonic-gate  *                    start of the next line until one more character
4146*7c478bd9Sstevel@tonic-gate  *                    is written. Some terminals don't do this, so
4147*7c478bd9Sstevel@tonic-gate  *                    after such a write, we don't know where the
4148*7c478bd9Sstevel@tonic-gate  *                    terminal is unless we output an extra character.
4149*7c478bd9Sstevel@tonic-gate  *                    This argument specifies the character to write.
4150*7c478bd9Sstevel@tonic-gate  *                    If at the end of the input line send '\0' or a
4151*7c478bd9Sstevel@tonic-gate  *                    space, and a space will be written. Otherwise,
4152*7c478bd9Sstevel@tonic-gate  *                    pass the next character in the input line
4153*7c478bd9Sstevel@tonic-gate  *                    following the one being written.
4154*7c478bd9Sstevel@tonic-gate  * Output:
4155*7c478bd9Sstevel@tonic-gate  *  return    int     0 - OK.
4156*7c478bd9Sstevel@tonic-gate  */
4157*7c478bd9Sstevel@tonic-gate static int gl_print_string(GetLine *gl, const char *string, char pad)
4158*7c478bd9Sstevel@tonic-gate {
4159*7c478bd9Sstevel@tonic-gate   const char *cptr;   /* A pointer into string[] */
4160*7c478bd9Sstevel@tonic-gate   for(cptr=string; *cptr; cptr++) {
4161*7c478bd9Sstevel@tonic-gate     char nextc = cptr[1];
4162*7c478bd9Sstevel@tonic-gate     if(gl_print_char(gl, *cptr, nextc ? nextc : pad))
4163*7c478bd9Sstevel@tonic-gate       return 1;
4164*7c478bd9Sstevel@tonic-gate   };
4165*7c478bd9Sstevel@tonic-gate   return 0;
4166*7c478bd9Sstevel@tonic-gate }
4167*7c478bd9Sstevel@tonic-gate 
4168*7c478bd9Sstevel@tonic-gate /*.......................................................................
4169*7c478bd9Sstevel@tonic-gate  * Move the terminal cursor position.
4170*7c478bd9Sstevel@tonic-gate  *
4171*7c478bd9Sstevel@tonic-gate  * Input:
4172*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The resource object of this library.
4173*7c478bd9Sstevel@tonic-gate  *  term_curpos int    The destination terminal cursor position.
4174*7c478bd9Sstevel@tonic-gate  * Output:
4175*7c478bd9Sstevel@tonic-gate  *  return      int    0 - OK.
4176*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
4177*7c478bd9Sstevel@tonic-gate  */
4178*7c478bd9Sstevel@tonic-gate static int gl_set_term_curpos(GetLine *gl, int term_curpos)
4179*7c478bd9Sstevel@tonic-gate {
4180*7c478bd9Sstevel@tonic-gate   return gl_terminal_move_cursor(gl, term_curpos - gl->term_curpos);
4181*7c478bd9Sstevel@tonic-gate }
4182*7c478bd9Sstevel@tonic-gate 
4183*7c478bd9Sstevel@tonic-gate /*.......................................................................
4184*7c478bd9Sstevel@tonic-gate  * This is an action function that moves the buffer cursor one character
4185*7c478bd9Sstevel@tonic-gate  * left, and updates the terminal cursor to match.
4186*7c478bd9Sstevel@tonic-gate  */
4187*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_cursor_left)
4188*7c478bd9Sstevel@tonic-gate {
4189*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos - count);
4190*7c478bd9Sstevel@tonic-gate }
4191*7c478bd9Sstevel@tonic-gate 
4192*7c478bd9Sstevel@tonic-gate /*.......................................................................
4193*7c478bd9Sstevel@tonic-gate  * This is an action function that moves the buffer cursor one character
4194*7c478bd9Sstevel@tonic-gate  * right, and updates the terminal cursor to match.
4195*7c478bd9Sstevel@tonic-gate  */
4196*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_cursor_right)
4197*7c478bd9Sstevel@tonic-gate {
4198*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos + count);
4199*7c478bd9Sstevel@tonic-gate }
4200*7c478bd9Sstevel@tonic-gate 
4201*7c478bd9Sstevel@tonic-gate /*.......................................................................
4202*7c478bd9Sstevel@tonic-gate  * This is an action function that toggles between overwrite and insert
4203*7c478bd9Sstevel@tonic-gate  * mode.
4204*7c478bd9Sstevel@tonic-gate  */
4205*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_insert_mode)
4206*7c478bd9Sstevel@tonic-gate {
4207*7c478bd9Sstevel@tonic-gate   gl->insert = !gl->insert;
4208*7c478bd9Sstevel@tonic-gate   return 0;
4209*7c478bd9Sstevel@tonic-gate }
4210*7c478bd9Sstevel@tonic-gate 
4211*7c478bd9Sstevel@tonic-gate /*.......................................................................
4212*7c478bd9Sstevel@tonic-gate  * This is an action function which moves the cursor to the beginning of
4213*7c478bd9Sstevel@tonic-gate  * the line.
4214*7c478bd9Sstevel@tonic-gate  */
4215*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_beginning_of_line)
4216*7c478bd9Sstevel@tonic-gate {
4217*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, 0);
4218*7c478bd9Sstevel@tonic-gate }
4219*7c478bd9Sstevel@tonic-gate 
4220*7c478bd9Sstevel@tonic-gate /*.......................................................................
4221*7c478bd9Sstevel@tonic-gate  * This is an action function which moves the cursor to the end of
4222*7c478bd9Sstevel@tonic-gate  * the line.
4223*7c478bd9Sstevel@tonic-gate  */
4224*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_end_of_line)
4225*7c478bd9Sstevel@tonic-gate {
4226*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->ntotal);
4227*7c478bd9Sstevel@tonic-gate }
4228*7c478bd9Sstevel@tonic-gate 
4229*7c478bd9Sstevel@tonic-gate /*.......................................................................
4230*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes the entire contents of the
4231*7c478bd9Sstevel@tonic-gate  * current line.
4232*7c478bd9Sstevel@tonic-gate  */
4233*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_line)
4234*7c478bd9Sstevel@tonic-gate {
4235*7c478bd9Sstevel@tonic-gate /*
4236*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4237*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4238*7c478bd9Sstevel@tonic-gate  */
4239*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4240*7c478bd9Sstevel@tonic-gate /*
4241*7c478bd9Sstevel@tonic-gate  * Copy the contents of the line to the cut buffer.
4242*7c478bd9Sstevel@tonic-gate  */
4243*7c478bd9Sstevel@tonic-gate   strlcpy(gl->cutbuf, gl->line, gl->linelen);
4244*7c478bd9Sstevel@tonic-gate /*
4245*7c478bd9Sstevel@tonic-gate  * Clear the buffer.
4246*7c478bd9Sstevel@tonic-gate  */
4247*7c478bd9Sstevel@tonic-gate   gl_truncate_buffer(gl, 0);
4248*7c478bd9Sstevel@tonic-gate /*
4249*7c478bd9Sstevel@tonic-gate  * Move the terminal cursor to just after the prompt.
4250*7c478bd9Sstevel@tonic-gate  */
4251*7c478bd9Sstevel@tonic-gate   if(gl_place_cursor(gl, 0))
4252*7c478bd9Sstevel@tonic-gate     return 1;
4253*7c478bd9Sstevel@tonic-gate /*
4254*7c478bd9Sstevel@tonic-gate  * Clear from the end of the prompt to the end of the terminal.
4255*7c478bd9Sstevel@tonic-gate  */
4256*7c478bd9Sstevel@tonic-gate   if(gl_truncate_display(gl))
4257*7c478bd9Sstevel@tonic-gate     return 1;
4258*7c478bd9Sstevel@tonic-gate   return 0;
4259*7c478bd9Sstevel@tonic-gate }
4260*7c478bd9Sstevel@tonic-gate 
4261*7c478bd9Sstevel@tonic-gate /*.......................................................................
4262*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes all characters between the
4263*7c478bd9Sstevel@tonic-gate  * current cursor position and the end of the line.
4264*7c478bd9Sstevel@tonic-gate  */
4265*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_kill_line)
4266*7c478bd9Sstevel@tonic-gate {
4267*7c478bd9Sstevel@tonic-gate /*
4268*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4269*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4270*7c478bd9Sstevel@tonic-gate  */
4271*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4272*7c478bd9Sstevel@tonic-gate /*
4273*7c478bd9Sstevel@tonic-gate  * Copy the part of the line that is about to be deleted to the cut buffer.
4274*7c478bd9Sstevel@tonic-gate  */
4275*7c478bd9Sstevel@tonic-gate   strlcpy(gl->cutbuf, gl->line + gl->buff_curpos, gl->linelen);
4276*7c478bd9Sstevel@tonic-gate /*
4277*7c478bd9Sstevel@tonic-gate  * Terminate the buffered line at the current cursor position.
4278*7c478bd9Sstevel@tonic-gate  */
4279*7c478bd9Sstevel@tonic-gate   gl_truncate_buffer(gl, gl->buff_curpos);
4280*7c478bd9Sstevel@tonic-gate /*
4281*7c478bd9Sstevel@tonic-gate  * Clear the part of the line that follows the cursor.
4282*7c478bd9Sstevel@tonic-gate  */
4283*7c478bd9Sstevel@tonic-gate   if(gl_truncate_display(gl))
4284*7c478bd9Sstevel@tonic-gate     return 1;
4285*7c478bd9Sstevel@tonic-gate /*
4286*7c478bd9Sstevel@tonic-gate  * Explicitly reset the cursor position to allow vi command mode
4287*7c478bd9Sstevel@tonic-gate  * constraints on its position to be set.
4288*7c478bd9Sstevel@tonic-gate  */
4289*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos);
4290*7c478bd9Sstevel@tonic-gate }
4291*7c478bd9Sstevel@tonic-gate 
4292*7c478bd9Sstevel@tonic-gate /*.......................................................................
4293*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes all characters between the
4294*7c478bd9Sstevel@tonic-gate  * start of the line and the current cursor position.
4295*7c478bd9Sstevel@tonic-gate  */
4296*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_kill_line)
4297*7c478bd9Sstevel@tonic-gate {
4298*7c478bd9Sstevel@tonic-gate /*
4299*7c478bd9Sstevel@tonic-gate  * How many characters are to be deleted from before the cursor?
4300*7c478bd9Sstevel@tonic-gate  */
4301*7c478bd9Sstevel@tonic-gate   int nc = gl->buff_curpos - gl->insert_curpos;
4302*7c478bd9Sstevel@tonic-gate   if (!nc)
4303*7c478bd9Sstevel@tonic-gate     return 0;
4304*7c478bd9Sstevel@tonic-gate /*
4305*7c478bd9Sstevel@tonic-gate  * Move the cursor to the start of the line, or in vi input mode,
4306*7c478bd9Sstevel@tonic-gate  * the start of the sub-line at which insertion started, and delete
4307*7c478bd9Sstevel@tonic-gate  * up to the old cursor position.
4308*7c478bd9Sstevel@tonic-gate  */
4309*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->insert_curpos) ||
4310*7c478bd9Sstevel@tonic-gate          gl_delete_chars(gl, nc, gl->editor == GL_EMACS_MODE || gl->vi.command);
4311*7c478bd9Sstevel@tonic-gate }
4312*7c478bd9Sstevel@tonic-gate 
4313*7c478bd9Sstevel@tonic-gate /*.......................................................................
4314*7c478bd9Sstevel@tonic-gate  * This is an action function which moves the cursor forward by a word.
4315*7c478bd9Sstevel@tonic-gate  */
4316*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_word)
4317*7c478bd9Sstevel@tonic-gate {
4318*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl_nth_word_end_forward(gl, count) +
4319*7c478bd9Sstevel@tonic-gate 			 (gl->editor==GL_EMACS_MODE));
4320*7c478bd9Sstevel@tonic-gate }
4321*7c478bd9Sstevel@tonic-gate 
4322*7c478bd9Sstevel@tonic-gate /*.......................................................................
4323*7c478bd9Sstevel@tonic-gate  * This is an action function which moves the cursor forward to the start
4324*7c478bd9Sstevel@tonic-gate  * of the next word.
4325*7c478bd9Sstevel@tonic-gate  */
4326*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_to_word)
4327*7c478bd9Sstevel@tonic-gate {
4328*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl_nth_word_start_forward(gl, count));
4329*7c478bd9Sstevel@tonic-gate }
4330*7c478bd9Sstevel@tonic-gate 
4331*7c478bd9Sstevel@tonic-gate /*.......................................................................
4332*7c478bd9Sstevel@tonic-gate  * This is an action function which moves the cursor backward by a word.
4333*7c478bd9Sstevel@tonic-gate  */
4334*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_word)
4335*7c478bd9Sstevel@tonic-gate {
4336*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl_nth_word_start_backward(gl, count));
4337*7c478bd9Sstevel@tonic-gate }
4338*7c478bd9Sstevel@tonic-gate 
4339*7c478bd9Sstevel@tonic-gate /*.......................................................................
4340*7c478bd9Sstevel@tonic-gate  * Delete one or more characters, starting with the one under the cursor.
4341*7c478bd9Sstevel@tonic-gate  *
4342*7c478bd9Sstevel@tonic-gate  * Input:
4343*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  The resource object of this library.
4344*7c478bd9Sstevel@tonic-gate  *  nc         int    The number of characters to delete.
4345*7c478bd9Sstevel@tonic-gate  *  cut        int    If true, copy the characters to the cut buffer.
4346*7c478bd9Sstevel@tonic-gate  * Output:
4347*7c478bd9Sstevel@tonic-gate  *  return     int    0 - OK.
4348*7c478bd9Sstevel@tonic-gate  *                    1 - Error.
4349*7c478bd9Sstevel@tonic-gate  */
4350*7c478bd9Sstevel@tonic-gate static int gl_delete_chars(GetLine *gl, int nc, int cut)
4351*7c478bd9Sstevel@tonic-gate {
4352*7c478bd9Sstevel@tonic-gate /*
4353*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4354*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4355*7c478bd9Sstevel@tonic-gate  */
4356*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4357*7c478bd9Sstevel@tonic-gate /*
4358*7c478bd9Sstevel@tonic-gate  * If there are fewer than nc characters following the cursor, limit
4359*7c478bd9Sstevel@tonic-gate  * nc to the number available.
4360*7c478bd9Sstevel@tonic-gate  */
4361*7c478bd9Sstevel@tonic-gate   if(gl->buff_curpos + nc > gl->ntotal)
4362*7c478bd9Sstevel@tonic-gate     nc = gl->ntotal - gl->buff_curpos;
4363*7c478bd9Sstevel@tonic-gate /*
4364*7c478bd9Sstevel@tonic-gate  * Copy the about to be deleted region to the cut buffer.
4365*7c478bd9Sstevel@tonic-gate  */
4366*7c478bd9Sstevel@tonic-gate   if(cut) {
4367*7c478bd9Sstevel@tonic-gate     memcpy(gl->cutbuf, gl->line + gl->buff_curpos, nc);
4368*7c478bd9Sstevel@tonic-gate     gl->cutbuf[nc] = '\0';
4369*7c478bd9Sstevel@tonic-gate   }
4370*7c478bd9Sstevel@tonic-gate /*
4371*7c478bd9Sstevel@tonic-gate  * Nothing to delete?
4372*7c478bd9Sstevel@tonic-gate  */
4373*7c478bd9Sstevel@tonic-gate   if(nc <= 0)
4374*7c478bd9Sstevel@tonic-gate     return 0;
4375*7c478bd9Sstevel@tonic-gate /*
4376*7c478bd9Sstevel@tonic-gate  * In vi overwrite mode, restore any previously overwritten characters
4377*7c478bd9Sstevel@tonic-gate  * from the undo buffer.
4378*7c478bd9Sstevel@tonic-gate  */
4379*7c478bd9Sstevel@tonic-gate   if(gl->editor == GL_VI_MODE && !gl->vi.command && !gl->insert) {
4380*7c478bd9Sstevel@tonic-gate /*
4381*7c478bd9Sstevel@tonic-gate  * How many of the characters being deleted can be restored from the
4382*7c478bd9Sstevel@tonic-gate  * undo buffer?
4383*7c478bd9Sstevel@tonic-gate  */
4384*7c478bd9Sstevel@tonic-gate     int nrestore = gl->buff_curpos + nc <= gl->vi.undo.ntotal ?
4385*7c478bd9Sstevel@tonic-gate       nc : gl->vi.undo.ntotal - gl->buff_curpos;
4386*7c478bd9Sstevel@tonic-gate /*
4387*7c478bd9Sstevel@tonic-gate  * Restore any available characters.
4388*7c478bd9Sstevel@tonic-gate  */
4389*7c478bd9Sstevel@tonic-gate     if(nrestore > 0) {
4390*7c478bd9Sstevel@tonic-gate       gl_buffer_string(gl, gl->vi.undo.line + gl->buff_curpos, nrestore,
4391*7c478bd9Sstevel@tonic-gate 		       gl->buff_curpos);
4392*7c478bd9Sstevel@tonic-gate     };
4393*7c478bd9Sstevel@tonic-gate /*
4394*7c478bd9Sstevel@tonic-gate  * If their were insufficient characters in the undo buffer, then this
4395*7c478bd9Sstevel@tonic-gate  * implies that we are deleting from the end of the line, so we need
4396*7c478bd9Sstevel@tonic-gate  * to terminate the line either where the undo buffer ran out, or if
4397*7c478bd9Sstevel@tonic-gate  * we are deleting from beyond the end of the undo buffer, at the current
4398*7c478bd9Sstevel@tonic-gate  * cursor position.
4399*7c478bd9Sstevel@tonic-gate  */
4400*7c478bd9Sstevel@tonic-gate     if(nc != nrestore) {
4401*7c478bd9Sstevel@tonic-gate       gl_truncate_buffer(gl, (gl->vi.undo.ntotal > gl->buff_curpos) ?
4402*7c478bd9Sstevel@tonic-gate 			 gl->vi.undo.ntotal : gl->buff_curpos);
4403*7c478bd9Sstevel@tonic-gate     };
4404*7c478bd9Sstevel@tonic-gate   } else {
4405*7c478bd9Sstevel@tonic-gate /*
4406*7c478bd9Sstevel@tonic-gate  * Copy the remaining part of the line back over the deleted characters.
4407*7c478bd9Sstevel@tonic-gate  */
4408*7c478bd9Sstevel@tonic-gate     gl_remove_from_buffer(gl, gl->buff_curpos, nc);
4409*7c478bd9Sstevel@tonic-gate   };
4410*7c478bd9Sstevel@tonic-gate /*
4411*7c478bd9Sstevel@tonic-gate  * Redraw the remaining characters following the cursor.
4412*7c478bd9Sstevel@tonic-gate  */
4413*7c478bd9Sstevel@tonic-gate   if(gl_print_string(gl, gl->line + gl->buff_curpos, '\0'))
4414*7c478bd9Sstevel@tonic-gate     return 1;
4415*7c478bd9Sstevel@tonic-gate /*
4416*7c478bd9Sstevel@tonic-gate  * Clear to the end of the terminal.
4417*7c478bd9Sstevel@tonic-gate  */
4418*7c478bd9Sstevel@tonic-gate   if(gl_truncate_display(gl))
4419*7c478bd9Sstevel@tonic-gate     return 1;
4420*7c478bd9Sstevel@tonic-gate /*
4421*7c478bd9Sstevel@tonic-gate  * Place the cursor at the start of where the deletion was performed.
4422*7c478bd9Sstevel@tonic-gate  */
4423*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos);
4424*7c478bd9Sstevel@tonic-gate }
4425*7c478bd9Sstevel@tonic-gate 
4426*7c478bd9Sstevel@tonic-gate /*.......................................................................
4427*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes character(s) under the
4428*7c478bd9Sstevel@tonic-gate  * cursor without moving the cursor.
4429*7c478bd9Sstevel@tonic-gate  */
4430*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_delete_char)
4431*7c478bd9Sstevel@tonic-gate {
4432*7c478bd9Sstevel@tonic-gate /*
4433*7c478bd9Sstevel@tonic-gate  * Delete 'count' characters.
4434*7c478bd9Sstevel@tonic-gate  */
4435*7c478bd9Sstevel@tonic-gate   return gl_delete_chars(gl, count, gl->vi.command);
4436*7c478bd9Sstevel@tonic-gate }
4437*7c478bd9Sstevel@tonic-gate 
4438*7c478bd9Sstevel@tonic-gate /*.......................................................................
4439*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes character(s) under the
4440*7c478bd9Sstevel@tonic-gate  * cursor and moves the cursor back one character.
4441*7c478bd9Sstevel@tonic-gate  */
4442*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_delete_char)
4443*7c478bd9Sstevel@tonic-gate {
4444*7c478bd9Sstevel@tonic-gate /*
4445*7c478bd9Sstevel@tonic-gate  * Restrict the deletion count to the number of characters that
4446*7c478bd9Sstevel@tonic-gate  * precede the insertion point.
4447*7c478bd9Sstevel@tonic-gate  */
4448*7c478bd9Sstevel@tonic-gate   if(count > gl->buff_curpos - gl->insert_curpos)
4449*7c478bd9Sstevel@tonic-gate     count = gl->buff_curpos - gl->insert_curpos;
4450*7c478bd9Sstevel@tonic-gate /*
4451*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4452*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4453*7c478bd9Sstevel@tonic-gate  */
4454*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4455*7c478bd9Sstevel@tonic-gate   return gl_cursor_left(gl, count, NULL) ||
4456*7c478bd9Sstevel@tonic-gate     gl_delete_chars(gl, count, gl->vi.command);
4457*7c478bd9Sstevel@tonic-gate }
4458*7c478bd9Sstevel@tonic-gate 
4459*7c478bd9Sstevel@tonic-gate /*.......................................................................
4460*7c478bd9Sstevel@tonic-gate  * Starting from the cursor position delete to the specified column.
4461*7c478bd9Sstevel@tonic-gate  */
4462*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_to_column)
4463*7c478bd9Sstevel@tonic-gate {
4464*7c478bd9Sstevel@tonic-gate   if (--count >= gl->buff_curpos)
4465*7c478bd9Sstevel@tonic-gate     return gl_forward_delete_char(gl, count - gl->buff_curpos, NULL);
4466*7c478bd9Sstevel@tonic-gate   else
4467*7c478bd9Sstevel@tonic-gate     return gl_backward_delete_char(gl, gl->buff_curpos - count, NULL);
4468*7c478bd9Sstevel@tonic-gate }
4469*7c478bd9Sstevel@tonic-gate 
4470*7c478bd9Sstevel@tonic-gate /*.......................................................................
4471*7c478bd9Sstevel@tonic-gate  * Starting from the cursor position delete characters to a matching
4472*7c478bd9Sstevel@tonic-gate  * parenthesis.
4473*7c478bd9Sstevel@tonic-gate  */
4474*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_to_parenthesis)
4475*7c478bd9Sstevel@tonic-gate {
4476*7c478bd9Sstevel@tonic-gate   int curpos = gl_index_of_matching_paren(gl);
4477*7c478bd9Sstevel@tonic-gate   if(curpos >= 0) {
4478*7c478bd9Sstevel@tonic-gate     gl_save_for_undo(gl);
4479*7c478bd9Sstevel@tonic-gate     if(curpos >= gl->buff_curpos)
4480*7c478bd9Sstevel@tonic-gate       return gl_forward_delete_char(gl, curpos - gl->buff_curpos + 1, NULL);
4481*7c478bd9Sstevel@tonic-gate     else
4482*7c478bd9Sstevel@tonic-gate       return gl_backward_delete_char(gl, ++gl->buff_curpos - curpos + 1, NULL);
4483*7c478bd9Sstevel@tonic-gate   };
4484*7c478bd9Sstevel@tonic-gate   return 0;
4485*7c478bd9Sstevel@tonic-gate }
4486*7c478bd9Sstevel@tonic-gate 
4487*7c478bd9Sstevel@tonic-gate /*.......................................................................
4488*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes from the cursor to the end
4489*7c478bd9Sstevel@tonic-gate  * of the word that the cursor is either in or precedes.
4490*7c478bd9Sstevel@tonic-gate  */
4491*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_delete_word)
4492*7c478bd9Sstevel@tonic-gate {
4493*7c478bd9Sstevel@tonic-gate /*
4494*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4495*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4496*7c478bd9Sstevel@tonic-gate  */
4497*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4498*7c478bd9Sstevel@tonic-gate /*
4499*7c478bd9Sstevel@tonic-gate  * In emacs mode delete to the end of the word. In vi mode delete to the
4500*7c478bd9Sstevel@tonic-gate  * start of the net word.
4501*7c478bd9Sstevel@tonic-gate  */
4502*7c478bd9Sstevel@tonic-gate   if(gl->editor == GL_EMACS_MODE) {
4503*7c478bd9Sstevel@tonic-gate     return gl_delete_chars(gl,
4504*7c478bd9Sstevel@tonic-gate 		gl_nth_word_end_forward(gl,count) - gl->buff_curpos + 1, 1);
4505*7c478bd9Sstevel@tonic-gate   } else {
4506*7c478bd9Sstevel@tonic-gate     return gl_delete_chars(gl,
4507*7c478bd9Sstevel@tonic-gate 		gl_nth_word_start_forward(gl,count) - gl->buff_curpos,
4508*7c478bd9Sstevel@tonic-gate 		gl->vi.command);
4509*7c478bd9Sstevel@tonic-gate   };
4510*7c478bd9Sstevel@tonic-gate }
4511*7c478bd9Sstevel@tonic-gate 
4512*7c478bd9Sstevel@tonic-gate /*.......................................................................
4513*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes the word that precedes the
4514*7c478bd9Sstevel@tonic-gate  * cursor.
4515*7c478bd9Sstevel@tonic-gate  */
4516*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_delete_word)
4517*7c478bd9Sstevel@tonic-gate {
4518*7c478bd9Sstevel@tonic-gate /*
4519*7c478bd9Sstevel@tonic-gate  * Keep a record of the current cursor position.
4520*7c478bd9Sstevel@tonic-gate  */
4521*7c478bd9Sstevel@tonic-gate   int buff_curpos = gl->buff_curpos;
4522*7c478bd9Sstevel@tonic-gate /*
4523*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4524*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4525*7c478bd9Sstevel@tonic-gate  */
4526*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4527*7c478bd9Sstevel@tonic-gate /*
4528*7c478bd9Sstevel@tonic-gate  * Move back 'count' words.
4529*7c478bd9Sstevel@tonic-gate  */
4530*7c478bd9Sstevel@tonic-gate   if(gl_backward_word(gl, count, NULL))
4531*7c478bd9Sstevel@tonic-gate     return 1;
4532*7c478bd9Sstevel@tonic-gate /*
4533*7c478bd9Sstevel@tonic-gate  * Delete from the new cursor position to the original one.
4534*7c478bd9Sstevel@tonic-gate  */
4535*7c478bd9Sstevel@tonic-gate   return gl_delete_chars(gl, buff_curpos - gl->buff_curpos,
4536*7c478bd9Sstevel@tonic-gate   			 gl->editor == GL_EMACS_MODE || gl->vi.command);
4537*7c478bd9Sstevel@tonic-gate }
4538*7c478bd9Sstevel@tonic-gate 
4539*7c478bd9Sstevel@tonic-gate /*.......................................................................
4540*7c478bd9Sstevel@tonic-gate  * Searching in a given direction, delete to the count'th
4541*7c478bd9Sstevel@tonic-gate  * instance of a specified or queried character, in the input line.
4542*7c478bd9Sstevel@tonic-gate  *
4543*7c478bd9Sstevel@tonic-gate  * Input:
4544*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
4545*7c478bd9Sstevel@tonic-gate  *  count        int    The number of times to search.
4546*7c478bd9Sstevel@tonic-gate  *  c           char    The character to be searched for, or '\0' if
4547*7c478bd9Sstevel@tonic-gate  *                      the character should be read from the user.
4548*7c478bd9Sstevel@tonic-gate  *  forward      int    True if searching forward.
4549*7c478bd9Sstevel@tonic-gate  *  onto         int    True if the search should end on top of the
4550*7c478bd9Sstevel@tonic-gate  *                      character, false if the search should stop
4551*7c478bd9Sstevel@tonic-gate  *                      one character before the character in the
4552*7c478bd9Sstevel@tonic-gate  *                      specified search direction.
4553*7c478bd9Sstevel@tonic-gate  *  change       int    If true, this function is being called upon
4554*7c478bd9Sstevel@tonic-gate  *                      to do a vi change command, in which case the
4555*7c478bd9Sstevel@tonic-gate  *                      user will be left in insert mode after the
4556*7c478bd9Sstevel@tonic-gate  *                      deletion.
4557*7c478bd9Sstevel@tonic-gate  * Output:
4558*7c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
4559*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
4560*7c478bd9Sstevel@tonic-gate  */
4561*7c478bd9Sstevel@tonic-gate static int gl_delete_find(GetLine *gl, int count, char c, int forward,
4562*7c478bd9Sstevel@tonic-gate 			  int onto, int change)
4563*7c478bd9Sstevel@tonic-gate {
4564*7c478bd9Sstevel@tonic-gate /*
4565*7c478bd9Sstevel@tonic-gate  * Search for the character, and abort the deletion if not found.
4566*7c478bd9Sstevel@tonic-gate  */
4567*7c478bd9Sstevel@tonic-gate   int pos = gl_find_char(gl, count, forward, onto, c);
4568*7c478bd9Sstevel@tonic-gate   if(pos < 0)
4569*7c478bd9Sstevel@tonic-gate     return 0;
4570*7c478bd9Sstevel@tonic-gate /*
4571*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4572*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4573*7c478bd9Sstevel@tonic-gate  */
4574*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4575*7c478bd9Sstevel@tonic-gate /*
4576*7c478bd9Sstevel@tonic-gate  * Allow the cursor to be at the end of the line if this is a change
4577*7c478bd9Sstevel@tonic-gate  * command.
4578*7c478bd9Sstevel@tonic-gate  */
4579*7c478bd9Sstevel@tonic-gate   if(change)
4580*7c478bd9Sstevel@tonic-gate     gl->vi.command = 0;
4581*7c478bd9Sstevel@tonic-gate /*
4582*7c478bd9Sstevel@tonic-gate  * Delete the appropriate span of characters.
4583*7c478bd9Sstevel@tonic-gate  */
4584*7c478bd9Sstevel@tonic-gate   if(forward) {
4585*7c478bd9Sstevel@tonic-gate     if(gl_delete_chars(gl, pos - gl->buff_curpos + 1, 1))
4586*7c478bd9Sstevel@tonic-gate       return 1;
4587*7c478bd9Sstevel@tonic-gate   } else {
4588*7c478bd9Sstevel@tonic-gate     int buff_curpos = gl->buff_curpos;
4589*7c478bd9Sstevel@tonic-gate     if(gl_place_cursor(gl, pos) ||
4590*7c478bd9Sstevel@tonic-gate        gl_delete_chars(gl, buff_curpos - gl->buff_curpos, 1))
4591*7c478bd9Sstevel@tonic-gate       return 1;
4592*7c478bd9Sstevel@tonic-gate   };
4593*7c478bd9Sstevel@tonic-gate /*
4594*7c478bd9Sstevel@tonic-gate  * If this is a change operation, switch the insert mode.
4595*7c478bd9Sstevel@tonic-gate  */
4596*7c478bd9Sstevel@tonic-gate   if(change && gl_vi_insert(gl, 0, NULL))
4597*7c478bd9Sstevel@tonic-gate     return 1;
4598*7c478bd9Sstevel@tonic-gate   return 0;
4599*7c478bd9Sstevel@tonic-gate }
4600*7c478bd9Sstevel@tonic-gate 
4601*7c478bd9Sstevel@tonic-gate /*.......................................................................
4602*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes forward from the cursor up to and
4603*7c478bd9Sstevel@tonic-gate  * including a specified character.
4604*7c478bd9Sstevel@tonic-gate  */
4605*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_delete_find)
4606*7c478bd9Sstevel@tonic-gate {
4607*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, '\0', 1, 1, 0);
4608*7c478bd9Sstevel@tonic-gate }
4609*7c478bd9Sstevel@tonic-gate 
4610*7c478bd9Sstevel@tonic-gate /*.......................................................................
4611*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes backward from the cursor back to
4612*7c478bd9Sstevel@tonic-gate  * and including a specified character.
4613*7c478bd9Sstevel@tonic-gate  */
4614*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_delete_find)
4615*7c478bd9Sstevel@tonic-gate {
4616*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, '\0', 0, 1, 0);
4617*7c478bd9Sstevel@tonic-gate }
4618*7c478bd9Sstevel@tonic-gate 
4619*7c478bd9Sstevel@tonic-gate /*.......................................................................
4620*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes forward from the cursor up to but
4621*7c478bd9Sstevel@tonic-gate  * not including a specified character.
4622*7c478bd9Sstevel@tonic-gate  */
4623*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_delete_to)
4624*7c478bd9Sstevel@tonic-gate {
4625*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, '\0', 1, 0, 0);
4626*7c478bd9Sstevel@tonic-gate }
4627*7c478bd9Sstevel@tonic-gate 
4628*7c478bd9Sstevel@tonic-gate /*.......................................................................
4629*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes backward from the cursor back to
4630*7c478bd9Sstevel@tonic-gate  * but not including a specified character.
4631*7c478bd9Sstevel@tonic-gate  */
4632*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_delete_to)
4633*7c478bd9Sstevel@tonic-gate {
4634*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, '\0', 0, 0, 0);
4635*7c478bd9Sstevel@tonic-gate }
4636*7c478bd9Sstevel@tonic-gate 
4637*7c478bd9Sstevel@tonic-gate /*.......................................................................
4638*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes to a character specified by a
4639*7c478bd9Sstevel@tonic-gate  * previous search.
4640*7c478bd9Sstevel@tonic-gate  */
4641*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_refind)
4642*7c478bd9Sstevel@tonic-gate {
4643*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward,
4644*7c478bd9Sstevel@tonic-gate 			gl->vi.find_onto, 0);
4645*7c478bd9Sstevel@tonic-gate }
4646*7c478bd9Sstevel@tonic-gate 
4647*7c478bd9Sstevel@tonic-gate /*.......................................................................
4648*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes to a character specified by a
4649*7c478bd9Sstevel@tonic-gate  * previous search, but in the opposite direction.
4650*7c478bd9Sstevel@tonic-gate  */
4651*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_delete_invert_refind)
4652*7c478bd9Sstevel@tonic-gate {
4653*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, gl->vi.find_char,
4654*7c478bd9Sstevel@tonic-gate 			!gl->vi.find_forward, gl->vi.find_onto, 0);
4655*7c478bd9Sstevel@tonic-gate }
4656*7c478bd9Sstevel@tonic-gate 
4657*7c478bd9Sstevel@tonic-gate /*.......................................................................
4658*7c478bd9Sstevel@tonic-gate  * This is an action function which converts the characters in the word
4659*7c478bd9Sstevel@tonic-gate  * following the cursor to upper case.
4660*7c478bd9Sstevel@tonic-gate  */
4661*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_upcase_word)
4662*7c478bd9Sstevel@tonic-gate {
4663*7c478bd9Sstevel@tonic-gate /*
4664*7c478bd9Sstevel@tonic-gate  * Locate the count'th word ending after the cursor.
4665*7c478bd9Sstevel@tonic-gate  */
4666*7c478bd9Sstevel@tonic-gate   int last = gl_nth_word_end_forward(gl, count);
4667*7c478bd9Sstevel@tonic-gate /*
4668*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4669*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4670*7c478bd9Sstevel@tonic-gate  */
4671*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4672*7c478bd9Sstevel@tonic-gate /*
4673*7c478bd9Sstevel@tonic-gate  * Upcase characters from the current cursor position to 'last'.
4674*7c478bd9Sstevel@tonic-gate  */
4675*7c478bd9Sstevel@tonic-gate   while(gl->buff_curpos <= last) {
4676*7c478bd9Sstevel@tonic-gate     char *cptr = gl->line + gl->buff_curpos;
4677*7c478bd9Sstevel@tonic-gate /*
4678*7c478bd9Sstevel@tonic-gate  * Convert the character to upper case?
4679*7c478bd9Sstevel@tonic-gate  */
4680*7c478bd9Sstevel@tonic-gate     if(islower((int)(unsigned char) *cptr))
4681*7c478bd9Sstevel@tonic-gate       gl_buffer_char(gl, toupper((int) *cptr), gl->buff_curpos);
4682*7c478bd9Sstevel@tonic-gate     gl->buff_curpos++;
4683*7c478bd9Sstevel@tonic-gate /*
4684*7c478bd9Sstevel@tonic-gate  * Write the possibly modified character back. Note that for non-modified
4685*7c478bd9Sstevel@tonic-gate  * characters we want to do this as well, so as to advance the cursor.
4686*7c478bd9Sstevel@tonic-gate  */
4687*7c478bd9Sstevel@tonic-gate     if(gl_print_char(gl, *cptr, cptr[1]))
4688*7c478bd9Sstevel@tonic-gate       return 1;
4689*7c478bd9Sstevel@tonic-gate   };
4690*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos);	/* bounds check */
4691*7c478bd9Sstevel@tonic-gate }
4692*7c478bd9Sstevel@tonic-gate 
4693*7c478bd9Sstevel@tonic-gate /*.......................................................................
4694*7c478bd9Sstevel@tonic-gate  * This is an action function which converts the characters in the word
4695*7c478bd9Sstevel@tonic-gate  * following the cursor to lower case.
4696*7c478bd9Sstevel@tonic-gate  */
4697*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_downcase_word)
4698*7c478bd9Sstevel@tonic-gate {
4699*7c478bd9Sstevel@tonic-gate /*
4700*7c478bd9Sstevel@tonic-gate  * Locate the count'th word ending after the cursor.
4701*7c478bd9Sstevel@tonic-gate  */
4702*7c478bd9Sstevel@tonic-gate   int last = gl_nth_word_end_forward(gl, count);
4703*7c478bd9Sstevel@tonic-gate /*
4704*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4705*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4706*7c478bd9Sstevel@tonic-gate  */
4707*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4708*7c478bd9Sstevel@tonic-gate /*
4709*7c478bd9Sstevel@tonic-gate  * Upcase characters from the current cursor position to 'last'.
4710*7c478bd9Sstevel@tonic-gate  */
4711*7c478bd9Sstevel@tonic-gate   while(gl->buff_curpos <= last) {
4712*7c478bd9Sstevel@tonic-gate     char *cptr = gl->line + gl->buff_curpos;
4713*7c478bd9Sstevel@tonic-gate /*
4714*7c478bd9Sstevel@tonic-gate  * Convert the character to upper case?
4715*7c478bd9Sstevel@tonic-gate  */
4716*7c478bd9Sstevel@tonic-gate     if(isupper((int)(unsigned char) *cptr))
4717*7c478bd9Sstevel@tonic-gate       gl_buffer_char(gl, tolower((int) *cptr), gl->buff_curpos);
4718*7c478bd9Sstevel@tonic-gate     gl->buff_curpos++;
4719*7c478bd9Sstevel@tonic-gate /*
4720*7c478bd9Sstevel@tonic-gate  * Write the possibly modified character back. Note that for non-modified
4721*7c478bd9Sstevel@tonic-gate  * characters we want to do this as well, so as to advance the cursor.
4722*7c478bd9Sstevel@tonic-gate  */
4723*7c478bd9Sstevel@tonic-gate     if(gl_print_char(gl, *cptr, cptr[1]))
4724*7c478bd9Sstevel@tonic-gate       return 1;
4725*7c478bd9Sstevel@tonic-gate   };
4726*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos);	/* bounds check */
4727*7c478bd9Sstevel@tonic-gate }
4728*7c478bd9Sstevel@tonic-gate 
4729*7c478bd9Sstevel@tonic-gate /*.......................................................................
4730*7c478bd9Sstevel@tonic-gate  * This is an action function which converts the first character of the
4731*7c478bd9Sstevel@tonic-gate  * following word to upper case, in order to capitalize the word, and
4732*7c478bd9Sstevel@tonic-gate  * leaves the cursor at the end of the word.
4733*7c478bd9Sstevel@tonic-gate  */
4734*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_capitalize_word)
4735*7c478bd9Sstevel@tonic-gate {
4736*7c478bd9Sstevel@tonic-gate   char *cptr;   /* &gl->line[gl->buff_curpos] */
4737*7c478bd9Sstevel@tonic-gate   int first;    /* True for the first letter of the word */
4738*7c478bd9Sstevel@tonic-gate   int i;
4739*7c478bd9Sstevel@tonic-gate /*
4740*7c478bd9Sstevel@tonic-gate  * Keep a record of the current insert mode and the cursor position.
4741*7c478bd9Sstevel@tonic-gate  */
4742*7c478bd9Sstevel@tonic-gate   int insert = gl->insert;
4743*7c478bd9Sstevel@tonic-gate /*
4744*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4745*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4746*7c478bd9Sstevel@tonic-gate  */
4747*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4748*7c478bd9Sstevel@tonic-gate /*
4749*7c478bd9Sstevel@tonic-gate  * We want to overwrite the modified word.
4750*7c478bd9Sstevel@tonic-gate  */
4751*7c478bd9Sstevel@tonic-gate   gl->insert = 0;
4752*7c478bd9Sstevel@tonic-gate /*
4753*7c478bd9Sstevel@tonic-gate  * Capitalize 'count' words.
4754*7c478bd9Sstevel@tonic-gate  */
4755*7c478bd9Sstevel@tonic-gate   for(i=0; i<count && gl->buff_curpos < gl->ntotal; i++) {
4756*7c478bd9Sstevel@tonic-gate     int pos = gl->buff_curpos;
4757*7c478bd9Sstevel@tonic-gate /*
4758*7c478bd9Sstevel@tonic-gate  * If we are not already within a word, skip to the start of the word.
4759*7c478bd9Sstevel@tonic-gate  */
4760*7c478bd9Sstevel@tonic-gate     for(cptr = gl->line + pos ; pos<gl->ntotal && !gl_is_word_char((int) *cptr);
4761*7c478bd9Sstevel@tonic-gate 	pos++, cptr++)
4762*7c478bd9Sstevel@tonic-gate       ;
4763*7c478bd9Sstevel@tonic-gate /*
4764*7c478bd9Sstevel@tonic-gate  * Move the cursor to the new position.
4765*7c478bd9Sstevel@tonic-gate  */
4766*7c478bd9Sstevel@tonic-gate     if(gl_place_cursor(gl, pos))
4767*7c478bd9Sstevel@tonic-gate       return 1;
4768*7c478bd9Sstevel@tonic-gate /*
4769*7c478bd9Sstevel@tonic-gate  * While searching for the end of the word, change lower case letters
4770*7c478bd9Sstevel@tonic-gate  * to upper case.
4771*7c478bd9Sstevel@tonic-gate  */
4772*7c478bd9Sstevel@tonic-gate     for(first=1; gl->buff_curpos<gl->ntotal && gl_is_word_char((int) *cptr);
4773*7c478bd9Sstevel@tonic-gate 	gl->buff_curpos++, cptr++) {
4774*7c478bd9Sstevel@tonic-gate /*
4775*7c478bd9Sstevel@tonic-gate  * Convert the character to upper case?
4776*7c478bd9Sstevel@tonic-gate  */
4777*7c478bd9Sstevel@tonic-gate       if(first) {
4778*7c478bd9Sstevel@tonic-gate 	if(islower((int)(unsigned char) *cptr))
4779*7c478bd9Sstevel@tonic-gate 	  gl_buffer_char(gl, toupper((int) *cptr), cptr - gl->line);
4780*7c478bd9Sstevel@tonic-gate       } else {
4781*7c478bd9Sstevel@tonic-gate 	if(isupper((int)(unsigned char) *cptr))
4782*7c478bd9Sstevel@tonic-gate 	  gl_buffer_char(gl, tolower((int) *cptr), cptr - gl->line);
4783*7c478bd9Sstevel@tonic-gate       };
4784*7c478bd9Sstevel@tonic-gate       first = 0;
4785*7c478bd9Sstevel@tonic-gate /*
4786*7c478bd9Sstevel@tonic-gate  * Write the possibly modified character back. Note that for non-modified
4787*7c478bd9Sstevel@tonic-gate  * characters we want to do this as well, so as to advance the cursor.
4788*7c478bd9Sstevel@tonic-gate  */
4789*7c478bd9Sstevel@tonic-gate       if(gl_print_char(gl, *cptr, cptr[1]))
4790*7c478bd9Sstevel@tonic-gate 	return 1;
4791*7c478bd9Sstevel@tonic-gate     };
4792*7c478bd9Sstevel@tonic-gate   };
4793*7c478bd9Sstevel@tonic-gate /*
4794*7c478bd9Sstevel@tonic-gate  * Restore the insertion mode.
4795*7c478bd9Sstevel@tonic-gate  */
4796*7c478bd9Sstevel@tonic-gate   gl->insert = insert;
4797*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos);	/* bounds check */
4798*7c478bd9Sstevel@tonic-gate }
4799*7c478bd9Sstevel@tonic-gate 
4800*7c478bd9Sstevel@tonic-gate /*.......................................................................
4801*7c478bd9Sstevel@tonic-gate  * This is an action function which redraws the current line.
4802*7c478bd9Sstevel@tonic-gate  */
4803*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_redisplay)
4804*7c478bd9Sstevel@tonic-gate {
4805*7c478bd9Sstevel@tonic-gate /*
4806*7c478bd9Sstevel@tonic-gate  * Keep a record of the current cursor position.
4807*7c478bd9Sstevel@tonic-gate  */
4808*7c478bd9Sstevel@tonic-gate   int buff_curpos = gl->buff_curpos;
4809*7c478bd9Sstevel@tonic-gate /*
4810*7c478bd9Sstevel@tonic-gate  * Do nothing if there is no line to be redisplayed.
4811*7c478bd9Sstevel@tonic-gate  */
4812*7c478bd9Sstevel@tonic-gate   if(gl->endline)
4813*7c478bd9Sstevel@tonic-gate     return 0;
4814*7c478bd9Sstevel@tonic-gate /*
4815*7c478bd9Sstevel@tonic-gate  * Erase the current input line.
4816*7c478bd9Sstevel@tonic-gate  */
4817*7c478bd9Sstevel@tonic-gate   if(gl_erase_line(gl))
4818*7c478bd9Sstevel@tonic-gate     return 1;
4819*7c478bd9Sstevel@tonic-gate /*
4820*7c478bd9Sstevel@tonic-gate  * Display the current prompt.
4821*7c478bd9Sstevel@tonic-gate  */
4822*7c478bd9Sstevel@tonic-gate   if(gl_display_prompt(gl))
4823*7c478bd9Sstevel@tonic-gate     return 1;
4824*7c478bd9Sstevel@tonic-gate /*
4825*7c478bd9Sstevel@tonic-gate  * Render the part of the line that the user has typed in so far.
4826*7c478bd9Sstevel@tonic-gate  */
4827*7c478bd9Sstevel@tonic-gate   if(gl_print_string(gl, gl->line, '\0'))
4828*7c478bd9Sstevel@tonic-gate     return 1;
4829*7c478bd9Sstevel@tonic-gate /*
4830*7c478bd9Sstevel@tonic-gate  * Restore the cursor position.
4831*7c478bd9Sstevel@tonic-gate  */
4832*7c478bd9Sstevel@tonic-gate   if(gl_place_cursor(gl, buff_curpos))
4833*7c478bd9Sstevel@tonic-gate     return 1;
4834*7c478bd9Sstevel@tonic-gate /*
4835*7c478bd9Sstevel@tonic-gate  * Mark the redisplay operation as having been completed.
4836*7c478bd9Sstevel@tonic-gate  */
4837*7c478bd9Sstevel@tonic-gate   gl->redisplay = 0;
4838*7c478bd9Sstevel@tonic-gate /*
4839*7c478bd9Sstevel@tonic-gate  * Flush the redisplayed line to the terminal.
4840*7c478bd9Sstevel@tonic-gate  */
4841*7c478bd9Sstevel@tonic-gate   return gl_flush_output(gl);
4842*7c478bd9Sstevel@tonic-gate }
4843*7c478bd9Sstevel@tonic-gate 
4844*7c478bd9Sstevel@tonic-gate /*.......................................................................
4845*7c478bd9Sstevel@tonic-gate  * This is an action function which clears the display and redraws the
4846*7c478bd9Sstevel@tonic-gate  * input line from the home position.
4847*7c478bd9Sstevel@tonic-gate  */
4848*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_clear_screen)
4849*7c478bd9Sstevel@tonic-gate {
4850*7c478bd9Sstevel@tonic-gate /*
4851*7c478bd9Sstevel@tonic-gate  * Home the cursor and clear from there to the end of the display.
4852*7c478bd9Sstevel@tonic-gate  */
4853*7c478bd9Sstevel@tonic-gate   if(gl_print_control_sequence(gl, gl->nline, gl->home) ||
4854*7c478bd9Sstevel@tonic-gate      gl_print_control_sequence(gl, gl->nline, gl->clear_eod))
4855*7c478bd9Sstevel@tonic-gate     return 1;
4856*7c478bd9Sstevel@tonic-gate /*
4857*7c478bd9Sstevel@tonic-gate  * The input line is no longer displayed.
4858*7c478bd9Sstevel@tonic-gate  */
4859*7c478bd9Sstevel@tonic-gate   gl_line_erased(gl);
4860*7c478bd9Sstevel@tonic-gate /*
4861*7c478bd9Sstevel@tonic-gate  * Arrange for the input line to be redisplayed.
4862*7c478bd9Sstevel@tonic-gate  */
4863*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
4864*7c478bd9Sstevel@tonic-gate   return 0;
4865*7c478bd9Sstevel@tonic-gate }
4866*7c478bd9Sstevel@tonic-gate 
4867*7c478bd9Sstevel@tonic-gate /*.......................................................................
4868*7c478bd9Sstevel@tonic-gate  * This is an action function which swaps the character under the cursor
4869*7c478bd9Sstevel@tonic-gate  * with the character to the left of the cursor.
4870*7c478bd9Sstevel@tonic-gate  */
4871*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_transpose_chars)
4872*7c478bd9Sstevel@tonic-gate {
4873*7c478bd9Sstevel@tonic-gate   char from[3];     /* The original string of 2 characters */
4874*7c478bd9Sstevel@tonic-gate   char swap[3];     /* The swapped string of two characters */
4875*7c478bd9Sstevel@tonic-gate /*
4876*7c478bd9Sstevel@tonic-gate  * If we are at the beginning or end of the line, there aren't two
4877*7c478bd9Sstevel@tonic-gate  * characters to swap.
4878*7c478bd9Sstevel@tonic-gate  */
4879*7c478bd9Sstevel@tonic-gate   if(gl->buff_curpos < 1 || gl->buff_curpos >= gl->ntotal)
4880*7c478bd9Sstevel@tonic-gate     return 0;
4881*7c478bd9Sstevel@tonic-gate /*
4882*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4883*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4884*7c478bd9Sstevel@tonic-gate  */
4885*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4886*7c478bd9Sstevel@tonic-gate /*
4887*7c478bd9Sstevel@tonic-gate  * Get the original and swapped strings of the two characters.
4888*7c478bd9Sstevel@tonic-gate  */
4889*7c478bd9Sstevel@tonic-gate   from[0] = gl->line[gl->buff_curpos - 1];
4890*7c478bd9Sstevel@tonic-gate   from[1] = gl->line[gl->buff_curpos];
4891*7c478bd9Sstevel@tonic-gate   from[2] = '\0';
4892*7c478bd9Sstevel@tonic-gate   swap[0] = gl->line[gl->buff_curpos];
4893*7c478bd9Sstevel@tonic-gate   swap[1] = gl->line[gl->buff_curpos - 1];
4894*7c478bd9Sstevel@tonic-gate   swap[2] = '\0';
4895*7c478bd9Sstevel@tonic-gate /*
4896*7c478bd9Sstevel@tonic-gate  * Move the cursor to the start of the two characters.
4897*7c478bd9Sstevel@tonic-gate  */
4898*7c478bd9Sstevel@tonic-gate   if(gl_place_cursor(gl, gl->buff_curpos-1))
4899*7c478bd9Sstevel@tonic-gate     return 1;
4900*7c478bd9Sstevel@tonic-gate /*
4901*7c478bd9Sstevel@tonic-gate  * Swap the two characters in the buffer.
4902*7c478bd9Sstevel@tonic-gate  */
4903*7c478bd9Sstevel@tonic-gate   gl_buffer_char(gl, swap[0], gl->buff_curpos);
4904*7c478bd9Sstevel@tonic-gate   gl_buffer_char(gl, swap[1], gl->buff_curpos+1);
4905*7c478bd9Sstevel@tonic-gate /*
4906*7c478bd9Sstevel@tonic-gate  * If the sum of the displayed width of the two characters
4907*7c478bd9Sstevel@tonic-gate  * in their current and final positions is the same, swapping can
4908*7c478bd9Sstevel@tonic-gate  * be done by just overwriting with the two swapped characters.
4909*7c478bd9Sstevel@tonic-gate  */
4910*7c478bd9Sstevel@tonic-gate   if(gl_displayed_string_width(gl, from, -1, gl->term_curpos) ==
4911*7c478bd9Sstevel@tonic-gate      gl_displayed_string_width(gl, swap, -1, gl->term_curpos)) {
4912*7c478bd9Sstevel@tonic-gate     int insert = gl->insert;
4913*7c478bd9Sstevel@tonic-gate     gl->insert = 0;
4914*7c478bd9Sstevel@tonic-gate     if(gl_print_char(gl, swap[0], swap[1]) ||
4915*7c478bd9Sstevel@tonic-gate        gl_print_char(gl, swap[1], gl->line[gl->buff_curpos+2]))
4916*7c478bd9Sstevel@tonic-gate       return 1;
4917*7c478bd9Sstevel@tonic-gate     gl->insert = insert;
4918*7c478bd9Sstevel@tonic-gate /*
4919*7c478bd9Sstevel@tonic-gate  * If the swapped substring has a different displayed size, we need to
4920*7c478bd9Sstevel@tonic-gate  * redraw everything after the first of the characters.
4921*7c478bd9Sstevel@tonic-gate  */
4922*7c478bd9Sstevel@tonic-gate   } else {
4923*7c478bd9Sstevel@tonic-gate     if(gl_print_string(gl, gl->line + gl->buff_curpos, '\0') ||
4924*7c478bd9Sstevel@tonic-gate        gl_truncate_display(gl))
4925*7c478bd9Sstevel@tonic-gate       return 1;
4926*7c478bd9Sstevel@tonic-gate   };
4927*7c478bd9Sstevel@tonic-gate /*
4928*7c478bd9Sstevel@tonic-gate  * Advance the cursor to the character after the swapped pair.
4929*7c478bd9Sstevel@tonic-gate  */
4930*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos + 2);
4931*7c478bd9Sstevel@tonic-gate }
4932*7c478bd9Sstevel@tonic-gate 
4933*7c478bd9Sstevel@tonic-gate /*.......................................................................
4934*7c478bd9Sstevel@tonic-gate  * This is an action function which sets a mark at the current cursor
4935*7c478bd9Sstevel@tonic-gate  * location.
4936*7c478bd9Sstevel@tonic-gate  */
4937*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_set_mark)
4938*7c478bd9Sstevel@tonic-gate {
4939*7c478bd9Sstevel@tonic-gate   gl->buff_mark = gl->buff_curpos;
4940*7c478bd9Sstevel@tonic-gate   return 0;
4941*7c478bd9Sstevel@tonic-gate }
4942*7c478bd9Sstevel@tonic-gate 
4943*7c478bd9Sstevel@tonic-gate /*.......................................................................
4944*7c478bd9Sstevel@tonic-gate  * This is an action function which swaps the mark location for the
4945*7c478bd9Sstevel@tonic-gate  * cursor location.
4946*7c478bd9Sstevel@tonic-gate  */
4947*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_exchange_point_and_mark)
4948*7c478bd9Sstevel@tonic-gate {
4949*7c478bd9Sstevel@tonic-gate /*
4950*7c478bd9Sstevel@tonic-gate  * Get the old mark position, and limit to the extent of the input
4951*7c478bd9Sstevel@tonic-gate  * line.
4952*7c478bd9Sstevel@tonic-gate  */
4953*7c478bd9Sstevel@tonic-gate   int old_mark = gl->buff_mark <= gl->ntotal ? gl->buff_mark : gl->ntotal;
4954*7c478bd9Sstevel@tonic-gate /*
4955*7c478bd9Sstevel@tonic-gate  * Make the current cursor position the new mark.
4956*7c478bd9Sstevel@tonic-gate  */
4957*7c478bd9Sstevel@tonic-gate   gl->buff_mark = gl->buff_curpos;
4958*7c478bd9Sstevel@tonic-gate /*
4959*7c478bd9Sstevel@tonic-gate  * Move the cursor to the old mark position.
4960*7c478bd9Sstevel@tonic-gate  */
4961*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, old_mark);
4962*7c478bd9Sstevel@tonic-gate }
4963*7c478bd9Sstevel@tonic-gate 
4964*7c478bd9Sstevel@tonic-gate /*.......................................................................
4965*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes the characters between the
4966*7c478bd9Sstevel@tonic-gate  * mark and the cursor, recording them in gl->cutbuf for later pasting.
4967*7c478bd9Sstevel@tonic-gate  */
4968*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_kill_region)
4969*7c478bd9Sstevel@tonic-gate {
4970*7c478bd9Sstevel@tonic-gate /*
4971*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
4972*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
4973*7c478bd9Sstevel@tonic-gate  */
4974*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
4975*7c478bd9Sstevel@tonic-gate /*
4976*7c478bd9Sstevel@tonic-gate  * Limit the mark to be within the line.
4977*7c478bd9Sstevel@tonic-gate  */
4978*7c478bd9Sstevel@tonic-gate   if(gl->buff_mark > gl->ntotal)
4979*7c478bd9Sstevel@tonic-gate     gl->buff_mark = gl->ntotal;
4980*7c478bd9Sstevel@tonic-gate /*
4981*7c478bd9Sstevel@tonic-gate  * If there are no characters between the cursor and the mark, simply clear
4982*7c478bd9Sstevel@tonic-gate  * the cut buffer.
4983*7c478bd9Sstevel@tonic-gate  */
4984*7c478bd9Sstevel@tonic-gate   if(gl->buff_mark == gl->buff_curpos) {
4985*7c478bd9Sstevel@tonic-gate     gl->cutbuf[0] = '\0';
4986*7c478bd9Sstevel@tonic-gate     return 0;
4987*7c478bd9Sstevel@tonic-gate   };
4988*7c478bd9Sstevel@tonic-gate /*
4989*7c478bd9Sstevel@tonic-gate  * If the mark is before the cursor, swap the cursor and the mark.
4990*7c478bd9Sstevel@tonic-gate  */
4991*7c478bd9Sstevel@tonic-gate   if(gl->buff_mark < gl->buff_curpos && gl_exchange_point_and_mark(gl,1,NULL))
4992*7c478bd9Sstevel@tonic-gate     return 1;
4993*7c478bd9Sstevel@tonic-gate /*
4994*7c478bd9Sstevel@tonic-gate  * Delete the characters.
4995*7c478bd9Sstevel@tonic-gate  */
4996*7c478bd9Sstevel@tonic-gate   if(gl_delete_chars(gl, gl->buff_mark - gl->buff_curpos, 1))
4997*7c478bd9Sstevel@tonic-gate     return 1;
4998*7c478bd9Sstevel@tonic-gate /*
4999*7c478bd9Sstevel@tonic-gate  * Make the mark the same as the cursor position.
5000*7c478bd9Sstevel@tonic-gate  */
5001*7c478bd9Sstevel@tonic-gate   gl->buff_mark = gl->buff_curpos;
5002*7c478bd9Sstevel@tonic-gate   return 0;
5003*7c478bd9Sstevel@tonic-gate }
5004*7c478bd9Sstevel@tonic-gate 
5005*7c478bd9Sstevel@tonic-gate /*.......................................................................
5006*7c478bd9Sstevel@tonic-gate  * This is an action function which records the characters between the
5007*7c478bd9Sstevel@tonic-gate  * mark and the cursor, in gl->cutbuf for later pasting.
5008*7c478bd9Sstevel@tonic-gate  */
5009*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_region_as_kill)
5010*7c478bd9Sstevel@tonic-gate {
5011*7c478bd9Sstevel@tonic-gate   int ca, cb;  /* The indexes of the first and last characters in the region */
5012*7c478bd9Sstevel@tonic-gate   int mark;    /* The position of the mark */
5013*7c478bd9Sstevel@tonic-gate /*
5014*7c478bd9Sstevel@tonic-gate  * Get the position of the mark, limiting it to lie within the line.
5015*7c478bd9Sstevel@tonic-gate  */
5016*7c478bd9Sstevel@tonic-gate   mark = gl->buff_mark > gl->ntotal ? gl->ntotal : gl->buff_mark;
5017*7c478bd9Sstevel@tonic-gate /*
5018*7c478bd9Sstevel@tonic-gate  * If there are no characters between the cursor and the mark, clear
5019*7c478bd9Sstevel@tonic-gate  * the cut buffer.
5020*7c478bd9Sstevel@tonic-gate  */
5021*7c478bd9Sstevel@tonic-gate   if(mark == gl->buff_curpos) {
5022*7c478bd9Sstevel@tonic-gate     gl->cutbuf[0] = '\0';
5023*7c478bd9Sstevel@tonic-gate     return 0;
5024*7c478bd9Sstevel@tonic-gate   };
5025*7c478bd9Sstevel@tonic-gate /*
5026*7c478bd9Sstevel@tonic-gate  * Get the line indexes of the first and last characters in the region.
5027*7c478bd9Sstevel@tonic-gate  */
5028*7c478bd9Sstevel@tonic-gate   if(mark < gl->buff_curpos) {
5029*7c478bd9Sstevel@tonic-gate     ca = mark;
5030*7c478bd9Sstevel@tonic-gate     cb = gl->buff_curpos - 1;
5031*7c478bd9Sstevel@tonic-gate   } else {
5032*7c478bd9Sstevel@tonic-gate     ca = gl->buff_curpos;
5033*7c478bd9Sstevel@tonic-gate     cb = mark - 1;
5034*7c478bd9Sstevel@tonic-gate   };
5035*7c478bd9Sstevel@tonic-gate /*
5036*7c478bd9Sstevel@tonic-gate  * Copy the region to the cut buffer.
5037*7c478bd9Sstevel@tonic-gate  */
5038*7c478bd9Sstevel@tonic-gate   memcpy(gl->cutbuf, gl->line + ca, cb + 1 - ca);
5039*7c478bd9Sstevel@tonic-gate   gl->cutbuf[cb + 1 - ca] = '\0';
5040*7c478bd9Sstevel@tonic-gate   return 0;
5041*7c478bd9Sstevel@tonic-gate }
5042*7c478bd9Sstevel@tonic-gate 
5043*7c478bd9Sstevel@tonic-gate /*.......................................................................
5044*7c478bd9Sstevel@tonic-gate  * This is an action function which inserts the contents of the cut
5045*7c478bd9Sstevel@tonic-gate  * buffer at the current cursor location.
5046*7c478bd9Sstevel@tonic-gate  */
5047*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_yank)
5048*7c478bd9Sstevel@tonic-gate {
5049*7c478bd9Sstevel@tonic-gate   int i;
5050*7c478bd9Sstevel@tonic-gate /*
5051*7c478bd9Sstevel@tonic-gate  * Set the mark at the current location.
5052*7c478bd9Sstevel@tonic-gate  */
5053*7c478bd9Sstevel@tonic-gate   gl->buff_mark = gl->buff_curpos;
5054*7c478bd9Sstevel@tonic-gate /*
5055*7c478bd9Sstevel@tonic-gate  * Do nothing else if the cut buffer is empty.
5056*7c478bd9Sstevel@tonic-gate  */
5057*7c478bd9Sstevel@tonic-gate   if(gl->cutbuf[0] == '\0')
5058*7c478bd9Sstevel@tonic-gate     return gl_ring_bell(gl, 1, NULL);
5059*7c478bd9Sstevel@tonic-gate /*
5060*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
5061*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
5062*7c478bd9Sstevel@tonic-gate  */
5063*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
5064*7c478bd9Sstevel@tonic-gate /*
5065*7c478bd9Sstevel@tonic-gate  * Insert the string count times.
5066*7c478bd9Sstevel@tonic-gate  */
5067*7c478bd9Sstevel@tonic-gate   for(i=0; i<count; i++) {
5068*7c478bd9Sstevel@tonic-gate     if(gl_add_string_to_line(gl, gl->cutbuf))
5069*7c478bd9Sstevel@tonic-gate       return 1;
5070*7c478bd9Sstevel@tonic-gate   };
5071*7c478bd9Sstevel@tonic-gate /*
5072*7c478bd9Sstevel@tonic-gate  * gl_add_string_to_line() leaves the cursor after the last character that
5073*7c478bd9Sstevel@tonic-gate  * was pasted, whereas vi leaves the cursor over the last character pasted.
5074*7c478bd9Sstevel@tonic-gate  */
5075*7c478bd9Sstevel@tonic-gate   if(gl->editor == GL_VI_MODE && gl_cursor_left(gl, 1, NULL))
5076*7c478bd9Sstevel@tonic-gate     return 1;
5077*7c478bd9Sstevel@tonic-gate   return 0;
5078*7c478bd9Sstevel@tonic-gate }
5079*7c478bd9Sstevel@tonic-gate 
5080*7c478bd9Sstevel@tonic-gate /*.......................................................................
5081*7c478bd9Sstevel@tonic-gate  * This is an action function which inserts the contents of the cut
5082*7c478bd9Sstevel@tonic-gate  * buffer one character beyond the current cursor location.
5083*7c478bd9Sstevel@tonic-gate  */
5084*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_append_yank)
5085*7c478bd9Sstevel@tonic-gate {
5086*7c478bd9Sstevel@tonic-gate   int was_command = gl->vi.command;
5087*7c478bd9Sstevel@tonic-gate   int i;
5088*7c478bd9Sstevel@tonic-gate /*
5089*7c478bd9Sstevel@tonic-gate  * If the cut buffer is empty, ring the terminal bell.
5090*7c478bd9Sstevel@tonic-gate  */
5091*7c478bd9Sstevel@tonic-gate   if(gl->cutbuf[0] == '\0')
5092*7c478bd9Sstevel@tonic-gate     return gl_ring_bell(gl, 1, NULL);
5093*7c478bd9Sstevel@tonic-gate /*
5094*7c478bd9Sstevel@tonic-gate  * Set the mark at the current location + 1.
5095*7c478bd9Sstevel@tonic-gate  */
5096*7c478bd9Sstevel@tonic-gate   gl->buff_mark = gl->buff_curpos + 1;
5097*7c478bd9Sstevel@tonic-gate /*
5098*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
5099*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
5100*7c478bd9Sstevel@tonic-gate  */
5101*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
5102*7c478bd9Sstevel@tonic-gate /*
5103*7c478bd9Sstevel@tonic-gate  * Arrange to paste the text in insert mode after the current character.
5104*7c478bd9Sstevel@tonic-gate  */
5105*7c478bd9Sstevel@tonic-gate   if(gl_vi_append(gl, 0, NULL))
5106*7c478bd9Sstevel@tonic-gate     return 1;
5107*7c478bd9Sstevel@tonic-gate /*
5108*7c478bd9Sstevel@tonic-gate  * Insert the string count times.
5109*7c478bd9Sstevel@tonic-gate  */
5110*7c478bd9Sstevel@tonic-gate   for(i=0; i<count; i++) {
5111*7c478bd9Sstevel@tonic-gate     if(gl_add_string_to_line(gl, gl->cutbuf))
5112*7c478bd9Sstevel@tonic-gate       return 1;
5113*7c478bd9Sstevel@tonic-gate   };
5114*7c478bd9Sstevel@tonic-gate /*
5115*7c478bd9Sstevel@tonic-gate  * Switch back to command mode if necessary.
5116*7c478bd9Sstevel@tonic-gate  */
5117*7c478bd9Sstevel@tonic-gate   if(was_command)
5118*7c478bd9Sstevel@tonic-gate     gl_vi_command_mode(gl);
5119*7c478bd9Sstevel@tonic-gate   return 0;
5120*7c478bd9Sstevel@tonic-gate }
5121*7c478bd9Sstevel@tonic-gate 
5122*7c478bd9Sstevel@tonic-gate /*.......................................................................
5123*7c478bd9Sstevel@tonic-gate  * Attempt to ask the terminal for its current size. On systems that
5124*7c478bd9Sstevel@tonic-gate  * don't support the TIOCWINSZ ioctl() for querying the terminal size,
5125*7c478bd9Sstevel@tonic-gate  * the current values of gl->ncolumn and gl->nrow are returned.
5126*7c478bd9Sstevel@tonic-gate  *
5127*7c478bd9Sstevel@tonic-gate  * Input:
5128*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  The resource object of gl_get_line().
5129*7c478bd9Sstevel@tonic-gate  * Input/Output:
5130*7c478bd9Sstevel@tonic-gate  *  ncolumn    int *  The number of columns will be assigned to *ncolumn.
5131*7c478bd9Sstevel@tonic-gate  *  nline      int *  The number of lines will be assigned to *nline.
5132*7c478bd9Sstevel@tonic-gate  */
5133*7c478bd9Sstevel@tonic-gate static void gl_query_size(GetLine *gl, int *ncolumn, int *nline)
5134*7c478bd9Sstevel@tonic-gate {
5135*7c478bd9Sstevel@tonic-gate #ifdef TIOCGWINSZ
5136*7c478bd9Sstevel@tonic-gate /*
5137*7c478bd9Sstevel@tonic-gate  * Query the new terminal window size. Ignore invalid responses.
5138*7c478bd9Sstevel@tonic-gate  */
5139*7c478bd9Sstevel@tonic-gate   struct winsize size;
5140*7c478bd9Sstevel@tonic-gate   if(ioctl(gl->output_fd, TIOCGWINSZ, &size) == 0 &&
5141*7c478bd9Sstevel@tonic-gate      size.ws_row > 0 && size.ws_col > 0) {
5142*7c478bd9Sstevel@tonic-gate     *ncolumn = size.ws_col;
5143*7c478bd9Sstevel@tonic-gate     *nline = size.ws_row;
5144*7c478bd9Sstevel@tonic-gate     return;
5145*7c478bd9Sstevel@tonic-gate   };
5146*7c478bd9Sstevel@tonic-gate #endif
5147*7c478bd9Sstevel@tonic-gate /*
5148*7c478bd9Sstevel@tonic-gate  * Return the existing values.
5149*7c478bd9Sstevel@tonic-gate  */
5150*7c478bd9Sstevel@tonic-gate   *ncolumn = gl->ncolumn;
5151*7c478bd9Sstevel@tonic-gate   *nline = gl->nline;
5152*7c478bd9Sstevel@tonic-gate   return;
5153*7c478bd9Sstevel@tonic-gate }
5154*7c478bd9Sstevel@tonic-gate 
5155*7c478bd9Sstevel@tonic-gate /*.......................................................................
5156*7c478bd9Sstevel@tonic-gate  * Query the size of the terminal, and if it has changed, redraw the
5157*7c478bd9Sstevel@tonic-gate  * current input line accordingly.
5158*7c478bd9Sstevel@tonic-gate  *
5159*7c478bd9Sstevel@tonic-gate  * Input:
5160*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  The resource object of gl_get_line().
5161*7c478bd9Sstevel@tonic-gate  * Output:
5162*7c478bd9Sstevel@tonic-gate  *  return     int    0 - OK.
5163*7c478bd9Sstevel@tonic-gate  *                    1 - Error.
5164*7c478bd9Sstevel@tonic-gate  */
5165*7c478bd9Sstevel@tonic-gate static int _gl_update_size(GetLine *gl)
5166*7c478bd9Sstevel@tonic-gate {
5167*7c478bd9Sstevel@tonic-gate   int ncolumn, nline;    /* The new size of the terminal */
5168*7c478bd9Sstevel@tonic-gate /*
5169*7c478bd9Sstevel@tonic-gate  * Query the new terminal window size.
5170*7c478bd9Sstevel@tonic-gate  */
5171*7c478bd9Sstevel@tonic-gate   gl_query_size(gl, &ncolumn, &nline);
5172*7c478bd9Sstevel@tonic-gate /*
5173*7c478bd9Sstevel@tonic-gate  * Update gl and the displayed line to fit the new dimensions.
5174*7c478bd9Sstevel@tonic-gate  */
5175*7c478bd9Sstevel@tonic-gate   return gl_handle_tty_resize(gl, ncolumn, nline);
5176*7c478bd9Sstevel@tonic-gate }
5177*7c478bd9Sstevel@tonic-gate 
5178*7c478bd9Sstevel@tonic-gate /*.......................................................................
5179*7c478bd9Sstevel@tonic-gate  * Redraw the current input line to account for a change in the terminal
5180*7c478bd9Sstevel@tonic-gate  * size. Also install the new size in gl.
5181*7c478bd9Sstevel@tonic-gate  *
5182*7c478bd9Sstevel@tonic-gate  * Input:
5183*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  The resource object of gl_get_line().
5184*7c478bd9Sstevel@tonic-gate  *  ncolumn    int    The new number of columns.
5185*7c478bd9Sstevel@tonic-gate  *  nline      int    The new number of lines.
5186*7c478bd9Sstevel@tonic-gate  * Output:
5187*7c478bd9Sstevel@tonic-gate  *  return     int    0 - OK.
5188*7c478bd9Sstevel@tonic-gate  *                    1 - Error.
5189*7c478bd9Sstevel@tonic-gate  */
5190*7c478bd9Sstevel@tonic-gate static int gl_handle_tty_resize(GetLine *gl, int ncolumn, int nline)
5191*7c478bd9Sstevel@tonic-gate {
5192*7c478bd9Sstevel@tonic-gate /*
5193*7c478bd9Sstevel@tonic-gate  * If the input device isn't a terminal, just record the new size.
5194*7c478bd9Sstevel@tonic-gate  */
5195*7c478bd9Sstevel@tonic-gate   if(!gl->is_term) {
5196*7c478bd9Sstevel@tonic-gate     gl->nline = nline;
5197*7c478bd9Sstevel@tonic-gate     gl->ncolumn = ncolumn;
5198*7c478bd9Sstevel@tonic-gate /*
5199*7c478bd9Sstevel@tonic-gate  * Has the size actually changed?
5200*7c478bd9Sstevel@tonic-gate  */
5201*7c478bd9Sstevel@tonic-gate   } else if(ncolumn != gl->ncolumn || nline != gl->nline) {
5202*7c478bd9Sstevel@tonic-gate /*
5203*7c478bd9Sstevel@tonic-gate  * If we are currently editing a line, erase it.
5204*7c478bd9Sstevel@tonic-gate  */
5205*7c478bd9Sstevel@tonic-gate     if(gl_erase_line(gl))
5206*7c478bd9Sstevel@tonic-gate       return 1;
5207*7c478bd9Sstevel@tonic-gate /*
5208*7c478bd9Sstevel@tonic-gate  * Update the recorded window size.
5209*7c478bd9Sstevel@tonic-gate  */
5210*7c478bd9Sstevel@tonic-gate     gl->nline = nline;
5211*7c478bd9Sstevel@tonic-gate     gl->ncolumn = ncolumn;
5212*7c478bd9Sstevel@tonic-gate /*
5213*7c478bd9Sstevel@tonic-gate  * Arrange for the input line to be redrawn before the next character
5214*7c478bd9Sstevel@tonic-gate  * is read from the terminal.
5215*7c478bd9Sstevel@tonic-gate  */
5216*7c478bd9Sstevel@tonic-gate     gl_queue_redisplay(gl);
5217*7c478bd9Sstevel@tonic-gate   };
5218*7c478bd9Sstevel@tonic-gate   return 0;
5219*7c478bd9Sstevel@tonic-gate }
5220*7c478bd9Sstevel@tonic-gate 
5221*7c478bd9Sstevel@tonic-gate /*.......................................................................
5222*7c478bd9Sstevel@tonic-gate  * This is the action function that recalls the previous line in the
5223*7c478bd9Sstevel@tonic-gate  * history buffer.
5224*7c478bd9Sstevel@tonic-gate  */
5225*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_up_history)
5226*7c478bd9Sstevel@tonic-gate {
5227*7c478bd9Sstevel@tonic-gate /*
5228*7c478bd9Sstevel@tonic-gate  * In vi mode, switch to command mode, since the user is very
5229*7c478bd9Sstevel@tonic-gate  * likely to want to move around newly recalled lines.
5230*7c478bd9Sstevel@tonic-gate  */
5231*7c478bd9Sstevel@tonic-gate   gl_vi_command_mode(gl);
5232*7c478bd9Sstevel@tonic-gate /*
5233*7c478bd9Sstevel@tonic-gate  * Forget any previous recall session.
5234*7c478bd9Sstevel@tonic-gate  */
5235*7c478bd9Sstevel@tonic-gate   gl->preload_id = 0;
5236*7c478bd9Sstevel@tonic-gate /*
5237*7c478bd9Sstevel@tonic-gate  * Record the key sequence number of this search action.
5238*7c478bd9Sstevel@tonic-gate  */
5239*7c478bd9Sstevel@tonic-gate   gl->last_search = gl->keyseq_count;
5240*7c478bd9Sstevel@tonic-gate /*
5241*7c478bd9Sstevel@tonic-gate  * We don't want a search prefix for this function.
5242*7c478bd9Sstevel@tonic-gate  */
5243*7c478bd9Sstevel@tonic-gate   if(_glh_search_prefix(gl->glh, gl->line, 0)) {
5244*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
5245*7c478bd9Sstevel@tonic-gate     return 1;
5246*7c478bd9Sstevel@tonic-gate   };
5247*7c478bd9Sstevel@tonic-gate /*
5248*7c478bd9Sstevel@tonic-gate  * Recall the count'th next older line in the history list. If the first one
5249*7c478bd9Sstevel@tonic-gate  * fails we can return since nothing has changed, otherwise we must continue
5250*7c478bd9Sstevel@tonic-gate  * and update the line state.
5251*7c478bd9Sstevel@tonic-gate  */
5252*7c478bd9Sstevel@tonic-gate   if(_glh_find_backwards(gl->glh, gl->line, gl->linelen+1) == NULL)
5253*7c478bd9Sstevel@tonic-gate     return 0;
5254*7c478bd9Sstevel@tonic-gate   while(--count && _glh_find_backwards(gl->glh, gl->line, gl->linelen+1))
5255*7c478bd9Sstevel@tonic-gate     ;
5256*7c478bd9Sstevel@tonic-gate /*
5257*7c478bd9Sstevel@tonic-gate  * Accomodate the new contents of gl->line[].
5258*7c478bd9Sstevel@tonic-gate  */
5259*7c478bd9Sstevel@tonic-gate   gl_update_buffer(gl);
5260*7c478bd9Sstevel@tonic-gate /*
5261*7c478bd9Sstevel@tonic-gate  * Arrange to have the cursor placed at the end of the new line.
5262*7c478bd9Sstevel@tonic-gate  */
5263*7c478bd9Sstevel@tonic-gate   gl->buff_curpos = gl->ntotal;
5264*7c478bd9Sstevel@tonic-gate /*
5265*7c478bd9Sstevel@tonic-gate  * Erase and display the new line.
5266*7c478bd9Sstevel@tonic-gate  */
5267*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
5268*7c478bd9Sstevel@tonic-gate   return 0;
5269*7c478bd9Sstevel@tonic-gate }
5270*7c478bd9Sstevel@tonic-gate 
5271*7c478bd9Sstevel@tonic-gate /*.......................................................................
5272*7c478bd9Sstevel@tonic-gate  * This is the action function that recalls the next line in the
5273*7c478bd9Sstevel@tonic-gate  * history buffer.
5274*7c478bd9Sstevel@tonic-gate  */
5275*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_down_history)
5276*7c478bd9Sstevel@tonic-gate {
5277*7c478bd9Sstevel@tonic-gate /*
5278*7c478bd9Sstevel@tonic-gate  * In vi mode, switch to command mode, since the user is very
5279*7c478bd9Sstevel@tonic-gate  * likely to want to move around newly recalled lines.
5280*7c478bd9Sstevel@tonic-gate  */
5281*7c478bd9Sstevel@tonic-gate   gl_vi_command_mode(gl);
5282*7c478bd9Sstevel@tonic-gate /*
5283*7c478bd9Sstevel@tonic-gate  * Record the key sequence number of this search action.
5284*7c478bd9Sstevel@tonic-gate  */
5285*7c478bd9Sstevel@tonic-gate   gl->last_search = gl->keyseq_count;
5286*7c478bd9Sstevel@tonic-gate /*
5287*7c478bd9Sstevel@tonic-gate  * If no search is currently in progress continue a previous recall
5288*7c478bd9Sstevel@tonic-gate  * session from a previous entered line if possible.
5289*7c478bd9Sstevel@tonic-gate  */
5290*7c478bd9Sstevel@tonic-gate   if(_glh_line_id(gl->glh, 0) == 0 && gl->preload_id) {
5291*7c478bd9Sstevel@tonic-gate     _glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen+1);
5292*7c478bd9Sstevel@tonic-gate     gl->preload_id = 0;
5293*7c478bd9Sstevel@tonic-gate   } else {
5294*7c478bd9Sstevel@tonic-gate /*
5295*7c478bd9Sstevel@tonic-gate  * We don't want a search prefix for this function.
5296*7c478bd9Sstevel@tonic-gate  */
5297*7c478bd9Sstevel@tonic-gate     if(_glh_search_prefix(gl->glh, gl->line, 0)) {
5298*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
5299*7c478bd9Sstevel@tonic-gate       return 1;
5300*7c478bd9Sstevel@tonic-gate     };
5301*7c478bd9Sstevel@tonic-gate /*
5302*7c478bd9Sstevel@tonic-gate  * Recall the count'th next newer line in the history list. If the first one
5303*7c478bd9Sstevel@tonic-gate  * fails we can return since nothing has changed otherwise we must continue
5304*7c478bd9Sstevel@tonic-gate  * and update the line state.
5305*7c478bd9Sstevel@tonic-gate  */
5306*7c478bd9Sstevel@tonic-gate     if(_glh_find_forwards(gl->glh, gl->line, gl->linelen+1) == NULL)
5307*7c478bd9Sstevel@tonic-gate       return 0;
5308*7c478bd9Sstevel@tonic-gate     while(--count && _glh_find_forwards(gl->glh, gl->line, gl->linelen+1))
5309*7c478bd9Sstevel@tonic-gate       ;
5310*7c478bd9Sstevel@tonic-gate   };
5311*7c478bd9Sstevel@tonic-gate /*
5312*7c478bd9Sstevel@tonic-gate  * Accomodate the new contents of gl->line[].
5313*7c478bd9Sstevel@tonic-gate  */
5314*7c478bd9Sstevel@tonic-gate   gl_update_buffer(gl);
5315*7c478bd9Sstevel@tonic-gate /*
5316*7c478bd9Sstevel@tonic-gate  * Arrange to have the cursor placed at the end of the new line.
5317*7c478bd9Sstevel@tonic-gate  */
5318*7c478bd9Sstevel@tonic-gate   gl->buff_curpos = gl->ntotal;
5319*7c478bd9Sstevel@tonic-gate /*
5320*7c478bd9Sstevel@tonic-gate  * Erase and display the new line.
5321*7c478bd9Sstevel@tonic-gate  */
5322*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
5323*7c478bd9Sstevel@tonic-gate   return 0;
5324*7c478bd9Sstevel@tonic-gate }
5325*7c478bd9Sstevel@tonic-gate 
5326*7c478bd9Sstevel@tonic-gate /*.......................................................................
5327*7c478bd9Sstevel@tonic-gate  * This is the action function that recalls the previous line in the
5328*7c478bd9Sstevel@tonic-gate  * history buffer whos prefix matches the characters that currently
5329*7c478bd9Sstevel@tonic-gate  * precede the cursor. By setting count=-1, this can be used internally
5330*7c478bd9Sstevel@tonic-gate  * to force searching for the prefix used in the last search.
5331*7c478bd9Sstevel@tonic-gate  */
5332*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_history_search_backward)
5333*7c478bd9Sstevel@tonic-gate {
5334*7c478bd9Sstevel@tonic-gate /*
5335*7c478bd9Sstevel@tonic-gate  * In vi mode, switch to command mode, since the user is very
5336*7c478bd9Sstevel@tonic-gate  * likely to want to move around newly recalled lines.
5337*7c478bd9Sstevel@tonic-gate  */
5338*7c478bd9Sstevel@tonic-gate   gl_vi_command_mode(gl);
5339*7c478bd9Sstevel@tonic-gate /*
5340*7c478bd9Sstevel@tonic-gate  * Forget any previous recall session.
5341*7c478bd9Sstevel@tonic-gate  */
5342*7c478bd9Sstevel@tonic-gate   gl->preload_id = 0;
5343*7c478bd9Sstevel@tonic-gate /*
5344*7c478bd9Sstevel@tonic-gate  * Record the key sequence number of this search action.
5345*7c478bd9Sstevel@tonic-gate  */
5346*7c478bd9Sstevel@tonic-gate   gl->last_search = gl->keyseq_count;
5347*7c478bd9Sstevel@tonic-gate /*
5348*7c478bd9Sstevel@tonic-gate  * If a prefix search isn't already in progress, replace the search
5349*7c478bd9Sstevel@tonic-gate  * prefix to the string that precedes the cursor. In vi command mode
5350*7c478bd9Sstevel@tonic-gate  * include the character that is under the cursor in the string.  If
5351*7c478bd9Sstevel@tonic-gate  * count<0 keep the previous search prefix regardless, so as to force
5352*7c478bd9Sstevel@tonic-gate  * a repeat search even if the last command wasn't a history command.
5353*7c478bd9Sstevel@tonic-gate  */
5354*7c478bd9Sstevel@tonic-gate   if(count >= 0 && !_glh_search_active(gl->glh) &&
5355*7c478bd9Sstevel@tonic-gate      _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos +
5356*7c478bd9Sstevel@tonic-gate 			(gl->editor==GL_VI_MODE && gl->ntotal>0))) {
5357*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
5358*7c478bd9Sstevel@tonic-gate     return 1;
5359*7c478bd9Sstevel@tonic-gate   };
5360*7c478bd9Sstevel@tonic-gate /*
5361*7c478bd9Sstevel@tonic-gate  * Search backwards for a match to the part of the line which precedes the
5362*7c478bd9Sstevel@tonic-gate  * cursor.
5363*7c478bd9Sstevel@tonic-gate  */
5364*7c478bd9Sstevel@tonic-gate   if(_glh_find_backwards(gl->glh, gl->line, gl->linelen+1) == NULL)
5365*7c478bd9Sstevel@tonic-gate     return 0;
5366*7c478bd9Sstevel@tonic-gate /*
5367*7c478bd9Sstevel@tonic-gate  * Accomodate the new contents of gl->line[].
5368*7c478bd9Sstevel@tonic-gate  */
5369*7c478bd9Sstevel@tonic-gate   gl_update_buffer(gl);
5370*7c478bd9Sstevel@tonic-gate /*
5371*7c478bd9Sstevel@tonic-gate  * Arrange to have the cursor placed at the end of the new line.
5372*7c478bd9Sstevel@tonic-gate  */
5373*7c478bd9Sstevel@tonic-gate   gl->buff_curpos = gl->ntotal;
5374*7c478bd9Sstevel@tonic-gate /*
5375*7c478bd9Sstevel@tonic-gate  * Erase and display the new line.
5376*7c478bd9Sstevel@tonic-gate  */
5377*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
5378*7c478bd9Sstevel@tonic-gate   return 0;
5379*7c478bd9Sstevel@tonic-gate }
5380*7c478bd9Sstevel@tonic-gate 
5381*7c478bd9Sstevel@tonic-gate /*.......................................................................
5382*7c478bd9Sstevel@tonic-gate  * This is the action function that recalls the previous line in the
5383*7c478bd9Sstevel@tonic-gate  * history buffer who's prefix matches that specified in an earlier call
5384*7c478bd9Sstevel@tonic-gate  * to gl_history_search_backward() or gl_history_search_forward().
5385*7c478bd9Sstevel@tonic-gate  */
5386*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_history_re_search_backward)
5387*7c478bd9Sstevel@tonic-gate {
5388*7c478bd9Sstevel@tonic-gate   return gl_history_search_backward(gl, -1, NULL);
5389*7c478bd9Sstevel@tonic-gate }
5390*7c478bd9Sstevel@tonic-gate 
5391*7c478bd9Sstevel@tonic-gate /*.......................................................................
5392*7c478bd9Sstevel@tonic-gate  * This is the action function that recalls the next line in the
5393*7c478bd9Sstevel@tonic-gate  * history buffer who's prefix matches that specified in the earlier call
5394*7c478bd9Sstevel@tonic-gate  * to gl_history_search_backward) which started the history search.
5395*7c478bd9Sstevel@tonic-gate  * By setting count=-1, this can be used internally to force searching
5396*7c478bd9Sstevel@tonic-gate  * for the prefix used in the last search.
5397*7c478bd9Sstevel@tonic-gate  */
5398*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_history_search_forward)
5399*7c478bd9Sstevel@tonic-gate {
5400*7c478bd9Sstevel@tonic-gate /*
5401*7c478bd9Sstevel@tonic-gate  * In vi mode, switch to command mode, since the user is very
5402*7c478bd9Sstevel@tonic-gate  * likely to want to move around newly recalled lines.
5403*7c478bd9Sstevel@tonic-gate  */
5404*7c478bd9Sstevel@tonic-gate   gl_vi_command_mode(gl);
5405*7c478bd9Sstevel@tonic-gate /*
5406*7c478bd9Sstevel@tonic-gate  * Record the key sequence number of this search action.
5407*7c478bd9Sstevel@tonic-gate  */
5408*7c478bd9Sstevel@tonic-gate   gl->last_search = gl->keyseq_count;
5409*7c478bd9Sstevel@tonic-gate /*
5410*7c478bd9Sstevel@tonic-gate  * If a prefix search isn't already in progress, replace the search
5411*7c478bd9Sstevel@tonic-gate  * prefix to the string that precedes the cursor. In vi command mode
5412*7c478bd9Sstevel@tonic-gate  * include the character that is under the cursor in the string.  If
5413*7c478bd9Sstevel@tonic-gate  * count<0 keep the previous search prefix regardless, so as to force
5414*7c478bd9Sstevel@tonic-gate  * a repeat search even if the last command wasn't a history command.
5415*7c478bd9Sstevel@tonic-gate  */
5416*7c478bd9Sstevel@tonic-gate   if(count >= 0 && !_glh_search_active(gl->glh) &&
5417*7c478bd9Sstevel@tonic-gate      _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos +
5418*7c478bd9Sstevel@tonic-gate 			(gl->editor==GL_VI_MODE && gl->ntotal>0))) {
5419*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
5420*7c478bd9Sstevel@tonic-gate     return 1;
5421*7c478bd9Sstevel@tonic-gate   };
5422*7c478bd9Sstevel@tonic-gate /*
5423*7c478bd9Sstevel@tonic-gate  * Search forwards for the next matching line.
5424*7c478bd9Sstevel@tonic-gate  */
5425*7c478bd9Sstevel@tonic-gate   if(_glh_find_forwards(gl->glh, gl->line, gl->linelen+1) == NULL)
5426*7c478bd9Sstevel@tonic-gate     return 0;
5427*7c478bd9Sstevel@tonic-gate /*
5428*7c478bd9Sstevel@tonic-gate  * Accomodate the new contents of gl->line[].
5429*7c478bd9Sstevel@tonic-gate  */
5430*7c478bd9Sstevel@tonic-gate   gl_update_buffer(gl);
5431*7c478bd9Sstevel@tonic-gate /*
5432*7c478bd9Sstevel@tonic-gate  * Arrange for the cursor to be placed at the end of the new line.
5433*7c478bd9Sstevel@tonic-gate  */
5434*7c478bd9Sstevel@tonic-gate   gl->buff_curpos = gl->ntotal;
5435*7c478bd9Sstevel@tonic-gate /*
5436*7c478bd9Sstevel@tonic-gate  * Erase and display the new line.
5437*7c478bd9Sstevel@tonic-gate  */
5438*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
5439*7c478bd9Sstevel@tonic-gate   return 0;
5440*7c478bd9Sstevel@tonic-gate }
5441*7c478bd9Sstevel@tonic-gate 
5442*7c478bd9Sstevel@tonic-gate /*.......................................................................
5443*7c478bd9Sstevel@tonic-gate  * This is the action function that recalls the next line in the
5444*7c478bd9Sstevel@tonic-gate  * history buffer who's prefix matches that specified in an earlier call
5445*7c478bd9Sstevel@tonic-gate  * to gl_history_search_backward() or gl_history_search_forward().
5446*7c478bd9Sstevel@tonic-gate  */
5447*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_history_re_search_forward)
5448*7c478bd9Sstevel@tonic-gate {
5449*7c478bd9Sstevel@tonic-gate   return gl_history_search_forward(gl, -1, NULL);
5450*7c478bd9Sstevel@tonic-gate }
5451*7c478bd9Sstevel@tonic-gate 
5452*7c478bd9Sstevel@tonic-gate #ifdef HIDE_FILE_SYSTEM
5453*7c478bd9Sstevel@tonic-gate /*.......................................................................
5454*7c478bd9Sstevel@tonic-gate  * The following function is used as the default completion handler when
5455*7c478bd9Sstevel@tonic-gate  * the filesystem is to be hidden. It simply reports no completions.
5456*7c478bd9Sstevel@tonic-gate  */
5457*7c478bd9Sstevel@tonic-gate static CPL_MATCH_FN(gl_no_completions)
5458*7c478bd9Sstevel@tonic-gate {
5459*7c478bd9Sstevel@tonic-gate   return 0;
5460*7c478bd9Sstevel@tonic-gate }
5461*7c478bd9Sstevel@tonic-gate #endif
5462*7c478bd9Sstevel@tonic-gate 
5463*7c478bd9Sstevel@tonic-gate /*.......................................................................
5464*7c478bd9Sstevel@tonic-gate  * This is the tab completion function that completes the filename that
5465*7c478bd9Sstevel@tonic-gate  * precedes the cursor position. Its callback data argument must be a
5466*7c478bd9Sstevel@tonic-gate  * pointer to a GlCplCallback containing the completion callback function
5467*7c478bd9Sstevel@tonic-gate  * and its callback data, or NULL to use the builtin filename completer.
5468*7c478bd9Sstevel@tonic-gate  */
5469*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_complete_word)
5470*7c478bd9Sstevel@tonic-gate {
5471*7c478bd9Sstevel@tonic-gate   CplMatches *matches;    /* The possible completions */
5472*7c478bd9Sstevel@tonic-gate   int suffix_len;         /* The length of the completion extension */
5473*7c478bd9Sstevel@tonic-gate   int cont_len;           /* The length of any continuation suffix */
5474*7c478bd9Sstevel@tonic-gate   int nextra;             /* The number of characters being added to the */
5475*7c478bd9Sstevel@tonic-gate                           /*  total length of the line. */
5476*7c478bd9Sstevel@tonic-gate   int buff_pos;           /* The buffer index at which the completion is */
5477*7c478bd9Sstevel@tonic-gate                           /*  to be inserted. */
5478*7c478bd9Sstevel@tonic-gate   int waserr = 0;         /* True after errors */
5479*7c478bd9Sstevel@tonic-gate /*
5480*7c478bd9Sstevel@tonic-gate  * Get the container of the completion callback and its callback data.
5481*7c478bd9Sstevel@tonic-gate  */
5482*7c478bd9Sstevel@tonic-gate   GlCplCallback *cb = data ? (GlCplCallback *) data : &gl->cplfn;
5483*7c478bd9Sstevel@tonic-gate /*
5484*7c478bd9Sstevel@tonic-gate  * In vi command mode, switch to append mode so that the character under
5485*7c478bd9Sstevel@tonic-gate  * the cursor is included in the completion (otherwise people can't
5486*7c478bd9Sstevel@tonic-gate  * complete at the end of the line).
5487*7c478bd9Sstevel@tonic-gate  */
5488*7c478bd9Sstevel@tonic-gate   if(gl->vi.command && gl_vi_append(gl, 0, NULL))
5489*7c478bd9Sstevel@tonic-gate     return 1;
5490*7c478bd9Sstevel@tonic-gate /*
5491*7c478bd9Sstevel@tonic-gate  * Get the cursor position at which the completion is to be inserted.
5492*7c478bd9Sstevel@tonic-gate  */
5493*7c478bd9Sstevel@tonic-gate   buff_pos = gl->buff_curpos;
5494*7c478bd9Sstevel@tonic-gate /*
5495*7c478bd9Sstevel@tonic-gate  * Perform the completion.
5496*7c478bd9Sstevel@tonic-gate  */
5497*7c478bd9Sstevel@tonic-gate   matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, cb->data,
5498*7c478bd9Sstevel@tonic-gate 			      cb->fn);
5499*7c478bd9Sstevel@tonic-gate /*
5500*7c478bd9Sstevel@tonic-gate  * No matching completions?
5501*7c478bd9Sstevel@tonic-gate  */
5502*7c478bd9Sstevel@tonic-gate   if(!matches) {
5503*7c478bd9Sstevel@tonic-gate     waserr = gl_print_info(gl, cpl_last_error(gl->cpl), GL_END_INFO);
5504*7c478bd9Sstevel@tonic-gate /*
5505*7c478bd9Sstevel@tonic-gate  * Are there any completions?
5506*7c478bd9Sstevel@tonic-gate  */
5507*7c478bd9Sstevel@tonic-gate   } else if(matches->nmatch >= 1) {
5508*7c478bd9Sstevel@tonic-gate /*
5509*7c478bd9Sstevel@tonic-gate  * If there any ambiguous matches, report them, starting on a new line.
5510*7c478bd9Sstevel@tonic-gate  */
5511*7c478bd9Sstevel@tonic-gate     if(matches->nmatch > 1 && gl->echo) {
5512*7c478bd9Sstevel@tonic-gate       if(_gl_normal_io(gl) ||
5513*7c478bd9Sstevel@tonic-gate 	 _cpl_output_completions(matches, gl_write_fn, gl, gl->ncolumn))
5514*7c478bd9Sstevel@tonic-gate 	waserr = 1;
5515*7c478bd9Sstevel@tonic-gate     };
5516*7c478bd9Sstevel@tonic-gate /*
5517*7c478bd9Sstevel@tonic-gate  * Get the length of the suffix and any continuation suffix to add to it.
5518*7c478bd9Sstevel@tonic-gate  */
5519*7c478bd9Sstevel@tonic-gate     suffix_len = strlen(matches->suffix);
5520*7c478bd9Sstevel@tonic-gate     cont_len = strlen(matches->cont_suffix);
5521*7c478bd9Sstevel@tonic-gate /*
5522*7c478bd9Sstevel@tonic-gate  * If there is an unambiguous match, and the continuation suffix ends in
5523*7c478bd9Sstevel@tonic-gate  * a newline, strip that newline and arrange to have getline return
5524*7c478bd9Sstevel@tonic-gate  * after this action function returns.
5525*7c478bd9Sstevel@tonic-gate  */
5526*7c478bd9Sstevel@tonic-gate     if(matches->nmatch==1 && cont_len > 0 &&
5527*7c478bd9Sstevel@tonic-gate        matches->cont_suffix[cont_len - 1] == '\n') {
5528*7c478bd9Sstevel@tonic-gate       cont_len--;
5529*7c478bd9Sstevel@tonic-gate       if(gl_newline(gl, 1, NULL))
5530*7c478bd9Sstevel@tonic-gate 	waserr = 1;
5531*7c478bd9Sstevel@tonic-gate     };
5532*7c478bd9Sstevel@tonic-gate /*
5533*7c478bd9Sstevel@tonic-gate  * Work out the number of characters that are to be added.
5534*7c478bd9Sstevel@tonic-gate  */
5535*7c478bd9Sstevel@tonic-gate     nextra = suffix_len + cont_len;
5536*7c478bd9Sstevel@tonic-gate /*
5537*7c478bd9Sstevel@tonic-gate  * Is there anything to be added?
5538*7c478bd9Sstevel@tonic-gate  */
5539*7c478bd9Sstevel@tonic-gate     if(!waserr && nextra) {
5540*7c478bd9Sstevel@tonic-gate /*
5541*7c478bd9Sstevel@tonic-gate  * Will there be space for the expansion in the line buffer?
5542*7c478bd9Sstevel@tonic-gate  */
5543*7c478bd9Sstevel@tonic-gate       if(gl->ntotal + nextra < gl->linelen) {
5544*7c478bd9Sstevel@tonic-gate /*
5545*7c478bd9Sstevel@tonic-gate  * Make room to insert the filename extension.
5546*7c478bd9Sstevel@tonic-gate  */
5547*7c478bd9Sstevel@tonic-gate 	gl_make_gap_in_buffer(gl, gl->buff_curpos, nextra);
5548*7c478bd9Sstevel@tonic-gate /*
5549*7c478bd9Sstevel@tonic-gate  * Insert the filename extension.
5550*7c478bd9Sstevel@tonic-gate  */
5551*7c478bd9Sstevel@tonic-gate 	gl_buffer_string(gl, matches->suffix, suffix_len, gl->buff_curpos);
5552*7c478bd9Sstevel@tonic-gate /*
5553*7c478bd9Sstevel@tonic-gate  * Add the terminating characters.
5554*7c478bd9Sstevel@tonic-gate  */
5555*7c478bd9Sstevel@tonic-gate 	gl_buffer_string(gl, matches->cont_suffix, cont_len,
5556*7c478bd9Sstevel@tonic-gate 			 gl->buff_curpos + suffix_len);
5557*7c478bd9Sstevel@tonic-gate /*
5558*7c478bd9Sstevel@tonic-gate  * Place the cursor position at the end of the completion.
5559*7c478bd9Sstevel@tonic-gate  */
5560*7c478bd9Sstevel@tonic-gate 	gl->buff_curpos += nextra;
5561*7c478bd9Sstevel@tonic-gate /*
5562*7c478bd9Sstevel@tonic-gate  * If we don't have to redisplay the whole line, redisplay the part
5563*7c478bd9Sstevel@tonic-gate  * of the line which follows the original cursor position, and place
5564*7c478bd9Sstevel@tonic-gate  * the cursor at the end of the completion.
5565*7c478bd9Sstevel@tonic-gate  */
5566*7c478bd9Sstevel@tonic-gate 	if(gl->displayed) {
5567*7c478bd9Sstevel@tonic-gate 	  if(gl_truncate_display(gl) ||
5568*7c478bd9Sstevel@tonic-gate 	     gl_print_string(gl, gl->line + buff_pos, '\0') ||
5569*7c478bd9Sstevel@tonic-gate 	     gl_place_cursor(gl, gl->buff_curpos))
5570*7c478bd9Sstevel@tonic-gate 	    waserr = 1;
5571*7c478bd9Sstevel@tonic-gate 	};
5572*7c478bd9Sstevel@tonic-gate       } else {
5573*7c478bd9Sstevel@tonic-gate 	(void) gl_print_info(gl,
5574*7c478bd9Sstevel@tonic-gate 			     "Insufficient room in line for file completion.",
5575*7c478bd9Sstevel@tonic-gate 			     GL_END_INFO);
5576*7c478bd9Sstevel@tonic-gate 	waserr = 1;
5577*7c478bd9Sstevel@tonic-gate       };
5578*7c478bd9Sstevel@tonic-gate     };
5579*7c478bd9Sstevel@tonic-gate   };
5580*7c478bd9Sstevel@tonic-gate /*
5581*7c478bd9Sstevel@tonic-gate  * If any output had to be written to the terminal, then editing will
5582*7c478bd9Sstevel@tonic-gate  * have been suspended, make sure that we are back in raw line editing
5583*7c478bd9Sstevel@tonic-gate  * mode before returning.
5584*7c478bd9Sstevel@tonic-gate  */
5585*7c478bd9Sstevel@tonic-gate   if(_gl_raw_io(gl, 1))
5586*7c478bd9Sstevel@tonic-gate     waserr = 1;
5587*7c478bd9Sstevel@tonic-gate   return 0;
5588*7c478bd9Sstevel@tonic-gate }
5589*7c478bd9Sstevel@tonic-gate 
5590*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
5591*7c478bd9Sstevel@tonic-gate /*.......................................................................
5592*7c478bd9Sstevel@tonic-gate  * This is the function that expands the filename that precedes the
5593*7c478bd9Sstevel@tonic-gate  * cursor position. It expands ~user/ expressions, $envvar expressions,
5594*7c478bd9Sstevel@tonic-gate  * and wildcards.
5595*7c478bd9Sstevel@tonic-gate  */
5596*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_expand_filename)
5597*7c478bd9Sstevel@tonic-gate {
5598*7c478bd9Sstevel@tonic-gate   char *start_path;      /* The pointer to the start of the pathname in */
5599*7c478bd9Sstevel@tonic-gate                          /*  gl->line[]. */
5600*7c478bd9Sstevel@tonic-gate   FileExpansion *result; /* The results of the filename expansion */
5601*7c478bd9Sstevel@tonic-gate   int pathlen;           /* The length of the pathname being expanded */
5602*7c478bd9Sstevel@tonic-gate   int length;            /* The number of characters needed to display the */
5603*7c478bd9Sstevel@tonic-gate                          /*  expanded files. */
5604*7c478bd9Sstevel@tonic-gate   int nextra;            /* The number of characters to be added */
5605*7c478bd9Sstevel@tonic-gate   int i,j;
5606*7c478bd9Sstevel@tonic-gate /*
5607*7c478bd9Sstevel@tonic-gate  * In vi command mode, switch to append mode so that the character under
5608*7c478bd9Sstevel@tonic-gate  * the cursor is included in the completion (otherwise people can't
5609*7c478bd9Sstevel@tonic-gate  * complete at the end of the line).
5610*7c478bd9Sstevel@tonic-gate  */
5611*7c478bd9Sstevel@tonic-gate   if(gl->vi.command && gl_vi_append(gl, 0, NULL))
5612*7c478bd9Sstevel@tonic-gate     return 1;
5613*7c478bd9Sstevel@tonic-gate /*
5614*7c478bd9Sstevel@tonic-gate  * Locate the start of the filename that precedes the cursor position.
5615*7c478bd9Sstevel@tonic-gate  */
5616*7c478bd9Sstevel@tonic-gate   start_path = _pu_start_of_path(gl->line, gl->buff_curpos);
5617*7c478bd9Sstevel@tonic-gate   if(!start_path)
5618*7c478bd9Sstevel@tonic-gate     return 1;
5619*7c478bd9Sstevel@tonic-gate /*
5620*7c478bd9Sstevel@tonic-gate  * Get the length of the string that is to be expanded.
5621*7c478bd9Sstevel@tonic-gate  */
5622*7c478bd9Sstevel@tonic-gate   pathlen = gl->buff_curpos - (start_path - gl->line);
5623*7c478bd9Sstevel@tonic-gate /*
5624*7c478bd9Sstevel@tonic-gate  * Attempt to expand it.
5625*7c478bd9Sstevel@tonic-gate  */
5626*7c478bd9Sstevel@tonic-gate   result = ef_expand_file(gl->ef, start_path, pathlen);
5627*7c478bd9Sstevel@tonic-gate /*
5628*7c478bd9Sstevel@tonic-gate  * If there was an error, report the error on a new line.
5629*7c478bd9Sstevel@tonic-gate  */
5630*7c478bd9Sstevel@tonic-gate   if(!result)
5631*7c478bd9Sstevel@tonic-gate     return gl_print_info(gl, ef_last_error(gl->ef), GL_END_INFO);
5632*7c478bd9Sstevel@tonic-gate /*
5633*7c478bd9Sstevel@tonic-gate  * If no files matched, report this as well.
5634*7c478bd9Sstevel@tonic-gate  */
5635*7c478bd9Sstevel@tonic-gate   if(result->nfile == 0 || !result->exists)
5636*7c478bd9Sstevel@tonic-gate     return gl_print_info(gl, "No files match.", GL_END_INFO);
5637*7c478bd9Sstevel@tonic-gate /*
5638*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential use by
5639*7c478bd9Sstevel@tonic-gate  * vi-undo.
5640*7c478bd9Sstevel@tonic-gate  */
5641*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
5642*7c478bd9Sstevel@tonic-gate /*
5643*7c478bd9Sstevel@tonic-gate  * Work out how much space we will need to display all of the matching
5644*7c478bd9Sstevel@tonic-gate  * filenames, taking account of the space that we need to place between
5645*7c478bd9Sstevel@tonic-gate  * them, and the number of additional '\' characters needed to escape
5646*7c478bd9Sstevel@tonic-gate  * spaces, tabs and backslash characters in the individual filenames.
5647*7c478bd9Sstevel@tonic-gate  */
5648*7c478bd9Sstevel@tonic-gate   length = 0;
5649*7c478bd9Sstevel@tonic-gate   for(i=0; i<result->nfile; i++) {
5650*7c478bd9Sstevel@tonic-gate     char *file = result->files[i];
5651*7c478bd9Sstevel@tonic-gate     while(*file) {
5652*7c478bd9Sstevel@tonic-gate       int c = *file++;
5653*7c478bd9Sstevel@tonic-gate       switch(c) {
5654*7c478bd9Sstevel@tonic-gate       case ' ': case '\t': case '\\': case '*': case '?': case '[':
5655*7c478bd9Sstevel@tonic-gate 	length++;  /* Count extra backslash characters */
5656*7c478bd9Sstevel@tonic-gate       };
5657*7c478bd9Sstevel@tonic-gate       length++;    /* Count the character itself */
5658*7c478bd9Sstevel@tonic-gate     };
5659*7c478bd9Sstevel@tonic-gate     length++;      /* Count the space that follows each filename */
5660*7c478bd9Sstevel@tonic-gate   };
5661*7c478bd9Sstevel@tonic-gate /*
5662*7c478bd9Sstevel@tonic-gate  * Work out the number of characters that are to be added.
5663*7c478bd9Sstevel@tonic-gate  */
5664*7c478bd9Sstevel@tonic-gate   nextra = length - pathlen;
5665*7c478bd9Sstevel@tonic-gate /*
5666*7c478bd9Sstevel@tonic-gate  * Will there be space for the expansion in the line buffer?
5667*7c478bd9Sstevel@tonic-gate  */
5668*7c478bd9Sstevel@tonic-gate   if(gl->ntotal + nextra >= gl->linelen) {
5669*7c478bd9Sstevel@tonic-gate     return gl_print_info(gl, "Insufficient room in line for file expansion.",
5670*7c478bd9Sstevel@tonic-gate 			 GL_END_INFO);
5671*7c478bd9Sstevel@tonic-gate   } else {
5672*7c478bd9Sstevel@tonic-gate /*
5673*7c478bd9Sstevel@tonic-gate  * Do we need to move the part of the line that followed the unexpanded
5674*7c478bd9Sstevel@tonic-gate  * filename?
5675*7c478bd9Sstevel@tonic-gate  */
5676*7c478bd9Sstevel@tonic-gate     if(nextra > 0) {
5677*7c478bd9Sstevel@tonic-gate       gl_make_gap_in_buffer(gl, gl->buff_curpos, nextra);
5678*7c478bd9Sstevel@tonic-gate     } else if(nextra < 0) {
5679*7c478bd9Sstevel@tonic-gate       gl->buff_curpos += nextra;
5680*7c478bd9Sstevel@tonic-gate       gl_remove_from_buffer(gl, gl->buff_curpos, -nextra);
5681*7c478bd9Sstevel@tonic-gate     };
5682*7c478bd9Sstevel@tonic-gate /*
5683*7c478bd9Sstevel@tonic-gate  * Insert the filenames, separated by spaces, and with internal spaces,
5684*7c478bd9Sstevel@tonic-gate  * tabs and backslashes escaped with backslashes.
5685*7c478bd9Sstevel@tonic-gate  */
5686*7c478bd9Sstevel@tonic-gate     for(i=0,j=start_path - gl->line; i<result->nfile; i++) {
5687*7c478bd9Sstevel@tonic-gate       char *file = result->files[i];
5688*7c478bd9Sstevel@tonic-gate       while(*file) {
5689*7c478bd9Sstevel@tonic-gate 	int c = *file++;
5690*7c478bd9Sstevel@tonic-gate 	switch(c) {
5691*7c478bd9Sstevel@tonic-gate 	case ' ': case '\t': case '\\': case '*': case '?': case '[':
5692*7c478bd9Sstevel@tonic-gate 	  gl_buffer_char(gl, '\\', j++);
5693*7c478bd9Sstevel@tonic-gate 	};
5694*7c478bd9Sstevel@tonic-gate 	gl_buffer_char(gl, c, j++);
5695*7c478bd9Sstevel@tonic-gate       };
5696*7c478bd9Sstevel@tonic-gate       gl_buffer_char(gl, ' ', j++);
5697*7c478bd9Sstevel@tonic-gate     };
5698*7c478bd9Sstevel@tonic-gate   };
5699*7c478bd9Sstevel@tonic-gate /*
5700*7c478bd9Sstevel@tonic-gate  * Redisplay the part of the line which follows the start of
5701*7c478bd9Sstevel@tonic-gate  * the original filename.
5702*7c478bd9Sstevel@tonic-gate  */
5703*7c478bd9Sstevel@tonic-gate   if(gl_place_cursor(gl, start_path - gl->line) ||
5704*7c478bd9Sstevel@tonic-gate      gl_truncate_display(gl) ||
5705*7c478bd9Sstevel@tonic-gate      gl_print_string(gl, start_path, start_path[length]))
5706*7c478bd9Sstevel@tonic-gate     return 1;
5707*7c478bd9Sstevel@tonic-gate /*
5708*7c478bd9Sstevel@tonic-gate  * Move the cursor to the end of the expansion.
5709*7c478bd9Sstevel@tonic-gate  */
5710*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, (start_path - gl->line) + length);
5711*7c478bd9Sstevel@tonic-gate }
5712*7c478bd9Sstevel@tonic-gate #endif
5713*7c478bd9Sstevel@tonic-gate 
5714*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
5715*7c478bd9Sstevel@tonic-gate /*.......................................................................
5716*7c478bd9Sstevel@tonic-gate  * This is the action function that lists glob expansions of the
5717*7c478bd9Sstevel@tonic-gate  * filename that precedes the cursor position. It expands ~user/
5718*7c478bd9Sstevel@tonic-gate  * expressions, $envvar expressions, and wildcards.
5719*7c478bd9Sstevel@tonic-gate  */
5720*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_list_glob)
5721*7c478bd9Sstevel@tonic-gate {
5722*7c478bd9Sstevel@tonic-gate   char *start_path;      /* The pointer to the start of the pathname in */
5723*7c478bd9Sstevel@tonic-gate                          /*  gl->line[]. */
5724*7c478bd9Sstevel@tonic-gate   FileExpansion *result; /* The results of the filename expansion */
5725*7c478bd9Sstevel@tonic-gate   int pathlen;           /* The length of the pathname being expanded */
5726*7c478bd9Sstevel@tonic-gate /*
5727*7c478bd9Sstevel@tonic-gate  * Locate the start of the filename that precedes the cursor position.
5728*7c478bd9Sstevel@tonic-gate  */
5729*7c478bd9Sstevel@tonic-gate   start_path = _pu_start_of_path(gl->line, gl->buff_curpos);
5730*7c478bd9Sstevel@tonic-gate   if(!start_path)
5731*7c478bd9Sstevel@tonic-gate     return 1;
5732*7c478bd9Sstevel@tonic-gate /*
5733*7c478bd9Sstevel@tonic-gate  * Get the length of the string that is to be expanded.
5734*7c478bd9Sstevel@tonic-gate  */
5735*7c478bd9Sstevel@tonic-gate   pathlen = gl->buff_curpos - (start_path - gl->line);
5736*7c478bd9Sstevel@tonic-gate /*
5737*7c478bd9Sstevel@tonic-gate  * Attempt to expand it.
5738*7c478bd9Sstevel@tonic-gate  */
5739*7c478bd9Sstevel@tonic-gate   result = ef_expand_file(gl->ef, start_path, pathlen);
5740*7c478bd9Sstevel@tonic-gate /*
5741*7c478bd9Sstevel@tonic-gate  * If there was an error, report it.
5742*7c478bd9Sstevel@tonic-gate  */
5743*7c478bd9Sstevel@tonic-gate   if(!result) {
5744*7c478bd9Sstevel@tonic-gate     return gl_print_info(gl,  ef_last_error(gl->ef), GL_END_INFO);
5745*7c478bd9Sstevel@tonic-gate /*
5746*7c478bd9Sstevel@tonic-gate  * If no files matched, report this as well.
5747*7c478bd9Sstevel@tonic-gate  */
5748*7c478bd9Sstevel@tonic-gate   } else if(result->nfile == 0 || !result->exists) {
5749*7c478bd9Sstevel@tonic-gate     return gl_print_info(gl, "No files match.", GL_END_INFO);
5750*7c478bd9Sstevel@tonic-gate /*
5751*7c478bd9Sstevel@tonic-gate  * List the matching expansions.
5752*7c478bd9Sstevel@tonic-gate  */
5753*7c478bd9Sstevel@tonic-gate   } else if(gl->echo) {
5754*7c478bd9Sstevel@tonic-gate     if(gl_start_newline(gl, 1) ||
5755*7c478bd9Sstevel@tonic-gate        _ef_output_expansions(result, gl_write_fn, gl, gl->ncolumn))
5756*7c478bd9Sstevel@tonic-gate       return 1;
5757*7c478bd9Sstevel@tonic-gate     gl_queue_redisplay(gl);
5758*7c478bd9Sstevel@tonic-gate   };
5759*7c478bd9Sstevel@tonic-gate   return 0;
5760*7c478bd9Sstevel@tonic-gate }
5761*7c478bd9Sstevel@tonic-gate #endif
5762*7c478bd9Sstevel@tonic-gate 
5763*7c478bd9Sstevel@tonic-gate /*.......................................................................
5764*7c478bd9Sstevel@tonic-gate  * Return non-zero if a character should be considered a part of a word.
5765*7c478bd9Sstevel@tonic-gate  *
5766*7c478bd9Sstevel@tonic-gate  * Input:
5767*7c478bd9Sstevel@tonic-gate  *  c       int  The character to be tested.
5768*7c478bd9Sstevel@tonic-gate  * Output:
5769*7c478bd9Sstevel@tonic-gate  *  return  int  True if the character should be considered part of a word.
5770*7c478bd9Sstevel@tonic-gate  */
5771*7c478bd9Sstevel@tonic-gate static int gl_is_word_char(int c)
5772*7c478bd9Sstevel@tonic-gate {
5773*7c478bd9Sstevel@tonic-gate   return isalnum((int)(unsigned char)c) || strchr(GL_WORD_CHARS, c) != NULL;
5774*7c478bd9Sstevel@tonic-gate }
5775*7c478bd9Sstevel@tonic-gate 
5776*7c478bd9Sstevel@tonic-gate /*.......................................................................
5777*7c478bd9Sstevel@tonic-gate  * Override the builtin file-completion callback that is bound to the
5778*7c478bd9Sstevel@tonic-gate  * "complete_word" action function.
5779*7c478bd9Sstevel@tonic-gate  *
5780*7c478bd9Sstevel@tonic-gate  * Input:
5781*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of the command-line input
5782*7c478bd9Sstevel@tonic-gate  *                           module.
5783*7c478bd9Sstevel@tonic-gate  *  data             void *  This is passed to match_fn() whenever it is
5784*7c478bd9Sstevel@tonic-gate  *                           called. It could, for example, point to a
5785*7c478bd9Sstevel@tonic-gate  *                           symbol table where match_fn() could look
5786*7c478bd9Sstevel@tonic-gate  *                           for possible completions.
5787*7c478bd9Sstevel@tonic-gate  *  match_fn   CplMatchFn *  The function that will identify the prefix
5788*7c478bd9Sstevel@tonic-gate  *                           to be completed from the input line, and
5789*7c478bd9Sstevel@tonic-gate  *                           report matching symbols.
5790*7c478bd9Sstevel@tonic-gate  * Output:
5791*7c478bd9Sstevel@tonic-gate  *  return            int    0 - OK.
5792*7c478bd9Sstevel@tonic-gate  *                           1 - Error.
5793*7c478bd9Sstevel@tonic-gate  */
5794*7c478bd9Sstevel@tonic-gate int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn)
5795*7c478bd9Sstevel@tonic-gate {
5796*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
5797*7c478bd9Sstevel@tonic-gate /*
5798*7c478bd9Sstevel@tonic-gate  * Check the arguments.
5799*7c478bd9Sstevel@tonic-gate  */
5800*7c478bd9Sstevel@tonic-gate   if(!gl || !match_fn) {
5801*7c478bd9Sstevel@tonic-gate     if(gl)
5802*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "NULL argument", END_ERR_MSG);
5803*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
5804*7c478bd9Sstevel@tonic-gate     return 1;
5805*7c478bd9Sstevel@tonic-gate   };
5806*7c478bd9Sstevel@tonic-gate /*
5807*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
5808*7c478bd9Sstevel@tonic-gate  */
5809*7c478bd9Sstevel@tonic-gate   gl_mask_signals(gl, &oldset);
5810*7c478bd9Sstevel@tonic-gate /*
5811*7c478bd9Sstevel@tonic-gate  * Record the new completion function and its callback data.
5812*7c478bd9Sstevel@tonic-gate  */
5813*7c478bd9Sstevel@tonic-gate   gl->cplfn.fn = match_fn;
5814*7c478bd9Sstevel@tonic-gate   gl->cplfn.data = data;
5815*7c478bd9Sstevel@tonic-gate /*
5816*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
5817*7c478bd9Sstevel@tonic-gate  */
5818*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
5819*7c478bd9Sstevel@tonic-gate   return 0;
5820*7c478bd9Sstevel@tonic-gate }
5821*7c478bd9Sstevel@tonic-gate 
5822*7c478bd9Sstevel@tonic-gate /*.......................................................................
5823*7c478bd9Sstevel@tonic-gate  * Change the terminal (or stream) that getline interacts with.
5824*7c478bd9Sstevel@tonic-gate  *
5825*7c478bd9Sstevel@tonic-gate  * Input:
5826*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of the command-line input
5827*7c478bd9Sstevel@tonic-gate  *                           module.
5828*7c478bd9Sstevel@tonic-gate  *  input_fp         FILE *  The stdio stream to read from.
5829*7c478bd9Sstevel@tonic-gate  *  output_fp        FILE *  The stdio stream to write to.
5830*7c478bd9Sstevel@tonic-gate  *  term             char *  The terminal type. This can be NULL if
5831*7c478bd9Sstevel@tonic-gate  *                           either or both of input_fp and output_fp don't
5832*7c478bd9Sstevel@tonic-gate  *                           refer to a terminal. Otherwise it should refer
5833*7c478bd9Sstevel@tonic-gate  *                           to an entry in the terminal information database.
5834*7c478bd9Sstevel@tonic-gate  * Output:
5835*7c478bd9Sstevel@tonic-gate  *  return            int    0 - OK.
5836*7c478bd9Sstevel@tonic-gate  *                           1 - Error.
5837*7c478bd9Sstevel@tonic-gate  */
5838*7c478bd9Sstevel@tonic-gate int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp,
5839*7c478bd9Sstevel@tonic-gate 		       const char *term)
5840*7c478bd9Sstevel@tonic-gate {
5841*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
5842*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_change_terminal() */
5843*7c478bd9Sstevel@tonic-gate /*
5844*7c478bd9Sstevel@tonic-gate  * Check the arguments.
5845*7c478bd9Sstevel@tonic-gate  */
5846*7c478bd9Sstevel@tonic-gate   if(!gl) {
5847*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
5848*7c478bd9Sstevel@tonic-gate     return 1;
5849*7c478bd9Sstevel@tonic-gate   };
5850*7c478bd9Sstevel@tonic-gate /*
5851*7c478bd9Sstevel@tonic-gate  * Block all signals.
5852*7c478bd9Sstevel@tonic-gate  */
5853*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
5854*7c478bd9Sstevel@tonic-gate     return 1;
5855*7c478bd9Sstevel@tonic-gate /*
5856*7c478bd9Sstevel@tonic-gate  * Execute the private body of the function while signals are blocked.
5857*7c478bd9Sstevel@tonic-gate  */
5858*7c478bd9Sstevel@tonic-gate   status = _gl_change_terminal(gl, input_fp, output_fp, term);
5859*7c478bd9Sstevel@tonic-gate /*
5860*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
5861*7c478bd9Sstevel@tonic-gate  */
5862*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
5863*7c478bd9Sstevel@tonic-gate   return status;
5864*7c478bd9Sstevel@tonic-gate }
5865*7c478bd9Sstevel@tonic-gate 
5866*7c478bd9Sstevel@tonic-gate /*.......................................................................
5867*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_change_terminal() function. It
5868*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
5869*7c478bd9Sstevel@tonic-gate  * delivery of signals.
5870*7c478bd9Sstevel@tonic-gate  */
5871*7c478bd9Sstevel@tonic-gate static int _gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp,
5872*7c478bd9Sstevel@tonic-gate 			       const char *term)
5873*7c478bd9Sstevel@tonic-gate {
5874*7c478bd9Sstevel@tonic-gate   int is_term = 0;   /* True if both input_fd and output_fd are associated */
5875*7c478bd9Sstevel@tonic-gate                      /*  with a terminal. */
5876*7c478bd9Sstevel@tonic-gate /*
5877*7c478bd9Sstevel@tonic-gate  * Require that input_fp and output_fp both be valid.
5878*7c478bd9Sstevel@tonic-gate  */
5879*7c478bd9Sstevel@tonic-gate   if(!input_fp || !output_fp) {
5880*7c478bd9Sstevel@tonic-gate     gl_print_info(gl, "Can't change terminal. Bad input/output stream(s).",
5881*7c478bd9Sstevel@tonic-gate 		  GL_END_INFO);
5882*7c478bd9Sstevel@tonic-gate     return 1;
5883*7c478bd9Sstevel@tonic-gate   };
5884*7c478bd9Sstevel@tonic-gate /*
5885*7c478bd9Sstevel@tonic-gate  * Are we displacing an existing terminal (as opposed to setting the
5886*7c478bd9Sstevel@tonic-gate  * initial terminal)?
5887*7c478bd9Sstevel@tonic-gate  */
5888*7c478bd9Sstevel@tonic-gate   if(gl->input_fd >= 0) {
5889*7c478bd9Sstevel@tonic-gate /*
5890*7c478bd9Sstevel@tonic-gate  * Make sure to leave the previous terminal in a usable state.
5891*7c478bd9Sstevel@tonic-gate  */
5892*7c478bd9Sstevel@tonic-gate     if(_gl_normal_io(gl))
5893*7c478bd9Sstevel@tonic-gate       return 1;
5894*7c478bd9Sstevel@tonic-gate /*
5895*7c478bd9Sstevel@tonic-gate  * Remove the displaced terminal from the list of fds to watch.
5896*7c478bd9Sstevel@tonic-gate  */
5897*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
5898*7c478bd9Sstevel@tonic-gate     FD_CLR(gl->input_fd, &gl->rfds);
5899*7c478bd9Sstevel@tonic-gate #endif
5900*7c478bd9Sstevel@tonic-gate   };
5901*7c478bd9Sstevel@tonic-gate /*
5902*7c478bd9Sstevel@tonic-gate  * Record the file descriptors and streams.
5903*7c478bd9Sstevel@tonic-gate  */
5904*7c478bd9Sstevel@tonic-gate   gl->input_fp = input_fp;
5905*7c478bd9Sstevel@tonic-gate   gl->input_fd = fileno(input_fp);
5906*7c478bd9Sstevel@tonic-gate   gl->output_fp = output_fp;
5907*7c478bd9Sstevel@tonic-gate   gl->output_fd = fileno(output_fp);
5908*7c478bd9Sstevel@tonic-gate /*
5909*7c478bd9Sstevel@tonic-gate  * If needed, expand the record of the maximum file-descriptor that might
5910*7c478bd9Sstevel@tonic-gate  * need to be monitored with select().
5911*7c478bd9Sstevel@tonic-gate  */
5912*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
5913*7c478bd9Sstevel@tonic-gate   if(gl->input_fd > gl->max_fd)
5914*7c478bd9Sstevel@tonic-gate     gl->max_fd = gl->input_fd;
5915*7c478bd9Sstevel@tonic-gate #endif
5916*7c478bd9Sstevel@tonic-gate /*
5917*7c478bd9Sstevel@tonic-gate  * Disable terminal interaction until we have enough info to interact
5918*7c478bd9Sstevel@tonic-gate  * with the terminal.
5919*7c478bd9Sstevel@tonic-gate  */
5920*7c478bd9Sstevel@tonic-gate   gl->is_term = 0;
5921*7c478bd9Sstevel@tonic-gate /*
5922*7c478bd9Sstevel@tonic-gate  * For terminal editing, we need both output_fd and input_fd to refer to
5923*7c478bd9Sstevel@tonic-gate  * a terminal. While we can't verify that they both point to the same
5924*7c478bd9Sstevel@tonic-gate  * terminal, we can verify that they point to terminals.
5925*7c478bd9Sstevel@tonic-gate  */
5926*7c478bd9Sstevel@tonic-gate   is_term = isatty(gl->input_fd) && isatty(gl->output_fd);
5927*7c478bd9Sstevel@tonic-gate /*
5928*7c478bd9Sstevel@tonic-gate  * If we are interacting with a terminal and no terminal type has been
5929*7c478bd9Sstevel@tonic-gate  * specified, treat it as a generic ANSI terminal.
5930*7c478bd9Sstevel@tonic-gate  */
5931*7c478bd9Sstevel@tonic-gate   if(is_term && !term)
5932*7c478bd9Sstevel@tonic-gate     term = "ansi";
5933*7c478bd9Sstevel@tonic-gate /*
5934*7c478bd9Sstevel@tonic-gate  * Make a copy of the terminal type string.
5935*7c478bd9Sstevel@tonic-gate  */
5936*7c478bd9Sstevel@tonic-gate   if(term != gl->term) {
5937*7c478bd9Sstevel@tonic-gate /*
5938*7c478bd9Sstevel@tonic-gate  * Delete any old terminal type string.
5939*7c478bd9Sstevel@tonic-gate  */
5940*7c478bd9Sstevel@tonic-gate     if(gl->term) {
5941*7c478bd9Sstevel@tonic-gate       free(gl->term);
5942*7c478bd9Sstevel@tonic-gate       gl->term = NULL;
5943*7c478bd9Sstevel@tonic-gate     };
5944*7c478bd9Sstevel@tonic-gate /*
5945*7c478bd9Sstevel@tonic-gate  * Make a copy of the new terminal-type string, if any.
5946*7c478bd9Sstevel@tonic-gate  */
5947*7c478bd9Sstevel@tonic-gate     if(term) {
5948*7c478bd9Sstevel@tonic-gate       size_t termsz = strlen(term)+1;
5949*7c478bd9Sstevel@tonic-gate 
5950*7c478bd9Sstevel@tonic-gate       gl->term = (char *) malloc(termsz);
5951*7c478bd9Sstevel@tonic-gate       if(gl->term)
5952*7c478bd9Sstevel@tonic-gate 	strlcpy(gl->term, term, termsz);
5953*7c478bd9Sstevel@tonic-gate     };
5954*7c478bd9Sstevel@tonic-gate   };
5955*7c478bd9Sstevel@tonic-gate /*
5956*7c478bd9Sstevel@tonic-gate  * Clear any terminal-specific key bindings that were taken from the
5957*7c478bd9Sstevel@tonic-gate  * settings of the last terminal.
5958*7c478bd9Sstevel@tonic-gate  */
5959*7c478bd9Sstevel@tonic-gate   _kt_clear_bindings(gl->bindings, KTB_TERM);
5960*7c478bd9Sstevel@tonic-gate /*
5961*7c478bd9Sstevel@tonic-gate  * If we have a terminal install new bindings for it.
5962*7c478bd9Sstevel@tonic-gate  */
5963*7c478bd9Sstevel@tonic-gate   if(is_term) {
5964*7c478bd9Sstevel@tonic-gate /*
5965*7c478bd9Sstevel@tonic-gate  * Get the current settings of the terminal.
5966*7c478bd9Sstevel@tonic-gate  */
5967*7c478bd9Sstevel@tonic-gate     if(tcgetattr(gl->input_fd, &gl->oldattr)) {
5968*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG);
5969*7c478bd9Sstevel@tonic-gate       return 1;
5970*7c478bd9Sstevel@tonic-gate     };
5971*7c478bd9Sstevel@tonic-gate /*
5972*7c478bd9Sstevel@tonic-gate  * If we don't set this now, gl_control_strings() won't know
5973*7c478bd9Sstevel@tonic-gate  * that it is talking to a terminal.
5974*7c478bd9Sstevel@tonic-gate  */
5975*7c478bd9Sstevel@tonic-gate     gl->is_term = 1;
5976*7c478bd9Sstevel@tonic-gate /*
5977*7c478bd9Sstevel@tonic-gate  * Lookup the terminal control string and size information.
5978*7c478bd9Sstevel@tonic-gate  */
5979*7c478bd9Sstevel@tonic-gate     if(gl_control_strings(gl, term)) {
5980*7c478bd9Sstevel@tonic-gate       gl->is_term = 0;
5981*7c478bd9Sstevel@tonic-gate       return 1;
5982*7c478bd9Sstevel@tonic-gate     };
5983*7c478bd9Sstevel@tonic-gate /*
5984*7c478bd9Sstevel@tonic-gate  * Bind terminal-specific keys.
5985*7c478bd9Sstevel@tonic-gate  */
5986*7c478bd9Sstevel@tonic-gate     if(gl_bind_terminal_keys(gl))
5987*7c478bd9Sstevel@tonic-gate       return 1;
5988*7c478bd9Sstevel@tonic-gate   };
5989*7c478bd9Sstevel@tonic-gate /*
5990*7c478bd9Sstevel@tonic-gate  * Assume that the caller has given us a terminal in a sane state.
5991*7c478bd9Sstevel@tonic-gate  */
5992*7c478bd9Sstevel@tonic-gate   gl->io_mode = GL_NORMAL_MODE;
5993*7c478bd9Sstevel@tonic-gate /*
5994*7c478bd9Sstevel@tonic-gate  * Switch into the currently configured I/O mode.
5995*7c478bd9Sstevel@tonic-gate  */
5996*7c478bd9Sstevel@tonic-gate   if(_gl_io_mode(gl, gl->io_mode))
5997*7c478bd9Sstevel@tonic-gate     return 1;
5998*7c478bd9Sstevel@tonic-gate   return 0;
5999*7c478bd9Sstevel@tonic-gate }
6000*7c478bd9Sstevel@tonic-gate 
6001*7c478bd9Sstevel@tonic-gate /*.......................................................................
6002*7c478bd9Sstevel@tonic-gate  * Set up terminal-specific key bindings.
6003*7c478bd9Sstevel@tonic-gate  *
6004*7c478bd9Sstevel@tonic-gate  * Input:
6005*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of the command-line input
6006*7c478bd9Sstevel@tonic-gate  *                           module.
6007*7c478bd9Sstevel@tonic-gate  * Output:
6008*7c478bd9Sstevel@tonic-gate  *  return            int    0 - OK.
6009*7c478bd9Sstevel@tonic-gate  *                           1 - Error.
6010*7c478bd9Sstevel@tonic-gate  */
6011*7c478bd9Sstevel@tonic-gate static int gl_bind_terminal_keys(GetLine *gl)
6012*7c478bd9Sstevel@tonic-gate {
6013*7c478bd9Sstevel@tonic-gate /*
6014*7c478bd9Sstevel@tonic-gate  * Install key-bindings for the special terminal characters.
6015*7c478bd9Sstevel@tonic-gate  */
6016*7c478bd9Sstevel@tonic-gate   if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VINTR],
6017*7c478bd9Sstevel@tonic-gate 			  "user-interrupt") ||
6018*7c478bd9Sstevel@tonic-gate      gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VQUIT], "abort") ||
6019*7c478bd9Sstevel@tonic-gate      gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VSUSP], "suspend"))
6020*7c478bd9Sstevel@tonic-gate     return 1;
6021*7c478bd9Sstevel@tonic-gate /*
6022*7c478bd9Sstevel@tonic-gate  * In vi-mode, arrange for the above characters to be seen in command
6023*7c478bd9Sstevel@tonic-gate  * mode.
6024*7c478bd9Sstevel@tonic-gate  */
6025*7c478bd9Sstevel@tonic-gate   if(gl->editor == GL_VI_MODE) {
6026*7c478bd9Sstevel@tonic-gate     if(gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VINTR]),
6027*7c478bd9Sstevel@tonic-gate 			    "user-interrupt") ||
6028*7c478bd9Sstevel@tonic-gate        gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VQUIT]),
6029*7c478bd9Sstevel@tonic-gate 			    "abort") ||
6030*7c478bd9Sstevel@tonic-gate        gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VSUSP]),
6031*7c478bd9Sstevel@tonic-gate 			    "suspend"))
6032*7c478bd9Sstevel@tonic-gate       return 1;
6033*7c478bd9Sstevel@tonic-gate   };
6034*7c478bd9Sstevel@tonic-gate /*
6035*7c478bd9Sstevel@tonic-gate  * Non-universal special keys.
6036*7c478bd9Sstevel@tonic-gate  */
6037*7c478bd9Sstevel@tonic-gate #ifdef VLNEXT
6038*7c478bd9Sstevel@tonic-gate   if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VLNEXT],
6039*7c478bd9Sstevel@tonic-gate 			  "literal-next"))
6040*7c478bd9Sstevel@tonic-gate     return 1;
6041*7c478bd9Sstevel@tonic-gate #else
6042*7c478bd9Sstevel@tonic-gate   if(_kt_set_keybinding(gl->bindings, KTB_TERM, "^V", "literal-next")) {
6043*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG);
6044*7c478bd9Sstevel@tonic-gate     return 1;
6045*7c478bd9Sstevel@tonic-gate   };
6046*7c478bd9Sstevel@tonic-gate #endif
6047*7c478bd9Sstevel@tonic-gate /*
6048*7c478bd9Sstevel@tonic-gate  * Bind action functions to the terminal-specific arrow keys
6049*7c478bd9Sstevel@tonic-gate  * looked up by gl_control_strings().
6050*7c478bd9Sstevel@tonic-gate  */
6051*7c478bd9Sstevel@tonic-gate   if(_gl_bind_arrow_keys(gl))
6052*7c478bd9Sstevel@tonic-gate     return 1;
6053*7c478bd9Sstevel@tonic-gate   return 0;
6054*7c478bd9Sstevel@tonic-gate }
6055*7c478bd9Sstevel@tonic-gate 
6056*7c478bd9Sstevel@tonic-gate /*.......................................................................
6057*7c478bd9Sstevel@tonic-gate  * This function is normally bound to control-D. When it is invoked within
6058*7c478bd9Sstevel@tonic-gate  * a line it deletes the character which follows the cursor. When invoked
6059*7c478bd9Sstevel@tonic-gate  * at the end of the line it lists possible file completions, and when
6060*7c478bd9Sstevel@tonic-gate  * invoked on an empty line it causes gl_get_line() to return EOF. This
6061*7c478bd9Sstevel@tonic-gate  * function emulates the one that is normally bound to control-D by tcsh.
6062*7c478bd9Sstevel@tonic-gate  */
6063*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_del_char_or_list_or_eof)
6064*7c478bd9Sstevel@tonic-gate {
6065*7c478bd9Sstevel@tonic-gate /*
6066*7c478bd9Sstevel@tonic-gate  * If we have an empty line arrange to return EOF.
6067*7c478bd9Sstevel@tonic-gate  */
6068*7c478bd9Sstevel@tonic-gate   if(gl->ntotal < 1) {
6069*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_EOF, 0);
6070*7c478bd9Sstevel@tonic-gate     return 1;
6071*7c478bd9Sstevel@tonic-gate /*
6072*7c478bd9Sstevel@tonic-gate  * If we are at the end of the line list possible completions.
6073*7c478bd9Sstevel@tonic-gate  */
6074*7c478bd9Sstevel@tonic-gate   } else if(gl->buff_curpos >= gl->ntotal) {
6075*7c478bd9Sstevel@tonic-gate     return gl_list_completions(gl, 1, NULL);
6076*7c478bd9Sstevel@tonic-gate /*
6077*7c478bd9Sstevel@tonic-gate  * Within the line delete the character that follows the cursor.
6078*7c478bd9Sstevel@tonic-gate  */
6079*7c478bd9Sstevel@tonic-gate   } else {
6080*7c478bd9Sstevel@tonic-gate /*
6081*7c478bd9Sstevel@tonic-gate  * If in vi command mode, first preserve the current line for potential use
6082*7c478bd9Sstevel@tonic-gate  * by vi-undo.
6083*7c478bd9Sstevel@tonic-gate  */
6084*7c478bd9Sstevel@tonic-gate     gl_save_for_undo(gl);
6085*7c478bd9Sstevel@tonic-gate /*
6086*7c478bd9Sstevel@tonic-gate  * Delete 'count' characters.
6087*7c478bd9Sstevel@tonic-gate  */
6088*7c478bd9Sstevel@tonic-gate     return gl_forward_delete_char(gl, count, NULL);
6089*7c478bd9Sstevel@tonic-gate   };
6090*7c478bd9Sstevel@tonic-gate }
6091*7c478bd9Sstevel@tonic-gate 
6092*7c478bd9Sstevel@tonic-gate /*.......................................................................
6093*7c478bd9Sstevel@tonic-gate  * This function is normally bound to control-D in vi mode. When it is
6094*7c478bd9Sstevel@tonic-gate  * invoked within a line it lists possible file completions, and when
6095*7c478bd9Sstevel@tonic-gate  * invoked on an empty line it causes gl_get_line() to return EOF. This
6096*7c478bd9Sstevel@tonic-gate  * function emulates the one that is normally bound to control-D by tcsh.
6097*7c478bd9Sstevel@tonic-gate  */
6098*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_list_or_eof)
6099*7c478bd9Sstevel@tonic-gate {
6100*7c478bd9Sstevel@tonic-gate /*
6101*7c478bd9Sstevel@tonic-gate  * If we have an empty line arrange to return EOF.
6102*7c478bd9Sstevel@tonic-gate  */
6103*7c478bd9Sstevel@tonic-gate   if(gl->ntotal < 1) {
6104*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_EOF, 0);
6105*7c478bd9Sstevel@tonic-gate     return 1;
6106*7c478bd9Sstevel@tonic-gate /*
6107*7c478bd9Sstevel@tonic-gate  * Otherwise list possible completions.
6108*7c478bd9Sstevel@tonic-gate  */
6109*7c478bd9Sstevel@tonic-gate   } else {
6110*7c478bd9Sstevel@tonic-gate     return gl_list_completions(gl, 1, NULL);
6111*7c478bd9Sstevel@tonic-gate   };
6112*7c478bd9Sstevel@tonic-gate }
6113*7c478bd9Sstevel@tonic-gate 
6114*7c478bd9Sstevel@tonic-gate /*.......................................................................
6115*7c478bd9Sstevel@tonic-gate  * List possible completions of the word that precedes the cursor. The
6116*7c478bd9Sstevel@tonic-gate  * callback data argument must either be NULL to select the default
6117*7c478bd9Sstevel@tonic-gate  * file completion callback, or be a GlCplCallback object containing the
6118*7c478bd9Sstevel@tonic-gate  * completion callback function to call.
6119*7c478bd9Sstevel@tonic-gate  */
6120*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_list_completions)
6121*7c478bd9Sstevel@tonic-gate {
6122*7c478bd9Sstevel@tonic-gate   int waserr = 0;   /* True after errors */
6123*7c478bd9Sstevel@tonic-gate /*
6124*7c478bd9Sstevel@tonic-gate  * Get the container of the completion callback and its callback data.
6125*7c478bd9Sstevel@tonic-gate  */
6126*7c478bd9Sstevel@tonic-gate   GlCplCallback *cb = data ? (GlCplCallback *) data : &gl->cplfn;
6127*7c478bd9Sstevel@tonic-gate /*
6128*7c478bd9Sstevel@tonic-gate  * Get the list of possible completions.
6129*7c478bd9Sstevel@tonic-gate  */
6130*7c478bd9Sstevel@tonic-gate   CplMatches *matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos,
6131*7c478bd9Sstevel@tonic-gate 					  cb->data, cb->fn);
6132*7c478bd9Sstevel@tonic-gate /*
6133*7c478bd9Sstevel@tonic-gate  * No matching completions?
6134*7c478bd9Sstevel@tonic-gate  */
6135*7c478bd9Sstevel@tonic-gate   if(!matches) {
6136*7c478bd9Sstevel@tonic-gate     waserr = gl_print_info(gl, cpl_last_error(gl->cpl), GL_END_INFO);
6137*7c478bd9Sstevel@tonic-gate /*
6138*7c478bd9Sstevel@tonic-gate  * List the matches.
6139*7c478bd9Sstevel@tonic-gate  */
6140*7c478bd9Sstevel@tonic-gate   } else if(matches->nmatch > 0 && gl->echo) {
6141*7c478bd9Sstevel@tonic-gate     if(_gl_normal_io(gl) ||
6142*7c478bd9Sstevel@tonic-gate        _cpl_output_completions(matches, gl_write_fn, gl, gl->ncolumn))
6143*7c478bd9Sstevel@tonic-gate       waserr = 1;
6144*7c478bd9Sstevel@tonic-gate   };
6145*7c478bd9Sstevel@tonic-gate /*
6146*7c478bd9Sstevel@tonic-gate  * If any output had to be written to the terminal, then editing will
6147*7c478bd9Sstevel@tonic-gate  * have been suspended, make sure that we are back in raw line editing
6148*7c478bd9Sstevel@tonic-gate  * mode before returning.
6149*7c478bd9Sstevel@tonic-gate  */
6150*7c478bd9Sstevel@tonic-gate   if(_gl_raw_io(gl, 1))
6151*7c478bd9Sstevel@tonic-gate     waserr = 1;
6152*7c478bd9Sstevel@tonic-gate   return waserr;
6153*7c478bd9Sstevel@tonic-gate }
6154*7c478bd9Sstevel@tonic-gate 
6155*7c478bd9Sstevel@tonic-gate /*.......................................................................
6156*7c478bd9Sstevel@tonic-gate  * Where the user has used the symbolic arrow-key names to specify
6157*7c478bd9Sstevel@tonic-gate  * arrow key bindings, bind the specified action functions to the default
6158*7c478bd9Sstevel@tonic-gate  * and terminal specific arrow key sequences.
6159*7c478bd9Sstevel@tonic-gate  *
6160*7c478bd9Sstevel@tonic-gate  * Input:
6161*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *   The getline resource object.
6162*7c478bd9Sstevel@tonic-gate  * Output:
6163*7c478bd9Sstevel@tonic-gate  *  return     int     0 - OK.
6164*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
6165*7c478bd9Sstevel@tonic-gate  */
6166*7c478bd9Sstevel@tonic-gate static int _gl_bind_arrow_keys(GetLine *gl)
6167*7c478bd9Sstevel@tonic-gate {
6168*7c478bd9Sstevel@tonic-gate /*
6169*7c478bd9Sstevel@tonic-gate  * Process each of the arrow keys.
6170*7c478bd9Sstevel@tonic-gate  */
6171*7c478bd9Sstevel@tonic-gate   if(_gl_rebind_arrow_key(gl, "up", gl->u_arrow, "^[[A", "^[OA") ||
6172*7c478bd9Sstevel@tonic-gate      _gl_rebind_arrow_key(gl, "down", gl->d_arrow, "^[[B", "^[OB") ||
6173*7c478bd9Sstevel@tonic-gate      _gl_rebind_arrow_key(gl, "left", gl->l_arrow, "^[[D", "^[OD") ||
6174*7c478bd9Sstevel@tonic-gate      _gl_rebind_arrow_key(gl, "right", gl->r_arrow, "^[[C", "^[OC"))
6175*7c478bd9Sstevel@tonic-gate     return 1;
6176*7c478bd9Sstevel@tonic-gate   return 0;
6177*7c478bd9Sstevel@tonic-gate }
6178*7c478bd9Sstevel@tonic-gate 
6179*7c478bd9Sstevel@tonic-gate /*.......................................................................
6180*7c478bd9Sstevel@tonic-gate  * Lookup the action function of a symbolic arrow-key binding, and bind
6181*7c478bd9Sstevel@tonic-gate  * it to the terminal-specific and default arrow-key sequences. Note that
6182*7c478bd9Sstevel@tonic-gate  * we don't trust the terminal-specified key sequences to be correct.
6183*7c478bd9Sstevel@tonic-gate  * The main reason for this is that on some machines the xterm terminfo
6184*7c478bd9Sstevel@tonic-gate  * entry is for hardware X-terminals, rather than xterm terminal emulators
6185*7c478bd9Sstevel@tonic-gate  * and the two terminal types emit different character sequences when the
6186*7c478bd9Sstevel@tonic-gate  * their cursor keys are pressed. As a result we also supply a couple
6187*7c478bd9Sstevel@tonic-gate  * of default key sequences.
6188*7c478bd9Sstevel@tonic-gate  *
6189*7c478bd9Sstevel@tonic-gate  * Input:
6190*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *   The resource object of gl_get_line().
6191*7c478bd9Sstevel@tonic-gate  *  name           char *   The symbolic name of the arrow key.
6192*7c478bd9Sstevel@tonic-gate  *  term_seq       char *   The terminal-specific arrow-key sequence.
6193*7c478bd9Sstevel@tonic-gate  *  def_seq1       char *   The first default arrow-key sequence.
6194*7c478bd9Sstevel@tonic-gate  *  def_seq2       char *   The second arrow-key sequence.
6195*7c478bd9Sstevel@tonic-gate  * Output:
6196*7c478bd9Sstevel@tonic-gate  *  return          int     0 - OK.
6197*7c478bd9Sstevel@tonic-gate  *                          1 - Error.
6198*7c478bd9Sstevel@tonic-gate  */
6199*7c478bd9Sstevel@tonic-gate static int _gl_rebind_arrow_key(GetLine *gl, const char *name,
6200*7c478bd9Sstevel@tonic-gate 				const char *term_seq, const char *def_seq1,
6201*7c478bd9Sstevel@tonic-gate 				const char *def_seq2)
6202*7c478bd9Sstevel@tonic-gate {
6203*7c478bd9Sstevel@tonic-gate   KeySym *keysym;  /* The binding-table entry matching the arrow-key name */
6204*7c478bd9Sstevel@tonic-gate   int nsym;        /* The number of ambiguous matches */
6205*7c478bd9Sstevel@tonic-gate /*
6206*7c478bd9Sstevel@tonic-gate  * Lookup the key binding for the symbolic name of the arrow key. This
6207*7c478bd9Sstevel@tonic-gate  * will either be the default action, or a user provided one.
6208*7c478bd9Sstevel@tonic-gate  */
6209*7c478bd9Sstevel@tonic-gate   if(_kt_lookup_keybinding(gl->bindings, name, strlen(name), &keysym, &nsym)
6210*7c478bd9Sstevel@tonic-gate      == KT_EXACT_MATCH) {
6211*7c478bd9Sstevel@tonic-gate /*
6212*7c478bd9Sstevel@tonic-gate  * Get the action function.
6213*7c478bd9Sstevel@tonic-gate  */
6214*7c478bd9Sstevel@tonic-gate     KtAction *action = keysym->actions + keysym->binder;
6215*7c478bd9Sstevel@tonic-gate     KtKeyFn *fn = action->fn;
6216*7c478bd9Sstevel@tonic-gate     void *data = action->data;
6217*7c478bd9Sstevel@tonic-gate /*
6218*7c478bd9Sstevel@tonic-gate  * Bind this to each of the specified key sequences.
6219*7c478bd9Sstevel@tonic-gate  */
6220*7c478bd9Sstevel@tonic-gate     if((term_seq &&
6221*7c478bd9Sstevel@tonic-gate 	_kt_set_keyfn(gl->bindings, KTB_TERM, term_seq, fn, data)) ||
6222*7c478bd9Sstevel@tonic-gate        (def_seq1 &&
6223*7c478bd9Sstevel@tonic-gate 	_kt_set_keyfn(gl->bindings, KTB_NORM, def_seq1, fn, data)) ||
6224*7c478bd9Sstevel@tonic-gate        (def_seq2 &&
6225*7c478bd9Sstevel@tonic-gate 	_kt_set_keyfn(gl->bindings, KTB_NORM, def_seq2, fn, data))) {
6226*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG);
6227*7c478bd9Sstevel@tonic-gate       return 1;
6228*7c478bd9Sstevel@tonic-gate     };
6229*7c478bd9Sstevel@tonic-gate   };
6230*7c478bd9Sstevel@tonic-gate   return 0;
6231*7c478bd9Sstevel@tonic-gate }
6232*7c478bd9Sstevel@tonic-gate 
6233*7c478bd9Sstevel@tonic-gate /*.......................................................................
6234*7c478bd9Sstevel@tonic-gate  * Read getline configuration information from a given file.
6235*7c478bd9Sstevel@tonic-gate  *
6236*7c478bd9Sstevel@tonic-gate  * Input:
6237*7c478bd9Sstevel@tonic-gate  *  gl           GetLine *  The getline resource object.
6238*7c478bd9Sstevel@tonic-gate  *  filename  const char *  The name of the file to read configuration
6239*7c478bd9Sstevel@tonic-gate  *                          information from. The contents of this file
6240*7c478bd9Sstevel@tonic-gate  *                          are as described in the gl_get_line(3) man
6241*7c478bd9Sstevel@tonic-gate  *                          page for the default ~/.teclarc configuration
6242*7c478bd9Sstevel@tonic-gate  *                          file.
6243*7c478bd9Sstevel@tonic-gate  *  who         KtBinder    Who bindings are to be installed for.
6244*7c478bd9Sstevel@tonic-gate  * Output:
6245*7c478bd9Sstevel@tonic-gate  *  return           int    0 - OK.
6246*7c478bd9Sstevel@tonic-gate  *                          1 - Irrecoverable error.
6247*7c478bd9Sstevel@tonic-gate  */
6248*7c478bd9Sstevel@tonic-gate static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who)
6249*7c478bd9Sstevel@tonic-gate {
6250*7c478bd9Sstevel@tonic-gate /*
6251*7c478bd9Sstevel@tonic-gate  * If filesystem access is to be excluded, configuration files can't
6252*7c478bd9Sstevel@tonic-gate  * be read.
6253*7c478bd9Sstevel@tonic-gate  */
6254*7c478bd9Sstevel@tonic-gate #ifdef WITHOUT_FILE_SYSTEM
6255*7c478bd9Sstevel@tonic-gate   _err_record_msg(gl->err,
6256*7c478bd9Sstevel@tonic-gate 		  "Can't read configuration files without filesystem access",
6257*7c478bd9Sstevel@tonic-gate 		  END_ERR_MSG);
6258*7c478bd9Sstevel@tonic-gate   errno = EINVAL;
6259*7c478bd9Sstevel@tonic-gate   return 1;
6260*7c478bd9Sstevel@tonic-gate #else
6261*7c478bd9Sstevel@tonic-gate   FileExpansion *expansion; /* The expansion of the filename */
6262*7c478bd9Sstevel@tonic-gate   FILE *fp;                 /* The opened file */
6263*7c478bd9Sstevel@tonic-gate   int waserr = 0;           /* True if an error occurred while reading */
6264*7c478bd9Sstevel@tonic-gate   int lineno = 1;           /* The line number being processed */
6265*7c478bd9Sstevel@tonic-gate /*
6266*7c478bd9Sstevel@tonic-gate  * Check the arguments.
6267*7c478bd9Sstevel@tonic-gate  */
6268*7c478bd9Sstevel@tonic-gate   if(!gl || !filename) {
6269*7c478bd9Sstevel@tonic-gate     if(gl)
6270*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG);
6271*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
6272*7c478bd9Sstevel@tonic-gate     return 1;
6273*7c478bd9Sstevel@tonic-gate   };
6274*7c478bd9Sstevel@tonic-gate /*
6275*7c478bd9Sstevel@tonic-gate  * Expand the filename.
6276*7c478bd9Sstevel@tonic-gate  */
6277*7c478bd9Sstevel@tonic-gate   expansion = ef_expand_file(gl->ef, filename, -1);
6278*7c478bd9Sstevel@tonic-gate   if(!expansion) {
6279*7c478bd9Sstevel@tonic-gate     gl_print_info(gl, "Unable to expand ", filename, " (",
6280*7c478bd9Sstevel@tonic-gate 		  ef_last_error(gl->ef), ").", GL_END_INFO);
6281*7c478bd9Sstevel@tonic-gate     return 1;
6282*7c478bd9Sstevel@tonic-gate   };
6283*7c478bd9Sstevel@tonic-gate /*
6284*7c478bd9Sstevel@tonic-gate  * Attempt to open the file.
6285*7c478bd9Sstevel@tonic-gate  */
6286*7c478bd9Sstevel@tonic-gate   fp = fopen(expansion->files[0], "r");
6287*7c478bd9Sstevel@tonic-gate /*
6288*7c478bd9Sstevel@tonic-gate  * It isn't an error for there to be no configuration file.
6289*7c478bd9Sstevel@tonic-gate  */
6290*7c478bd9Sstevel@tonic-gate   if(!fp)
6291*7c478bd9Sstevel@tonic-gate     return 0;
6292*7c478bd9Sstevel@tonic-gate /*
6293*7c478bd9Sstevel@tonic-gate  * Parse the contents of the file.
6294*7c478bd9Sstevel@tonic-gate  */
6295*7c478bd9Sstevel@tonic-gate   while(!waserr && !feof(fp))
6296*7c478bd9Sstevel@tonic-gate     waserr = _gl_parse_config_line(gl, fp, glc_file_getc, filename, who,
6297*7c478bd9Sstevel@tonic-gate 				   &lineno);
6298*7c478bd9Sstevel@tonic-gate /*
6299*7c478bd9Sstevel@tonic-gate  * Bind action functions to the terminal-specific arrow keys.
6300*7c478bd9Sstevel@tonic-gate  */
6301*7c478bd9Sstevel@tonic-gate   if(_gl_bind_arrow_keys(gl))
6302*7c478bd9Sstevel@tonic-gate     return 1;
6303*7c478bd9Sstevel@tonic-gate /*
6304*7c478bd9Sstevel@tonic-gate  * Clean up.
6305*7c478bd9Sstevel@tonic-gate  */
6306*7c478bd9Sstevel@tonic-gate   (void) fclose(fp);
6307*7c478bd9Sstevel@tonic-gate   return waserr;
6308*7c478bd9Sstevel@tonic-gate #endif
6309*7c478bd9Sstevel@tonic-gate }
6310*7c478bd9Sstevel@tonic-gate 
6311*7c478bd9Sstevel@tonic-gate /*.......................................................................
6312*7c478bd9Sstevel@tonic-gate  * Read GetLine configuration information from a string. The contents of
6313*7c478bd9Sstevel@tonic-gate  * the string are the same as those described in the gl_get_line(3)
6314*7c478bd9Sstevel@tonic-gate  * man page for the contents of the ~/.teclarc configuration file.
6315*7c478bd9Sstevel@tonic-gate  */
6316*7c478bd9Sstevel@tonic-gate static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who)
6317*7c478bd9Sstevel@tonic-gate {
6318*7c478bd9Sstevel@tonic-gate   const char *bptr;         /* A pointer into buffer[] */
6319*7c478bd9Sstevel@tonic-gate   int waserr = 0;           /* True if an error occurred while reading */
6320*7c478bd9Sstevel@tonic-gate   int lineno = 1;           /* The line number being processed */
6321*7c478bd9Sstevel@tonic-gate /*
6322*7c478bd9Sstevel@tonic-gate  * Check the arguments.
6323*7c478bd9Sstevel@tonic-gate  */
6324*7c478bd9Sstevel@tonic-gate   if(!gl || !buffer) {
6325*7c478bd9Sstevel@tonic-gate     if(gl)
6326*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG);
6327*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
6328*7c478bd9Sstevel@tonic-gate     return 1;
6329*7c478bd9Sstevel@tonic-gate   };
6330*7c478bd9Sstevel@tonic-gate /*
6331*7c478bd9Sstevel@tonic-gate  * Get a pointer to the start of the buffer.
6332*7c478bd9Sstevel@tonic-gate  */
6333*7c478bd9Sstevel@tonic-gate   bptr = buffer;
6334*7c478bd9Sstevel@tonic-gate /*
6335*7c478bd9Sstevel@tonic-gate  * Parse the contents of the buffer.
6336*7c478bd9Sstevel@tonic-gate  */
6337*7c478bd9Sstevel@tonic-gate   while(!waserr && *bptr)
6338*7c478bd9Sstevel@tonic-gate     waserr = _gl_parse_config_line(gl, &bptr, glc_buff_getc, "", who, &lineno);
6339*7c478bd9Sstevel@tonic-gate /*
6340*7c478bd9Sstevel@tonic-gate  * Bind action functions to the terminal-specific arrow keys.
6341*7c478bd9Sstevel@tonic-gate  */
6342*7c478bd9Sstevel@tonic-gate   if(_gl_bind_arrow_keys(gl))
6343*7c478bd9Sstevel@tonic-gate     return 1;
6344*7c478bd9Sstevel@tonic-gate   return waserr;
6345*7c478bd9Sstevel@tonic-gate }
6346*7c478bd9Sstevel@tonic-gate 
6347*7c478bd9Sstevel@tonic-gate /*.......................................................................
6348*7c478bd9Sstevel@tonic-gate  * Parse the next line of a getline configuration file.
6349*7c478bd9Sstevel@tonic-gate  *
6350*7c478bd9Sstevel@tonic-gate  * Input:
6351*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The getline resource object.
6352*7c478bd9Sstevel@tonic-gate  *  stream        void *  The pointer representing the stream to be read
6353*7c478bd9Sstevel@tonic-gate  *                        by getc_fn().
6354*7c478bd9Sstevel@tonic-gate  *  getc_fn  GlcGetcFn *  A callback function which when called with
6355*7c478bd9Sstevel@tonic-gate  *                       'stream' as its argument, returns the next
6356*7c478bd9Sstevel@tonic-gate  *                        unread character from the stream.
6357*7c478bd9Sstevel@tonic-gate  *  origin  const char *  The name of the entity being read (eg. a
6358*7c478bd9Sstevel@tonic-gate  *                        file name).
6359*7c478bd9Sstevel@tonic-gate  *  who       KtBinder    Who bindings are to be installed for.
6360*7c478bd9Sstevel@tonic-gate  * Input/Output:
6361*7c478bd9Sstevel@tonic-gate  *  lineno         int *  The line number being processed is to be
6362*7c478bd9Sstevel@tonic-gate  *                        maintained in *lineno.
6363*7c478bd9Sstevel@tonic-gate  * Output:
6364*7c478bd9Sstevel@tonic-gate  *  return         int    0 - OK.
6365*7c478bd9Sstevel@tonic-gate  *                        1 - Irrecoverable error.
6366*7c478bd9Sstevel@tonic-gate  */
6367*7c478bd9Sstevel@tonic-gate static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn,
6368*7c478bd9Sstevel@tonic-gate 				 const char *origin, KtBinder who, int *lineno)
6369*7c478bd9Sstevel@tonic-gate {
6370*7c478bd9Sstevel@tonic-gate   char buffer[GL_CONF_BUFLEN+1];  /* The input line buffer */
6371*7c478bd9Sstevel@tonic-gate   char *argv[GL_CONF_MAXARG];     /* The argument list */
6372*7c478bd9Sstevel@tonic-gate   int argc = 0;                   /* The number of arguments in argv[] */
6373*7c478bd9Sstevel@tonic-gate   int c;                          /* A character from the file */
6374*7c478bd9Sstevel@tonic-gate   int escaped = 0;                /* True if the next character is escaped */
6375*7c478bd9Sstevel@tonic-gate   int i;
6376*7c478bd9Sstevel@tonic-gate /*
6377*7c478bd9Sstevel@tonic-gate  * Skip spaces and tabs.
6378*7c478bd9Sstevel@tonic-gate  */
6379*7c478bd9Sstevel@tonic-gate   do c = getc_fn(stream); while(c==' ' || c=='\t');
6380*7c478bd9Sstevel@tonic-gate /*
6381*7c478bd9Sstevel@tonic-gate  * Comments extend to the end of the line.
6382*7c478bd9Sstevel@tonic-gate  */
6383*7c478bd9Sstevel@tonic-gate   if(c=='#')
6384*7c478bd9Sstevel@tonic-gate     do c = getc_fn(stream); while(c != '\n' && c != EOF);
6385*7c478bd9Sstevel@tonic-gate /*
6386*7c478bd9Sstevel@tonic-gate  * Ignore empty lines.
6387*7c478bd9Sstevel@tonic-gate  */
6388*7c478bd9Sstevel@tonic-gate   if(c=='\n' || c==EOF) {
6389*7c478bd9Sstevel@tonic-gate     (*lineno)++;
6390*7c478bd9Sstevel@tonic-gate     return 0;
6391*7c478bd9Sstevel@tonic-gate   };
6392*7c478bd9Sstevel@tonic-gate /*
6393*7c478bd9Sstevel@tonic-gate  * Record the buffer location of the start of the first argument.
6394*7c478bd9Sstevel@tonic-gate  */
6395*7c478bd9Sstevel@tonic-gate   argv[argc] = buffer;
6396*7c478bd9Sstevel@tonic-gate /*
6397*7c478bd9Sstevel@tonic-gate  * Read the rest of the line, stopping early if a comment is seen, or
6398*7c478bd9Sstevel@tonic-gate  * the buffer overflows, and replacing sequences of spaces with a
6399*7c478bd9Sstevel@tonic-gate  * '\0', and recording the thus terminated string as an argument.
6400*7c478bd9Sstevel@tonic-gate  */
6401*7c478bd9Sstevel@tonic-gate   i = 0;
6402*7c478bd9Sstevel@tonic-gate   while(i<GL_CONF_BUFLEN) {
6403*7c478bd9Sstevel@tonic-gate /*
6404*7c478bd9Sstevel@tonic-gate  * Did we hit the end of the latest argument?
6405*7c478bd9Sstevel@tonic-gate  */
6406*7c478bd9Sstevel@tonic-gate     if(c==EOF || (!escaped && (c==' ' || c=='\n' || c=='\t' || c=='#'))) {
6407*7c478bd9Sstevel@tonic-gate /*
6408*7c478bd9Sstevel@tonic-gate  * Terminate the argument.
6409*7c478bd9Sstevel@tonic-gate  */
6410*7c478bd9Sstevel@tonic-gate       buffer[i++] = '\0';
6411*7c478bd9Sstevel@tonic-gate       argc++;
6412*7c478bd9Sstevel@tonic-gate /*
6413*7c478bd9Sstevel@tonic-gate  * Skip spaces and tabs.
6414*7c478bd9Sstevel@tonic-gate  */
6415*7c478bd9Sstevel@tonic-gate       while(c==' ' || c=='\t')
6416*7c478bd9Sstevel@tonic-gate 	c = getc_fn(stream);
6417*7c478bd9Sstevel@tonic-gate /*
6418*7c478bd9Sstevel@tonic-gate  * If we hit the end of the line, or the start of a comment, exit the loop.
6419*7c478bd9Sstevel@tonic-gate  */
6420*7c478bd9Sstevel@tonic-gate       if(c==EOF || c=='\n' || c=='#')
6421*7c478bd9Sstevel@tonic-gate 	break;
6422*7c478bd9Sstevel@tonic-gate /*
6423*7c478bd9Sstevel@tonic-gate  * Start recording the next argument.
6424*7c478bd9Sstevel@tonic-gate  */
6425*7c478bd9Sstevel@tonic-gate       if(argc >= GL_CONF_MAXARG) {
6426*7c478bd9Sstevel@tonic-gate 	gl_report_config_error(gl, origin, *lineno, "Too many arguments.");
6427*7c478bd9Sstevel@tonic-gate 	do c = getc_fn(stream); while(c!='\n' && c!=EOF);  /* Skip past eol */
6428*7c478bd9Sstevel@tonic-gate 	return 0;
6429*7c478bd9Sstevel@tonic-gate       };
6430*7c478bd9Sstevel@tonic-gate       argv[argc] = buffer + i;
6431*7c478bd9Sstevel@tonic-gate /*
6432*7c478bd9Sstevel@tonic-gate  * The next character was preceded by spaces, so it isn't escaped.
6433*7c478bd9Sstevel@tonic-gate  */
6434*7c478bd9Sstevel@tonic-gate       escaped = 0;
6435*7c478bd9Sstevel@tonic-gate     } else {
6436*7c478bd9Sstevel@tonic-gate /*
6437*7c478bd9Sstevel@tonic-gate  * If we hit an unescaped backslash, this means that we should arrange
6438*7c478bd9Sstevel@tonic-gate  * to treat the next character like a simple alphabetical character.
6439*7c478bd9Sstevel@tonic-gate  */
6440*7c478bd9Sstevel@tonic-gate       if(c=='\\' && !escaped) {
6441*7c478bd9Sstevel@tonic-gate 	escaped = 1;
6442*7c478bd9Sstevel@tonic-gate /*
6443*7c478bd9Sstevel@tonic-gate  * Splice lines where the newline is escaped.
6444*7c478bd9Sstevel@tonic-gate  */
6445*7c478bd9Sstevel@tonic-gate       } else if(c=='\n' && escaped) {
6446*7c478bd9Sstevel@tonic-gate 	(*lineno)++;
6447*7c478bd9Sstevel@tonic-gate /*
6448*7c478bd9Sstevel@tonic-gate  * Record a normal character, preserving any preceding backslash.
6449*7c478bd9Sstevel@tonic-gate  */
6450*7c478bd9Sstevel@tonic-gate       } else {
6451*7c478bd9Sstevel@tonic-gate 	if(escaped)
6452*7c478bd9Sstevel@tonic-gate 	  buffer[i++] = '\\';
6453*7c478bd9Sstevel@tonic-gate 	if(i>=GL_CONF_BUFLEN)
6454*7c478bd9Sstevel@tonic-gate 	  break;
6455*7c478bd9Sstevel@tonic-gate 	escaped = 0;
6456*7c478bd9Sstevel@tonic-gate 	buffer[i++] = c;
6457*7c478bd9Sstevel@tonic-gate       };
6458*7c478bd9Sstevel@tonic-gate /*
6459*7c478bd9Sstevel@tonic-gate  * Get the next character.
6460*7c478bd9Sstevel@tonic-gate  */
6461*7c478bd9Sstevel@tonic-gate       c = getc_fn(stream);
6462*7c478bd9Sstevel@tonic-gate     };
6463*7c478bd9Sstevel@tonic-gate   };
6464*7c478bd9Sstevel@tonic-gate /*
6465*7c478bd9Sstevel@tonic-gate  * Did the buffer overflow?
6466*7c478bd9Sstevel@tonic-gate  */
6467*7c478bd9Sstevel@tonic-gate   if(i>=GL_CONF_BUFLEN) {
6468*7c478bd9Sstevel@tonic-gate     gl_report_config_error(gl, origin, *lineno, "Line too long.");
6469*7c478bd9Sstevel@tonic-gate     return 0;
6470*7c478bd9Sstevel@tonic-gate   };
6471*7c478bd9Sstevel@tonic-gate /*
6472*7c478bd9Sstevel@tonic-gate  * The first argument should be a command name.
6473*7c478bd9Sstevel@tonic-gate  */
6474*7c478bd9Sstevel@tonic-gate   if(strcmp(argv[0], "bind") == 0) {
6475*7c478bd9Sstevel@tonic-gate     const char *action = NULL; /* A NULL action removes a keybinding */
6476*7c478bd9Sstevel@tonic-gate     const char *keyseq = NULL;
6477*7c478bd9Sstevel@tonic-gate     switch(argc) {
6478*7c478bd9Sstevel@tonic-gate     case 3:
6479*7c478bd9Sstevel@tonic-gate       action = argv[2];
6480*7c478bd9Sstevel@tonic-gate     case 2:              /* Note the intentional fallthrough */
6481*7c478bd9Sstevel@tonic-gate       keyseq = argv[1];
6482*7c478bd9Sstevel@tonic-gate /*
6483*7c478bd9Sstevel@tonic-gate  * Attempt to record the new keybinding.
6484*7c478bd9Sstevel@tonic-gate  */
6485*7c478bd9Sstevel@tonic-gate       if(_kt_set_keybinding(gl->bindings, who, keyseq, action)) {
6486*7c478bd9Sstevel@tonic-gate 	gl_report_config_error(gl, origin, *lineno,
6487*7c478bd9Sstevel@tonic-gate 			       _kt_last_error(gl->bindings));
6488*7c478bd9Sstevel@tonic-gate       };
6489*7c478bd9Sstevel@tonic-gate       break;
6490*7c478bd9Sstevel@tonic-gate     default:
6491*7c478bd9Sstevel@tonic-gate       gl_report_config_error(gl, origin, *lineno, "Wrong number of arguments.");
6492*7c478bd9Sstevel@tonic-gate     };
6493*7c478bd9Sstevel@tonic-gate   } else if(strcmp(argv[0], "edit-mode") == 0) {
6494*7c478bd9Sstevel@tonic-gate     if(argc == 2 && strcmp(argv[1], "emacs") == 0) {
6495*7c478bd9Sstevel@tonic-gate       gl_change_editor(gl, GL_EMACS_MODE);
6496*7c478bd9Sstevel@tonic-gate     } else if(argc == 2 && strcmp(argv[1], "vi") == 0) {
6497*7c478bd9Sstevel@tonic-gate       gl_change_editor(gl, GL_VI_MODE);
6498*7c478bd9Sstevel@tonic-gate     } else if(argc == 2 && strcmp(argv[1], "none") == 0) {
6499*7c478bd9Sstevel@tonic-gate       gl_change_editor(gl, GL_NO_EDITOR);
6500*7c478bd9Sstevel@tonic-gate     } else {
6501*7c478bd9Sstevel@tonic-gate       gl_report_config_error(gl, origin, *lineno,
6502*7c478bd9Sstevel@tonic-gate 			     "The argument of editor should be vi or emacs.");
6503*7c478bd9Sstevel@tonic-gate     };
6504*7c478bd9Sstevel@tonic-gate   } else if(strcmp(argv[0], "nobeep") == 0) {
6505*7c478bd9Sstevel@tonic-gate     gl->silence_bell = 1;
6506*7c478bd9Sstevel@tonic-gate   } else {
6507*7c478bd9Sstevel@tonic-gate     gl_report_config_error(gl, origin, *lineno, "Unknown command name.");
6508*7c478bd9Sstevel@tonic-gate   };
6509*7c478bd9Sstevel@tonic-gate /*
6510*7c478bd9Sstevel@tonic-gate  * Skip any trailing comment.
6511*7c478bd9Sstevel@tonic-gate  */
6512*7c478bd9Sstevel@tonic-gate   while(c != '\n' && c != EOF)
6513*7c478bd9Sstevel@tonic-gate     c = getc_fn(stream);
6514*7c478bd9Sstevel@tonic-gate   (*lineno)++;
6515*7c478bd9Sstevel@tonic-gate   return 0;
6516*7c478bd9Sstevel@tonic-gate }
6517*7c478bd9Sstevel@tonic-gate 
6518*7c478bd9Sstevel@tonic-gate /*.......................................................................
6519*7c478bd9Sstevel@tonic-gate  * This is a private function of _gl_parse_config_line() which prints
6520*7c478bd9Sstevel@tonic-gate  * out an error message about the contents of the line, prefixed by the
6521*7c478bd9Sstevel@tonic-gate  * name of the origin of the line and its line number.
6522*7c478bd9Sstevel@tonic-gate  *
6523*7c478bd9Sstevel@tonic-gate  * Input:
6524*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
6525*7c478bd9Sstevel@tonic-gate  *  origin  const char *  The name of the entity being read (eg. a
6526*7c478bd9Sstevel@tonic-gate  *                        file name).
6527*7c478bd9Sstevel@tonic-gate  *  lineno         int    The line number at which the error occurred.
6528*7c478bd9Sstevel@tonic-gate  *  errmsg  const char *  The error message.
6529*7c478bd9Sstevel@tonic-gate  * Output:
6530*7c478bd9Sstevel@tonic-gate  *  return         int    0 - OK.
6531*7c478bd9Sstevel@tonic-gate  *                        1 - Error.
6532*7c478bd9Sstevel@tonic-gate  */
6533*7c478bd9Sstevel@tonic-gate static int gl_report_config_error(GetLine *gl, const char *origin, int lineno,
6534*7c478bd9Sstevel@tonic-gate 				  const char *errmsg)
6535*7c478bd9Sstevel@tonic-gate {
6536*7c478bd9Sstevel@tonic-gate   char lnum[20];   /* A buffer in which to render a single integer */
6537*7c478bd9Sstevel@tonic-gate /*
6538*7c478bd9Sstevel@tonic-gate  * Convert the line number into a string.
6539*7c478bd9Sstevel@tonic-gate  */
6540*7c478bd9Sstevel@tonic-gate   snprintf(lnum, sizeof(lnum), "%d", lineno);
6541*7c478bd9Sstevel@tonic-gate /*
6542*7c478bd9Sstevel@tonic-gate  * Have the string printed on the terminal.
6543*7c478bd9Sstevel@tonic-gate  */
6544*7c478bd9Sstevel@tonic-gate   return gl_print_info(gl, origin, ":", lnum, ": ", errmsg, GL_END_INFO);
6545*7c478bd9Sstevel@tonic-gate }
6546*7c478bd9Sstevel@tonic-gate 
6547*7c478bd9Sstevel@tonic-gate /*.......................................................................
6548*7c478bd9Sstevel@tonic-gate  * This is the _gl_parse_config_line() callback function which reads the
6549*7c478bd9Sstevel@tonic-gate  * next character from a configuration file.
6550*7c478bd9Sstevel@tonic-gate  */
6551*7c478bd9Sstevel@tonic-gate static GLC_GETC_FN(glc_file_getc)
6552*7c478bd9Sstevel@tonic-gate {
6553*7c478bd9Sstevel@tonic-gate   return fgetc((FILE *) stream);
6554*7c478bd9Sstevel@tonic-gate }
6555*7c478bd9Sstevel@tonic-gate 
6556*7c478bd9Sstevel@tonic-gate /*.......................................................................
6557*7c478bd9Sstevel@tonic-gate  * This is the _gl_parse_config_line() callback function which reads the
6558*7c478bd9Sstevel@tonic-gate  * next character from a buffer. Its stream argument is a pointer to a
6559*7c478bd9Sstevel@tonic-gate  * variable which is, in turn, a pointer into the buffer being read from.
6560*7c478bd9Sstevel@tonic-gate  */
6561*7c478bd9Sstevel@tonic-gate static GLC_GETC_FN(glc_buff_getc)
6562*7c478bd9Sstevel@tonic-gate {
6563*7c478bd9Sstevel@tonic-gate   const char **lptr = (char const **) stream;
6564*7c478bd9Sstevel@tonic-gate   return **lptr ? *(*lptr)++ : EOF;
6565*7c478bd9Sstevel@tonic-gate }
6566*7c478bd9Sstevel@tonic-gate 
6567*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
6568*7c478bd9Sstevel@tonic-gate /*.......................................................................
6569*7c478bd9Sstevel@tonic-gate  * When this action is triggered, it arranges to temporarily read command
6570*7c478bd9Sstevel@tonic-gate  * lines from the regular file whos name precedes the cursor.
6571*7c478bd9Sstevel@tonic-gate  * The current line is first discarded.
6572*7c478bd9Sstevel@tonic-gate  */
6573*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_read_from_file)
6574*7c478bd9Sstevel@tonic-gate {
6575*7c478bd9Sstevel@tonic-gate   char *start_path;       /* The pointer to the start of the pathname in */
6576*7c478bd9Sstevel@tonic-gate                           /*  gl->line[]. */
6577*7c478bd9Sstevel@tonic-gate   FileExpansion *result;  /* The results of the filename expansion */
6578*7c478bd9Sstevel@tonic-gate   int pathlen;            /* The length of the pathname being expanded */
6579*7c478bd9Sstevel@tonic-gate /*
6580*7c478bd9Sstevel@tonic-gate  * Locate the start of the filename that precedes the cursor position.
6581*7c478bd9Sstevel@tonic-gate  */
6582*7c478bd9Sstevel@tonic-gate   start_path = _pu_start_of_path(gl->line, gl->buff_curpos);
6583*7c478bd9Sstevel@tonic-gate   if(!start_path)
6584*7c478bd9Sstevel@tonic-gate     return 1;
6585*7c478bd9Sstevel@tonic-gate /*
6586*7c478bd9Sstevel@tonic-gate  * Get the length of the pathname string.
6587*7c478bd9Sstevel@tonic-gate  */
6588*7c478bd9Sstevel@tonic-gate   pathlen = gl->buff_curpos - (start_path - gl->line);
6589*7c478bd9Sstevel@tonic-gate /*
6590*7c478bd9Sstevel@tonic-gate  * Attempt to expand the pathname.
6591*7c478bd9Sstevel@tonic-gate  */
6592*7c478bd9Sstevel@tonic-gate   result = ef_expand_file(gl->ef, start_path, pathlen);
6593*7c478bd9Sstevel@tonic-gate /*
6594*7c478bd9Sstevel@tonic-gate  * If there was an error, report the error on a new line.
6595*7c478bd9Sstevel@tonic-gate  */
6596*7c478bd9Sstevel@tonic-gate   if(!result) {
6597*7c478bd9Sstevel@tonic-gate     return gl_print_info(gl, ef_last_error(gl->ef), GL_END_INFO);
6598*7c478bd9Sstevel@tonic-gate /*
6599*7c478bd9Sstevel@tonic-gate  * If no files matched, report this as well.
6600*7c478bd9Sstevel@tonic-gate  */
6601*7c478bd9Sstevel@tonic-gate   } else if(result->nfile == 0 || !result->exists) {
6602*7c478bd9Sstevel@tonic-gate     return gl_print_info(gl, "No files match.", GL_END_INFO);
6603*7c478bd9Sstevel@tonic-gate /*
6604*7c478bd9Sstevel@tonic-gate  * Complain if more than one file matches.
6605*7c478bd9Sstevel@tonic-gate  */
6606*7c478bd9Sstevel@tonic-gate   } else if(result->nfile > 1) {
6607*7c478bd9Sstevel@tonic-gate     return gl_print_info(gl, "More than one file matches.", GL_END_INFO);
6608*7c478bd9Sstevel@tonic-gate /*
6609*7c478bd9Sstevel@tonic-gate  * Disallow input from anything but normal files. In principle we could
6610*7c478bd9Sstevel@tonic-gate  * also support input from named pipes. Terminal files would be a problem
6611*7c478bd9Sstevel@tonic-gate  * since we wouldn't know the terminal type, and other types of files
6612*7c478bd9Sstevel@tonic-gate  * might cause the library to lock up.
6613*7c478bd9Sstevel@tonic-gate  */
6614*7c478bd9Sstevel@tonic-gate   } else if(!_pu_path_is_file(result->files[0])) {
6615*7c478bd9Sstevel@tonic-gate     return gl_print_info(gl, "Not a normal file.", GL_END_INFO);
6616*7c478bd9Sstevel@tonic-gate   } else {
6617*7c478bd9Sstevel@tonic-gate /*
6618*7c478bd9Sstevel@tonic-gate  * Attempt to open and install the specified file for reading.
6619*7c478bd9Sstevel@tonic-gate  */
6620*7c478bd9Sstevel@tonic-gate     gl->file_fp = fopen(result->files[0], "r");
6621*7c478bd9Sstevel@tonic-gate     if(!gl->file_fp) {
6622*7c478bd9Sstevel@tonic-gate       return gl_print_info(gl, "Unable to open: ", result->files[0],
6623*7c478bd9Sstevel@tonic-gate 			   GL_END_INFO);
6624*7c478bd9Sstevel@tonic-gate     };
6625*7c478bd9Sstevel@tonic-gate /*
6626*7c478bd9Sstevel@tonic-gate  * If needed, expand the record of the maximum file-descriptor that might
6627*7c478bd9Sstevel@tonic-gate  * need to be monitored with select().
6628*7c478bd9Sstevel@tonic-gate  */
6629*7c478bd9Sstevel@tonic-gate #ifdef HAVE_SELECT
6630*7c478bd9Sstevel@tonic-gate     if(fileno(gl->file_fp) > gl->max_fd)
6631*7c478bd9Sstevel@tonic-gate       gl->max_fd = fileno(gl->file_fp);
6632*7c478bd9Sstevel@tonic-gate #endif
6633*7c478bd9Sstevel@tonic-gate /*
6634*7c478bd9Sstevel@tonic-gate  * Is non-blocking I/O needed?
6635*7c478bd9Sstevel@tonic-gate  */
6636*7c478bd9Sstevel@tonic-gate     if(gl->raw_mode && gl->io_mode==GL_SERVER_MODE &&
6637*7c478bd9Sstevel@tonic-gate        gl_nonblocking_io(gl, fileno(gl->file_fp))) {
6638*7c478bd9Sstevel@tonic-gate       gl_revert_input(gl);
6639*7c478bd9Sstevel@tonic-gate       return gl_print_info(gl, "Can't read file %s with non-blocking I/O",
6640*7c478bd9Sstevel@tonic-gate 			   result->files[0]);
6641*7c478bd9Sstevel@tonic-gate     };
6642*7c478bd9Sstevel@tonic-gate /*
6643*7c478bd9Sstevel@tonic-gate  * Inform the user what is happening.
6644*7c478bd9Sstevel@tonic-gate  */
6645*7c478bd9Sstevel@tonic-gate     if(gl_print_info(gl, "<Taking input from ", result->files[0], ">",
6646*7c478bd9Sstevel@tonic-gate 		     GL_END_INFO))
6647*7c478bd9Sstevel@tonic-gate       return 1;
6648*7c478bd9Sstevel@tonic-gate   };
6649*7c478bd9Sstevel@tonic-gate   return 0;
6650*7c478bd9Sstevel@tonic-gate }
6651*7c478bd9Sstevel@tonic-gate #endif
6652*7c478bd9Sstevel@tonic-gate 
6653*7c478bd9Sstevel@tonic-gate /*.......................................................................
6654*7c478bd9Sstevel@tonic-gate  * Close any temporary file that is being used for input.
6655*7c478bd9Sstevel@tonic-gate  *
6656*7c478bd9Sstevel@tonic-gate  * Input:
6657*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  The getline resource object.
6658*7c478bd9Sstevel@tonic-gate  */
6659*7c478bd9Sstevel@tonic-gate static void gl_revert_input(GetLine *gl)
6660*7c478bd9Sstevel@tonic-gate {
6661*7c478bd9Sstevel@tonic-gate   if(gl->file_fp)
6662*7c478bd9Sstevel@tonic-gate     fclose(gl->file_fp);
6663*7c478bd9Sstevel@tonic-gate   gl->file_fp = NULL;
6664*7c478bd9Sstevel@tonic-gate   gl->endline = 1;
6665*7c478bd9Sstevel@tonic-gate }
6666*7c478bd9Sstevel@tonic-gate 
6667*7c478bd9Sstevel@tonic-gate /*.......................................................................
6668*7c478bd9Sstevel@tonic-gate  * This is the action function that recalls the oldest line in the
6669*7c478bd9Sstevel@tonic-gate  * history buffer.
6670*7c478bd9Sstevel@tonic-gate  */
6671*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_beginning_of_history)
6672*7c478bd9Sstevel@tonic-gate {
6673*7c478bd9Sstevel@tonic-gate /*
6674*7c478bd9Sstevel@tonic-gate  * In vi mode, switch to command mode, since the user is very
6675*7c478bd9Sstevel@tonic-gate  * likely to want to move around newly recalled lines.
6676*7c478bd9Sstevel@tonic-gate  */
6677*7c478bd9Sstevel@tonic-gate   gl_vi_command_mode(gl);
6678*7c478bd9Sstevel@tonic-gate /*
6679*7c478bd9Sstevel@tonic-gate  * Forget any previous recall session.
6680*7c478bd9Sstevel@tonic-gate  */
6681*7c478bd9Sstevel@tonic-gate   gl->preload_id = 0;
6682*7c478bd9Sstevel@tonic-gate /*
6683*7c478bd9Sstevel@tonic-gate  * Record the key sequence number of this search action.
6684*7c478bd9Sstevel@tonic-gate  */
6685*7c478bd9Sstevel@tonic-gate   gl->last_search = gl->keyseq_count;
6686*7c478bd9Sstevel@tonic-gate /*
6687*7c478bd9Sstevel@tonic-gate  * Recall the next oldest line in the history list.
6688*7c478bd9Sstevel@tonic-gate  */
6689*7c478bd9Sstevel@tonic-gate   if(_glh_oldest_line(gl->glh, gl->line, gl->linelen+1) == NULL)
6690*7c478bd9Sstevel@tonic-gate     return 0;
6691*7c478bd9Sstevel@tonic-gate /*
6692*7c478bd9Sstevel@tonic-gate  * Accomodate the new contents of gl->line[].
6693*7c478bd9Sstevel@tonic-gate  */
6694*7c478bd9Sstevel@tonic-gate   gl_update_buffer(gl);
6695*7c478bd9Sstevel@tonic-gate /*
6696*7c478bd9Sstevel@tonic-gate  * Arrange to have the cursor placed at the end of the new line.
6697*7c478bd9Sstevel@tonic-gate  */
6698*7c478bd9Sstevel@tonic-gate   gl->buff_curpos = gl->ntotal;
6699*7c478bd9Sstevel@tonic-gate /*
6700*7c478bd9Sstevel@tonic-gate  * Erase and display the new line.
6701*7c478bd9Sstevel@tonic-gate  */
6702*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
6703*7c478bd9Sstevel@tonic-gate   return 0;
6704*7c478bd9Sstevel@tonic-gate }
6705*7c478bd9Sstevel@tonic-gate 
6706*7c478bd9Sstevel@tonic-gate /*.......................................................................
6707*7c478bd9Sstevel@tonic-gate  * If a history session is currently in progress, this action function
6708*7c478bd9Sstevel@tonic-gate  * recalls the line that was being edited when the session started. If
6709*7c478bd9Sstevel@tonic-gate  * no history session is in progress, it does nothing.
6710*7c478bd9Sstevel@tonic-gate  */
6711*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_end_of_history)
6712*7c478bd9Sstevel@tonic-gate {
6713*7c478bd9Sstevel@tonic-gate /*
6714*7c478bd9Sstevel@tonic-gate  * In vi mode, switch to command mode, since the user is very
6715*7c478bd9Sstevel@tonic-gate  * likely to want to move around newly recalled lines.
6716*7c478bd9Sstevel@tonic-gate  */
6717*7c478bd9Sstevel@tonic-gate   gl_vi_command_mode(gl);
6718*7c478bd9Sstevel@tonic-gate /*
6719*7c478bd9Sstevel@tonic-gate  * Forget any previous recall session.
6720*7c478bd9Sstevel@tonic-gate  */
6721*7c478bd9Sstevel@tonic-gate   gl->preload_id = 0;
6722*7c478bd9Sstevel@tonic-gate /*
6723*7c478bd9Sstevel@tonic-gate  * Record the key sequence number of this search action.
6724*7c478bd9Sstevel@tonic-gate  */
6725*7c478bd9Sstevel@tonic-gate   gl->last_search = gl->keyseq_count;
6726*7c478bd9Sstevel@tonic-gate /*
6727*7c478bd9Sstevel@tonic-gate  * Recall the next oldest line in the history list.
6728*7c478bd9Sstevel@tonic-gate  */
6729*7c478bd9Sstevel@tonic-gate   if(_glh_current_line(gl->glh, gl->line, gl->linelen+1) == NULL)
6730*7c478bd9Sstevel@tonic-gate     return 0;
6731*7c478bd9Sstevel@tonic-gate /*
6732*7c478bd9Sstevel@tonic-gate  * Accomodate the new contents of gl->line[].
6733*7c478bd9Sstevel@tonic-gate  */
6734*7c478bd9Sstevel@tonic-gate   gl_update_buffer(gl);
6735*7c478bd9Sstevel@tonic-gate /*
6736*7c478bd9Sstevel@tonic-gate  * Arrange to have the cursor placed at the end of the new line.
6737*7c478bd9Sstevel@tonic-gate  */
6738*7c478bd9Sstevel@tonic-gate   gl->buff_curpos = gl->ntotal;
6739*7c478bd9Sstevel@tonic-gate /*
6740*7c478bd9Sstevel@tonic-gate  * Erase and display the new line.
6741*7c478bd9Sstevel@tonic-gate  */
6742*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
6743*7c478bd9Sstevel@tonic-gate   return 0;
6744*7c478bd9Sstevel@tonic-gate }
6745*7c478bd9Sstevel@tonic-gate 
6746*7c478bd9Sstevel@tonic-gate /*.......................................................................
6747*7c478bd9Sstevel@tonic-gate  * This action function is treated specially, in that its count argument
6748*7c478bd9Sstevel@tonic-gate  * is set to the end keystroke of the keysequence that activated it.
6749*7c478bd9Sstevel@tonic-gate  * It accumulates a numeric argument, adding one digit on each call in
6750*7c478bd9Sstevel@tonic-gate  * which the last keystroke was a numeric digit.
6751*7c478bd9Sstevel@tonic-gate  */
6752*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_digit_argument)
6753*7c478bd9Sstevel@tonic-gate {
6754*7c478bd9Sstevel@tonic-gate /*
6755*7c478bd9Sstevel@tonic-gate  * Was the last keystroke a digit?
6756*7c478bd9Sstevel@tonic-gate  */
6757*7c478bd9Sstevel@tonic-gate   int is_digit = isdigit((int)(unsigned char) count);
6758*7c478bd9Sstevel@tonic-gate /*
6759*7c478bd9Sstevel@tonic-gate  * In vi command mode, a lone '0' means goto-start-of-line.
6760*7c478bd9Sstevel@tonic-gate  */
6761*7c478bd9Sstevel@tonic-gate   if(gl->vi.command && gl->number < 0 && count == '0')
6762*7c478bd9Sstevel@tonic-gate     return gl_beginning_of_line(gl, count, NULL);
6763*7c478bd9Sstevel@tonic-gate /*
6764*7c478bd9Sstevel@tonic-gate  * Are we starting to accumulate a new number?
6765*7c478bd9Sstevel@tonic-gate  */
6766*7c478bd9Sstevel@tonic-gate   if(gl->number < 0 || !is_digit)
6767*7c478bd9Sstevel@tonic-gate     gl->number = 0;
6768*7c478bd9Sstevel@tonic-gate /*
6769*7c478bd9Sstevel@tonic-gate  * Was the last keystroke a digit?
6770*7c478bd9Sstevel@tonic-gate  */
6771*7c478bd9Sstevel@tonic-gate   if(is_digit) {
6772*7c478bd9Sstevel@tonic-gate /*
6773*7c478bd9Sstevel@tonic-gate  * Read the numeric value of the digit, without assuming ASCII.
6774*7c478bd9Sstevel@tonic-gate  */
6775*7c478bd9Sstevel@tonic-gate     int n;
6776*7c478bd9Sstevel@tonic-gate     char s[2]; s[0] = count; s[1] = '\0';
6777*7c478bd9Sstevel@tonic-gate     n = atoi(s);
6778*7c478bd9Sstevel@tonic-gate /*
6779*7c478bd9Sstevel@tonic-gate  * Append the new digit.
6780*7c478bd9Sstevel@tonic-gate  */
6781*7c478bd9Sstevel@tonic-gate     gl->number = gl->number * 10 + n;
6782*7c478bd9Sstevel@tonic-gate   };
6783*7c478bd9Sstevel@tonic-gate   return 0;
6784*7c478bd9Sstevel@tonic-gate }
6785*7c478bd9Sstevel@tonic-gate 
6786*7c478bd9Sstevel@tonic-gate /*.......................................................................
6787*7c478bd9Sstevel@tonic-gate  * The newline action function sets gl->endline to tell
6788*7c478bd9Sstevel@tonic-gate  * gl_get_input_line() that the line is now complete.
6789*7c478bd9Sstevel@tonic-gate  */
6790*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_newline)
6791*7c478bd9Sstevel@tonic-gate {
6792*7c478bd9Sstevel@tonic-gate   GlhLineID id;    /* The last history line recalled while entering this line */
6793*7c478bd9Sstevel@tonic-gate /*
6794*7c478bd9Sstevel@tonic-gate  * Flag the line as ended.
6795*7c478bd9Sstevel@tonic-gate  */
6796*7c478bd9Sstevel@tonic-gate   gl->endline = 1;
6797*7c478bd9Sstevel@tonic-gate /*
6798*7c478bd9Sstevel@tonic-gate  * Record the next position in the history buffer, for potential
6799*7c478bd9Sstevel@tonic-gate  * recall by an action function on the next call to gl_get_line().
6800*7c478bd9Sstevel@tonic-gate  */
6801*7c478bd9Sstevel@tonic-gate   id = _glh_line_id(gl->glh, 1);
6802*7c478bd9Sstevel@tonic-gate   if(id)
6803*7c478bd9Sstevel@tonic-gate     gl->preload_id = id;
6804*7c478bd9Sstevel@tonic-gate   return 0;
6805*7c478bd9Sstevel@tonic-gate }
6806*7c478bd9Sstevel@tonic-gate 
6807*7c478bd9Sstevel@tonic-gate /*.......................................................................
6808*7c478bd9Sstevel@tonic-gate  * The 'repeat' action function sets gl->endline to tell
6809*7c478bd9Sstevel@tonic-gate  * gl_get_input_line() that the line is now complete, and records the
6810*7c478bd9Sstevel@tonic-gate  * ID of the next history line in gl->preload_id so that the next call
6811*7c478bd9Sstevel@tonic-gate  * to gl_get_input_line() will preload the line with that history line.
6812*7c478bd9Sstevel@tonic-gate  */
6813*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_repeat_history)
6814*7c478bd9Sstevel@tonic-gate {
6815*7c478bd9Sstevel@tonic-gate   gl->endline = 1;
6816*7c478bd9Sstevel@tonic-gate   gl->preload_id = _glh_line_id(gl->glh, 1);
6817*7c478bd9Sstevel@tonic-gate   gl->preload_history = 1;
6818*7c478bd9Sstevel@tonic-gate   return 0;
6819*7c478bd9Sstevel@tonic-gate }
6820*7c478bd9Sstevel@tonic-gate 
6821*7c478bd9Sstevel@tonic-gate /*.......................................................................
6822*7c478bd9Sstevel@tonic-gate  * Flush unwritten characters to the terminal.
6823*7c478bd9Sstevel@tonic-gate  *
6824*7c478bd9Sstevel@tonic-gate  * Input:
6825*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  The getline resource object.
6826*7c478bd9Sstevel@tonic-gate  * Output:
6827*7c478bd9Sstevel@tonic-gate  *  return     int    0 - OK.
6828*7c478bd9Sstevel@tonic-gate  *                    1 - Either an error occured, or the output
6829*7c478bd9Sstevel@tonic-gate  *                        blocked and non-blocking I/O is being used.
6830*7c478bd9Sstevel@tonic-gate  *                        See gl->rtn_status for details.
6831*7c478bd9Sstevel@tonic-gate  */
6832*7c478bd9Sstevel@tonic-gate static int gl_flush_output(GetLine *gl)
6833*7c478bd9Sstevel@tonic-gate {
6834*7c478bd9Sstevel@tonic-gate /*
6835*7c478bd9Sstevel@tonic-gate  * Record the fact that we are about to write to the terminal.
6836*7c478bd9Sstevel@tonic-gate  */
6837*7c478bd9Sstevel@tonic-gate   gl->pending_io = GLP_WRITE;
6838*7c478bd9Sstevel@tonic-gate /*
6839*7c478bd9Sstevel@tonic-gate  * Attempt to flush the output to the terminal.
6840*7c478bd9Sstevel@tonic-gate  */
6841*7c478bd9Sstevel@tonic-gate   errno = 0;
6842*7c478bd9Sstevel@tonic-gate   switch(_glq_flush_queue(gl->cq, gl->flush_fn, gl)) {
6843*7c478bd9Sstevel@tonic-gate   case GLQ_FLUSH_DONE:
6844*7c478bd9Sstevel@tonic-gate     return gl->redisplay && !gl->postpone && gl_redisplay(gl, 1, NULL);
6845*7c478bd9Sstevel@tonic-gate     break;
6846*7c478bd9Sstevel@tonic-gate   case GLQ_FLUSH_AGAIN:      /* Output blocked */
6847*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO);
6848*7c478bd9Sstevel@tonic-gate     return 1;
6849*7c478bd9Sstevel@tonic-gate     break;
6850*7c478bd9Sstevel@tonic-gate   default:                   /* Abort the line if an error occurs */
6851*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, errno==EINTR ? GLR_SIGNAL : GLR_ERROR, errno);
6852*7c478bd9Sstevel@tonic-gate     return 1;
6853*7c478bd9Sstevel@tonic-gate     break;
6854*7c478bd9Sstevel@tonic-gate   };
6855*7c478bd9Sstevel@tonic-gate }
6856*7c478bd9Sstevel@tonic-gate 
6857*7c478bd9Sstevel@tonic-gate /*.......................................................................
6858*7c478bd9Sstevel@tonic-gate  * This is the callback which _glq_flush_queue() uses to write buffered
6859*7c478bd9Sstevel@tonic-gate  * characters to the terminal.
6860*7c478bd9Sstevel@tonic-gate  */
6861*7c478bd9Sstevel@tonic-gate static GL_WRITE_FN(gl_flush_terminal)
6862*7c478bd9Sstevel@tonic-gate {
6863*7c478bd9Sstevel@tonic-gate   int ndone = 0;    /* The number of characters written so far */
6864*7c478bd9Sstevel@tonic-gate /*
6865*7c478bd9Sstevel@tonic-gate  * Get the line-editor resource object.
6866*7c478bd9Sstevel@tonic-gate  */
6867*7c478bd9Sstevel@tonic-gate   GetLine *gl = (GetLine *) data;
6868*7c478bd9Sstevel@tonic-gate /*
6869*7c478bd9Sstevel@tonic-gate  * Transfer the latest array of characters to stdio.
6870*7c478bd9Sstevel@tonic-gate  */
6871*7c478bd9Sstevel@tonic-gate   while(ndone < n) {
6872*7c478bd9Sstevel@tonic-gate     int nnew = write(gl->output_fd, s, n-ndone);
6873*7c478bd9Sstevel@tonic-gate /*
6874*7c478bd9Sstevel@tonic-gate  * If the write was successful, add to the recorded number of bytes
6875*7c478bd9Sstevel@tonic-gate  * that have now been written.
6876*7c478bd9Sstevel@tonic-gate  */
6877*7c478bd9Sstevel@tonic-gate     if(nnew > 0) {
6878*7c478bd9Sstevel@tonic-gate       ndone += nnew;
6879*7c478bd9Sstevel@tonic-gate /*
6880*7c478bd9Sstevel@tonic-gate  * If a signal interrupted the call, restart the write(), since all of
6881*7c478bd9Sstevel@tonic-gate  * the signals that gl_get_line() has been told to watch for are
6882*7c478bd9Sstevel@tonic-gate  * currently blocked.
6883*7c478bd9Sstevel@tonic-gate  */
6884*7c478bd9Sstevel@tonic-gate     } else if(errno == EINTR) {
6885*7c478bd9Sstevel@tonic-gate       continue;
6886*7c478bd9Sstevel@tonic-gate /*
6887*7c478bd9Sstevel@tonic-gate  * If we managed to write something before an I/O error occurred, or
6888*7c478bd9Sstevel@tonic-gate  * output blocked before anything was written, report the number of
6889*7c478bd9Sstevel@tonic-gate  * bytes that were successfully written before this happened.
6890*7c478bd9Sstevel@tonic-gate  */
6891*7c478bd9Sstevel@tonic-gate     } else if(ndone > 0
6892*7c478bd9Sstevel@tonic-gate #if defined(EAGAIN)
6893*7c478bd9Sstevel@tonic-gate 	      || errno==EAGAIN
6894*7c478bd9Sstevel@tonic-gate #endif
6895*7c478bd9Sstevel@tonic-gate #if defined(EWOULDBLOCK)
6896*7c478bd9Sstevel@tonic-gate 	      || errno==EWOULDBLOCK
6897*7c478bd9Sstevel@tonic-gate #endif
6898*7c478bd9Sstevel@tonic-gate 	      ) {
6899*7c478bd9Sstevel@tonic-gate       return ndone;
6900*7c478bd9Sstevel@tonic-gate 
6901*7c478bd9Sstevel@tonic-gate /*
6902*7c478bd9Sstevel@tonic-gate  * To get here, an error must have occurred before anything new could
6903*7c478bd9Sstevel@tonic-gate  * be written.
6904*7c478bd9Sstevel@tonic-gate  */
6905*7c478bd9Sstevel@tonic-gate     } else {
6906*7c478bd9Sstevel@tonic-gate       return -1;
6907*7c478bd9Sstevel@tonic-gate     };
6908*7c478bd9Sstevel@tonic-gate   };
6909*7c478bd9Sstevel@tonic-gate /*
6910*7c478bd9Sstevel@tonic-gate  * To get here, we must have successfully written the number of
6911*7c478bd9Sstevel@tonic-gate  * bytes that was specified.
6912*7c478bd9Sstevel@tonic-gate  */
6913*7c478bd9Sstevel@tonic-gate   return n;
6914*7c478bd9Sstevel@tonic-gate }
6915*7c478bd9Sstevel@tonic-gate 
6916*7c478bd9Sstevel@tonic-gate /*.......................................................................
6917*7c478bd9Sstevel@tonic-gate  * Change the style of editing to emulate a given editor.
6918*7c478bd9Sstevel@tonic-gate  *
6919*7c478bd9Sstevel@tonic-gate  * Input:
6920*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
6921*7c478bd9Sstevel@tonic-gate  *  editor  GlEditor    The type of editor to emulate.
6922*7c478bd9Sstevel@tonic-gate  * Output:
6923*7c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
6924*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
6925*7c478bd9Sstevel@tonic-gate  */
6926*7c478bd9Sstevel@tonic-gate static int gl_change_editor(GetLine *gl, GlEditor editor)
6927*7c478bd9Sstevel@tonic-gate {
6928*7c478bd9Sstevel@tonic-gate /*
6929*7c478bd9Sstevel@tonic-gate  * Install the default key-bindings of the requested editor.
6930*7c478bd9Sstevel@tonic-gate  */
6931*7c478bd9Sstevel@tonic-gate   switch(editor) {
6932*7c478bd9Sstevel@tonic-gate   case GL_EMACS_MODE:
6933*7c478bd9Sstevel@tonic-gate     _kt_clear_bindings(gl->bindings, KTB_NORM);
6934*7c478bd9Sstevel@tonic-gate     _kt_clear_bindings(gl->bindings, KTB_TERM);
6935*7c478bd9Sstevel@tonic-gate     (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_emacs_bindings,
6936*7c478bd9Sstevel@tonic-gate 		   sizeof(gl_emacs_bindings)/sizeof(gl_emacs_bindings[0]));
6937*7c478bd9Sstevel@tonic-gate     break;
6938*7c478bd9Sstevel@tonic-gate   case GL_VI_MODE:
6939*7c478bd9Sstevel@tonic-gate     _kt_clear_bindings(gl->bindings, KTB_NORM);
6940*7c478bd9Sstevel@tonic-gate     _kt_clear_bindings(gl->bindings, KTB_TERM);
6941*7c478bd9Sstevel@tonic-gate     (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_vi_bindings,
6942*7c478bd9Sstevel@tonic-gate 			    sizeof(gl_vi_bindings)/sizeof(gl_vi_bindings[0]));
6943*7c478bd9Sstevel@tonic-gate     break;
6944*7c478bd9Sstevel@tonic-gate   case GL_NO_EDITOR:
6945*7c478bd9Sstevel@tonic-gate     break;
6946*7c478bd9Sstevel@tonic-gate   default:
6947*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "Unknown editor", END_ERR_MSG);
6948*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
6949*7c478bd9Sstevel@tonic-gate     return 1;
6950*7c478bd9Sstevel@tonic-gate   };
6951*7c478bd9Sstevel@tonic-gate /*
6952*7c478bd9Sstevel@tonic-gate  * Record the new editing mode.
6953*7c478bd9Sstevel@tonic-gate  */
6954*7c478bd9Sstevel@tonic-gate   gl->editor = editor;
6955*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;     /* Start in input mode */
6956*7c478bd9Sstevel@tonic-gate   gl->insert_curpos = 0;
6957*7c478bd9Sstevel@tonic-gate /*
6958*7c478bd9Sstevel@tonic-gate  * Reinstate terminal-specific bindings.
6959*7c478bd9Sstevel@tonic-gate  */
6960*7c478bd9Sstevel@tonic-gate   if(gl->editor != GL_NO_EDITOR && gl->input_fp)
6961*7c478bd9Sstevel@tonic-gate     (void) gl_bind_terminal_keys(gl);
6962*7c478bd9Sstevel@tonic-gate   return 0;
6963*7c478bd9Sstevel@tonic-gate }
6964*7c478bd9Sstevel@tonic-gate 
6965*7c478bd9Sstevel@tonic-gate /*.......................................................................
6966*7c478bd9Sstevel@tonic-gate  * This is an action function that switches to editing using emacs bindings
6967*7c478bd9Sstevel@tonic-gate  */
6968*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_emacs_editing_mode)
6969*7c478bd9Sstevel@tonic-gate {
6970*7c478bd9Sstevel@tonic-gate   return gl_change_editor(gl, GL_EMACS_MODE);
6971*7c478bd9Sstevel@tonic-gate }
6972*7c478bd9Sstevel@tonic-gate 
6973*7c478bd9Sstevel@tonic-gate /*.......................................................................
6974*7c478bd9Sstevel@tonic-gate  * This is an action function that switches to editing using vi bindings
6975*7c478bd9Sstevel@tonic-gate  */
6976*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_editing_mode)
6977*7c478bd9Sstevel@tonic-gate {
6978*7c478bd9Sstevel@tonic-gate   return gl_change_editor(gl, GL_VI_MODE);
6979*7c478bd9Sstevel@tonic-gate }
6980*7c478bd9Sstevel@tonic-gate 
6981*7c478bd9Sstevel@tonic-gate /*.......................................................................
6982*7c478bd9Sstevel@tonic-gate  * This is the action function that switches to insert mode.
6983*7c478bd9Sstevel@tonic-gate  */
6984*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_insert)
6985*7c478bd9Sstevel@tonic-gate {
6986*7c478bd9Sstevel@tonic-gate /*
6987*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
6988*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
6989*7c478bd9Sstevel@tonic-gate  */
6990*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
6991*7c478bd9Sstevel@tonic-gate /*
6992*7c478bd9Sstevel@tonic-gate  * Switch to vi insert mode.
6993*7c478bd9Sstevel@tonic-gate  */
6994*7c478bd9Sstevel@tonic-gate   gl->insert = 1;
6995*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;
6996*7c478bd9Sstevel@tonic-gate   gl->insert_curpos = gl->buff_curpos;
6997*7c478bd9Sstevel@tonic-gate   return 0;
6998*7c478bd9Sstevel@tonic-gate }
6999*7c478bd9Sstevel@tonic-gate 
7000*7c478bd9Sstevel@tonic-gate /*.......................................................................
7001*7c478bd9Sstevel@tonic-gate  * This is an action function that switches to overwrite mode.
7002*7c478bd9Sstevel@tonic-gate  */
7003*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_overwrite)
7004*7c478bd9Sstevel@tonic-gate {
7005*7c478bd9Sstevel@tonic-gate /*
7006*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
7007*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
7008*7c478bd9Sstevel@tonic-gate  */
7009*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
7010*7c478bd9Sstevel@tonic-gate /*
7011*7c478bd9Sstevel@tonic-gate  * Switch to vi overwrite mode.
7012*7c478bd9Sstevel@tonic-gate  */
7013*7c478bd9Sstevel@tonic-gate   gl->insert = 0;
7014*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;
7015*7c478bd9Sstevel@tonic-gate   gl->insert_curpos = gl->buff_curpos;
7016*7c478bd9Sstevel@tonic-gate   return 0;
7017*7c478bd9Sstevel@tonic-gate }
7018*7c478bd9Sstevel@tonic-gate 
7019*7c478bd9Sstevel@tonic-gate /*.......................................................................
7020*7c478bd9Sstevel@tonic-gate  * This action function toggles the case of the character under the
7021*7c478bd9Sstevel@tonic-gate  * cursor.
7022*7c478bd9Sstevel@tonic-gate  */
7023*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_change_case)
7024*7c478bd9Sstevel@tonic-gate {
7025*7c478bd9Sstevel@tonic-gate   int i;
7026*7c478bd9Sstevel@tonic-gate /*
7027*7c478bd9Sstevel@tonic-gate  * Keep a record of the current insert mode and the cursor position.
7028*7c478bd9Sstevel@tonic-gate  */
7029*7c478bd9Sstevel@tonic-gate   int insert = gl->insert;
7030*7c478bd9Sstevel@tonic-gate /*
7031*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
7032*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
7033*7c478bd9Sstevel@tonic-gate  */
7034*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
7035*7c478bd9Sstevel@tonic-gate /*
7036*7c478bd9Sstevel@tonic-gate  * We want to overwrite the modified word.
7037*7c478bd9Sstevel@tonic-gate  */
7038*7c478bd9Sstevel@tonic-gate   gl->insert = 0;
7039*7c478bd9Sstevel@tonic-gate /*
7040*7c478bd9Sstevel@tonic-gate  * Toggle the case of 'count' characters.
7041*7c478bd9Sstevel@tonic-gate  */
7042*7c478bd9Sstevel@tonic-gate   for(i=0; i<count && gl->buff_curpos < gl->ntotal; i++) {
7043*7c478bd9Sstevel@tonic-gate     char *cptr = gl->line + gl->buff_curpos++;
7044*7c478bd9Sstevel@tonic-gate /*
7045*7c478bd9Sstevel@tonic-gate  * Convert the character to upper case?
7046*7c478bd9Sstevel@tonic-gate  */
7047*7c478bd9Sstevel@tonic-gate     if(islower((int)(unsigned char) *cptr))
7048*7c478bd9Sstevel@tonic-gate       gl_buffer_char(gl, toupper((int) *cptr), cptr - gl->line);
7049*7c478bd9Sstevel@tonic-gate     else if(isupper((int)(unsigned char) *cptr))
7050*7c478bd9Sstevel@tonic-gate       gl_buffer_char(gl, tolower((int) *cptr), cptr - gl->line);
7051*7c478bd9Sstevel@tonic-gate /*
7052*7c478bd9Sstevel@tonic-gate  * Write the possibly modified character back. Note that for non-modified
7053*7c478bd9Sstevel@tonic-gate  * characters we want to do this as well, so as to advance the cursor.
7054*7c478bd9Sstevel@tonic-gate  */
7055*7c478bd9Sstevel@tonic-gate       if(gl_print_char(gl, *cptr, cptr[1]))
7056*7c478bd9Sstevel@tonic-gate 	return 1;
7057*7c478bd9Sstevel@tonic-gate   };
7058*7c478bd9Sstevel@tonic-gate /*
7059*7c478bd9Sstevel@tonic-gate  * Restore the insertion mode.
7060*7c478bd9Sstevel@tonic-gate  */
7061*7c478bd9Sstevel@tonic-gate   gl->insert = insert;
7062*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos);	/* bounds check */
7063*7c478bd9Sstevel@tonic-gate }
7064*7c478bd9Sstevel@tonic-gate 
7065*7c478bd9Sstevel@tonic-gate /*.......................................................................
7066*7c478bd9Sstevel@tonic-gate  * This is the action function which implements the vi-style action which
7067*7c478bd9Sstevel@tonic-gate  * moves the cursor to the start of the line, then switches to insert mode.
7068*7c478bd9Sstevel@tonic-gate  */
7069*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_insert_at_bol)
7070*7c478bd9Sstevel@tonic-gate {
7071*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
7072*7c478bd9Sstevel@tonic-gate   return gl_beginning_of_line(gl, 0, NULL) ||
7073*7c478bd9Sstevel@tonic-gate          gl_vi_insert(gl, 0, NULL);
7074*7c478bd9Sstevel@tonic-gate 
7075*7c478bd9Sstevel@tonic-gate }
7076*7c478bd9Sstevel@tonic-gate 
7077*7c478bd9Sstevel@tonic-gate /*.......................................................................
7078*7c478bd9Sstevel@tonic-gate  * This is the action function which implements the vi-style action which
7079*7c478bd9Sstevel@tonic-gate  * moves the cursor to the end of the line, then switches to insert mode
7080*7c478bd9Sstevel@tonic-gate  * to allow text to be appended to the line.
7081*7c478bd9Sstevel@tonic-gate  */
7082*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_append_at_eol)
7083*7c478bd9Sstevel@tonic-gate {
7084*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
7085*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;	/* Allow cursor at EOL */
7086*7c478bd9Sstevel@tonic-gate   return gl_end_of_line(gl, 0, NULL) ||
7087*7c478bd9Sstevel@tonic-gate          gl_vi_insert(gl, 0, NULL);
7088*7c478bd9Sstevel@tonic-gate }
7089*7c478bd9Sstevel@tonic-gate 
7090*7c478bd9Sstevel@tonic-gate /*.......................................................................
7091*7c478bd9Sstevel@tonic-gate  * This is the action function which implements the vi-style action which
7092*7c478bd9Sstevel@tonic-gate  * moves the cursor to right one then switches to insert mode, thus
7093*7c478bd9Sstevel@tonic-gate  * allowing text to be appended after the next character.
7094*7c478bd9Sstevel@tonic-gate  */
7095*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_append)
7096*7c478bd9Sstevel@tonic-gate {
7097*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
7098*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;	/* Allow cursor at EOL */
7099*7c478bd9Sstevel@tonic-gate   return gl_cursor_right(gl, 1, NULL) ||
7100*7c478bd9Sstevel@tonic-gate          gl_vi_insert(gl, 0, NULL);
7101*7c478bd9Sstevel@tonic-gate }
7102*7c478bd9Sstevel@tonic-gate 
7103*7c478bd9Sstevel@tonic-gate /*.......................................................................
7104*7c478bd9Sstevel@tonic-gate  * This action function moves the cursor to the column specified by the
7105*7c478bd9Sstevel@tonic-gate  * numeric argument. Column indexes start at 1.
7106*7c478bd9Sstevel@tonic-gate  */
7107*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_goto_column)
7108*7c478bd9Sstevel@tonic-gate {
7109*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, count - 1);
7110*7c478bd9Sstevel@tonic-gate }
7111*7c478bd9Sstevel@tonic-gate 
7112*7c478bd9Sstevel@tonic-gate /*.......................................................................
7113*7c478bd9Sstevel@tonic-gate  * Starting with the character under the cursor, replace 'count'
7114*7c478bd9Sstevel@tonic-gate  * characters with the next character that the user types.
7115*7c478bd9Sstevel@tonic-gate  */
7116*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_replace_char)
7117*7c478bd9Sstevel@tonic-gate {
7118*7c478bd9Sstevel@tonic-gate   char c;  /* The replacement character */
7119*7c478bd9Sstevel@tonic-gate   int i;
7120*7c478bd9Sstevel@tonic-gate /*
7121*7c478bd9Sstevel@tonic-gate  * Keep a record of the current insert mode.
7122*7c478bd9Sstevel@tonic-gate  */
7123*7c478bd9Sstevel@tonic-gate   int insert = gl->insert;
7124*7c478bd9Sstevel@tonic-gate /*
7125*7c478bd9Sstevel@tonic-gate  * Get the replacement character.
7126*7c478bd9Sstevel@tonic-gate  */
7127*7c478bd9Sstevel@tonic-gate   if(gl->vi.repeat.active) {
7128*7c478bd9Sstevel@tonic-gate     c = gl->vi.repeat.input_char;
7129*7c478bd9Sstevel@tonic-gate   } else {
7130*7c478bd9Sstevel@tonic-gate     if(gl_read_terminal(gl, 1, &c))
7131*7c478bd9Sstevel@tonic-gate       return 1;
7132*7c478bd9Sstevel@tonic-gate     gl->vi.repeat.input_char = c;
7133*7c478bd9Sstevel@tonic-gate   };
7134*7c478bd9Sstevel@tonic-gate /*
7135*7c478bd9Sstevel@tonic-gate  * Are there 'count' characters to be replaced?
7136*7c478bd9Sstevel@tonic-gate  */
7137*7c478bd9Sstevel@tonic-gate   if(gl->ntotal - gl->buff_curpos >= count) {
7138*7c478bd9Sstevel@tonic-gate /*
7139*7c478bd9Sstevel@tonic-gate  * If in vi command mode, preserve the current line for potential
7140*7c478bd9Sstevel@tonic-gate  * use by vi-undo.
7141*7c478bd9Sstevel@tonic-gate  */
7142*7c478bd9Sstevel@tonic-gate     gl_save_for_undo(gl);
7143*7c478bd9Sstevel@tonic-gate /*
7144*7c478bd9Sstevel@tonic-gate  * Temporarily switch to overwrite mode.
7145*7c478bd9Sstevel@tonic-gate  */
7146*7c478bd9Sstevel@tonic-gate     gl->insert = 0;
7147*7c478bd9Sstevel@tonic-gate /*
7148*7c478bd9Sstevel@tonic-gate  * Overwrite the current character plus count-1 subsequent characters
7149*7c478bd9Sstevel@tonic-gate  * with the replacement character.
7150*7c478bd9Sstevel@tonic-gate  */
7151*7c478bd9Sstevel@tonic-gate     for(i=0; i<count; i++)
7152*7c478bd9Sstevel@tonic-gate       gl_add_char_to_line(gl, c);
7153*7c478bd9Sstevel@tonic-gate /*
7154*7c478bd9Sstevel@tonic-gate  * Restore the original insert/overwrite mode.
7155*7c478bd9Sstevel@tonic-gate  */
7156*7c478bd9Sstevel@tonic-gate     gl->insert = insert;
7157*7c478bd9Sstevel@tonic-gate   };
7158*7c478bd9Sstevel@tonic-gate   return gl_place_cursor(gl, gl->buff_curpos);	/* bounds check */
7159*7c478bd9Sstevel@tonic-gate }
7160*7c478bd9Sstevel@tonic-gate 
7161*7c478bd9Sstevel@tonic-gate /*.......................................................................
7162*7c478bd9Sstevel@tonic-gate  * This is an action function which changes all characters between the
7163*7c478bd9Sstevel@tonic-gate  * current cursor position and the end of the line.
7164*7c478bd9Sstevel@tonic-gate  */
7165*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_rest_of_line)
7166*7c478bd9Sstevel@tonic-gate {
7167*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
7168*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;	/* Allow cursor at EOL */
7169*7c478bd9Sstevel@tonic-gate   return gl_kill_line(gl, count, NULL) || gl_vi_insert(gl, 0, NULL);
7170*7c478bd9Sstevel@tonic-gate }
7171*7c478bd9Sstevel@tonic-gate 
7172*7c478bd9Sstevel@tonic-gate /*.......................................................................
7173*7c478bd9Sstevel@tonic-gate  * This is an action function which changes all characters between the
7174*7c478bd9Sstevel@tonic-gate  * start of the line and the current cursor position.
7175*7c478bd9Sstevel@tonic-gate  */
7176*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_to_bol)
7177*7c478bd9Sstevel@tonic-gate {
7178*7c478bd9Sstevel@tonic-gate   return gl_backward_kill_line(gl,count,NULL) || gl_vi_insert(gl,0,NULL);
7179*7c478bd9Sstevel@tonic-gate }
7180*7c478bd9Sstevel@tonic-gate 
7181*7c478bd9Sstevel@tonic-gate /*.......................................................................
7182*7c478bd9Sstevel@tonic-gate  * This is an action function which deletes the entire contents of the
7183*7c478bd9Sstevel@tonic-gate  * current line and switches to insert mode.
7184*7c478bd9Sstevel@tonic-gate  */
7185*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_line)
7186*7c478bd9Sstevel@tonic-gate {
7187*7c478bd9Sstevel@tonic-gate   return gl_delete_line(gl,count,NULL) || gl_vi_insert(gl,0,NULL);
7188*7c478bd9Sstevel@tonic-gate }
7189*7c478bd9Sstevel@tonic-gate 
7190*7c478bd9Sstevel@tonic-gate /*.......................................................................
7191*7c478bd9Sstevel@tonic-gate  * Starting from the cursor position and looking towards the end of the
7192*7c478bd9Sstevel@tonic-gate  * line, copy 'count' characters to the cut buffer.
7193*7c478bd9Sstevel@tonic-gate  */
7194*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_copy_char)
7195*7c478bd9Sstevel@tonic-gate {
7196*7c478bd9Sstevel@tonic-gate /*
7197*7c478bd9Sstevel@tonic-gate  * Limit the count to the number of characters available.
7198*7c478bd9Sstevel@tonic-gate  */
7199*7c478bd9Sstevel@tonic-gate   if(gl->buff_curpos + count >= gl->ntotal)
7200*7c478bd9Sstevel@tonic-gate     count = gl->ntotal - gl->buff_curpos;
7201*7c478bd9Sstevel@tonic-gate   if(count < 0)
7202*7c478bd9Sstevel@tonic-gate     count = 0;
7203*7c478bd9Sstevel@tonic-gate /*
7204*7c478bd9Sstevel@tonic-gate  * Copy the characters to the cut buffer.
7205*7c478bd9Sstevel@tonic-gate  */
7206*7c478bd9Sstevel@tonic-gate   memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count);
7207*7c478bd9Sstevel@tonic-gate   gl->cutbuf[count] = '\0';
7208*7c478bd9Sstevel@tonic-gate   return 0;
7209*7c478bd9Sstevel@tonic-gate }
7210*7c478bd9Sstevel@tonic-gate 
7211*7c478bd9Sstevel@tonic-gate /*.......................................................................
7212*7c478bd9Sstevel@tonic-gate  * Starting from the character before the cursor position and looking
7213*7c478bd9Sstevel@tonic-gate  * backwards towards the start of the line, copy 'count' characters to
7214*7c478bd9Sstevel@tonic-gate  * the cut buffer.
7215*7c478bd9Sstevel@tonic-gate  */
7216*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_copy_char)
7217*7c478bd9Sstevel@tonic-gate {
7218*7c478bd9Sstevel@tonic-gate /*
7219*7c478bd9Sstevel@tonic-gate  * Limit the count to the number of characters available.
7220*7c478bd9Sstevel@tonic-gate  */
7221*7c478bd9Sstevel@tonic-gate   if(count > gl->buff_curpos)
7222*7c478bd9Sstevel@tonic-gate     count = gl->buff_curpos;
7223*7c478bd9Sstevel@tonic-gate   if(count < 0)
7224*7c478bd9Sstevel@tonic-gate     count = 0;
7225*7c478bd9Sstevel@tonic-gate   gl_place_cursor(gl, gl->buff_curpos - count);
7226*7c478bd9Sstevel@tonic-gate /*
7227*7c478bd9Sstevel@tonic-gate  * Copy the characters to the cut buffer.
7228*7c478bd9Sstevel@tonic-gate  */
7229*7c478bd9Sstevel@tonic-gate   memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count);
7230*7c478bd9Sstevel@tonic-gate   gl->cutbuf[count] = '\0';
7231*7c478bd9Sstevel@tonic-gate   return 0;
7232*7c478bd9Sstevel@tonic-gate }
7233*7c478bd9Sstevel@tonic-gate 
7234*7c478bd9Sstevel@tonic-gate /*.......................................................................
7235*7c478bd9Sstevel@tonic-gate  * Starting from the cursor position copy to the specified column into the
7236*7c478bd9Sstevel@tonic-gate  * cut buffer.
7237*7c478bd9Sstevel@tonic-gate  */
7238*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_to_column)
7239*7c478bd9Sstevel@tonic-gate {
7240*7c478bd9Sstevel@tonic-gate   if (--count >= gl->buff_curpos)
7241*7c478bd9Sstevel@tonic-gate     return gl_forward_copy_char(gl, count - gl->buff_curpos, NULL);
7242*7c478bd9Sstevel@tonic-gate   else
7243*7c478bd9Sstevel@tonic-gate     return gl_backward_copy_char(gl, gl->buff_curpos - count, NULL);
7244*7c478bd9Sstevel@tonic-gate }
7245*7c478bd9Sstevel@tonic-gate 
7246*7c478bd9Sstevel@tonic-gate /*.......................................................................
7247*7c478bd9Sstevel@tonic-gate  * Starting from the cursor position copy characters up to a matching
7248*7c478bd9Sstevel@tonic-gate  * parenthesis into the cut buffer.
7249*7c478bd9Sstevel@tonic-gate  */
7250*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_to_parenthesis)
7251*7c478bd9Sstevel@tonic-gate {
7252*7c478bd9Sstevel@tonic-gate   int curpos = gl_index_of_matching_paren(gl);
7253*7c478bd9Sstevel@tonic-gate   if(curpos >= 0) {
7254*7c478bd9Sstevel@tonic-gate     gl_save_for_undo(gl);
7255*7c478bd9Sstevel@tonic-gate     if(curpos >= gl->buff_curpos)
7256*7c478bd9Sstevel@tonic-gate       return gl_forward_copy_char(gl, curpos - gl->buff_curpos + 1, NULL);
7257*7c478bd9Sstevel@tonic-gate     else
7258*7c478bd9Sstevel@tonic-gate       return gl_backward_copy_char(gl, ++gl->buff_curpos - curpos + 1, NULL);
7259*7c478bd9Sstevel@tonic-gate   };
7260*7c478bd9Sstevel@tonic-gate   return 0;
7261*7c478bd9Sstevel@tonic-gate }
7262*7c478bd9Sstevel@tonic-gate 
7263*7c478bd9Sstevel@tonic-gate /*.......................................................................
7264*7c478bd9Sstevel@tonic-gate  * Starting from the cursor position copy the rest of the line into the
7265*7c478bd9Sstevel@tonic-gate  * cut buffer.
7266*7c478bd9Sstevel@tonic-gate  */
7267*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_rest_of_line)
7268*7c478bd9Sstevel@tonic-gate {
7269*7c478bd9Sstevel@tonic-gate /*
7270*7c478bd9Sstevel@tonic-gate  * Copy the characters to the cut buffer.
7271*7c478bd9Sstevel@tonic-gate  */
7272*7c478bd9Sstevel@tonic-gate   memcpy(gl->cutbuf, gl->line + gl->buff_curpos, gl->ntotal - gl->buff_curpos);
7273*7c478bd9Sstevel@tonic-gate   gl->cutbuf[gl->ntotal - gl->buff_curpos] = '\0';
7274*7c478bd9Sstevel@tonic-gate   return 0;
7275*7c478bd9Sstevel@tonic-gate }
7276*7c478bd9Sstevel@tonic-gate 
7277*7c478bd9Sstevel@tonic-gate /*.......................................................................
7278*7c478bd9Sstevel@tonic-gate  * Copy from the beginning of the line to the cursor position into the
7279*7c478bd9Sstevel@tonic-gate  * cut buffer.
7280*7c478bd9Sstevel@tonic-gate  */
7281*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_to_bol)
7282*7c478bd9Sstevel@tonic-gate {
7283*7c478bd9Sstevel@tonic-gate /*
7284*7c478bd9Sstevel@tonic-gate  * Copy the characters to the cut buffer.
7285*7c478bd9Sstevel@tonic-gate  */
7286*7c478bd9Sstevel@tonic-gate   memcpy(gl->cutbuf, gl->line, gl->buff_curpos);
7287*7c478bd9Sstevel@tonic-gate   gl->cutbuf[gl->buff_curpos] = '\0';
7288*7c478bd9Sstevel@tonic-gate   gl_place_cursor(gl, 0);
7289*7c478bd9Sstevel@tonic-gate   return 0;
7290*7c478bd9Sstevel@tonic-gate }
7291*7c478bd9Sstevel@tonic-gate 
7292*7c478bd9Sstevel@tonic-gate /*.......................................................................
7293*7c478bd9Sstevel@tonic-gate  * Copy the entire line into the cut buffer.
7294*7c478bd9Sstevel@tonic-gate  */
7295*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_line)
7296*7c478bd9Sstevel@tonic-gate {
7297*7c478bd9Sstevel@tonic-gate /*
7298*7c478bd9Sstevel@tonic-gate  * Copy the characters to the cut buffer.
7299*7c478bd9Sstevel@tonic-gate  */
7300*7c478bd9Sstevel@tonic-gate   memcpy(gl->cutbuf, gl->line, gl->ntotal);
7301*7c478bd9Sstevel@tonic-gate   gl->cutbuf[gl->ntotal] = '\0';
7302*7c478bd9Sstevel@tonic-gate   return 0;
7303*7c478bd9Sstevel@tonic-gate }
7304*7c478bd9Sstevel@tonic-gate 
7305*7c478bd9Sstevel@tonic-gate /*.......................................................................
7306*7c478bd9Sstevel@tonic-gate  * Search forwards for the next character that the user enters.
7307*7c478bd9Sstevel@tonic-gate  */
7308*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_find_char)
7309*7c478bd9Sstevel@tonic-gate {
7310*7c478bd9Sstevel@tonic-gate   int pos = gl_find_char(gl, count, 1, 1, '\0');
7311*7c478bd9Sstevel@tonic-gate   return pos >= 0 && gl_place_cursor(gl, pos);
7312*7c478bd9Sstevel@tonic-gate }
7313*7c478bd9Sstevel@tonic-gate 
7314*7c478bd9Sstevel@tonic-gate /*.......................................................................
7315*7c478bd9Sstevel@tonic-gate  * Search backwards for the next character that the user enters.
7316*7c478bd9Sstevel@tonic-gate  */
7317*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_find_char)
7318*7c478bd9Sstevel@tonic-gate {
7319*7c478bd9Sstevel@tonic-gate   int pos = gl_find_char(gl, count, 0, 1, '\0');
7320*7c478bd9Sstevel@tonic-gate   return pos >= 0 && gl_place_cursor(gl, pos);
7321*7c478bd9Sstevel@tonic-gate }
7322*7c478bd9Sstevel@tonic-gate 
7323*7c478bd9Sstevel@tonic-gate /*.......................................................................
7324*7c478bd9Sstevel@tonic-gate  * Search forwards for the next character that the user enters. Move up to,
7325*7c478bd9Sstevel@tonic-gate  * but not onto, the found character.
7326*7c478bd9Sstevel@tonic-gate  */
7327*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_to_char)
7328*7c478bd9Sstevel@tonic-gate {
7329*7c478bd9Sstevel@tonic-gate   int pos = gl_find_char(gl, count, 1, 0, '\0');
7330*7c478bd9Sstevel@tonic-gate   return pos >= 0 && gl_place_cursor(gl, pos);
7331*7c478bd9Sstevel@tonic-gate }
7332*7c478bd9Sstevel@tonic-gate 
7333*7c478bd9Sstevel@tonic-gate /*.......................................................................
7334*7c478bd9Sstevel@tonic-gate  * Search backwards for the next character that the user enters. Move back to,
7335*7c478bd9Sstevel@tonic-gate  * but not onto, the found character.
7336*7c478bd9Sstevel@tonic-gate  */
7337*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_to_char)
7338*7c478bd9Sstevel@tonic-gate {
7339*7c478bd9Sstevel@tonic-gate   int pos = gl_find_char(gl, count, 0, 0, '\0');
7340*7c478bd9Sstevel@tonic-gate   return pos >= 0 && gl_place_cursor(gl, pos);
7341*7c478bd9Sstevel@tonic-gate }
7342*7c478bd9Sstevel@tonic-gate 
7343*7c478bd9Sstevel@tonic-gate /*.......................................................................
7344*7c478bd9Sstevel@tonic-gate  * Searching in a given direction, return the index of a given (or
7345*7c478bd9Sstevel@tonic-gate  * read) character in the input line, or the character that precedes
7346*7c478bd9Sstevel@tonic-gate  * it in the specified search direction. Return -1 if not found.
7347*7c478bd9Sstevel@tonic-gate  *
7348*7c478bd9Sstevel@tonic-gate  * Input:
7349*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
7350*7c478bd9Sstevel@tonic-gate  *  count        int    The number of times to search.
7351*7c478bd9Sstevel@tonic-gate  *  forward      int    True if searching forward.
7352*7c478bd9Sstevel@tonic-gate  *  onto         int    True if the search should end on top of the
7353*7c478bd9Sstevel@tonic-gate  *                      character, false if the search should stop
7354*7c478bd9Sstevel@tonic-gate  *                      one character before the character in the
7355*7c478bd9Sstevel@tonic-gate  *                      specified search direction.
7356*7c478bd9Sstevel@tonic-gate  *  c           char    The character to be sought, or '\0' if the
7357*7c478bd9Sstevel@tonic-gate  *                      character should be read from the user.
7358*7c478bd9Sstevel@tonic-gate  * Output:
7359*7c478bd9Sstevel@tonic-gate  *  return       int    The index of the character in gl->line[], or
7360*7c478bd9Sstevel@tonic-gate  *                      -1 if not found.
7361*7c478bd9Sstevel@tonic-gate  */
7362*7c478bd9Sstevel@tonic-gate static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c)
7363*7c478bd9Sstevel@tonic-gate {
7364*7c478bd9Sstevel@tonic-gate   int pos;     /* The index reached in searching the input line */
7365*7c478bd9Sstevel@tonic-gate   int i;
7366*7c478bd9Sstevel@tonic-gate /*
7367*7c478bd9Sstevel@tonic-gate  * Get a character from the user?
7368*7c478bd9Sstevel@tonic-gate  */
7369*7c478bd9Sstevel@tonic-gate   if(!c) {
7370*7c478bd9Sstevel@tonic-gate /*
7371*7c478bd9Sstevel@tonic-gate  * If we are in the process of repeating a previous change command, substitute
7372*7c478bd9Sstevel@tonic-gate  * the last find character.
7373*7c478bd9Sstevel@tonic-gate  */
7374*7c478bd9Sstevel@tonic-gate     if(gl->vi.repeat.active) {
7375*7c478bd9Sstevel@tonic-gate       c = gl->vi.find_char;
7376*7c478bd9Sstevel@tonic-gate     } else {
7377*7c478bd9Sstevel@tonic-gate       if(gl_read_terminal(gl, 1, &c))
7378*7c478bd9Sstevel@tonic-gate 	return -1;
7379*7c478bd9Sstevel@tonic-gate /*
7380*7c478bd9Sstevel@tonic-gate  * Record the details of the new search, for use by repeat finds.
7381*7c478bd9Sstevel@tonic-gate  */
7382*7c478bd9Sstevel@tonic-gate       gl->vi.find_forward = forward;
7383*7c478bd9Sstevel@tonic-gate       gl->vi.find_onto = onto;
7384*7c478bd9Sstevel@tonic-gate       gl->vi.find_char = c;
7385*7c478bd9Sstevel@tonic-gate     };
7386*7c478bd9Sstevel@tonic-gate   };
7387*7c478bd9Sstevel@tonic-gate /*
7388*7c478bd9Sstevel@tonic-gate  * Which direction should we search?
7389*7c478bd9Sstevel@tonic-gate  */
7390*7c478bd9Sstevel@tonic-gate   if(forward) {
7391*7c478bd9Sstevel@tonic-gate /*
7392*7c478bd9Sstevel@tonic-gate  * Search forwards 'count' times for the character, starting with the
7393*7c478bd9Sstevel@tonic-gate  * character that follows the cursor.
7394*7c478bd9Sstevel@tonic-gate  */
7395*7c478bd9Sstevel@tonic-gate     for(i=0, pos=gl->buff_curpos; i<count && pos < gl->ntotal; i++) {
7396*7c478bd9Sstevel@tonic-gate /*
7397*7c478bd9Sstevel@tonic-gate  * Advance past the last match (or past the current cursor position
7398*7c478bd9Sstevel@tonic-gate  * on the first search).
7399*7c478bd9Sstevel@tonic-gate  */
7400*7c478bd9Sstevel@tonic-gate       pos++;
7401*7c478bd9Sstevel@tonic-gate /*
7402*7c478bd9Sstevel@tonic-gate  * Search for the next instance of c.
7403*7c478bd9Sstevel@tonic-gate  */
7404*7c478bd9Sstevel@tonic-gate       for( ; pos<gl->ntotal && c!=gl->line[pos]; pos++)
7405*7c478bd9Sstevel@tonic-gate 	;
7406*7c478bd9Sstevel@tonic-gate     };
7407*7c478bd9Sstevel@tonic-gate /*
7408*7c478bd9Sstevel@tonic-gate  * If the character was found and we have been requested to return the
7409*7c478bd9Sstevel@tonic-gate  * position of the character that precedes the desired character, then
7410*7c478bd9Sstevel@tonic-gate  * we have gone one character too far.
7411*7c478bd9Sstevel@tonic-gate  */
7412*7c478bd9Sstevel@tonic-gate     if(!onto && pos<gl->ntotal)
7413*7c478bd9Sstevel@tonic-gate       pos--;
7414*7c478bd9Sstevel@tonic-gate   } else {
7415*7c478bd9Sstevel@tonic-gate /*
7416*7c478bd9Sstevel@tonic-gate  * Search backwards 'count' times for the character, starting with the
7417*7c478bd9Sstevel@tonic-gate  * character that precedes the cursor.
7418*7c478bd9Sstevel@tonic-gate  */
7419*7c478bd9Sstevel@tonic-gate     for(i=0, pos=gl->buff_curpos; i<count && pos >= gl->insert_curpos; i++) {
7420*7c478bd9Sstevel@tonic-gate /*
7421*7c478bd9Sstevel@tonic-gate  * Step back one from the last match (or from the current cursor
7422*7c478bd9Sstevel@tonic-gate  * position on the first search).
7423*7c478bd9Sstevel@tonic-gate  */
7424*7c478bd9Sstevel@tonic-gate       pos--;
7425*7c478bd9Sstevel@tonic-gate /*
7426*7c478bd9Sstevel@tonic-gate  * Search for the next instance of c.
7427*7c478bd9Sstevel@tonic-gate  */
7428*7c478bd9Sstevel@tonic-gate       for( ; pos>=gl->insert_curpos && c!=gl->line[pos]; pos--)
7429*7c478bd9Sstevel@tonic-gate 	;
7430*7c478bd9Sstevel@tonic-gate     };
7431*7c478bd9Sstevel@tonic-gate /*
7432*7c478bd9Sstevel@tonic-gate  * If the character was found and we have been requested to return the
7433*7c478bd9Sstevel@tonic-gate  * position of the character that precedes the desired character, then
7434*7c478bd9Sstevel@tonic-gate  * we have gone one character too far.
7435*7c478bd9Sstevel@tonic-gate  */
7436*7c478bd9Sstevel@tonic-gate     if(!onto && pos>=gl->insert_curpos)
7437*7c478bd9Sstevel@tonic-gate       pos++;
7438*7c478bd9Sstevel@tonic-gate   };
7439*7c478bd9Sstevel@tonic-gate /*
7440*7c478bd9Sstevel@tonic-gate  * If found, return the cursor position of the count'th match.
7441*7c478bd9Sstevel@tonic-gate  * Otherwise ring the terminal bell.
7442*7c478bd9Sstevel@tonic-gate  */
7443*7c478bd9Sstevel@tonic-gate   if(pos >= gl->insert_curpos && pos < gl->ntotal) {
7444*7c478bd9Sstevel@tonic-gate     return pos;
7445*7c478bd9Sstevel@tonic-gate   } else {
7446*7c478bd9Sstevel@tonic-gate     (void) gl_ring_bell(gl, 1, NULL);
7447*7c478bd9Sstevel@tonic-gate     return -1;
7448*7c478bd9Sstevel@tonic-gate   }
7449*7c478bd9Sstevel@tonic-gate }
7450*7c478bd9Sstevel@tonic-gate 
7451*7c478bd9Sstevel@tonic-gate /*.......................................................................
7452*7c478bd9Sstevel@tonic-gate  * Repeat the last character search in the same direction as the last
7453*7c478bd9Sstevel@tonic-gate  * search.
7454*7c478bd9Sstevel@tonic-gate  */
7455*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_repeat_find_char)
7456*7c478bd9Sstevel@tonic-gate {
7457*7c478bd9Sstevel@tonic-gate   int pos = gl->vi.find_char ?
7458*7c478bd9Sstevel@tonic-gate     gl_find_char(gl, count, gl->vi.find_forward, gl->vi.find_onto,
7459*7c478bd9Sstevel@tonic-gate 		 gl->vi.find_char) : -1;
7460*7c478bd9Sstevel@tonic-gate   return pos >= 0 && gl_place_cursor(gl, pos);
7461*7c478bd9Sstevel@tonic-gate }
7462*7c478bd9Sstevel@tonic-gate 
7463*7c478bd9Sstevel@tonic-gate /*.......................................................................
7464*7c478bd9Sstevel@tonic-gate  * Repeat the last character search in the opposite direction as the last
7465*7c478bd9Sstevel@tonic-gate  * search.
7466*7c478bd9Sstevel@tonic-gate  */
7467*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_invert_refind_char)
7468*7c478bd9Sstevel@tonic-gate {
7469*7c478bd9Sstevel@tonic-gate   int pos = gl->vi.find_char ?
7470*7c478bd9Sstevel@tonic-gate     gl_find_char(gl, count, !gl->vi.find_forward, gl->vi.find_onto,
7471*7c478bd9Sstevel@tonic-gate 		 gl->vi.find_char) : -1;
7472*7c478bd9Sstevel@tonic-gate   return pos >= 0 && gl_place_cursor(gl, pos);
7473*7c478bd9Sstevel@tonic-gate }
7474*7c478bd9Sstevel@tonic-gate 
7475*7c478bd9Sstevel@tonic-gate /*.......................................................................
7476*7c478bd9Sstevel@tonic-gate  * Search forward from the current position of the cursor for 'count'
7477*7c478bd9Sstevel@tonic-gate  * word endings, returning the index of the last one found, or the end of
7478*7c478bd9Sstevel@tonic-gate  * the line if there were less than 'count' words.
7479*7c478bd9Sstevel@tonic-gate  *
7480*7c478bd9Sstevel@tonic-gate  * Input:
7481*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
7482*7c478bd9Sstevel@tonic-gate  *  n            int    The number of word boundaries to search for.
7483*7c478bd9Sstevel@tonic-gate  * Output:
7484*7c478bd9Sstevel@tonic-gate  *  return       int    The buffer index of the located position.
7485*7c478bd9Sstevel@tonic-gate  */
7486*7c478bd9Sstevel@tonic-gate static int gl_nth_word_end_forward(GetLine *gl, int n)
7487*7c478bd9Sstevel@tonic-gate {
7488*7c478bd9Sstevel@tonic-gate   int bufpos;   /* The buffer index being checked. */
7489*7c478bd9Sstevel@tonic-gate   int i;
7490*7c478bd9Sstevel@tonic-gate /*
7491*7c478bd9Sstevel@tonic-gate  * In order to guarantee forward motion to the next word ending,
7492*7c478bd9Sstevel@tonic-gate  * we need to start from one position to the right of the cursor
7493*7c478bd9Sstevel@tonic-gate  * position, since this may already be at the end of a word.
7494*7c478bd9Sstevel@tonic-gate  */
7495*7c478bd9Sstevel@tonic-gate   bufpos = gl->buff_curpos + 1;
7496*7c478bd9Sstevel@tonic-gate /*
7497*7c478bd9Sstevel@tonic-gate  * If we are at the end of the line, return the index of the last
7498*7c478bd9Sstevel@tonic-gate  * real character on the line. Note that this will be -1 if the line
7499*7c478bd9Sstevel@tonic-gate  * is empty.
7500*7c478bd9Sstevel@tonic-gate  */
7501*7c478bd9Sstevel@tonic-gate   if(bufpos >= gl->ntotal)
7502*7c478bd9Sstevel@tonic-gate     return gl->ntotal - 1;
7503*7c478bd9Sstevel@tonic-gate /*
7504*7c478bd9Sstevel@tonic-gate  * Search 'n' times, unless the end of the input line is reached first.
7505*7c478bd9Sstevel@tonic-gate  */
7506*7c478bd9Sstevel@tonic-gate   for(i=0; i<n && bufpos<gl->ntotal; i++) {
7507*7c478bd9Sstevel@tonic-gate /*
7508*7c478bd9Sstevel@tonic-gate  * If we are not already within a word, skip to the start of the next word.
7509*7c478bd9Sstevel@tonic-gate  */
7510*7c478bd9Sstevel@tonic-gate     for( ; bufpos<gl->ntotal && !gl_is_word_char((int)gl->line[bufpos]);
7511*7c478bd9Sstevel@tonic-gate 	bufpos++)
7512*7c478bd9Sstevel@tonic-gate       ;
7513*7c478bd9Sstevel@tonic-gate /*
7514*7c478bd9Sstevel@tonic-gate  * Find the end of the next word.
7515*7c478bd9Sstevel@tonic-gate  */
7516*7c478bd9Sstevel@tonic-gate     for( ; bufpos<gl->ntotal && gl_is_word_char((int)gl->line[bufpos]);
7517*7c478bd9Sstevel@tonic-gate 	bufpos++)
7518*7c478bd9Sstevel@tonic-gate       ;
7519*7c478bd9Sstevel@tonic-gate   };
7520*7c478bd9Sstevel@tonic-gate /*
7521*7c478bd9Sstevel@tonic-gate  * We will have overshot.
7522*7c478bd9Sstevel@tonic-gate  */
7523*7c478bd9Sstevel@tonic-gate   return bufpos > 0 ? bufpos-1 : bufpos;
7524*7c478bd9Sstevel@tonic-gate }
7525*7c478bd9Sstevel@tonic-gate 
7526*7c478bd9Sstevel@tonic-gate /*.......................................................................
7527*7c478bd9Sstevel@tonic-gate  * Search forward from the current position of the cursor for 'count'
7528*7c478bd9Sstevel@tonic-gate  * word starts, returning the index of the last one found, or the end of
7529*7c478bd9Sstevel@tonic-gate  * the line if there were less than 'count' words.
7530*7c478bd9Sstevel@tonic-gate  *
7531*7c478bd9Sstevel@tonic-gate  * Input:
7532*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
7533*7c478bd9Sstevel@tonic-gate  *  n            int    The number of word boundaries to search for.
7534*7c478bd9Sstevel@tonic-gate  * Output:
7535*7c478bd9Sstevel@tonic-gate  *  return       int    The buffer index of the located position.
7536*7c478bd9Sstevel@tonic-gate  */
7537*7c478bd9Sstevel@tonic-gate static int gl_nth_word_start_forward(GetLine *gl, int n)
7538*7c478bd9Sstevel@tonic-gate {
7539*7c478bd9Sstevel@tonic-gate   int bufpos;   /* The buffer index being checked. */
7540*7c478bd9Sstevel@tonic-gate   int i;
7541*7c478bd9Sstevel@tonic-gate /*
7542*7c478bd9Sstevel@tonic-gate  * Get the current cursor position.
7543*7c478bd9Sstevel@tonic-gate  */
7544*7c478bd9Sstevel@tonic-gate   bufpos = gl->buff_curpos;
7545*7c478bd9Sstevel@tonic-gate /*
7546*7c478bd9Sstevel@tonic-gate  * Search 'n' times, unless the end of the input line is reached first.
7547*7c478bd9Sstevel@tonic-gate  */
7548*7c478bd9Sstevel@tonic-gate   for(i=0; i<n && bufpos<gl->ntotal; i++) {
7549*7c478bd9Sstevel@tonic-gate /*
7550*7c478bd9Sstevel@tonic-gate  * Find the end of the current word.
7551*7c478bd9Sstevel@tonic-gate  */
7552*7c478bd9Sstevel@tonic-gate     for( ; bufpos<gl->ntotal && gl_is_word_char((int)gl->line[bufpos]);
7553*7c478bd9Sstevel@tonic-gate 	bufpos++)
7554*7c478bd9Sstevel@tonic-gate       ;
7555*7c478bd9Sstevel@tonic-gate /*
7556*7c478bd9Sstevel@tonic-gate  * Skip to the start of the next word.
7557*7c478bd9Sstevel@tonic-gate  */
7558*7c478bd9Sstevel@tonic-gate     for( ; bufpos<gl->ntotal && !gl_is_word_char((int)gl->line[bufpos]);
7559*7c478bd9Sstevel@tonic-gate 	bufpos++)
7560*7c478bd9Sstevel@tonic-gate       ;
7561*7c478bd9Sstevel@tonic-gate   };
7562*7c478bd9Sstevel@tonic-gate   return bufpos;
7563*7c478bd9Sstevel@tonic-gate }
7564*7c478bd9Sstevel@tonic-gate 
7565*7c478bd9Sstevel@tonic-gate /*.......................................................................
7566*7c478bd9Sstevel@tonic-gate  * Search backward from the current position of the cursor for 'count'
7567*7c478bd9Sstevel@tonic-gate  * word starts, returning the index of the last one found, or the start
7568*7c478bd9Sstevel@tonic-gate  * of the line if there were less than 'count' words.
7569*7c478bd9Sstevel@tonic-gate  *
7570*7c478bd9Sstevel@tonic-gate  * Input:
7571*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
7572*7c478bd9Sstevel@tonic-gate  *  n            int    The number of word boundaries to search for.
7573*7c478bd9Sstevel@tonic-gate  * Output:
7574*7c478bd9Sstevel@tonic-gate  *  return       int    The buffer index of the located position.
7575*7c478bd9Sstevel@tonic-gate  */
7576*7c478bd9Sstevel@tonic-gate static int gl_nth_word_start_backward(GetLine *gl, int n)
7577*7c478bd9Sstevel@tonic-gate {
7578*7c478bd9Sstevel@tonic-gate   int bufpos;   /* The buffer index being checked. */
7579*7c478bd9Sstevel@tonic-gate   int i;
7580*7c478bd9Sstevel@tonic-gate /*
7581*7c478bd9Sstevel@tonic-gate  * Get the current cursor position.
7582*7c478bd9Sstevel@tonic-gate  */
7583*7c478bd9Sstevel@tonic-gate   bufpos = gl->buff_curpos;
7584*7c478bd9Sstevel@tonic-gate /*
7585*7c478bd9Sstevel@tonic-gate  * Search 'n' times, unless the beginning of the input line (or vi insertion
7586*7c478bd9Sstevel@tonic-gate  * point) is reached first.
7587*7c478bd9Sstevel@tonic-gate  */
7588*7c478bd9Sstevel@tonic-gate   for(i=0; i<n && bufpos > gl->insert_curpos; i++) {
7589*7c478bd9Sstevel@tonic-gate /*
7590*7c478bd9Sstevel@tonic-gate  * Starting one character back from the last search, so as not to keep
7591*7c478bd9Sstevel@tonic-gate  * settling on the same word-start, search backwards until finding a
7592*7c478bd9Sstevel@tonic-gate  * word character.
7593*7c478bd9Sstevel@tonic-gate  */
7594*7c478bd9Sstevel@tonic-gate     while(--bufpos >= gl->insert_curpos &&
7595*7c478bd9Sstevel@tonic-gate           !gl_is_word_char((int)gl->line[bufpos]))
7596*7c478bd9Sstevel@tonic-gate       ;
7597*7c478bd9Sstevel@tonic-gate /*
7598*7c478bd9Sstevel@tonic-gate  * Find the start of the word.
7599*7c478bd9Sstevel@tonic-gate  */
7600*7c478bd9Sstevel@tonic-gate     while(--bufpos >= gl->insert_curpos &&
7601*7c478bd9Sstevel@tonic-gate           gl_is_word_char((int)gl->line[bufpos]))
7602*7c478bd9Sstevel@tonic-gate       ;
7603*7c478bd9Sstevel@tonic-gate /*
7604*7c478bd9Sstevel@tonic-gate  * We will have gone one character too far.
7605*7c478bd9Sstevel@tonic-gate  */
7606*7c478bd9Sstevel@tonic-gate     bufpos++;
7607*7c478bd9Sstevel@tonic-gate   };
7608*7c478bd9Sstevel@tonic-gate   return bufpos >= gl->insert_curpos ? bufpos : gl->insert_curpos;
7609*7c478bd9Sstevel@tonic-gate }
7610*7c478bd9Sstevel@tonic-gate 
7611*7c478bd9Sstevel@tonic-gate /*.......................................................................
7612*7c478bd9Sstevel@tonic-gate  * Copy one or more words into the cut buffer without moving the cursor
7613*7c478bd9Sstevel@tonic-gate  * or deleting text.
7614*7c478bd9Sstevel@tonic-gate  */
7615*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_copy_word)
7616*7c478bd9Sstevel@tonic-gate {
7617*7c478bd9Sstevel@tonic-gate /*
7618*7c478bd9Sstevel@tonic-gate  * Find the location of the count'th start or end of a word
7619*7c478bd9Sstevel@tonic-gate  * after the cursor, depending on whether in emacs or vi mode.
7620*7c478bd9Sstevel@tonic-gate  */
7621*7c478bd9Sstevel@tonic-gate   int next = gl->editor == GL_EMACS_MODE ?
7622*7c478bd9Sstevel@tonic-gate     gl_nth_word_end_forward(gl, count) :
7623*7c478bd9Sstevel@tonic-gate     gl_nth_word_start_forward(gl, count);
7624*7c478bd9Sstevel@tonic-gate /*
7625*7c478bd9Sstevel@tonic-gate  * How many characters are to be copied into the cut buffer?
7626*7c478bd9Sstevel@tonic-gate  */
7627*7c478bd9Sstevel@tonic-gate   int n = next - gl->buff_curpos;
7628*7c478bd9Sstevel@tonic-gate /*
7629*7c478bd9Sstevel@tonic-gate  * Copy the specified segment and terminate the string.
7630*7c478bd9Sstevel@tonic-gate  */
7631*7c478bd9Sstevel@tonic-gate   memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n);
7632*7c478bd9Sstevel@tonic-gate   gl->cutbuf[n] = '\0';
7633*7c478bd9Sstevel@tonic-gate   return 0;
7634*7c478bd9Sstevel@tonic-gate }
7635*7c478bd9Sstevel@tonic-gate 
7636*7c478bd9Sstevel@tonic-gate /*.......................................................................
7637*7c478bd9Sstevel@tonic-gate  * Copy one or more words preceding the cursor into the cut buffer,
7638*7c478bd9Sstevel@tonic-gate  * without moving the cursor or deleting text.
7639*7c478bd9Sstevel@tonic-gate  */
7640*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_copy_word)
7641*7c478bd9Sstevel@tonic-gate {
7642*7c478bd9Sstevel@tonic-gate /*
7643*7c478bd9Sstevel@tonic-gate  * Find the location of the count'th start of word before the cursor.
7644*7c478bd9Sstevel@tonic-gate  */
7645*7c478bd9Sstevel@tonic-gate   int next = gl_nth_word_start_backward(gl, count);
7646*7c478bd9Sstevel@tonic-gate /*
7647*7c478bd9Sstevel@tonic-gate  * How many characters are to be copied into the cut buffer?
7648*7c478bd9Sstevel@tonic-gate  */
7649*7c478bd9Sstevel@tonic-gate   int n = gl->buff_curpos - next;
7650*7c478bd9Sstevel@tonic-gate   gl_place_cursor(gl, next);
7651*7c478bd9Sstevel@tonic-gate /*
7652*7c478bd9Sstevel@tonic-gate  * Copy the specified segment and terminate the string.
7653*7c478bd9Sstevel@tonic-gate  */
7654*7c478bd9Sstevel@tonic-gate   memcpy(gl->cutbuf, gl->line + next, n);
7655*7c478bd9Sstevel@tonic-gate   gl->cutbuf[n] = '\0';
7656*7c478bd9Sstevel@tonic-gate   return 0;
7657*7c478bd9Sstevel@tonic-gate }
7658*7c478bd9Sstevel@tonic-gate 
7659*7c478bd9Sstevel@tonic-gate /*.......................................................................
7660*7c478bd9Sstevel@tonic-gate  * Copy the characters between the cursor and the count'th instance of
7661*7c478bd9Sstevel@tonic-gate  * a specified character in the input line, into the cut buffer.
7662*7c478bd9Sstevel@tonic-gate  *
7663*7c478bd9Sstevel@tonic-gate  * Input:
7664*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
7665*7c478bd9Sstevel@tonic-gate  *  count        int    The number of times to search.
7666*7c478bd9Sstevel@tonic-gate  *  c           char    The character to be searched for, or '\0' if
7667*7c478bd9Sstevel@tonic-gate  *                      the character should be read from the user.
7668*7c478bd9Sstevel@tonic-gate  *  forward      int    True if searching forward.
7669*7c478bd9Sstevel@tonic-gate  *  onto         int    True if the search should end on top of the
7670*7c478bd9Sstevel@tonic-gate  *                      character, false if the search should stop
7671*7c478bd9Sstevel@tonic-gate  *                      one character before the character in the
7672*7c478bd9Sstevel@tonic-gate  *                      specified search direction.
7673*7c478bd9Sstevel@tonic-gate  * Output:
7674*7c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
7675*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
7676*7c478bd9Sstevel@tonic-gate  *
7677*7c478bd9Sstevel@tonic-gate  */
7678*7c478bd9Sstevel@tonic-gate static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto)
7679*7c478bd9Sstevel@tonic-gate {
7680*7c478bd9Sstevel@tonic-gate   int n;  /* The number of characters in the cut buffer */
7681*7c478bd9Sstevel@tonic-gate /*
7682*7c478bd9Sstevel@tonic-gate  * Search for the character, and abort the operation if not found.
7683*7c478bd9Sstevel@tonic-gate  */
7684*7c478bd9Sstevel@tonic-gate   int pos = gl_find_char(gl, count, forward, onto, c);
7685*7c478bd9Sstevel@tonic-gate   if(pos < 0)
7686*7c478bd9Sstevel@tonic-gate     return 0;
7687*7c478bd9Sstevel@tonic-gate /*
7688*7c478bd9Sstevel@tonic-gate  * Copy the specified segment.
7689*7c478bd9Sstevel@tonic-gate  */
7690*7c478bd9Sstevel@tonic-gate   if(forward) {
7691*7c478bd9Sstevel@tonic-gate     n = pos + 1 - gl->buff_curpos;
7692*7c478bd9Sstevel@tonic-gate     memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n);
7693*7c478bd9Sstevel@tonic-gate   } else {
7694*7c478bd9Sstevel@tonic-gate     n = gl->buff_curpos - pos;
7695*7c478bd9Sstevel@tonic-gate     memcpy(gl->cutbuf, gl->line + pos, n);
7696*7c478bd9Sstevel@tonic-gate     if(gl->editor == GL_VI_MODE)
7697*7c478bd9Sstevel@tonic-gate       gl_place_cursor(gl, pos);
7698*7c478bd9Sstevel@tonic-gate   }
7699*7c478bd9Sstevel@tonic-gate /*
7700*7c478bd9Sstevel@tonic-gate  * Terminate the copy.
7701*7c478bd9Sstevel@tonic-gate  */
7702*7c478bd9Sstevel@tonic-gate   gl->cutbuf[n] = '\0';
7703*7c478bd9Sstevel@tonic-gate   return 0;
7704*7c478bd9Sstevel@tonic-gate }
7705*7c478bd9Sstevel@tonic-gate 
7706*7c478bd9Sstevel@tonic-gate /*.......................................................................
7707*7c478bd9Sstevel@tonic-gate  * Copy a section up to and including a specified character into the cut
7708*7c478bd9Sstevel@tonic-gate  * buffer without moving the cursor or deleting text.
7709*7c478bd9Sstevel@tonic-gate  */
7710*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_copy_find)
7711*7c478bd9Sstevel@tonic-gate {
7712*7c478bd9Sstevel@tonic-gate   return gl_copy_find(gl, count, '\0', 1, 1);
7713*7c478bd9Sstevel@tonic-gate }
7714*7c478bd9Sstevel@tonic-gate 
7715*7c478bd9Sstevel@tonic-gate /*.......................................................................
7716*7c478bd9Sstevel@tonic-gate  * Copy a section back to and including a specified character into the cut
7717*7c478bd9Sstevel@tonic-gate  * buffer without moving the cursor or deleting text.
7718*7c478bd9Sstevel@tonic-gate  */
7719*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_copy_find)
7720*7c478bd9Sstevel@tonic-gate {
7721*7c478bd9Sstevel@tonic-gate   return gl_copy_find(gl, count, '\0', 0, 1);
7722*7c478bd9Sstevel@tonic-gate }
7723*7c478bd9Sstevel@tonic-gate 
7724*7c478bd9Sstevel@tonic-gate /*.......................................................................
7725*7c478bd9Sstevel@tonic-gate  * Copy a section up to and not including a specified character into the cut
7726*7c478bd9Sstevel@tonic-gate  * buffer without moving the cursor or deleting text.
7727*7c478bd9Sstevel@tonic-gate  */
7728*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_forward_copy_to)
7729*7c478bd9Sstevel@tonic-gate {
7730*7c478bd9Sstevel@tonic-gate   return gl_copy_find(gl, count, '\0', 1, 0);
7731*7c478bd9Sstevel@tonic-gate }
7732*7c478bd9Sstevel@tonic-gate 
7733*7c478bd9Sstevel@tonic-gate /*.......................................................................
7734*7c478bd9Sstevel@tonic-gate  * Copy a section back to and not including a specified character into the cut
7735*7c478bd9Sstevel@tonic-gate  * buffer without moving the cursor or deleting text.
7736*7c478bd9Sstevel@tonic-gate  */
7737*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_backward_copy_to)
7738*7c478bd9Sstevel@tonic-gate {
7739*7c478bd9Sstevel@tonic-gate   return gl_copy_find(gl, count, '\0', 0, 0);
7740*7c478bd9Sstevel@tonic-gate }
7741*7c478bd9Sstevel@tonic-gate 
7742*7c478bd9Sstevel@tonic-gate /*.......................................................................
7743*7c478bd9Sstevel@tonic-gate  * Copy to a character specified in a previous search into the cut
7744*7c478bd9Sstevel@tonic-gate  * buffer without moving the cursor or deleting text.
7745*7c478bd9Sstevel@tonic-gate  */
7746*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_refind)
7747*7c478bd9Sstevel@tonic-gate {
7748*7c478bd9Sstevel@tonic-gate   return gl_copy_find(gl, count, gl->vi.find_char, gl->vi.find_forward,
7749*7c478bd9Sstevel@tonic-gate 		      gl->vi.find_onto);
7750*7c478bd9Sstevel@tonic-gate }
7751*7c478bd9Sstevel@tonic-gate 
7752*7c478bd9Sstevel@tonic-gate /*.......................................................................
7753*7c478bd9Sstevel@tonic-gate  * Copy to a character specified in a previous search, but in the opposite
7754*7c478bd9Sstevel@tonic-gate  * direction, into the cut buffer without moving the cursor or deleting text.
7755*7c478bd9Sstevel@tonic-gate  */
7756*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_copy_invert_refind)
7757*7c478bd9Sstevel@tonic-gate {
7758*7c478bd9Sstevel@tonic-gate   return gl_copy_find(gl, count, gl->vi.find_char, !gl->vi.find_forward,
7759*7c478bd9Sstevel@tonic-gate 		      gl->vi.find_onto);
7760*7c478bd9Sstevel@tonic-gate }
7761*7c478bd9Sstevel@tonic-gate 
7762*7c478bd9Sstevel@tonic-gate /*.......................................................................
7763*7c478bd9Sstevel@tonic-gate  * Set the position of the cursor in the line input buffer and the
7764*7c478bd9Sstevel@tonic-gate  * terminal.
7765*7c478bd9Sstevel@tonic-gate  *
7766*7c478bd9Sstevel@tonic-gate  * Input:
7767*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
7768*7c478bd9Sstevel@tonic-gate  *  buff_curpos  int    The new buffer cursor position.
7769*7c478bd9Sstevel@tonic-gate  * Output:
7770*7c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
7771*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
7772*7c478bd9Sstevel@tonic-gate  */
7773*7c478bd9Sstevel@tonic-gate static int gl_place_cursor(GetLine *gl, int buff_curpos)
7774*7c478bd9Sstevel@tonic-gate {
7775*7c478bd9Sstevel@tonic-gate /*
7776*7c478bd9Sstevel@tonic-gate  * Don't allow the cursor position to go out of the bounds of the input
7777*7c478bd9Sstevel@tonic-gate  * line.
7778*7c478bd9Sstevel@tonic-gate  */
7779*7c478bd9Sstevel@tonic-gate   if(buff_curpos >= gl->ntotal)
7780*7c478bd9Sstevel@tonic-gate     buff_curpos = gl->vi.command ? gl->ntotal-1 : gl->ntotal;
7781*7c478bd9Sstevel@tonic-gate   if(buff_curpos < 0)
7782*7c478bd9Sstevel@tonic-gate     buff_curpos = 0;
7783*7c478bd9Sstevel@tonic-gate /*
7784*7c478bd9Sstevel@tonic-gate  * Record the new buffer position.
7785*7c478bd9Sstevel@tonic-gate  */
7786*7c478bd9Sstevel@tonic-gate   gl->buff_curpos = buff_curpos;
7787*7c478bd9Sstevel@tonic-gate /*
7788*7c478bd9Sstevel@tonic-gate  * Move the terminal cursor to the corresponding character.
7789*7c478bd9Sstevel@tonic-gate  */
7790*7c478bd9Sstevel@tonic-gate   return gl_set_term_curpos(gl, gl->prompt_len +
7791*7c478bd9Sstevel@tonic-gate     gl_displayed_string_width(gl, gl->line, buff_curpos, gl->prompt_len));
7792*7c478bd9Sstevel@tonic-gate }
7793*7c478bd9Sstevel@tonic-gate 
7794*7c478bd9Sstevel@tonic-gate /*.......................................................................
7795*7c478bd9Sstevel@tonic-gate  * In vi command mode, this function saves the current line to the
7796*7c478bd9Sstevel@tonic-gate  * historical buffer needed by the undo command. In emacs mode it does
7797*7c478bd9Sstevel@tonic-gate  * nothing. In order to allow action functions to call other action
7798*7c478bd9Sstevel@tonic-gate  * functions, gl_interpret_char() sets gl->vi.undo.saved to 0 before
7799*7c478bd9Sstevel@tonic-gate  * invoking an action, and thereafter once any call to this function
7800*7c478bd9Sstevel@tonic-gate  * has set it to 1, further calls are ignored.
7801*7c478bd9Sstevel@tonic-gate  *
7802*7c478bd9Sstevel@tonic-gate  * Input:
7803*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
7804*7c478bd9Sstevel@tonic-gate  */
7805*7c478bd9Sstevel@tonic-gate static void gl_save_for_undo(GetLine *gl)
7806*7c478bd9Sstevel@tonic-gate {
7807*7c478bd9Sstevel@tonic-gate   if(gl->vi.command && !gl->vi.undo.saved) {
7808*7c478bd9Sstevel@tonic-gate     strlcpy(gl->vi.undo.line, gl->line, gl->linelen);
7809*7c478bd9Sstevel@tonic-gate     gl->vi.undo.buff_curpos = gl->buff_curpos;
7810*7c478bd9Sstevel@tonic-gate     gl->vi.undo.ntotal = gl->ntotal;
7811*7c478bd9Sstevel@tonic-gate     gl->vi.undo.saved = 1;
7812*7c478bd9Sstevel@tonic-gate   };
7813*7c478bd9Sstevel@tonic-gate   if(gl->vi.command && !gl->vi.repeat.saved &&
7814*7c478bd9Sstevel@tonic-gate      gl->current_action.fn != gl_vi_repeat_change) {
7815*7c478bd9Sstevel@tonic-gate     gl->vi.repeat.action = gl->current_action;
7816*7c478bd9Sstevel@tonic-gate     gl->vi.repeat.count = gl->current_count;
7817*7c478bd9Sstevel@tonic-gate     gl->vi.repeat.saved = 1;
7818*7c478bd9Sstevel@tonic-gate   };
7819*7c478bd9Sstevel@tonic-gate   return;
7820*7c478bd9Sstevel@tonic-gate }
7821*7c478bd9Sstevel@tonic-gate 
7822*7c478bd9Sstevel@tonic-gate /*.......................................................................
7823*7c478bd9Sstevel@tonic-gate  * In vi mode, restore the line to the way it was before the last command
7824*7c478bd9Sstevel@tonic-gate  * mode operation, storing the current line in the buffer so that the
7825*7c478bd9Sstevel@tonic-gate  * undo operation itself can subsequently be undone.
7826*7c478bd9Sstevel@tonic-gate  */
7827*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_undo)
7828*7c478bd9Sstevel@tonic-gate {
7829*7c478bd9Sstevel@tonic-gate /*
7830*7c478bd9Sstevel@tonic-gate  * Get pointers into the two lines.
7831*7c478bd9Sstevel@tonic-gate  */
7832*7c478bd9Sstevel@tonic-gate   char *undo_ptr = gl->vi.undo.line;
7833*7c478bd9Sstevel@tonic-gate   char *line_ptr = gl->line;
7834*7c478bd9Sstevel@tonic-gate /*
7835*7c478bd9Sstevel@tonic-gate  * Swap the characters of the two buffers up to the length of the shortest
7836*7c478bd9Sstevel@tonic-gate  * line.
7837*7c478bd9Sstevel@tonic-gate  */
7838*7c478bd9Sstevel@tonic-gate   while(*undo_ptr && *line_ptr) {
7839*7c478bd9Sstevel@tonic-gate     char c = *undo_ptr;
7840*7c478bd9Sstevel@tonic-gate     *undo_ptr++ = *line_ptr;
7841*7c478bd9Sstevel@tonic-gate     *line_ptr++ = c;
7842*7c478bd9Sstevel@tonic-gate   };
7843*7c478bd9Sstevel@tonic-gate /*
7844*7c478bd9Sstevel@tonic-gate  * Copy the rest directly.
7845*7c478bd9Sstevel@tonic-gate  */
7846*7c478bd9Sstevel@tonic-gate   if(gl->ntotal > gl->vi.undo.ntotal) {
7847*7c478bd9Sstevel@tonic-gate     strlcpy(undo_ptr, line_ptr, gl->linelen);
7848*7c478bd9Sstevel@tonic-gate     *line_ptr = '\0';
7849*7c478bd9Sstevel@tonic-gate   } else {
7850*7c478bd9Sstevel@tonic-gate     strlcpy(line_ptr, undo_ptr, gl->linelen);
7851*7c478bd9Sstevel@tonic-gate     *undo_ptr = '\0';
7852*7c478bd9Sstevel@tonic-gate   };
7853*7c478bd9Sstevel@tonic-gate /*
7854*7c478bd9Sstevel@tonic-gate  * Record the length of the stored string.
7855*7c478bd9Sstevel@tonic-gate  */
7856*7c478bd9Sstevel@tonic-gate   gl->vi.undo.ntotal = gl->ntotal;
7857*7c478bd9Sstevel@tonic-gate /*
7858*7c478bd9Sstevel@tonic-gate  * Accomodate the new contents of gl->line[].
7859*7c478bd9Sstevel@tonic-gate  */
7860*7c478bd9Sstevel@tonic-gate   gl_update_buffer(gl);
7861*7c478bd9Sstevel@tonic-gate /*
7862*7c478bd9Sstevel@tonic-gate  * Set both cursor positions to the leftmost of the saved and current
7863*7c478bd9Sstevel@tonic-gate  * cursor positions to emulate what vi does.
7864*7c478bd9Sstevel@tonic-gate  */
7865*7c478bd9Sstevel@tonic-gate   if(gl->buff_curpos < gl->vi.undo.buff_curpos)
7866*7c478bd9Sstevel@tonic-gate     gl->vi.undo.buff_curpos = gl->buff_curpos;
7867*7c478bd9Sstevel@tonic-gate   else
7868*7c478bd9Sstevel@tonic-gate     gl->buff_curpos = gl->vi.undo.buff_curpos;
7869*7c478bd9Sstevel@tonic-gate /*
7870*7c478bd9Sstevel@tonic-gate  * Since we have bipassed calling gl_save_for_undo(), record repeat
7871*7c478bd9Sstevel@tonic-gate  * information inline.
7872*7c478bd9Sstevel@tonic-gate  */
7873*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.action.fn = gl_vi_undo;
7874*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.action.data = NULL;
7875*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.count = 1;
7876*7c478bd9Sstevel@tonic-gate /*
7877*7c478bd9Sstevel@tonic-gate  * Display the restored line.
7878*7c478bd9Sstevel@tonic-gate  */
7879*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
7880*7c478bd9Sstevel@tonic-gate   return 0;
7881*7c478bd9Sstevel@tonic-gate }
7882*7c478bd9Sstevel@tonic-gate 
7883*7c478bd9Sstevel@tonic-gate /*.......................................................................
7884*7c478bd9Sstevel@tonic-gate  * Delete the following word and leave the user in vi insert mode.
7885*7c478bd9Sstevel@tonic-gate  */
7886*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_forward_change_word)
7887*7c478bd9Sstevel@tonic-gate {
7888*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
7889*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;	/* Allow cursor at EOL */
7890*7c478bd9Sstevel@tonic-gate   return gl_forward_delete_word(gl, count, NULL) || gl_vi_insert(gl, 0, NULL);
7891*7c478bd9Sstevel@tonic-gate }
7892*7c478bd9Sstevel@tonic-gate 
7893*7c478bd9Sstevel@tonic-gate /*.......................................................................
7894*7c478bd9Sstevel@tonic-gate  * Delete the preceding word and leave the user in vi insert mode.
7895*7c478bd9Sstevel@tonic-gate  */
7896*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_backward_change_word)
7897*7c478bd9Sstevel@tonic-gate {
7898*7c478bd9Sstevel@tonic-gate   return gl_backward_delete_word(gl, count, NULL) || gl_vi_insert(gl, 0, NULL);
7899*7c478bd9Sstevel@tonic-gate }
7900*7c478bd9Sstevel@tonic-gate 
7901*7c478bd9Sstevel@tonic-gate /*.......................................................................
7902*7c478bd9Sstevel@tonic-gate  * Delete the following section and leave the user in vi insert mode.
7903*7c478bd9Sstevel@tonic-gate  */
7904*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_forward_change_find)
7905*7c478bd9Sstevel@tonic-gate {
7906*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, '\0', 1, 1, 1);
7907*7c478bd9Sstevel@tonic-gate }
7908*7c478bd9Sstevel@tonic-gate 
7909*7c478bd9Sstevel@tonic-gate /*.......................................................................
7910*7c478bd9Sstevel@tonic-gate  * Delete the preceding section and leave the user in vi insert mode.
7911*7c478bd9Sstevel@tonic-gate  */
7912*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_backward_change_find)
7913*7c478bd9Sstevel@tonic-gate {
7914*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, '\0', 0, 1, 1);
7915*7c478bd9Sstevel@tonic-gate }
7916*7c478bd9Sstevel@tonic-gate 
7917*7c478bd9Sstevel@tonic-gate /*.......................................................................
7918*7c478bd9Sstevel@tonic-gate  * Delete the following section and leave the user in vi insert mode.
7919*7c478bd9Sstevel@tonic-gate  */
7920*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_forward_change_to)
7921*7c478bd9Sstevel@tonic-gate {
7922*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, '\0', 1, 0, 1);
7923*7c478bd9Sstevel@tonic-gate }
7924*7c478bd9Sstevel@tonic-gate 
7925*7c478bd9Sstevel@tonic-gate /*.......................................................................
7926*7c478bd9Sstevel@tonic-gate  * Delete the preceding section and leave the user in vi insert mode.
7927*7c478bd9Sstevel@tonic-gate  */
7928*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_backward_change_to)
7929*7c478bd9Sstevel@tonic-gate {
7930*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, '\0', 0, 0, 1);
7931*7c478bd9Sstevel@tonic-gate }
7932*7c478bd9Sstevel@tonic-gate 
7933*7c478bd9Sstevel@tonic-gate /*.......................................................................
7934*7c478bd9Sstevel@tonic-gate  * Delete to a character specified by a previous search and leave the user
7935*7c478bd9Sstevel@tonic-gate  * in vi insert mode.
7936*7c478bd9Sstevel@tonic-gate  */
7937*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_refind)
7938*7c478bd9Sstevel@tonic-gate {
7939*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward,
7940*7c478bd9Sstevel@tonic-gate 			gl->vi.find_onto, 1);
7941*7c478bd9Sstevel@tonic-gate }
7942*7c478bd9Sstevel@tonic-gate 
7943*7c478bd9Sstevel@tonic-gate /*.......................................................................
7944*7c478bd9Sstevel@tonic-gate  * Delete to a character specified by a previous search, but in the opposite
7945*7c478bd9Sstevel@tonic-gate  * direction, and leave the user in vi insert mode.
7946*7c478bd9Sstevel@tonic-gate  */
7947*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_invert_refind)
7948*7c478bd9Sstevel@tonic-gate {
7949*7c478bd9Sstevel@tonic-gate   return gl_delete_find(gl, count, gl->vi.find_char, !gl->vi.find_forward,
7950*7c478bd9Sstevel@tonic-gate 			gl->vi.find_onto, 1);
7951*7c478bd9Sstevel@tonic-gate }
7952*7c478bd9Sstevel@tonic-gate 
7953*7c478bd9Sstevel@tonic-gate /*.......................................................................
7954*7c478bd9Sstevel@tonic-gate  * Delete the following character and leave the user in vi insert mode.
7955*7c478bd9Sstevel@tonic-gate  */
7956*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_forward_change_char)
7957*7c478bd9Sstevel@tonic-gate {
7958*7c478bd9Sstevel@tonic-gate   gl_save_for_undo(gl);
7959*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;	/* Allow cursor at EOL */
7960*7c478bd9Sstevel@tonic-gate   return gl_delete_chars(gl, count, 1) || gl_vi_insert(gl, 0, NULL);
7961*7c478bd9Sstevel@tonic-gate }
7962*7c478bd9Sstevel@tonic-gate 
7963*7c478bd9Sstevel@tonic-gate /*.......................................................................
7964*7c478bd9Sstevel@tonic-gate  * Delete the preceding character and leave the user in vi insert mode.
7965*7c478bd9Sstevel@tonic-gate  */
7966*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_backward_change_char)
7967*7c478bd9Sstevel@tonic-gate {
7968*7c478bd9Sstevel@tonic-gate   return gl_backward_delete_char(gl, count, NULL) || gl_vi_insert(gl, 0, NULL);
7969*7c478bd9Sstevel@tonic-gate }
7970*7c478bd9Sstevel@tonic-gate 
7971*7c478bd9Sstevel@tonic-gate /*.......................................................................
7972*7c478bd9Sstevel@tonic-gate  * Starting from the cursor position change characters to the specified column.
7973*7c478bd9Sstevel@tonic-gate  */
7974*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_to_column)
7975*7c478bd9Sstevel@tonic-gate {
7976*7c478bd9Sstevel@tonic-gate   if (--count >= gl->buff_curpos)
7977*7c478bd9Sstevel@tonic-gate     return gl_vi_forward_change_char(gl, count - gl->buff_curpos, NULL);
7978*7c478bd9Sstevel@tonic-gate   else
7979*7c478bd9Sstevel@tonic-gate     return gl_vi_backward_change_char(gl, gl->buff_curpos - count, NULL);
7980*7c478bd9Sstevel@tonic-gate }
7981*7c478bd9Sstevel@tonic-gate 
7982*7c478bd9Sstevel@tonic-gate /*.......................................................................
7983*7c478bd9Sstevel@tonic-gate  * Starting from the cursor position change characters to a matching
7984*7c478bd9Sstevel@tonic-gate  * parenthesis.
7985*7c478bd9Sstevel@tonic-gate  */
7986*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_change_to_parenthesis)
7987*7c478bd9Sstevel@tonic-gate {
7988*7c478bd9Sstevel@tonic-gate   int curpos = gl_index_of_matching_paren(gl);
7989*7c478bd9Sstevel@tonic-gate   if(curpos >= 0) {
7990*7c478bd9Sstevel@tonic-gate     gl_save_for_undo(gl);
7991*7c478bd9Sstevel@tonic-gate     if(curpos >= gl->buff_curpos)
7992*7c478bd9Sstevel@tonic-gate       return gl_vi_forward_change_char(gl, curpos - gl->buff_curpos + 1, NULL);
7993*7c478bd9Sstevel@tonic-gate     else
7994*7c478bd9Sstevel@tonic-gate       return gl_vi_backward_change_char(gl, ++gl->buff_curpos - curpos + 1,
7995*7c478bd9Sstevel@tonic-gate 					NULL);
7996*7c478bd9Sstevel@tonic-gate   };
7997*7c478bd9Sstevel@tonic-gate   return 0;
7998*7c478bd9Sstevel@tonic-gate }
7999*7c478bd9Sstevel@tonic-gate 
8000*7c478bd9Sstevel@tonic-gate /*.......................................................................
8001*7c478bd9Sstevel@tonic-gate  * If in vi mode, switch to vi command mode.
8002*7c478bd9Sstevel@tonic-gate  *
8003*7c478bd9Sstevel@tonic-gate  * Input:
8004*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
8005*7c478bd9Sstevel@tonic-gate  */
8006*7c478bd9Sstevel@tonic-gate static void gl_vi_command_mode(GetLine *gl)
8007*7c478bd9Sstevel@tonic-gate {
8008*7c478bd9Sstevel@tonic-gate   if(gl->editor == GL_VI_MODE && !gl->vi.command) {
8009*7c478bd9Sstevel@tonic-gate     gl->insert = 1;
8010*7c478bd9Sstevel@tonic-gate     gl->vi.command = 1;
8011*7c478bd9Sstevel@tonic-gate     gl->vi.repeat.input_curpos = gl->insert_curpos;
8012*7c478bd9Sstevel@tonic-gate     gl->vi.repeat.command_curpos = gl->buff_curpos;
8013*7c478bd9Sstevel@tonic-gate     gl->insert_curpos = 0;	 /* unrestrict left motion boundary */
8014*7c478bd9Sstevel@tonic-gate     gl_cursor_left(gl, 1, NULL); /* Vi moves 1 left on entering command mode */
8015*7c478bd9Sstevel@tonic-gate   };
8016*7c478bd9Sstevel@tonic-gate }
8017*7c478bd9Sstevel@tonic-gate 
8018*7c478bd9Sstevel@tonic-gate /*.......................................................................
8019*7c478bd9Sstevel@tonic-gate  * This is an action function which rings the terminal bell.
8020*7c478bd9Sstevel@tonic-gate  */
8021*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_ring_bell)
8022*7c478bd9Sstevel@tonic-gate {
8023*7c478bd9Sstevel@tonic-gate   return gl->silence_bell ? 0 :
8024*7c478bd9Sstevel@tonic-gate     gl_print_control_sequence(gl, 1, gl->sound_bell);
8025*7c478bd9Sstevel@tonic-gate }
8026*7c478bd9Sstevel@tonic-gate 
8027*7c478bd9Sstevel@tonic-gate /*.......................................................................
8028*7c478bd9Sstevel@tonic-gate  * This is the action function which implements the vi-repeat-change
8029*7c478bd9Sstevel@tonic-gate  * action.
8030*7c478bd9Sstevel@tonic-gate  */
8031*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_vi_repeat_change)
8032*7c478bd9Sstevel@tonic-gate {
8033*7c478bd9Sstevel@tonic-gate   int status;   /* The return status of the repeated action function */
8034*7c478bd9Sstevel@tonic-gate   int i;
8035*7c478bd9Sstevel@tonic-gate /*
8036*7c478bd9Sstevel@tonic-gate  * Nothing to repeat?
8037*7c478bd9Sstevel@tonic-gate  */
8038*7c478bd9Sstevel@tonic-gate   if(!gl->vi.repeat.action.fn)
8039*7c478bd9Sstevel@tonic-gate     return gl_ring_bell(gl, 1, NULL);
8040*7c478bd9Sstevel@tonic-gate /*
8041*7c478bd9Sstevel@tonic-gate  * Provide a way for action functions to know whether they are being
8042*7c478bd9Sstevel@tonic-gate  * called by us.
8043*7c478bd9Sstevel@tonic-gate  */
8044*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.active = 1;
8045*7c478bd9Sstevel@tonic-gate /*
8046*7c478bd9Sstevel@tonic-gate  * Re-run the recorded function.
8047*7c478bd9Sstevel@tonic-gate  */
8048*7c478bd9Sstevel@tonic-gate   status = gl->vi.repeat.action.fn(gl, gl->vi.repeat.count,
8049*7c478bd9Sstevel@tonic-gate 				   gl->vi.repeat.action.data);
8050*7c478bd9Sstevel@tonic-gate /*
8051*7c478bd9Sstevel@tonic-gate  * Mark the repeat as completed.
8052*7c478bd9Sstevel@tonic-gate  */
8053*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.active = 0;
8054*7c478bd9Sstevel@tonic-gate /*
8055*7c478bd9Sstevel@tonic-gate  * Is we are repeating a function that has just switched to input
8056*7c478bd9Sstevel@tonic-gate  * mode to allow the user to type, re-enter the text that the user
8057*7c478bd9Sstevel@tonic-gate  * previously entered.
8058*7c478bd9Sstevel@tonic-gate  */
8059*7c478bd9Sstevel@tonic-gate   if(status==0 && !gl->vi.command) {
8060*7c478bd9Sstevel@tonic-gate /*
8061*7c478bd9Sstevel@tonic-gate  * Make sure that the current line has been saved.
8062*7c478bd9Sstevel@tonic-gate  */
8063*7c478bd9Sstevel@tonic-gate     gl_save_for_undo(gl);
8064*7c478bd9Sstevel@tonic-gate /*
8065*7c478bd9Sstevel@tonic-gate  * Repeat a previous insertion or overwrite?
8066*7c478bd9Sstevel@tonic-gate  */
8067*7c478bd9Sstevel@tonic-gate     if(gl->vi.repeat.input_curpos >= 0 &&
8068*7c478bd9Sstevel@tonic-gate        gl->vi.repeat.input_curpos <= gl->vi.repeat.command_curpos &&
8069*7c478bd9Sstevel@tonic-gate        gl->vi.repeat.command_curpos <= gl->vi.undo.ntotal) {
8070*7c478bd9Sstevel@tonic-gate /*
8071*7c478bd9Sstevel@tonic-gate  * Using the current line which is saved in the undo buffer, plus
8072*7c478bd9Sstevel@tonic-gate  * the range of characters therein, as recorded by gl_vi_command_mode(),
8073*7c478bd9Sstevel@tonic-gate  * add the characters that the user previously entered, to the input
8074*7c478bd9Sstevel@tonic-gate  * line.
8075*7c478bd9Sstevel@tonic-gate  */
8076*7c478bd9Sstevel@tonic-gate       for(i=gl->vi.repeat.input_curpos; i<gl->vi.repeat.command_curpos; i++) {
8077*7c478bd9Sstevel@tonic-gate 	if(gl_add_char_to_line(gl, gl->vi.undo.line[i]))
8078*7c478bd9Sstevel@tonic-gate 	  return 1;
8079*7c478bd9Sstevel@tonic-gate       };
8080*7c478bd9Sstevel@tonic-gate     };
8081*7c478bd9Sstevel@tonic-gate /*
8082*7c478bd9Sstevel@tonic-gate  * Switch back to command mode, now that the insertion has been repeated.
8083*7c478bd9Sstevel@tonic-gate  */
8084*7c478bd9Sstevel@tonic-gate     gl_vi_command_mode(gl);
8085*7c478bd9Sstevel@tonic-gate   };
8086*7c478bd9Sstevel@tonic-gate   return status;
8087*7c478bd9Sstevel@tonic-gate }
8088*7c478bd9Sstevel@tonic-gate 
8089*7c478bd9Sstevel@tonic-gate /*.......................................................................
8090*7c478bd9Sstevel@tonic-gate  * If the cursor is currently over a parenthesis character, return the
8091*7c478bd9Sstevel@tonic-gate  * index of its matching parenthesis. If not currently over a parenthesis
8092*7c478bd9Sstevel@tonic-gate  * character, return the next close parenthesis character to the right of
8093*7c478bd9Sstevel@tonic-gate  * the cursor. If the respective parenthesis character isn't found,
8094*7c478bd9Sstevel@tonic-gate  * ring the terminal bell and return -1.
8095*7c478bd9Sstevel@tonic-gate  *
8096*7c478bd9Sstevel@tonic-gate  * Input:
8097*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The getline resource object.
8098*7c478bd9Sstevel@tonic-gate  * Output:
8099*7c478bd9Sstevel@tonic-gate  *  return       int    Either the index of the matching parenthesis,
8100*7c478bd9Sstevel@tonic-gate  *                      or -1 if not found.
8101*7c478bd9Sstevel@tonic-gate  */
8102*7c478bd9Sstevel@tonic-gate static int gl_index_of_matching_paren(GetLine *gl)
8103*7c478bd9Sstevel@tonic-gate {
8104*7c478bd9Sstevel@tonic-gate   int i;
8105*7c478bd9Sstevel@tonic-gate /*
8106*7c478bd9Sstevel@tonic-gate  * List the recognized parentheses, and their matches.
8107*7c478bd9Sstevel@tonic-gate  */
8108*7c478bd9Sstevel@tonic-gate   const char *o_paren = "([{";
8109*7c478bd9Sstevel@tonic-gate   const char *c_paren = ")]}";
8110*7c478bd9Sstevel@tonic-gate   const char *cptr;
8111*7c478bd9Sstevel@tonic-gate /*
8112*7c478bd9Sstevel@tonic-gate  * Get the character that is currently under the cursor.
8113*7c478bd9Sstevel@tonic-gate  */
8114*7c478bd9Sstevel@tonic-gate   char c = gl->line[gl->buff_curpos];
8115*7c478bd9Sstevel@tonic-gate /*
8116*7c478bd9Sstevel@tonic-gate  * If the character under the cursor is an open parenthesis, look forward
8117*7c478bd9Sstevel@tonic-gate  * for the matching close parenthesis.
8118*7c478bd9Sstevel@tonic-gate  */
8119*7c478bd9Sstevel@tonic-gate   if((cptr=strchr(o_paren, c))) {
8120*7c478bd9Sstevel@tonic-gate     char match = c_paren[cptr - o_paren];
8121*7c478bd9Sstevel@tonic-gate     int matches_needed = 1;
8122*7c478bd9Sstevel@tonic-gate     for(i=gl->buff_curpos+1; i<gl->ntotal; i++) {
8123*7c478bd9Sstevel@tonic-gate       if(gl->line[i] == c)
8124*7c478bd9Sstevel@tonic-gate 	matches_needed++;
8125*7c478bd9Sstevel@tonic-gate       else if(gl->line[i] == match && --matches_needed==0)
8126*7c478bd9Sstevel@tonic-gate 	return i;
8127*7c478bd9Sstevel@tonic-gate     };
8128*7c478bd9Sstevel@tonic-gate /*
8129*7c478bd9Sstevel@tonic-gate  * If the character under the cursor is an close parenthesis, look forward
8130*7c478bd9Sstevel@tonic-gate  * for the matching open parenthesis.
8131*7c478bd9Sstevel@tonic-gate  */
8132*7c478bd9Sstevel@tonic-gate   } else if((cptr=strchr(c_paren, c))) {
8133*7c478bd9Sstevel@tonic-gate     char match = o_paren[cptr - c_paren];
8134*7c478bd9Sstevel@tonic-gate     int matches_needed = 1;
8135*7c478bd9Sstevel@tonic-gate     for(i=gl->buff_curpos-1; i>=0; i--) {
8136*7c478bd9Sstevel@tonic-gate       if(gl->line[i] == c)
8137*7c478bd9Sstevel@tonic-gate 	matches_needed++;
8138*7c478bd9Sstevel@tonic-gate       else if(gl->line[i] == match && --matches_needed==0)
8139*7c478bd9Sstevel@tonic-gate 	return i;
8140*7c478bd9Sstevel@tonic-gate     };
8141*7c478bd9Sstevel@tonic-gate /*
8142*7c478bd9Sstevel@tonic-gate  * If not currently over a parenthesis character, search forwards for
8143*7c478bd9Sstevel@tonic-gate  * the first close parenthesis (this is what the vi % binding does).
8144*7c478bd9Sstevel@tonic-gate  */
8145*7c478bd9Sstevel@tonic-gate   } else {
8146*7c478bd9Sstevel@tonic-gate     for(i=gl->buff_curpos+1; i<gl->ntotal; i++)
8147*7c478bd9Sstevel@tonic-gate       if(strchr(c_paren, gl->line[i]) != NULL)
8148*7c478bd9Sstevel@tonic-gate 	return i;
8149*7c478bd9Sstevel@tonic-gate   };
8150*7c478bd9Sstevel@tonic-gate /*
8151*7c478bd9Sstevel@tonic-gate  * Not found.
8152*7c478bd9Sstevel@tonic-gate  */
8153*7c478bd9Sstevel@tonic-gate   (void) gl_ring_bell(gl, 1, NULL);
8154*7c478bd9Sstevel@tonic-gate   return -1;
8155*7c478bd9Sstevel@tonic-gate }
8156*7c478bd9Sstevel@tonic-gate 
8157*7c478bd9Sstevel@tonic-gate /*.......................................................................
8158*7c478bd9Sstevel@tonic-gate  * If the cursor is currently over a parenthesis character, this action
8159*7c478bd9Sstevel@tonic-gate  * function moves the cursor to its matching parenthesis.
8160*7c478bd9Sstevel@tonic-gate  */
8161*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_find_parenthesis)
8162*7c478bd9Sstevel@tonic-gate {
8163*7c478bd9Sstevel@tonic-gate   int curpos = gl_index_of_matching_paren(gl);
8164*7c478bd9Sstevel@tonic-gate   if(curpos >= 0)
8165*7c478bd9Sstevel@tonic-gate     return gl_place_cursor(gl, curpos);
8166*7c478bd9Sstevel@tonic-gate   return 0;
8167*7c478bd9Sstevel@tonic-gate }
8168*7c478bd9Sstevel@tonic-gate 
8169*7c478bd9Sstevel@tonic-gate /*.......................................................................
8170*7c478bd9Sstevel@tonic-gate  * Handle the receipt of the potential start of a new key-sequence from
8171*7c478bd9Sstevel@tonic-gate  * the user.
8172*7c478bd9Sstevel@tonic-gate  *
8173*7c478bd9Sstevel@tonic-gate  * Input:
8174*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *   The resource object of this library.
8175*7c478bd9Sstevel@tonic-gate  *  first_char char     The first character of the sequence.
8176*7c478bd9Sstevel@tonic-gate  * Output:
8177*7c478bd9Sstevel@tonic-gate  *  return      int     0 - OK.
8178*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
8179*7c478bd9Sstevel@tonic-gate  */
8180*7c478bd9Sstevel@tonic-gate static int gl_interpret_char(GetLine *gl, char first_char)
8181*7c478bd9Sstevel@tonic-gate {
8182*7c478bd9Sstevel@tonic-gate   char keyseq[GL_KEY_MAX+1]; /* A special key sequence being read */
8183*7c478bd9Sstevel@tonic-gate   int nkey=0;                /* The number of characters in the key sequence */
8184*7c478bd9Sstevel@tonic-gate   int count;                 /* The repeat count of an action function */
8185*7c478bd9Sstevel@tonic-gate   int ret;                   /* The return value of an action function */
8186*7c478bd9Sstevel@tonic-gate   int i;
8187*7c478bd9Sstevel@tonic-gate /*
8188*7c478bd9Sstevel@tonic-gate  * Get the first character.
8189*7c478bd9Sstevel@tonic-gate  */
8190*7c478bd9Sstevel@tonic-gate   char c = first_char;
8191*7c478bd9Sstevel@tonic-gate /*
8192*7c478bd9Sstevel@tonic-gate  * If editing is disabled, just add newly entered characters to the
8193*7c478bd9Sstevel@tonic-gate  * input line buffer, and watch for the end of the line.
8194*7c478bd9Sstevel@tonic-gate  */
8195*7c478bd9Sstevel@tonic-gate   if(gl->editor == GL_NO_EDITOR) {
8196*7c478bd9Sstevel@tonic-gate     gl_discard_chars(gl, 1);
8197*7c478bd9Sstevel@tonic-gate     if(gl->ntotal >= gl->linelen)
8198*7c478bd9Sstevel@tonic-gate       return 0;
8199*7c478bd9Sstevel@tonic-gate     if(c == '\n' || c == '\r')
8200*7c478bd9Sstevel@tonic-gate       return gl_newline(gl, 1, NULL);
8201*7c478bd9Sstevel@tonic-gate     gl_buffer_char(gl, c, gl->ntotal);
8202*7c478bd9Sstevel@tonic-gate     return 0;
8203*7c478bd9Sstevel@tonic-gate   };
8204*7c478bd9Sstevel@tonic-gate /*
8205*7c478bd9Sstevel@tonic-gate  * If the user is in the process of specifying a repeat count and the
8206*7c478bd9Sstevel@tonic-gate  * new character is a digit, increment the repeat count accordingly.
8207*7c478bd9Sstevel@tonic-gate  */
8208*7c478bd9Sstevel@tonic-gate   if(gl->number >= 0 && isdigit((int)(unsigned char) c)) {
8209*7c478bd9Sstevel@tonic-gate     gl_discard_chars(gl, 1);
8210*7c478bd9Sstevel@tonic-gate     return gl_digit_argument(gl, c, NULL);
8211*7c478bd9Sstevel@tonic-gate /*
8212*7c478bd9Sstevel@tonic-gate  * In vi command mode, all key-sequences entered need to be
8213*7c478bd9Sstevel@tonic-gate  * either implicitly or explicitly prefixed with an escape character.
8214*7c478bd9Sstevel@tonic-gate  */
8215*7c478bd9Sstevel@tonic-gate   } else if(gl->vi.command && c != GL_ESC_CHAR) {
8216*7c478bd9Sstevel@tonic-gate     keyseq[nkey++] = GL_ESC_CHAR;
8217*7c478bd9Sstevel@tonic-gate /*
8218*7c478bd9Sstevel@tonic-gate  * If the first character of the sequence is a printable character,
8219*7c478bd9Sstevel@tonic-gate  * then to avoid confusion with the special "up", "down", "left"
8220*7c478bd9Sstevel@tonic-gate  * or "right" cursor key bindings, we need to prefix the
8221*7c478bd9Sstevel@tonic-gate  * printable character with a backslash escape before looking it up.
8222*7c478bd9Sstevel@tonic-gate  */
8223*7c478bd9Sstevel@tonic-gate   } else if(!IS_META_CHAR(c) && !IS_CTRL_CHAR(c)) {
8224*7c478bd9Sstevel@tonic-gate     keyseq[nkey++] = '\\';
8225*7c478bd9Sstevel@tonic-gate   };
8226*7c478bd9Sstevel@tonic-gate /*
8227*7c478bd9Sstevel@tonic-gate  * Compose a potentially multiple key-sequence in gl->keyseq.
8228*7c478bd9Sstevel@tonic-gate  */
8229*7c478bd9Sstevel@tonic-gate   while(nkey < GL_KEY_MAX) {
8230*7c478bd9Sstevel@tonic-gate     KtAction *action; /* An action function */
8231*7c478bd9Sstevel@tonic-gate     KeySym *keysym;   /* The symbol-table entry of a key-sequence */
8232*7c478bd9Sstevel@tonic-gate     int nsym;         /* The number of ambiguously matching key-sequences */
8233*7c478bd9Sstevel@tonic-gate /*
8234*7c478bd9Sstevel@tonic-gate  * If the character is an unprintable meta character, split it
8235*7c478bd9Sstevel@tonic-gate  * into two characters, an escape character and the character
8236*7c478bd9Sstevel@tonic-gate  * that was modified by the meta key.
8237*7c478bd9Sstevel@tonic-gate  */
8238*7c478bd9Sstevel@tonic-gate     if(IS_META_CHAR(c)) {
8239*7c478bd9Sstevel@tonic-gate       keyseq[nkey++] = GL_ESC_CHAR;
8240*7c478bd9Sstevel@tonic-gate       c = META_TO_CHAR(c);
8241*7c478bd9Sstevel@tonic-gate       continue;
8242*7c478bd9Sstevel@tonic-gate     };
8243*7c478bd9Sstevel@tonic-gate /*
8244*7c478bd9Sstevel@tonic-gate  * Append the latest character to the key sequence.
8245*7c478bd9Sstevel@tonic-gate  */
8246*7c478bd9Sstevel@tonic-gate     keyseq[nkey++] = c;
8247*7c478bd9Sstevel@tonic-gate /*
8248*7c478bd9Sstevel@tonic-gate  * When doing vi-style editing, an escape at the beginning of any binding
8249*7c478bd9Sstevel@tonic-gate  * switches to command mode.
8250*7c478bd9Sstevel@tonic-gate  */
8251*7c478bd9Sstevel@tonic-gate     if(keyseq[0] == GL_ESC_CHAR && !gl->vi.command)
8252*7c478bd9Sstevel@tonic-gate       gl_vi_command_mode(gl);
8253*7c478bd9Sstevel@tonic-gate /*
8254*7c478bd9Sstevel@tonic-gate  * Lookup the key sequence.
8255*7c478bd9Sstevel@tonic-gate  */
8256*7c478bd9Sstevel@tonic-gate     switch(_kt_lookup_keybinding(gl->bindings, keyseq, nkey, &keysym, &nsym)) {
8257*7c478bd9Sstevel@tonic-gate     case KT_EXACT_MATCH:
8258*7c478bd9Sstevel@tonic-gate /*
8259*7c478bd9Sstevel@tonic-gate  * Get the matching action function.
8260*7c478bd9Sstevel@tonic-gate  */
8261*7c478bd9Sstevel@tonic-gate       action = keysym->actions + keysym->binder;
8262*7c478bd9Sstevel@tonic-gate /*
8263*7c478bd9Sstevel@tonic-gate  * Get the repeat count, passing the last keystroke if executing the
8264*7c478bd9Sstevel@tonic-gate  * digit-argument action.
8265*7c478bd9Sstevel@tonic-gate  */
8266*7c478bd9Sstevel@tonic-gate       if(action->fn == gl_digit_argument) {
8267*7c478bd9Sstevel@tonic-gate 	count = c;
8268*7c478bd9Sstevel@tonic-gate       } else {
8269*7c478bd9Sstevel@tonic-gate 	count = gl->number >= 0 ? gl->number : 1;
8270*7c478bd9Sstevel@tonic-gate       };
8271*7c478bd9Sstevel@tonic-gate /*
8272*7c478bd9Sstevel@tonic-gate  * Record the function that is being invoked.
8273*7c478bd9Sstevel@tonic-gate  */
8274*7c478bd9Sstevel@tonic-gate       gl->current_action = *action;
8275*7c478bd9Sstevel@tonic-gate       gl->current_count = count;
8276*7c478bd9Sstevel@tonic-gate /*
8277*7c478bd9Sstevel@tonic-gate  * Mark the current line as not yet preserved for use by the vi undo command.
8278*7c478bd9Sstevel@tonic-gate  */
8279*7c478bd9Sstevel@tonic-gate       gl->vi.undo.saved = 0;
8280*7c478bd9Sstevel@tonic-gate       gl->vi.repeat.saved = 0;
8281*7c478bd9Sstevel@tonic-gate /*
8282*7c478bd9Sstevel@tonic-gate  * Execute the action function. Note the action function can tell
8283*7c478bd9Sstevel@tonic-gate  * whether the provided repeat count was defaulted or specified
8284*7c478bd9Sstevel@tonic-gate  * explicitly by looking at whether gl->number is -1 or not. If
8285*7c478bd9Sstevel@tonic-gate  * it is negative, then no repeat count was specified by the user.
8286*7c478bd9Sstevel@tonic-gate  */
8287*7c478bd9Sstevel@tonic-gate       ret = action->fn(gl, count, action->data);
8288*7c478bd9Sstevel@tonic-gate /*
8289*7c478bd9Sstevel@tonic-gate  * In server mode, the action will return immediately if it tries to
8290*7c478bd9Sstevel@tonic-gate  * read input from the terminal, and no input is currently available.
8291*7c478bd9Sstevel@tonic-gate  * If this happens, abort. Note that gl_get_input_line() will rewind
8292*7c478bd9Sstevel@tonic-gate  * the read-ahead buffer to allow the next call to redo the function
8293*7c478bd9Sstevel@tonic-gate  * from scratch.
8294*7c478bd9Sstevel@tonic-gate  */
8295*7c478bd9Sstevel@tonic-gate       if(gl->rtn_status == GLR_BLOCKED && gl->pending_io==GLP_READ)
8296*7c478bd9Sstevel@tonic-gate 	return 1;
8297*7c478bd9Sstevel@tonic-gate /*
8298*7c478bd9Sstevel@tonic-gate  * Discard the now processed characters from the key sequence buffer.
8299*7c478bd9Sstevel@tonic-gate  */
8300*7c478bd9Sstevel@tonic-gate       gl_discard_chars(gl, gl->nread);
8301*7c478bd9Sstevel@tonic-gate /*
8302*7c478bd9Sstevel@tonic-gate  * If the latest action function wasn't a history action, cancel any
8303*7c478bd9Sstevel@tonic-gate  * current history search.
8304*7c478bd9Sstevel@tonic-gate  */
8305*7c478bd9Sstevel@tonic-gate       if(gl->last_search != gl->keyseq_count)
8306*7c478bd9Sstevel@tonic-gate 	_glh_cancel_search(gl->glh);
8307*7c478bd9Sstevel@tonic-gate /*
8308*7c478bd9Sstevel@tonic-gate  * Reset the repeat count after running action functions.
8309*7c478bd9Sstevel@tonic-gate  */
8310*7c478bd9Sstevel@tonic-gate       if(action->fn != gl_digit_argument)
8311*7c478bd9Sstevel@tonic-gate 	gl->number = -1;
8312*7c478bd9Sstevel@tonic-gate       return ret ? 1 : 0;
8313*7c478bd9Sstevel@tonic-gate       break;
8314*7c478bd9Sstevel@tonic-gate     case KT_AMBIG_MATCH:    /* Ambiguous match - so read the next character */
8315*7c478bd9Sstevel@tonic-gate       if(gl_read_terminal(gl, 1, &c))
8316*7c478bd9Sstevel@tonic-gate 	return 1;
8317*7c478bd9Sstevel@tonic-gate       break;
8318*7c478bd9Sstevel@tonic-gate     case KT_NO_MATCH:
8319*7c478bd9Sstevel@tonic-gate /*
8320*7c478bd9Sstevel@tonic-gate  * If the first character looked like it might be a prefix of a key-sequence
8321*7c478bd9Sstevel@tonic-gate  * but it turned out not to be, ring the bell to tell the user that it
8322*7c478bd9Sstevel@tonic-gate  * wasn't recognised.
8323*7c478bd9Sstevel@tonic-gate  */
8324*7c478bd9Sstevel@tonic-gate       if(keyseq[0] != '\\' && keyseq[0] != '\t') {
8325*7c478bd9Sstevel@tonic-gate 	gl_ring_bell(gl, 1, NULL);
8326*7c478bd9Sstevel@tonic-gate       } else {
8327*7c478bd9Sstevel@tonic-gate /*
8328*7c478bd9Sstevel@tonic-gate  * The user typed a single printable character that doesn't match
8329*7c478bd9Sstevel@tonic-gate  * the start of any keysequence, so add it to the line in accordance
8330*7c478bd9Sstevel@tonic-gate  * with the current repeat count.
8331*7c478bd9Sstevel@tonic-gate  */
8332*7c478bd9Sstevel@tonic-gate 	count = gl->number >= 0 ? gl->number : 1;
8333*7c478bd9Sstevel@tonic-gate 	for(i=0; i<count; i++)
8334*7c478bd9Sstevel@tonic-gate 	  gl_add_char_to_line(gl, first_char);
8335*7c478bd9Sstevel@tonic-gate 	gl->number = -1;
8336*7c478bd9Sstevel@tonic-gate       };
8337*7c478bd9Sstevel@tonic-gate       gl_discard_chars(gl, 1);
8338*7c478bd9Sstevel@tonic-gate       _glh_cancel_search(gl->glh);
8339*7c478bd9Sstevel@tonic-gate       return 0;
8340*7c478bd9Sstevel@tonic-gate       break;
8341*7c478bd9Sstevel@tonic-gate     case KT_BAD_MATCH:
8342*7c478bd9Sstevel@tonic-gate       gl_ring_bell(gl, 1, NULL);
8343*7c478bd9Sstevel@tonic-gate       gl_discard_chars(gl, gl->nread);
8344*7c478bd9Sstevel@tonic-gate       _glh_cancel_search(gl->glh);
8345*7c478bd9Sstevel@tonic-gate       return 1;
8346*7c478bd9Sstevel@tonic-gate       break;
8347*7c478bd9Sstevel@tonic-gate     };
8348*7c478bd9Sstevel@tonic-gate   };
8349*7c478bd9Sstevel@tonic-gate /*
8350*7c478bd9Sstevel@tonic-gate  * If the key sequence was too long to match, ring the bell, then
8351*7c478bd9Sstevel@tonic-gate  * discard the first character, so that the next attempt to match a
8352*7c478bd9Sstevel@tonic-gate  * key-sequence continues with the next key press. In practice this
8353*7c478bd9Sstevel@tonic-gate  * shouldn't happen, since one isn't allowed to bind action functions
8354*7c478bd9Sstevel@tonic-gate  * to keysequences that are longer than GL_KEY_MAX.
8355*7c478bd9Sstevel@tonic-gate  */
8356*7c478bd9Sstevel@tonic-gate   gl_ring_bell(gl, 1, NULL);
8357*7c478bd9Sstevel@tonic-gate   gl_discard_chars(gl, 1);
8358*7c478bd9Sstevel@tonic-gate   return 0;
8359*7c478bd9Sstevel@tonic-gate }
8360*7c478bd9Sstevel@tonic-gate 
8361*7c478bd9Sstevel@tonic-gate /*.......................................................................
8362*7c478bd9Sstevel@tonic-gate  * Configure the application and/or user-specific behavior of
8363*7c478bd9Sstevel@tonic-gate  * gl_get_line().
8364*7c478bd9Sstevel@tonic-gate  *
8365*7c478bd9Sstevel@tonic-gate  * Note that calling this function between calling new_GetLine() and
8366*7c478bd9Sstevel@tonic-gate  * the first call to gl_get_line(), disables the otherwise automatic
8367*7c478bd9Sstevel@tonic-gate  * reading of ~/.teclarc on the first call to gl_get_line().
8368*7c478bd9Sstevel@tonic-gate  *
8369*7c478bd9Sstevel@tonic-gate  * Input:
8370*7c478bd9Sstevel@tonic-gate  *  gl             GetLine *  The resource object of this library.
8371*7c478bd9Sstevel@tonic-gate  *  app_string  const char *  Either NULL, or a string containing one
8372*7c478bd9Sstevel@tonic-gate  *                            or more .teclarc command lines, separated
8373*7c478bd9Sstevel@tonic-gate  *                            by newline characters. This can be used to
8374*7c478bd9Sstevel@tonic-gate  *                            establish an application-specific
8375*7c478bd9Sstevel@tonic-gate  *                            configuration, without the need for an external
8376*7c478bd9Sstevel@tonic-gate  *                            file. This is particularly useful in embedded
8377*7c478bd9Sstevel@tonic-gate  *                            environments where there is no filesystem.
8378*7c478bd9Sstevel@tonic-gate  *  app_file    const char *  Either NULL, or the pathname of an
8379*7c478bd9Sstevel@tonic-gate  *                            application-specific .teclarc file. The
8380*7c478bd9Sstevel@tonic-gate  *                            contents of this file, if provided, are
8381*7c478bd9Sstevel@tonic-gate  *                            read after the contents of app_string[].
8382*7c478bd9Sstevel@tonic-gate  *  user_file   const char *  Either NULL, or the pathname of a
8383*7c478bd9Sstevel@tonic-gate  *                            user-specific .teclarc file. Except in
8384*7c478bd9Sstevel@tonic-gate  *                            embedded applications, this should
8385*7c478bd9Sstevel@tonic-gate  *                            usually be "~/.teclarc".
8386*7c478bd9Sstevel@tonic-gate  * Output:
8387*7c478bd9Sstevel@tonic-gate  *  return             int    0 - OK.
8388*7c478bd9Sstevel@tonic-gate  *                            1 - Bad argument(s).
8389*7c478bd9Sstevel@tonic-gate  */
8390*7c478bd9Sstevel@tonic-gate int gl_configure_getline(GetLine *gl, const char *app_string,
8391*7c478bd9Sstevel@tonic-gate 			 const char *app_file, const char *user_file)
8392*7c478bd9Sstevel@tonic-gate {
8393*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
8394*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_configure_getline() */
8395*7c478bd9Sstevel@tonic-gate /*
8396*7c478bd9Sstevel@tonic-gate  * Check the arguments.
8397*7c478bd9Sstevel@tonic-gate  */
8398*7c478bd9Sstevel@tonic-gate   if(!gl) {
8399*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
8400*7c478bd9Sstevel@tonic-gate     return 1;
8401*7c478bd9Sstevel@tonic-gate   };
8402*7c478bd9Sstevel@tonic-gate /*
8403*7c478bd9Sstevel@tonic-gate  * Block all signals.
8404*7c478bd9Sstevel@tonic-gate  */
8405*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
8406*7c478bd9Sstevel@tonic-gate     return 1;
8407*7c478bd9Sstevel@tonic-gate /*
8408*7c478bd9Sstevel@tonic-gate  * Execute the private body of the function while signals are blocked.
8409*7c478bd9Sstevel@tonic-gate  */
8410*7c478bd9Sstevel@tonic-gate   status = _gl_configure_getline(gl, app_string, app_file, user_file);
8411*7c478bd9Sstevel@tonic-gate /*
8412*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
8413*7c478bd9Sstevel@tonic-gate  */
8414*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
8415*7c478bd9Sstevel@tonic-gate   return status;
8416*7c478bd9Sstevel@tonic-gate }
8417*7c478bd9Sstevel@tonic-gate 
8418*7c478bd9Sstevel@tonic-gate /*.......................................................................
8419*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_configure_getline() function. It
8420*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
8421*7c478bd9Sstevel@tonic-gate  * delivery of signals.
8422*7c478bd9Sstevel@tonic-gate  */
8423*7c478bd9Sstevel@tonic-gate static int _gl_configure_getline(GetLine *gl, const char *app_string,
8424*7c478bd9Sstevel@tonic-gate 				 const char *app_file, const char *user_file)
8425*7c478bd9Sstevel@tonic-gate {
8426*7c478bd9Sstevel@tonic-gate /*
8427*7c478bd9Sstevel@tonic-gate  * Mark getline as having been explicitly configured.
8428*7c478bd9Sstevel@tonic-gate  */
8429*7c478bd9Sstevel@tonic-gate   gl->configured = 1;
8430*7c478bd9Sstevel@tonic-gate /*
8431*7c478bd9Sstevel@tonic-gate  * Start by parsing the configuration string, if provided.
8432*7c478bd9Sstevel@tonic-gate  */
8433*7c478bd9Sstevel@tonic-gate   if(app_string)
8434*7c478bd9Sstevel@tonic-gate     (void) _gl_read_config_string(gl, app_string, KTB_NORM);
8435*7c478bd9Sstevel@tonic-gate /*
8436*7c478bd9Sstevel@tonic-gate  * Now parse the application-specific configuration file, if provided.
8437*7c478bd9Sstevel@tonic-gate  */
8438*7c478bd9Sstevel@tonic-gate   if(app_file)
8439*7c478bd9Sstevel@tonic-gate     (void) _gl_read_config_file(gl, app_file, KTB_NORM);
8440*7c478bd9Sstevel@tonic-gate /*
8441*7c478bd9Sstevel@tonic-gate  * Finally, parse the user-specific configuration file, if provided.
8442*7c478bd9Sstevel@tonic-gate  */
8443*7c478bd9Sstevel@tonic-gate   if(user_file)
8444*7c478bd9Sstevel@tonic-gate     (void) _gl_read_config_file(gl, user_file, KTB_USER);
8445*7c478bd9Sstevel@tonic-gate /*
8446*7c478bd9Sstevel@tonic-gate  * Record the names of the configuration files to allow them to
8447*7c478bd9Sstevel@tonic-gate  * be re-read if requested at a later time.
8448*7c478bd9Sstevel@tonic-gate  */
8449*7c478bd9Sstevel@tonic-gate   if(gl_record_string(&gl->app_file, app_file) ||
8450*7c478bd9Sstevel@tonic-gate      gl_record_string(&gl->user_file, user_file)) {
8451*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
8452*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err,
8453*7c478bd9Sstevel@tonic-gate 	   "Insufficient memory to record tecla configuration file names",
8454*7c478bd9Sstevel@tonic-gate 	   END_ERR_MSG);
8455*7c478bd9Sstevel@tonic-gate     return 1;
8456*7c478bd9Sstevel@tonic-gate   };
8457*7c478bd9Sstevel@tonic-gate   return 0;
8458*7c478bd9Sstevel@tonic-gate }
8459*7c478bd9Sstevel@tonic-gate 
8460*7c478bd9Sstevel@tonic-gate /*.......................................................................
8461*7c478bd9Sstevel@tonic-gate  * Replace a malloc'd string (or NULL), with another malloc'd copy of
8462*7c478bd9Sstevel@tonic-gate  * a string (or NULL).
8463*7c478bd9Sstevel@tonic-gate  *
8464*7c478bd9Sstevel@tonic-gate  * Input:
8465*7c478bd9Sstevel@tonic-gate  *  sptr          char **  On input if *sptr!=NULL, *sptr will be
8466*7c478bd9Sstevel@tonic-gate  *                         free'd and *sptr will be set to NULL. Then,
8467*7c478bd9Sstevel@tonic-gate  *                         on output, if string!=NULL a malloc'd copy
8468*7c478bd9Sstevel@tonic-gate  *                         of this string will be assigned to *sptr.
8469*7c478bd9Sstevel@tonic-gate  *  string  const char *   The string to be copied, or NULL to simply
8470*7c478bd9Sstevel@tonic-gate  *                         discard any existing string.
8471*7c478bd9Sstevel@tonic-gate  * Output:
8472*7c478bd9Sstevel@tonic-gate  *  return         int     0 - OK.
8473*7c478bd9Sstevel@tonic-gate  *                         1 - Malloc failure (no error message is generated).
8474*7c478bd9Sstevel@tonic-gate  */
8475*7c478bd9Sstevel@tonic-gate static int gl_record_string(char **sptr, const char *string)
8476*7c478bd9Sstevel@tonic-gate {
8477*7c478bd9Sstevel@tonic-gate /*
8478*7c478bd9Sstevel@tonic-gate  * If the original string is the same string, don't do anything.
8479*7c478bd9Sstevel@tonic-gate  */
8480*7c478bd9Sstevel@tonic-gate   if(*sptr == string || (*sptr && string && strcmp(*sptr, string)==0))
8481*7c478bd9Sstevel@tonic-gate     return 0;
8482*7c478bd9Sstevel@tonic-gate /*
8483*7c478bd9Sstevel@tonic-gate  * Discard any existing cached string.
8484*7c478bd9Sstevel@tonic-gate  */
8485*7c478bd9Sstevel@tonic-gate   if(*sptr) {
8486*7c478bd9Sstevel@tonic-gate     free(*sptr);
8487*7c478bd9Sstevel@tonic-gate     *sptr = NULL;
8488*7c478bd9Sstevel@tonic-gate   };
8489*7c478bd9Sstevel@tonic-gate /*
8490*7c478bd9Sstevel@tonic-gate  * Allocate memory for a copy of the specified string.
8491*7c478bd9Sstevel@tonic-gate  */
8492*7c478bd9Sstevel@tonic-gate   if(string) {
8493*7c478bd9Sstevel@tonic-gate     size_t ssz = strlen(string) + 1;
8494*7c478bd9Sstevel@tonic-gate     *sptr = (char *) malloc(ssz);
8495*7c478bd9Sstevel@tonic-gate     if(!*sptr)
8496*7c478bd9Sstevel@tonic-gate       return 1;
8497*7c478bd9Sstevel@tonic-gate /*
8498*7c478bd9Sstevel@tonic-gate  * Copy the string.
8499*7c478bd9Sstevel@tonic-gate  */
8500*7c478bd9Sstevel@tonic-gate     strlcpy(*sptr, string, ssz);
8501*7c478bd9Sstevel@tonic-gate   };
8502*7c478bd9Sstevel@tonic-gate   return 0;
8503*7c478bd9Sstevel@tonic-gate }
8504*7c478bd9Sstevel@tonic-gate 
8505*7c478bd9Sstevel@tonic-gate #ifndef HIDE_FILE_SYSTEM
8506*7c478bd9Sstevel@tonic-gate /*.......................................................................
8507*7c478bd9Sstevel@tonic-gate  * Re-read any application-specific and user-specific files previously
8508*7c478bd9Sstevel@tonic-gate  * specified via the gl_configure_getline() function.
8509*7c478bd9Sstevel@tonic-gate  */
8510*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_read_init_files)
8511*7c478bd9Sstevel@tonic-gate {
8512*7c478bd9Sstevel@tonic-gate   return _gl_configure_getline(gl, NULL, gl->app_file, gl->user_file);
8513*7c478bd9Sstevel@tonic-gate }
8514*7c478bd9Sstevel@tonic-gate #endif
8515*7c478bd9Sstevel@tonic-gate 
8516*7c478bd9Sstevel@tonic-gate /*.......................................................................
8517*7c478bd9Sstevel@tonic-gate  * Save the contents of the history buffer to a given new file.
8518*7c478bd9Sstevel@tonic-gate  *
8519*7c478bd9Sstevel@tonic-gate  * Input:
8520*7c478bd9Sstevel@tonic-gate  *  gl             GetLine *  The resource object of this library.
8521*7c478bd9Sstevel@tonic-gate  *  filename    const char *  The name of the new file to write to.
8522*7c478bd9Sstevel@tonic-gate  *  comment     const char *  Extra information such as timestamps will
8523*7c478bd9Sstevel@tonic-gate  *                            be recorded on a line started with this
8524*7c478bd9Sstevel@tonic-gate  *                            string, the idea being that the file can
8525*7c478bd9Sstevel@tonic-gate  *                            double as a command file. Specify "" if
8526*7c478bd9Sstevel@tonic-gate  *                            you don't care.
8527*7c478bd9Sstevel@tonic-gate  *  max_lines          int    The maximum number of lines to save, or -1
8528*7c478bd9Sstevel@tonic-gate  *                            to save all of the lines in the history
8529*7c478bd9Sstevel@tonic-gate  *                            list.
8530*7c478bd9Sstevel@tonic-gate  * Output:
8531*7c478bd9Sstevel@tonic-gate  *  return             int     0 - OK.
8532*7c478bd9Sstevel@tonic-gate  *                             1 - Error.
8533*7c478bd9Sstevel@tonic-gate  */
8534*7c478bd9Sstevel@tonic-gate int gl_save_history(GetLine *gl, const char *filename, const char *comment,
8535*7c478bd9Sstevel@tonic-gate 		    int max_lines)
8536*7c478bd9Sstevel@tonic-gate {
8537*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
8538*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_save_history() */
8539*7c478bd9Sstevel@tonic-gate /*
8540*7c478bd9Sstevel@tonic-gate  * Check the arguments.
8541*7c478bd9Sstevel@tonic-gate  */
8542*7c478bd9Sstevel@tonic-gate   if(!gl || !filename || !comment) {
8543*7c478bd9Sstevel@tonic-gate     if(gl)
8544*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG);
8545*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
8546*7c478bd9Sstevel@tonic-gate     return 1;
8547*7c478bd9Sstevel@tonic-gate   };
8548*7c478bd9Sstevel@tonic-gate /*
8549*7c478bd9Sstevel@tonic-gate  * Block all signals.
8550*7c478bd9Sstevel@tonic-gate  */
8551*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
8552*7c478bd9Sstevel@tonic-gate     return 1;
8553*7c478bd9Sstevel@tonic-gate /*
8554*7c478bd9Sstevel@tonic-gate  * Execute the private body of the function while signals are blocked.
8555*7c478bd9Sstevel@tonic-gate  */
8556*7c478bd9Sstevel@tonic-gate   status = _gl_save_history(gl, filename, comment, max_lines);
8557*7c478bd9Sstevel@tonic-gate /*
8558*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
8559*7c478bd9Sstevel@tonic-gate  */
8560*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
8561*7c478bd9Sstevel@tonic-gate   return status;
8562*7c478bd9Sstevel@tonic-gate }
8563*7c478bd9Sstevel@tonic-gate 
8564*7c478bd9Sstevel@tonic-gate /*.......................................................................
8565*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_save_history() function. It
8566*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
8567*7c478bd9Sstevel@tonic-gate  * delivery of signals.
8568*7c478bd9Sstevel@tonic-gate  */
8569*7c478bd9Sstevel@tonic-gate static int _gl_save_history(GetLine *gl, const char *filename,
8570*7c478bd9Sstevel@tonic-gate 			    const char *comment, int max_lines)
8571*7c478bd9Sstevel@tonic-gate {
8572*7c478bd9Sstevel@tonic-gate /*
8573*7c478bd9Sstevel@tonic-gate  * If filesystem access is to be excluded, then history files can't
8574*7c478bd9Sstevel@tonic-gate  * be written.
8575*7c478bd9Sstevel@tonic-gate  */
8576*7c478bd9Sstevel@tonic-gate #ifdef WITHOUT_FILE_SYSTEM
8577*7c478bd9Sstevel@tonic-gate   _err_record_msg(gl->err, "Can't save history without filesystem access",
8578*7c478bd9Sstevel@tonic-gate 		  END_ERR_MSG);
8579*7c478bd9Sstevel@tonic-gate   errno = EINVAL;
8580*7c478bd9Sstevel@tonic-gate   return 1;
8581*7c478bd9Sstevel@tonic-gate #else
8582*7c478bd9Sstevel@tonic-gate   FileExpansion *expansion; /* The expansion of the filename */
8583*7c478bd9Sstevel@tonic-gate /*
8584*7c478bd9Sstevel@tonic-gate  * Expand the filename.
8585*7c478bd9Sstevel@tonic-gate  */
8586*7c478bd9Sstevel@tonic-gate   expansion = ef_expand_file(gl->ef, filename, -1);
8587*7c478bd9Sstevel@tonic-gate   if(!expansion) {
8588*7c478bd9Sstevel@tonic-gate     gl_print_info(gl, "Unable to expand ", filename, " (",
8589*7c478bd9Sstevel@tonic-gate 		  ef_last_error(gl->ef), ").", GL_END_INFO);
8590*7c478bd9Sstevel@tonic-gate     return 1;
8591*7c478bd9Sstevel@tonic-gate   };
8592*7c478bd9Sstevel@tonic-gate /*
8593*7c478bd9Sstevel@tonic-gate  * Attempt to save to the specified file.
8594*7c478bd9Sstevel@tonic-gate  */
8595*7c478bd9Sstevel@tonic-gate   if(_glh_save_history(gl->glh, expansion->files[0], comment, max_lines)) {
8596*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
8597*7c478bd9Sstevel@tonic-gate     return 1;
8598*7c478bd9Sstevel@tonic-gate   };
8599*7c478bd9Sstevel@tonic-gate   return 0;
8600*7c478bd9Sstevel@tonic-gate #endif
8601*7c478bd9Sstevel@tonic-gate }
8602*7c478bd9Sstevel@tonic-gate 
8603*7c478bd9Sstevel@tonic-gate /*.......................................................................
8604*7c478bd9Sstevel@tonic-gate  * Restore the contents of the history buffer from a given new file.
8605*7c478bd9Sstevel@tonic-gate  *
8606*7c478bd9Sstevel@tonic-gate  * Input:
8607*7c478bd9Sstevel@tonic-gate  *  gl             GetLine *  The resource object of this library.
8608*7c478bd9Sstevel@tonic-gate  *  filename    const char *  The name of the new file to write to.
8609*7c478bd9Sstevel@tonic-gate  *  comment     const char *  This must be the same string that was
8610*7c478bd9Sstevel@tonic-gate  *                            passed to gl_save_history() when the file
8611*7c478bd9Sstevel@tonic-gate  *                            was written.
8612*7c478bd9Sstevel@tonic-gate  * Output:
8613*7c478bd9Sstevel@tonic-gate  *  return             int     0 - OK.
8614*7c478bd9Sstevel@tonic-gate  *                             1 - Error.
8615*7c478bd9Sstevel@tonic-gate  */
8616*7c478bd9Sstevel@tonic-gate int gl_load_history(GetLine *gl, const char *filename, const char *comment)
8617*7c478bd9Sstevel@tonic-gate {
8618*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
8619*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_load_history() */
8620*7c478bd9Sstevel@tonic-gate /*
8621*7c478bd9Sstevel@tonic-gate  * Check the arguments.
8622*7c478bd9Sstevel@tonic-gate  */
8623*7c478bd9Sstevel@tonic-gate   if(!gl || !filename || !comment) {
8624*7c478bd9Sstevel@tonic-gate     if(gl)
8625*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG);
8626*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
8627*7c478bd9Sstevel@tonic-gate     return 1;
8628*7c478bd9Sstevel@tonic-gate   };
8629*7c478bd9Sstevel@tonic-gate /*
8630*7c478bd9Sstevel@tonic-gate  * Block all signals.
8631*7c478bd9Sstevel@tonic-gate  */
8632*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
8633*7c478bd9Sstevel@tonic-gate     return 1;
8634*7c478bd9Sstevel@tonic-gate /*
8635*7c478bd9Sstevel@tonic-gate  * Execute the private body of the function while signals are blocked.
8636*7c478bd9Sstevel@tonic-gate  */
8637*7c478bd9Sstevel@tonic-gate   status = _gl_load_history(gl, filename, comment);
8638*7c478bd9Sstevel@tonic-gate /*
8639*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
8640*7c478bd9Sstevel@tonic-gate  */
8641*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
8642*7c478bd9Sstevel@tonic-gate   return status;
8643*7c478bd9Sstevel@tonic-gate }
8644*7c478bd9Sstevel@tonic-gate 
8645*7c478bd9Sstevel@tonic-gate /*.......................................................................
8646*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_load_history() function. It
8647*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
8648*7c478bd9Sstevel@tonic-gate  * delivery of signals.
8649*7c478bd9Sstevel@tonic-gate  */
8650*7c478bd9Sstevel@tonic-gate static int _gl_load_history(GetLine *gl, const char *filename,
8651*7c478bd9Sstevel@tonic-gate 			    const char *comment)
8652*7c478bd9Sstevel@tonic-gate {
8653*7c478bd9Sstevel@tonic-gate /*
8654*7c478bd9Sstevel@tonic-gate  * If filesystem access is to be excluded, then history files can't
8655*7c478bd9Sstevel@tonic-gate  * be read.
8656*7c478bd9Sstevel@tonic-gate  */
8657*7c478bd9Sstevel@tonic-gate #ifdef WITHOUT_FILE_SYSTEM
8658*7c478bd9Sstevel@tonic-gate   _err_record_msg(gl->err, "Can't load history without filesystem access",
8659*7c478bd9Sstevel@tonic-gate 		  END_ERR_MSG);
8660*7c478bd9Sstevel@tonic-gate   errno = EINVAL;
8661*7c478bd9Sstevel@tonic-gate   return 1;
8662*7c478bd9Sstevel@tonic-gate #else
8663*7c478bd9Sstevel@tonic-gate   FileExpansion *expansion; /* The expansion of the filename */
8664*7c478bd9Sstevel@tonic-gate /*
8665*7c478bd9Sstevel@tonic-gate  * Expand the filename.
8666*7c478bd9Sstevel@tonic-gate  */
8667*7c478bd9Sstevel@tonic-gate   expansion = ef_expand_file(gl->ef, filename, -1);
8668*7c478bd9Sstevel@tonic-gate   if(!expansion) {
8669*7c478bd9Sstevel@tonic-gate     gl_print_info(gl, "Unable to expand ", filename, " (",
8670*7c478bd9Sstevel@tonic-gate 		  ef_last_error(gl->ef), ").", GL_END_INFO);
8671*7c478bd9Sstevel@tonic-gate     return 1;
8672*7c478bd9Sstevel@tonic-gate   };
8673*7c478bd9Sstevel@tonic-gate /*
8674*7c478bd9Sstevel@tonic-gate  * Attempt to load from the specified file.
8675*7c478bd9Sstevel@tonic-gate  */
8676*7c478bd9Sstevel@tonic-gate   if(_glh_load_history(gl->glh, expansion->files[0], comment,
8677*7c478bd9Sstevel@tonic-gate 		       gl->cutbuf, gl->linelen+1)) {
8678*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
8679*7c478bd9Sstevel@tonic-gate     gl->cutbuf[0] = '\0';
8680*7c478bd9Sstevel@tonic-gate     return 1;
8681*7c478bd9Sstevel@tonic-gate   };
8682*7c478bd9Sstevel@tonic-gate   gl->cutbuf[0] = '\0';
8683*7c478bd9Sstevel@tonic-gate   return 0;
8684*7c478bd9Sstevel@tonic-gate #endif
8685*7c478bd9Sstevel@tonic-gate }
8686*7c478bd9Sstevel@tonic-gate 
8687*7c478bd9Sstevel@tonic-gate /*.......................................................................
8688*7c478bd9Sstevel@tonic-gate  * Where possible, register a function and associated data to be called
8689*7c478bd9Sstevel@tonic-gate  * whenever a specified event is seen on a file descriptor.
8690*7c478bd9Sstevel@tonic-gate  *
8691*7c478bd9Sstevel@tonic-gate  * Input:
8692*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of the command-line input
8693*7c478bd9Sstevel@tonic-gate  *                           module.
8694*7c478bd9Sstevel@tonic-gate  *  fd                int    The file descriptor to watch.
8695*7c478bd9Sstevel@tonic-gate  *  event       GlFdEvent    The type of activity to watch for.
8696*7c478bd9Sstevel@tonic-gate  *  callback  GlFdEventFn *  The function to call when the specified
8697*7c478bd9Sstevel@tonic-gate  *                           event occurs. Setting this to 0 removes
8698*7c478bd9Sstevel@tonic-gate  *                           any existing callback.
8699*7c478bd9Sstevel@tonic-gate  *  data             void *  A pointer to arbitrary data to pass to the
8700*7c478bd9Sstevel@tonic-gate  *                           callback function.
8701*7c478bd9Sstevel@tonic-gate  * Output:
8702*7c478bd9Sstevel@tonic-gate  *  return            int    0 - OK.
8703*7c478bd9Sstevel@tonic-gate  *                           1 - Either gl==NULL, or this facility isn't
8704*7c478bd9Sstevel@tonic-gate  *                               available on the the host system
8705*7c478bd9Sstevel@tonic-gate  *                               (ie. select() isn't available). No
8706*7c478bd9Sstevel@tonic-gate  *                               error message is generated in the latter
8707*7c478bd9Sstevel@tonic-gate  *                               case.
8708*7c478bd9Sstevel@tonic-gate  */
8709*7c478bd9Sstevel@tonic-gate int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
8710*7c478bd9Sstevel@tonic-gate 		GlFdEventFn *callback, void *data)
8711*7c478bd9Sstevel@tonic-gate {
8712*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
8713*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_watch_fd() */
8714*7c478bd9Sstevel@tonic-gate /*
8715*7c478bd9Sstevel@tonic-gate  * Check the arguments.
8716*7c478bd9Sstevel@tonic-gate  */
8717*7c478bd9Sstevel@tonic-gate   if(!gl) {
8718*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
8719*7c478bd9Sstevel@tonic-gate     return 1;
8720*7c478bd9Sstevel@tonic-gate   };
8721*7c478bd9Sstevel@tonic-gate   if(fd < 0) {
8722*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "Error: fd < 0", END_ERR_MSG);
8723*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
8724*7c478bd9Sstevel@tonic-gate     return 1;
8725*7c478bd9Sstevel@tonic-gate   };
8726*7c478bd9Sstevel@tonic-gate /*
8727*7c478bd9Sstevel@tonic-gate  * Block all signals.
8728*7c478bd9Sstevel@tonic-gate  */
8729*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
8730*7c478bd9Sstevel@tonic-gate     return 1;
8731*7c478bd9Sstevel@tonic-gate /*
8732*7c478bd9Sstevel@tonic-gate  * Execute the private body of the function while signals are blocked.
8733*7c478bd9Sstevel@tonic-gate  */
8734*7c478bd9Sstevel@tonic-gate   status = _gl_watch_fd(gl, fd, event, callback, data);
8735*7c478bd9Sstevel@tonic-gate /*
8736*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
8737*7c478bd9Sstevel@tonic-gate  */
8738*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
8739*7c478bd9Sstevel@tonic-gate   return status;
8740*7c478bd9Sstevel@tonic-gate }
8741*7c478bd9Sstevel@tonic-gate 
8742*7c478bd9Sstevel@tonic-gate /*.......................................................................
8743*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_watch_fd() function. It
8744*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
8745*7c478bd9Sstevel@tonic-gate  * delivery of signals.
8746*7c478bd9Sstevel@tonic-gate  */
8747*7c478bd9Sstevel@tonic-gate static int _gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
8748*7c478bd9Sstevel@tonic-gate 			GlFdEventFn *callback, void *data)
8749*7c478bd9Sstevel@tonic-gate #if !defined(HAVE_SELECT)
8750*7c478bd9Sstevel@tonic-gate {return 1;}               /* The facility isn't supported on this system */
8751*7c478bd9Sstevel@tonic-gate #else
8752*7c478bd9Sstevel@tonic-gate {
8753*7c478bd9Sstevel@tonic-gate   GlFdNode *prev;  /* The node that precedes 'node' in gl->fd_nodes */
8754*7c478bd9Sstevel@tonic-gate   GlFdNode *node;  /* The file-descriptor node being checked */
8755*7c478bd9Sstevel@tonic-gate /*
8756*7c478bd9Sstevel@tonic-gate  * Search the list of already registered fd activity nodes for the specified
8757*7c478bd9Sstevel@tonic-gate  * file descriptor.
8758*7c478bd9Sstevel@tonic-gate  */
8759*7c478bd9Sstevel@tonic-gate   for(prev=NULL,node=gl->fd_nodes; node && node->fd != fd;
8760*7c478bd9Sstevel@tonic-gate       prev=node, node=node->next)
8761*7c478bd9Sstevel@tonic-gate     ;
8762*7c478bd9Sstevel@tonic-gate /*
8763*7c478bd9Sstevel@tonic-gate  * Hasn't a node been allocated for this fd yet?
8764*7c478bd9Sstevel@tonic-gate  */
8765*7c478bd9Sstevel@tonic-gate   if(!node) {
8766*7c478bd9Sstevel@tonic-gate /*
8767*7c478bd9Sstevel@tonic-gate  * If there is no callback to record, just ignore the call.
8768*7c478bd9Sstevel@tonic-gate  */
8769*7c478bd9Sstevel@tonic-gate     if(!callback)
8770*7c478bd9Sstevel@tonic-gate       return 0;
8771*7c478bd9Sstevel@tonic-gate /*
8772*7c478bd9Sstevel@tonic-gate  * Allocate the new node.
8773*7c478bd9Sstevel@tonic-gate  */
8774*7c478bd9Sstevel@tonic-gate     node = (GlFdNode *) _new_FreeListNode(gl->fd_node_mem);
8775*7c478bd9Sstevel@tonic-gate     if(!node) {
8776*7c478bd9Sstevel@tonic-gate       errno = ENOMEM;
8777*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "Insufficient memory", END_ERR_MSG);
8778*7c478bd9Sstevel@tonic-gate       return 1;
8779*7c478bd9Sstevel@tonic-gate     };
8780*7c478bd9Sstevel@tonic-gate /*
8781*7c478bd9Sstevel@tonic-gate  * Prepend the node to the list.
8782*7c478bd9Sstevel@tonic-gate  */
8783*7c478bd9Sstevel@tonic-gate     node->next = gl->fd_nodes;
8784*7c478bd9Sstevel@tonic-gate     gl->fd_nodes = node;
8785*7c478bd9Sstevel@tonic-gate /*
8786*7c478bd9Sstevel@tonic-gate  * Initialize the node.
8787*7c478bd9Sstevel@tonic-gate  */
8788*7c478bd9Sstevel@tonic-gate     node->fd = fd;
8789*7c478bd9Sstevel@tonic-gate     node->rd.fn = 0;
8790*7c478bd9Sstevel@tonic-gate     node->rd.data = NULL;
8791*7c478bd9Sstevel@tonic-gate     node->ur = node->wr = node->rd;
8792*7c478bd9Sstevel@tonic-gate   };
8793*7c478bd9Sstevel@tonic-gate /*
8794*7c478bd9Sstevel@tonic-gate  * Record the new callback.
8795*7c478bd9Sstevel@tonic-gate  */
8796*7c478bd9Sstevel@tonic-gate   switch(event) {
8797*7c478bd9Sstevel@tonic-gate   case GLFD_READ:
8798*7c478bd9Sstevel@tonic-gate     node->rd.fn = callback;
8799*7c478bd9Sstevel@tonic-gate     node->rd.data = data;
8800*7c478bd9Sstevel@tonic-gate     if(callback)
8801*7c478bd9Sstevel@tonic-gate       FD_SET(fd, &gl->rfds);
8802*7c478bd9Sstevel@tonic-gate     else
8803*7c478bd9Sstevel@tonic-gate       FD_CLR(fd, &gl->rfds);
8804*7c478bd9Sstevel@tonic-gate     break;
8805*7c478bd9Sstevel@tonic-gate   case GLFD_WRITE:
8806*7c478bd9Sstevel@tonic-gate     node->wr.fn = callback;
8807*7c478bd9Sstevel@tonic-gate     node->wr.data = data;
8808*7c478bd9Sstevel@tonic-gate     if(callback)
8809*7c478bd9Sstevel@tonic-gate       FD_SET(fd, &gl->wfds);
8810*7c478bd9Sstevel@tonic-gate     else
8811*7c478bd9Sstevel@tonic-gate       FD_CLR(fd, &gl->wfds);
8812*7c478bd9Sstevel@tonic-gate     break;
8813*7c478bd9Sstevel@tonic-gate   case GLFD_URGENT:
8814*7c478bd9Sstevel@tonic-gate     node->ur.fn = callback;
8815*7c478bd9Sstevel@tonic-gate     node->ur.data = data;
8816*7c478bd9Sstevel@tonic-gate     if(callback)
8817*7c478bd9Sstevel@tonic-gate       FD_SET(fd, &gl->ufds);
8818*7c478bd9Sstevel@tonic-gate     else
8819*7c478bd9Sstevel@tonic-gate       FD_CLR(fd, &gl->ufds);
8820*7c478bd9Sstevel@tonic-gate     break;
8821*7c478bd9Sstevel@tonic-gate   };
8822*7c478bd9Sstevel@tonic-gate /*
8823*7c478bd9Sstevel@tonic-gate  * Keep a record of the largest file descriptor being watched.
8824*7c478bd9Sstevel@tonic-gate  */
8825*7c478bd9Sstevel@tonic-gate   if(fd > gl->max_fd)
8826*7c478bd9Sstevel@tonic-gate     gl->max_fd = fd;
8827*7c478bd9Sstevel@tonic-gate /*
8828*7c478bd9Sstevel@tonic-gate  * If we are deleting an existing callback, also delete the parent
8829*7c478bd9Sstevel@tonic-gate  * activity node if no callbacks are registered to the fd anymore.
8830*7c478bd9Sstevel@tonic-gate  */
8831*7c478bd9Sstevel@tonic-gate   if(!callback) {
8832*7c478bd9Sstevel@tonic-gate     if(!node->rd.fn && !node->wr.fn && !node->ur.fn) {
8833*7c478bd9Sstevel@tonic-gate       if(prev)
8834*7c478bd9Sstevel@tonic-gate 	prev->next = node->next;
8835*7c478bd9Sstevel@tonic-gate       else
8836*7c478bd9Sstevel@tonic-gate 	gl->fd_nodes = node->next;
8837*7c478bd9Sstevel@tonic-gate       node = (GlFdNode *) _del_FreeListNode(gl->fd_node_mem, node);
8838*7c478bd9Sstevel@tonic-gate     };
8839*7c478bd9Sstevel@tonic-gate   };
8840*7c478bd9Sstevel@tonic-gate   return 0;
8841*7c478bd9Sstevel@tonic-gate }
8842*7c478bd9Sstevel@tonic-gate #endif
8843*7c478bd9Sstevel@tonic-gate 
8844*7c478bd9Sstevel@tonic-gate /*.......................................................................
8845*7c478bd9Sstevel@tonic-gate  * On systems with the select() system call, the gl_inactivity_timeout()
8846*7c478bd9Sstevel@tonic-gate  * function provides the option of setting (or cancelling) an
8847*7c478bd9Sstevel@tonic-gate  * inactivity timeout. Inactivity, in this case, refers both to
8848*7c478bd9Sstevel@tonic-gate  * terminal input received from the user, and to I/O on any file
8849*7c478bd9Sstevel@tonic-gate  * descriptors registered by calls to gl_watch_fd(). If at any time,
8850*7c478bd9Sstevel@tonic-gate  * no activity is seen for the requested time period, the specified
8851*7c478bd9Sstevel@tonic-gate  * timeout callback function is called. On returning, this callback
8852*7c478bd9Sstevel@tonic-gate  * returns a code which tells gl_get_line() what to do next. Note that
8853*7c478bd9Sstevel@tonic-gate  * each call to gl_inactivity_timeout() replaces any previously installed
8854*7c478bd9Sstevel@tonic-gate  * timeout callback, and that specifying a callback of 0, turns off
8855*7c478bd9Sstevel@tonic-gate  * inactivity timing.
8856*7c478bd9Sstevel@tonic-gate  *
8857*7c478bd9Sstevel@tonic-gate  * Beware that although the timeout argument includes a nano-second
8858*7c478bd9Sstevel@tonic-gate  * component, few computer clocks presently have resolutions finer
8859*7c478bd9Sstevel@tonic-gate  * than a few milliseconds, so asking for less than a few milliseconds
8860*7c478bd9Sstevel@tonic-gate  * is equivalent to zero on a lot of systems.
8861*7c478bd9Sstevel@tonic-gate  *
8862*7c478bd9Sstevel@tonic-gate  * Input:
8863*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of the command-line input
8864*7c478bd9Sstevel@tonic-gate  *                           module.
8865*7c478bd9Sstevel@tonic-gate  *  callback  GlTimeoutFn *  The function to call when the inactivity
8866*7c478bd9Sstevel@tonic-gate  *                           timeout is exceeded. To turn off
8867*7c478bd9Sstevel@tonic-gate  *                           inactivity timeouts altogether, send 0.
8868*7c478bd9Sstevel@tonic-gate  *  data             void *  A pointer to arbitrary data to pass to the
8869*7c478bd9Sstevel@tonic-gate  *                           callback function.
8870*7c478bd9Sstevel@tonic-gate  *  sec     unsigned long    The number of whole seconds in the timeout.
8871*7c478bd9Sstevel@tonic-gate  *  nsec    unsigned long    The fractional number of seconds in the
8872*7c478bd9Sstevel@tonic-gate  *                           timeout, expressed in nano-seconds (see
8873*7c478bd9Sstevel@tonic-gate  *                           the caveat above).
8874*7c478bd9Sstevel@tonic-gate  * Output:
8875*7c478bd9Sstevel@tonic-gate  *  return            int    0 - OK.
8876*7c478bd9Sstevel@tonic-gate  *                           1 - Either gl==NULL, or this facility isn't
8877*7c478bd9Sstevel@tonic-gate  *                               available on the the host system
8878*7c478bd9Sstevel@tonic-gate  *                               (ie. select() isn't available). No
8879*7c478bd9Sstevel@tonic-gate  *                               error message is generated in the latter
8880*7c478bd9Sstevel@tonic-gate  *                               case.
8881*7c478bd9Sstevel@tonic-gate  */
8882*7c478bd9Sstevel@tonic-gate int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *timeout_fn, void *data,
8883*7c478bd9Sstevel@tonic-gate 		   unsigned long sec, unsigned long nsec)
8884*7c478bd9Sstevel@tonic-gate #if !defined(HAVE_SELECT)
8885*7c478bd9Sstevel@tonic-gate {return 1;}               /* The facility isn't supported on this system */
8886*7c478bd9Sstevel@tonic-gate #else
8887*7c478bd9Sstevel@tonic-gate {
8888*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
8889*7c478bd9Sstevel@tonic-gate /*
8890*7c478bd9Sstevel@tonic-gate  * Check the arguments.
8891*7c478bd9Sstevel@tonic-gate  */
8892*7c478bd9Sstevel@tonic-gate   if(!gl) {
8893*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
8894*7c478bd9Sstevel@tonic-gate     return 1;
8895*7c478bd9Sstevel@tonic-gate   };
8896*7c478bd9Sstevel@tonic-gate /*
8897*7c478bd9Sstevel@tonic-gate  * Block all signals.
8898*7c478bd9Sstevel@tonic-gate  */
8899*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
8900*7c478bd9Sstevel@tonic-gate     return 1;
8901*7c478bd9Sstevel@tonic-gate /*
8902*7c478bd9Sstevel@tonic-gate  * Install a new timeout?
8903*7c478bd9Sstevel@tonic-gate  */
8904*7c478bd9Sstevel@tonic-gate   if(timeout_fn) {
8905*7c478bd9Sstevel@tonic-gate     gl->timer.dt.tv_sec = sec;
8906*7c478bd9Sstevel@tonic-gate     gl->timer.dt.tv_usec = nsec / 1000;
8907*7c478bd9Sstevel@tonic-gate     gl->timer.fn = timeout_fn;
8908*7c478bd9Sstevel@tonic-gate     gl->timer.data = data;
8909*7c478bd9Sstevel@tonic-gate   } else {
8910*7c478bd9Sstevel@tonic-gate     gl->timer.fn = 0;
8911*7c478bd9Sstevel@tonic-gate     gl->timer.data = NULL;
8912*7c478bd9Sstevel@tonic-gate   };
8913*7c478bd9Sstevel@tonic-gate /*
8914*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
8915*7c478bd9Sstevel@tonic-gate  */
8916*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
8917*7c478bd9Sstevel@tonic-gate   return 0;
8918*7c478bd9Sstevel@tonic-gate }
8919*7c478bd9Sstevel@tonic-gate #endif
8920*7c478bd9Sstevel@tonic-gate 
8921*7c478bd9Sstevel@tonic-gate /*.......................................................................
8922*7c478bd9Sstevel@tonic-gate  * When select() is available, this is a private function of
8923*7c478bd9Sstevel@tonic-gate  * gl_read_input() which responds to file-descriptor events registered by
8924*7c478bd9Sstevel@tonic-gate  * the caller. Note that it assumes that it is being called from within
8925*7c478bd9Sstevel@tonic-gate  * gl_read_input()'s sigsetjump() clause.
8926*7c478bd9Sstevel@tonic-gate  *
8927*7c478bd9Sstevel@tonic-gate  * Input:
8928*7c478bd9Sstevel@tonic-gate  *  gl    GetLine *  The resource object of this module.
8929*7c478bd9Sstevel@tonic-gate  *  fd        int    The file descriptor to be watched for user input.
8930*7c478bd9Sstevel@tonic-gate  * Output:
8931*7c478bd9Sstevel@tonic-gate  *  return    int    0 - OK.
8932*7c478bd9Sstevel@tonic-gate  *                   1 - An error occurred.
8933*7c478bd9Sstevel@tonic-gate  */
8934*7c478bd9Sstevel@tonic-gate static int gl_event_handler(GetLine *gl, int fd)
8935*7c478bd9Sstevel@tonic-gate {
8936*7c478bd9Sstevel@tonic-gate #if !defined(HAVE_SELECT)
8937*7c478bd9Sstevel@tonic-gate   return 0;
8938*7c478bd9Sstevel@tonic-gate #else
8939*7c478bd9Sstevel@tonic-gate /*
8940*7c478bd9Sstevel@tonic-gate  * Set up a zero-second timeout.
8941*7c478bd9Sstevel@tonic-gate  */
8942*7c478bd9Sstevel@tonic-gate   struct timeval zero;
8943*7c478bd9Sstevel@tonic-gate   zero.tv_sec = zero.tv_usec = 0;
8944*7c478bd9Sstevel@tonic-gate /*
8945*7c478bd9Sstevel@tonic-gate  * If at any time no external callbacks remain, quit the loop return,
8946*7c478bd9Sstevel@tonic-gate  * so that we can simply wait in read(). This is designed as an
8947*7c478bd9Sstevel@tonic-gate  * optimization for when no callbacks have been registered on entry to
8948*7c478bd9Sstevel@tonic-gate  * this function, but since callbacks can delete themselves, it can
8949*7c478bd9Sstevel@tonic-gate  * also help later.
8950*7c478bd9Sstevel@tonic-gate  */
8951*7c478bd9Sstevel@tonic-gate   while(gl->fd_nodes || gl->timer.fn) {
8952*7c478bd9Sstevel@tonic-gate     int nready;   /* The number of file descriptors that are ready for I/O */
8953*7c478bd9Sstevel@tonic-gate /*
8954*7c478bd9Sstevel@tonic-gate  * Get the set of descriptors to be watched.
8955*7c478bd9Sstevel@tonic-gate  */
8956*7c478bd9Sstevel@tonic-gate     fd_set rfds = gl->rfds;
8957*7c478bd9Sstevel@tonic-gate     fd_set wfds = gl->wfds;
8958*7c478bd9Sstevel@tonic-gate     fd_set ufds = gl->ufds;
8959*7c478bd9Sstevel@tonic-gate /*
8960*7c478bd9Sstevel@tonic-gate  * Get the appropriate timeout.
8961*7c478bd9Sstevel@tonic-gate  */
8962*7c478bd9Sstevel@tonic-gate     struct timeval dt = gl->timer.fn ? gl->timer.dt : zero;
8963*7c478bd9Sstevel@tonic-gate /*
8964*7c478bd9Sstevel@tonic-gate  * Add the specified user-input file descriptor tot he set that is to
8965*7c478bd9Sstevel@tonic-gate  * be watched.
8966*7c478bd9Sstevel@tonic-gate  */
8967*7c478bd9Sstevel@tonic-gate     FD_SET(fd, &rfds);
8968*7c478bd9Sstevel@tonic-gate /*
8969*7c478bd9Sstevel@tonic-gate  * Unblock the signals that we are watching, while select is blocked
8970*7c478bd9Sstevel@tonic-gate  * waiting for I/O.
8971*7c478bd9Sstevel@tonic-gate  */
8972*7c478bd9Sstevel@tonic-gate     gl_catch_signals(gl);
8973*7c478bd9Sstevel@tonic-gate /*
8974*7c478bd9Sstevel@tonic-gate  * Wait for activity on any of the file descriptors.
8975*7c478bd9Sstevel@tonic-gate  */
8976*7c478bd9Sstevel@tonic-gate     nready = select(gl->max_fd+1, &rfds, &wfds, &ufds,
8977*7c478bd9Sstevel@tonic-gate 	    (gl->timer.fn || gl->io_mode==GL_SERVER_MODE) ? &dt : NULL);
8978*7c478bd9Sstevel@tonic-gate /*
8979*7c478bd9Sstevel@tonic-gate  * We don't want to do a longjmp in the middle of a callback that
8980*7c478bd9Sstevel@tonic-gate  * might be modifying global or heap data, so block all the signals
8981*7c478bd9Sstevel@tonic-gate  * that we are trapping before executing callback functions. Note that
8982*7c478bd9Sstevel@tonic-gate  * the caller will unblock them again when it needs to, so there is
8983*7c478bd9Sstevel@tonic-gate  * no need to undo this before returning.
8984*7c478bd9Sstevel@tonic-gate  */
8985*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, NULL);
8986*7c478bd9Sstevel@tonic-gate /*
8987*7c478bd9Sstevel@tonic-gate  * If select() returns but none of the file descriptors are reported
8988*7c478bd9Sstevel@tonic-gate  * to have activity, then select() timed out.
8989*7c478bd9Sstevel@tonic-gate  */
8990*7c478bd9Sstevel@tonic-gate     if(nready == 0) {
8991*7c478bd9Sstevel@tonic-gate /*
8992*7c478bd9Sstevel@tonic-gate  * Note that in non-blocking server mode, the inactivity timer is used
8993*7c478bd9Sstevel@tonic-gate  * to allow I/O to block for a specified amount of time, so in this
8994*7c478bd9Sstevel@tonic-gate  * mode we return the postponed blocked status when an abort is
8995*7c478bd9Sstevel@tonic-gate  * requested.
8996*7c478bd9Sstevel@tonic-gate  */
8997*7c478bd9Sstevel@tonic-gate       if(gl_call_timeout_handler(gl)) {
8998*7c478bd9Sstevel@tonic-gate 	return 1;
8999*7c478bd9Sstevel@tonic-gate       } else if(gl->io_mode == GL_SERVER_MODE) {
9000*7c478bd9Sstevel@tonic-gate 	gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO);
9001*7c478bd9Sstevel@tonic-gate 	return 1;
9002*7c478bd9Sstevel@tonic-gate       };
9003*7c478bd9Sstevel@tonic-gate /*
9004*7c478bd9Sstevel@tonic-gate  * If nready < 0, this means an error occurred.
9005*7c478bd9Sstevel@tonic-gate  */
9006*7c478bd9Sstevel@tonic-gate     } else if(nready < 0) {
9007*7c478bd9Sstevel@tonic-gate       if(errno != EINTR) {
9008*7c478bd9Sstevel@tonic-gate 	gl_record_status(gl, GLR_ERROR, errno);
9009*7c478bd9Sstevel@tonic-gate 	return 1;
9010*7c478bd9Sstevel@tonic-gate       };
9011*7c478bd9Sstevel@tonic-gate /*
9012*7c478bd9Sstevel@tonic-gate  * If the user-input file descriptor has data available, return.
9013*7c478bd9Sstevel@tonic-gate  */
9014*7c478bd9Sstevel@tonic-gate     } else if(FD_ISSET(fd, &rfds)) {
9015*7c478bd9Sstevel@tonic-gate       return 0;
9016*7c478bd9Sstevel@tonic-gate /*
9017*7c478bd9Sstevel@tonic-gate  * Check for activity on any of the file descriptors registered by the
9018*7c478bd9Sstevel@tonic-gate  * calling application, and call the associated callback functions.
9019*7c478bd9Sstevel@tonic-gate  */
9020*7c478bd9Sstevel@tonic-gate     } else {
9021*7c478bd9Sstevel@tonic-gate       GlFdNode *node;   /* The fd event node being checked */
9022*7c478bd9Sstevel@tonic-gate /*
9023*7c478bd9Sstevel@tonic-gate  * Search the list for the file descriptor that caused select() to return.
9024*7c478bd9Sstevel@tonic-gate  */
9025*7c478bd9Sstevel@tonic-gate       for(node=gl->fd_nodes; node; node=node->next) {
9026*7c478bd9Sstevel@tonic-gate /*
9027*7c478bd9Sstevel@tonic-gate  * Is there urgent out of band data waiting to be read on fd?
9028*7c478bd9Sstevel@tonic-gate  */
9029*7c478bd9Sstevel@tonic-gate 	if(node->ur.fn && FD_ISSET(node->fd, &ufds)) {
9030*7c478bd9Sstevel@tonic-gate 	  if(gl_call_fd_handler(gl, &node->ur, node->fd, GLFD_URGENT))
9031*7c478bd9Sstevel@tonic-gate 	    return 1;
9032*7c478bd9Sstevel@tonic-gate 	  break;  /* The callback may have changed the list of nodes */
9033*7c478bd9Sstevel@tonic-gate /*
9034*7c478bd9Sstevel@tonic-gate  * Is the fd readable?
9035*7c478bd9Sstevel@tonic-gate  */
9036*7c478bd9Sstevel@tonic-gate 	} else if(node->rd.fn && FD_ISSET(node->fd, &rfds)) {
9037*7c478bd9Sstevel@tonic-gate 	  if(gl_call_fd_handler(gl, &node->rd, node->fd, GLFD_READ))
9038*7c478bd9Sstevel@tonic-gate 	    return 1;
9039*7c478bd9Sstevel@tonic-gate 	  break;  /* The callback may have changed the list of nodes */
9040*7c478bd9Sstevel@tonic-gate /*
9041*7c478bd9Sstevel@tonic-gate  * Is the fd writable?
9042*7c478bd9Sstevel@tonic-gate  */
9043*7c478bd9Sstevel@tonic-gate 	} else if(node->wr.fn && FD_ISSET(node->fd, &wfds)) {
9044*7c478bd9Sstevel@tonic-gate 	  if(gl_call_fd_handler(gl, &node->wr, node->fd, GLFD_WRITE))
9045*7c478bd9Sstevel@tonic-gate 	    return 1;
9046*7c478bd9Sstevel@tonic-gate 	  break;  /* The callback may have changed the list of nodes */
9047*7c478bd9Sstevel@tonic-gate 	};
9048*7c478bd9Sstevel@tonic-gate       };
9049*7c478bd9Sstevel@tonic-gate     };
9050*7c478bd9Sstevel@tonic-gate /*
9051*7c478bd9Sstevel@tonic-gate  * Just in case the above event handlers asked for the input line to
9052*7c478bd9Sstevel@tonic-gate  * be redrawn, flush any pending output.
9053*7c478bd9Sstevel@tonic-gate  */
9054*7c478bd9Sstevel@tonic-gate     if(gl_flush_output(gl))
9055*7c478bd9Sstevel@tonic-gate       return 1;
9056*7c478bd9Sstevel@tonic-gate   };
9057*7c478bd9Sstevel@tonic-gate   return 0;
9058*7c478bd9Sstevel@tonic-gate }
9059*7c478bd9Sstevel@tonic-gate #endif
9060*7c478bd9Sstevel@tonic-gate 
9061*7c478bd9Sstevel@tonic-gate #if defined(HAVE_SELECT)
9062*7c478bd9Sstevel@tonic-gate /*.......................................................................
9063*7c478bd9Sstevel@tonic-gate  * This is a private function of gl_event_handler(), used to call a
9064*7c478bd9Sstevel@tonic-gate  * file-descriptor callback.
9065*7c478bd9Sstevel@tonic-gate  *
9066*7c478bd9Sstevel@tonic-gate  * Input:
9067*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The resource object of gl_get_line().
9068*7c478bd9Sstevel@tonic-gate  *  gfh  GlFdHandler *  The I/O handler.
9069*7c478bd9Sstevel@tonic-gate  *  fd           int    The file-descriptor being reported.
9070*7c478bd9Sstevel@tonic-gate  *  event  GlFdEvent    The I/O event being reported.
9071*7c478bd9Sstevel@tonic-gate  * Output:
9072*7c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
9073*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
9074*7c478bd9Sstevel@tonic-gate  */
9075*7c478bd9Sstevel@tonic-gate static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd,
9076*7c478bd9Sstevel@tonic-gate 			      GlFdEvent event)
9077*7c478bd9Sstevel@tonic-gate {
9078*7c478bd9Sstevel@tonic-gate   Termios attr;       /* The terminal attributes */
9079*7c478bd9Sstevel@tonic-gate   int waserr = 0;     /* True after any error */
9080*7c478bd9Sstevel@tonic-gate /*
9081*7c478bd9Sstevel@tonic-gate  * Re-enable conversion of newline characters to carriage-return/linefeed,
9082*7c478bd9Sstevel@tonic-gate  * so that the callback can write to the terminal without having to do
9083*7c478bd9Sstevel@tonic-gate  * anything special.
9084*7c478bd9Sstevel@tonic-gate  */
9085*7c478bd9Sstevel@tonic-gate   if(tcgetattr(gl->input_fd, &attr)) {
9086*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG);
9087*7c478bd9Sstevel@tonic-gate     return 1;
9088*7c478bd9Sstevel@tonic-gate   };
9089*7c478bd9Sstevel@tonic-gate   attr.c_oflag |= OPOST;
9090*7c478bd9Sstevel@tonic-gate   while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) {
9091*7c478bd9Sstevel@tonic-gate     if(errno != EINTR) {
9092*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG);
9093*7c478bd9Sstevel@tonic-gate       return 1;
9094*7c478bd9Sstevel@tonic-gate     };
9095*7c478bd9Sstevel@tonic-gate   };
9096*7c478bd9Sstevel@tonic-gate /*
9097*7c478bd9Sstevel@tonic-gate  * Invoke the application's callback function.
9098*7c478bd9Sstevel@tonic-gate  */
9099*7c478bd9Sstevel@tonic-gate   switch(gfh->fn(gl, gfh->data, fd, event)) {
9100*7c478bd9Sstevel@tonic-gate   default:
9101*7c478bd9Sstevel@tonic-gate   case GLFD_ABORT:
9102*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_FDABORT, 0);
9103*7c478bd9Sstevel@tonic-gate     waserr = 1;
9104*7c478bd9Sstevel@tonic-gate     break;
9105*7c478bd9Sstevel@tonic-gate   case GLFD_REFRESH:
9106*7c478bd9Sstevel@tonic-gate     gl_queue_redisplay(gl);
9107*7c478bd9Sstevel@tonic-gate     break;
9108*7c478bd9Sstevel@tonic-gate   case GLFD_CONTINUE:
9109*7c478bd9Sstevel@tonic-gate     break;
9110*7c478bd9Sstevel@tonic-gate   };
9111*7c478bd9Sstevel@tonic-gate /*
9112*7c478bd9Sstevel@tonic-gate  * Disable conversion of newline characters to carriage-return/linefeed.
9113*7c478bd9Sstevel@tonic-gate  */
9114*7c478bd9Sstevel@tonic-gate   attr.c_oflag &= ~(OPOST);
9115*7c478bd9Sstevel@tonic-gate   while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) {
9116*7c478bd9Sstevel@tonic-gate     if(errno != EINTR) {
9117*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG);
9118*7c478bd9Sstevel@tonic-gate       return 1;
9119*7c478bd9Sstevel@tonic-gate     };
9120*7c478bd9Sstevel@tonic-gate   };
9121*7c478bd9Sstevel@tonic-gate   return waserr;
9122*7c478bd9Sstevel@tonic-gate }
9123*7c478bd9Sstevel@tonic-gate 
9124*7c478bd9Sstevel@tonic-gate /*.......................................................................
9125*7c478bd9Sstevel@tonic-gate  * This is a private function of gl_event_handler(), used to call a
9126*7c478bd9Sstevel@tonic-gate  * inactivity timer callbacks.
9127*7c478bd9Sstevel@tonic-gate  *
9128*7c478bd9Sstevel@tonic-gate  * Input:
9129*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The resource object of gl_get_line().
9130*7c478bd9Sstevel@tonic-gate  * Output:
9131*7c478bd9Sstevel@tonic-gate  *  return       int    0 - OK.
9132*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
9133*7c478bd9Sstevel@tonic-gate  */
9134*7c478bd9Sstevel@tonic-gate static int gl_call_timeout_handler(GetLine *gl)
9135*7c478bd9Sstevel@tonic-gate {
9136*7c478bd9Sstevel@tonic-gate   Termios attr;       /* The terminal attributes */
9137*7c478bd9Sstevel@tonic-gate   int waserr = 0;     /* True after any error */
9138*7c478bd9Sstevel@tonic-gate /*
9139*7c478bd9Sstevel@tonic-gate  * Make sure that there is an inactivity timeout callback.
9140*7c478bd9Sstevel@tonic-gate  */
9141*7c478bd9Sstevel@tonic-gate   if(!gl->timer.fn)
9142*7c478bd9Sstevel@tonic-gate     return 0;
9143*7c478bd9Sstevel@tonic-gate /*
9144*7c478bd9Sstevel@tonic-gate  * Re-enable conversion of newline characters to carriage-return/linefeed,
9145*7c478bd9Sstevel@tonic-gate  * so that the callback can write to the terminal without having to do
9146*7c478bd9Sstevel@tonic-gate  * anything special.
9147*7c478bd9Sstevel@tonic-gate  */
9148*7c478bd9Sstevel@tonic-gate   if(tcgetattr(gl->input_fd, &attr)) {
9149*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG);
9150*7c478bd9Sstevel@tonic-gate     return 1;
9151*7c478bd9Sstevel@tonic-gate   };
9152*7c478bd9Sstevel@tonic-gate   attr.c_oflag |= OPOST;
9153*7c478bd9Sstevel@tonic-gate   while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) {
9154*7c478bd9Sstevel@tonic-gate     if(errno != EINTR) {
9155*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG);
9156*7c478bd9Sstevel@tonic-gate       return 1;
9157*7c478bd9Sstevel@tonic-gate     };
9158*7c478bd9Sstevel@tonic-gate   };
9159*7c478bd9Sstevel@tonic-gate /*
9160*7c478bd9Sstevel@tonic-gate  * Invoke the application's callback function.
9161*7c478bd9Sstevel@tonic-gate  */
9162*7c478bd9Sstevel@tonic-gate   switch(gl->timer.fn(gl, gl->timer.data)) {
9163*7c478bd9Sstevel@tonic-gate   default:
9164*7c478bd9Sstevel@tonic-gate   case GLTO_ABORT:
9165*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_TIMEOUT, 0);
9166*7c478bd9Sstevel@tonic-gate     waserr = 1;
9167*7c478bd9Sstevel@tonic-gate     break;
9168*7c478bd9Sstevel@tonic-gate   case GLTO_REFRESH:
9169*7c478bd9Sstevel@tonic-gate     gl_queue_redisplay(gl);
9170*7c478bd9Sstevel@tonic-gate     break;
9171*7c478bd9Sstevel@tonic-gate   case GLTO_CONTINUE:
9172*7c478bd9Sstevel@tonic-gate     break;
9173*7c478bd9Sstevel@tonic-gate   };
9174*7c478bd9Sstevel@tonic-gate /*
9175*7c478bd9Sstevel@tonic-gate  * Disable conversion of newline characters to carriage-return/linefeed.
9176*7c478bd9Sstevel@tonic-gate  */
9177*7c478bd9Sstevel@tonic-gate   attr.c_oflag &= ~(OPOST);
9178*7c478bd9Sstevel@tonic-gate   while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) {
9179*7c478bd9Sstevel@tonic-gate     if(errno != EINTR) {
9180*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG);
9181*7c478bd9Sstevel@tonic-gate       return 1;
9182*7c478bd9Sstevel@tonic-gate     };
9183*7c478bd9Sstevel@tonic-gate   };
9184*7c478bd9Sstevel@tonic-gate   return waserr;
9185*7c478bd9Sstevel@tonic-gate }
9186*7c478bd9Sstevel@tonic-gate #endif  /* HAVE_SELECT */
9187*7c478bd9Sstevel@tonic-gate 
9188*7c478bd9Sstevel@tonic-gate /*.......................................................................
9189*7c478bd9Sstevel@tonic-gate  * Switch history groups. History groups represent separate history
9190*7c478bd9Sstevel@tonic-gate  * lists recorded within a single history buffer. Different groups
9191*7c478bd9Sstevel@tonic-gate  * are distinguished by integer identifiers chosen by the calling
9192*7c478bd9Sstevel@tonic-gate  * appplicaton. Initially new_GetLine() sets the group identifier to
9193*7c478bd9Sstevel@tonic-gate  * 0. Whenever a new line is appended to the history list, the current
9194*7c478bd9Sstevel@tonic-gate  * group identifier is recorded with it, and history lookups only
9195*7c478bd9Sstevel@tonic-gate  * consider lines marked with the current group identifier.
9196*7c478bd9Sstevel@tonic-gate  *
9197*7c478bd9Sstevel@tonic-gate  * Input:
9198*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The resource object of gl_get_line().
9199*7c478bd9Sstevel@tonic-gate  *  id     unsigned    The new history group identifier.
9200*7c478bd9Sstevel@tonic-gate  * Output:
9201*7c478bd9Sstevel@tonic-gate  *  return      int    0 - OK.
9202*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
9203*7c478bd9Sstevel@tonic-gate  */
9204*7c478bd9Sstevel@tonic-gate int gl_group_history(GetLine *gl, unsigned id)
9205*7c478bd9Sstevel@tonic-gate {
9206*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
9207*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of this function */
9208*7c478bd9Sstevel@tonic-gate /*
9209*7c478bd9Sstevel@tonic-gate  * Check the arguments.
9210*7c478bd9Sstevel@tonic-gate  */
9211*7c478bd9Sstevel@tonic-gate   if(!gl) {
9212*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
9213*7c478bd9Sstevel@tonic-gate     return 1;
9214*7c478bd9Sstevel@tonic-gate   };
9215*7c478bd9Sstevel@tonic-gate /*
9216*7c478bd9Sstevel@tonic-gate  * Block all signals while we install the new configuration.
9217*7c478bd9Sstevel@tonic-gate  */
9218*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
9219*7c478bd9Sstevel@tonic-gate     return 1;
9220*7c478bd9Sstevel@tonic-gate /*
9221*7c478bd9Sstevel@tonic-gate  * If the group isn't being changed, do nothing.
9222*7c478bd9Sstevel@tonic-gate  */
9223*7c478bd9Sstevel@tonic-gate   if(_glh_get_group(gl->glh) == id) {
9224*7c478bd9Sstevel@tonic-gate     status = 0;
9225*7c478bd9Sstevel@tonic-gate /*
9226*7c478bd9Sstevel@tonic-gate  * Establish the new group.
9227*7c478bd9Sstevel@tonic-gate  */
9228*7c478bd9Sstevel@tonic-gate   } else if(_glh_set_group(gl->glh, id)) {
9229*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
9230*7c478bd9Sstevel@tonic-gate     status = 1;
9231*7c478bd9Sstevel@tonic-gate /*
9232*7c478bd9Sstevel@tonic-gate  * Prevent history information from the previous group being
9233*7c478bd9Sstevel@tonic-gate  * inappropriately used by the next call to gl_get_line().
9234*7c478bd9Sstevel@tonic-gate  */
9235*7c478bd9Sstevel@tonic-gate   } else {
9236*7c478bd9Sstevel@tonic-gate     gl->preload_history = 0;
9237*7c478bd9Sstevel@tonic-gate     gl->last_search = -1;
9238*7c478bd9Sstevel@tonic-gate     status = 0;
9239*7c478bd9Sstevel@tonic-gate   };
9240*7c478bd9Sstevel@tonic-gate /*
9241*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
9242*7c478bd9Sstevel@tonic-gate  */
9243*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
9244*7c478bd9Sstevel@tonic-gate   return status;
9245*7c478bd9Sstevel@tonic-gate }
9246*7c478bd9Sstevel@tonic-gate 
9247*7c478bd9Sstevel@tonic-gate /*.......................................................................
9248*7c478bd9Sstevel@tonic-gate  * Display the contents of the history list.
9249*7c478bd9Sstevel@tonic-gate  *
9250*7c478bd9Sstevel@tonic-gate  * Input:
9251*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The resource object of gl_get_line().
9252*7c478bd9Sstevel@tonic-gate  *  fp         FILE *  The stdio output stream to write to.
9253*7c478bd9Sstevel@tonic-gate  *  fmt  const char *  A format string. This containing characters to be
9254*7c478bd9Sstevel@tonic-gate  *                     written verbatim, plus any of the following
9255*7c478bd9Sstevel@tonic-gate  *                     format directives:
9256*7c478bd9Sstevel@tonic-gate  *                       %D  -  The date, formatted like 2001-11-20
9257*7c478bd9Sstevel@tonic-gate  *                       %T  -  The time of day, formatted like 23:59:59
9258*7c478bd9Sstevel@tonic-gate  *                       %N  -  The sequential entry number of the
9259*7c478bd9Sstevel@tonic-gate  *                              line in the history buffer.
9260*7c478bd9Sstevel@tonic-gate  *                       %G  -  The number of the history group that
9261*7c478bd9Sstevel@tonic-gate  *                              the line belongs to.
9262*7c478bd9Sstevel@tonic-gate  *                       %%  -  A literal % character.
9263*7c478bd9Sstevel@tonic-gate  *                       %H  -  The history line itself.
9264*7c478bd9Sstevel@tonic-gate  *                     Note that a '\n' newline character is not
9265*7c478bd9Sstevel@tonic-gate  *                     appended by default.
9266*7c478bd9Sstevel@tonic-gate  *  all_groups  int    If true, display history lines from all
9267*7c478bd9Sstevel@tonic-gate  *                     history groups. Otherwise only display
9268*7c478bd9Sstevel@tonic-gate  *                     those of the current history group.
9269*7c478bd9Sstevel@tonic-gate  *  max_lines   int    If max_lines is < 0, all available lines
9270*7c478bd9Sstevel@tonic-gate  *                     are displayed. Otherwise only the most
9271*7c478bd9Sstevel@tonic-gate  *                     recent max_lines lines will be displayed.
9272*7c478bd9Sstevel@tonic-gate  * Output:
9273*7c478bd9Sstevel@tonic-gate  *  return      int    0 - OK.
9274*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
9275*7c478bd9Sstevel@tonic-gate  */
9276*7c478bd9Sstevel@tonic-gate int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups,
9277*7c478bd9Sstevel@tonic-gate 		    int max_lines)
9278*7c478bd9Sstevel@tonic-gate {
9279*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
9280*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of this function */
9281*7c478bd9Sstevel@tonic-gate /*
9282*7c478bd9Sstevel@tonic-gate  * Check the arguments.
9283*7c478bd9Sstevel@tonic-gate  */
9284*7c478bd9Sstevel@tonic-gate   if(!gl || !fp || !fmt) {
9285*7c478bd9Sstevel@tonic-gate     if(gl)
9286*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG);
9287*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
9288*7c478bd9Sstevel@tonic-gate     return 1;
9289*7c478bd9Sstevel@tonic-gate   };
9290*7c478bd9Sstevel@tonic-gate /*
9291*7c478bd9Sstevel@tonic-gate  * Block all signals.
9292*7c478bd9Sstevel@tonic-gate  */
9293*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
9294*7c478bd9Sstevel@tonic-gate     return 1;
9295*7c478bd9Sstevel@tonic-gate /*
9296*7c478bd9Sstevel@tonic-gate  * Display the specified history group(s) while signals are blocked.
9297*7c478bd9Sstevel@tonic-gate  */
9298*7c478bd9Sstevel@tonic-gate   status = _glh_show_history(gl->glh, _io_write_stdio, fp, fmt, all_groups,
9299*7c478bd9Sstevel@tonic-gate 			     max_lines) || fflush(fp)==EOF;
9300*7c478bd9Sstevel@tonic-gate   if(!status)
9301*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
9302*7c478bd9Sstevel@tonic-gate /*
9303*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
9304*7c478bd9Sstevel@tonic-gate  */
9305*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
9306*7c478bd9Sstevel@tonic-gate   return status;
9307*7c478bd9Sstevel@tonic-gate }
9308*7c478bd9Sstevel@tonic-gate 
9309*7c478bd9Sstevel@tonic-gate /*.......................................................................
9310*7c478bd9Sstevel@tonic-gate  * Update if necessary, and return the current size of the terminal.
9311*7c478bd9Sstevel@tonic-gate  *
9312*7c478bd9Sstevel@tonic-gate  * Input:
9313*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of gl_get_line().
9314*7c478bd9Sstevel@tonic-gate  *  def_ncolumn       int    If the number of columns in the terminal
9315*7c478bd9Sstevel@tonic-gate  *                           can't be determined, substitute this number.
9316*7c478bd9Sstevel@tonic-gate  *  def_nline         int    If the number of lines in the terminal can't
9317*7c478bd9Sstevel@tonic-gate  *                           be determined, substitute this number.
9318*7c478bd9Sstevel@tonic-gate  * Output:
9319*7c478bd9Sstevel@tonic-gate  *  return GlTerminalSize    The current terminal size.
9320*7c478bd9Sstevel@tonic-gate  */
9321*7c478bd9Sstevel@tonic-gate GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline)
9322*7c478bd9Sstevel@tonic-gate {
9323*7c478bd9Sstevel@tonic-gate   GlTerminalSize size;  /* The object to be returned */
9324*7c478bd9Sstevel@tonic-gate   sigset_t oldset;      /* The signals that were blocked on entry */
9325*7c478bd9Sstevel@tonic-gate                         /*  to this function */
9326*7c478bd9Sstevel@tonic-gate /*
9327*7c478bd9Sstevel@tonic-gate  * Block all signals while accessing gl.
9328*7c478bd9Sstevel@tonic-gate  */
9329*7c478bd9Sstevel@tonic-gate   gl_mask_signals(gl, &oldset);
9330*7c478bd9Sstevel@tonic-gate /*
9331*7c478bd9Sstevel@tonic-gate  * Lookup/configure the terminal size.
9332*7c478bd9Sstevel@tonic-gate  */
9333*7c478bd9Sstevel@tonic-gate   _gl_terminal_size(gl, def_ncolumn, def_nline, &size);
9334*7c478bd9Sstevel@tonic-gate /*
9335*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9336*7c478bd9Sstevel@tonic-gate  */
9337*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
9338*7c478bd9Sstevel@tonic-gate   return size;
9339*7c478bd9Sstevel@tonic-gate }
9340*7c478bd9Sstevel@tonic-gate 
9341*7c478bd9Sstevel@tonic-gate /*.......................................................................
9342*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_terminal_size() function. It
9343*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
9344*7c478bd9Sstevel@tonic-gate  * delivery of signals.
9345*7c478bd9Sstevel@tonic-gate  */
9346*7c478bd9Sstevel@tonic-gate static void _gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline,
9347*7c478bd9Sstevel@tonic-gate 			      GlTerminalSize *size)
9348*7c478bd9Sstevel@tonic-gate {
9349*7c478bd9Sstevel@tonic-gate   const char *env;      /* The value of an environment variable */
9350*7c478bd9Sstevel@tonic-gate   int n;                /* A number read from env[] */
9351*7c478bd9Sstevel@tonic-gate /*
9352*7c478bd9Sstevel@tonic-gate  * Set the number of lines and columns to non-sensical values so that
9353*7c478bd9Sstevel@tonic-gate  * we know later if they have been set.
9354*7c478bd9Sstevel@tonic-gate  */
9355*7c478bd9Sstevel@tonic-gate   gl->nline = 0;
9356*7c478bd9Sstevel@tonic-gate   gl->ncolumn = 0;
9357*7c478bd9Sstevel@tonic-gate /*
9358*7c478bd9Sstevel@tonic-gate  * Are we reading from a terminal?
9359*7c478bd9Sstevel@tonic-gate  */
9360*7c478bd9Sstevel@tonic-gate   if(gl->is_term) {
9361*7c478bd9Sstevel@tonic-gate /*
9362*7c478bd9Sstevel@tonic-gate  * Ask the terminal directly if possible.
9363*7c478bd9Sstevel@tonic-gate  */
9364*7c478bd9Sstevel@tonic-gate     (void) _gl_update_size(gl);
9365*7c478bd9Sstevel@tonic-gate /*
9366*7c478bd9Sstevel@tonic-gate  * If gl_update_size() couldn't ask the terminal, it will have
9367*7c478bd9Sstevel@tonic-gate  * left gl->nrow and gl->ncolumn unchanged. If these values haven't
9368*7c478bd9Sstevel@tonic-gate  * been changed from their initial values of zero, we need to find
9369*7c478bd9Sstevel@tonic-gate  * a different method to get the terminal size.
9370*7c478bd9Sstevel@tonic-gate  *
9371*7c478bd9Sstevel@tonic-gate  * If the number of lines isn't known yet, first see if the
9372*7c478bd9Sstevel@tonic-gate  * LINES environment ariable exists and specifies a believable number.
9373*7c478bd9Sstevel@tonic-gate  * If this doesn't work, look up the default size in the terminal
9374*7c478bd9Sstevel@tonic-gate  * information database.
9375*7c478bd9Sstevel@tonic-gate  */
9376*7c478bd9Sstevel@tonic-gate     if(gl->nline < 1) {
9377*7c478bd9Sstevel@tonic-gate       if((env = getenv("LINES")) && (n=atoi(env)) > 0)
9378*7c478bd9Sstevel@tonic-gate 	gl->nline = n;
9379*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
9380*7c478bd9Sstevel@tonic-gate       else
9381*7c478bd9Sstevel@tonic-gate 	gl->nline = tigetnum((char *)"lines");
9382*7c478bd9Sstevel@tonic-gate #elif defined(USE_TERMCAP)
9383*7c478bd9Sstevel@tonic-gate       else
9384*7c478bd9Sstevel@tonic-gate         gl->nline = tgetnum("li");
9385*7c478bd9Sstevel@tonic-gate #endif
9386*7c478bd9Sstevel@tonic-gate     };
9387*7c478bd9Sstevel@tonic-gate /*
9388*7c478bd9Sstevel@tonic-gate  * If the number of lines isn't known yet, first see if the COLUMNS
9389*7c478bd9Sstevel@tonic-gate  * environment ariable exists and specifies a believable number.  If
9390*7c478bd9Sstevel@tonic-gate  * this doesn't work, look up the default size in the terminal
9391*7c478bd9Sstevel@tonic-gate  * information database.
9392*7c478bd9Sstevel@tonic-gate  */
9393*7c478bd9Sstevel@tonic-gate     if(gl->ncolumn < 1) {
9394*7c478bd9Sstevel@tonic-gate       if((env = getenv("COLUMNS")) && (n=atoi(env)) > 0)
9395*7c478bd9Sstevel@tonic-gate 	gl->ncolumn = n;
9396*7c478bd9Sstevel@tonic-gate #ifdef USE_TERMINFO
9397*7c478bd9Sstevel@tonic-gate       else
9398*7c478bd9Sstevel@tonic-gate 	gl->ncolumn = tigetnum((char *)"cols");
9399*7c478bd9Sstevel@tonic-gate #elif defined(USE_TERMCAP)
9400*7c478bd9Sstevel@tonic-gate       else
9401*7c478bd9Sstevel@tonic-gate 	gl->ncolumn = tgetnum("co");
9402*7c478bd9Sstevel@tonic-gate #endif
9403*7c478bd9Sstevel@tonic-gate     };
9404*7c478bd9Sstevel@tonic-gate   };
9405*7c478bd9Sstevel@tonic-gate /*
9406*7c478bd9Sstevel@tonic-gate  * If we still haven't been able to acquire reasonable values, substitute
9407*7c478bd9Sstevel@tonic-gate  * the default values specified by the caller.
9408*7c478bd9Sstevel@tonic-gate  */
9409*7c478bd9Sstevel@tonic-gate   if(gl->nline <= 0)
9410*7c478bd9Sstevel@tonic-gate     gl->nline = def_nline;
9411*7c478bd9Sstevel@tonic-gate   if(gl->ncolumn <= 0)
9412*7c478bd9Sstevel@tonic-gate     gl->ncolumn = def_ncolumn;
9413*7c478bd9Sstevel@tonic-gate /*
9414*7c478bd9Sstevel@tonic-gate  * Copy the new size into the return value.
9415*7c478bd9Sstevel@tonic-gate  */
9416*7c478bd9Sstevel@tonic-gate   if(size) {
9417*7c478bd9Sstevel@tonic-gate     size->nline = gl->nline;
9418*7c478bd9Sstevel@tonic-gate     size->ncolumn = gl->ncolumn;
9419*7c478bd9Sstevel@tonic-gate   };
9420*7c478bd9Sstevel@tonic-gate   return;
9421*7c478bd9Sstevel@tonic-gate }
9422*7c478bd9Sstevel@tonic-gate 
9423*7c478bd9Sstevel@tonic-gate /*.......................................................................
9424*7c478bd9Sstevel@tonic-gate  * Resize or delete the history buffer.
9425*7c478bd9Sstevel@tonic-gate  *
9426*7c478bd9Sstevel@tonic-gate  * Input:
9427*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The resource object of gl_get_line().
9428*7c478bd9Sstevel@tonic-gate  *  bufsize  size_t    The number of bytes in the history buffer, or 0
9429*7c478bd9Sstevel@tonic-gate  *                     to delete the buffer completely.
9430*7c478bd9Sstevel@tonic-gate  * Output:
9431*7c478bd9Sstevel@tonic-gate  *  return      int    0 - OK.
9432*7c478bd9Sstevel@tonic-gate  *                     1 - Insufficient memory (the previous buffer
9433*7c478bd9Sstevel@tonic-gate  *                         will have been retained). No error message
9434*7c478bd9Sstevel@tonic-gate  *                         will be displayed.
9435*7c478bd9Sstevel@tonic-gate  */
9436*7c478bd9Sstevel@tonic-gate int gl_resize_history(GetLine *gl, size_t bufsize)
9437*7c478bd9Sstevel@tonic-gate {
9438*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
9439*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of this function */
9440*7c478bd9Sstevel@tonic-gate /*
9441*7c478bd9Sstevel@tonic-gate  * Check the arguments.
9442*7c478bd9Sstevel@tonic-gate  */
9443*7c478bd9Sstevel@tonic-gate   if(!gl)
9444*7c478bd9Sstevel@tonic-gate     return 1;
9445*7c478bd9Sstevel@tonic-gate /*
9446*7c478bd9Sstevel@tonic-gate  * Block all signals while modifying the contents of gl.
9447*7c478bd9Sstevel@tonic-gate  */
9448*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
9449*7c478bd9Sstevel@tonic-gate     return 1;
9450*7c478bd9Sstevel@tonic-gate /*
9451*7c478bd9Sstevel@tonic-gate  * Perform the resize while signals are blocked.
9452*7c478bd9Sstevel@tonic-gate  */
9453*7c478bd9Sstevel@tonic-gate   status = _glh_resize_history(gl->glh, bufsize);
9454*7c478bd9Sstevel@tonic-gate   if(status)
9455*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
9456*7c478bd9Sstevel@tonic-gate /*
9457*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9458*7c478bd9Sstevel@tonic-gate  */
9459*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
9460*7c478bd9Sstevel@tonic-gate   return status;
9461*7c478bd9Sstevel@tonic-gate }
9462*7c478bd9Sstevel@tonic-gate 
9463*7c478bd9Sstevel@tonic-gate /*.......................................................................
9464*7c478bd9Sstevel@tonic-gate  * Set an upper limit to the number of lines that can be recorded in the
9465*7c478bd9Sstevel@tonic-gate  * history list, or remove a previously specified limit.
9466*7c478bd9Sstevel@tonic-gate  *
9467*7c478bd9Sstevel@tonic-gate  * Input:
9468*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The resource object of gl_get_line().
9469*7c478bd9Sstevel@tonic-gate  *  max_lines   int    The maximum number of lines to allow, or -1 to
9470*7c478bd9Sstevel@tonic-gate  *                     cancel a previous limit and allow as many lines
9471*7c478bd9Sstevel@tonic-gate  *                     as will fit in the current history buffer size.
9472*7c478bd9Sstevel@tonic-gate  */
9473*7c478bd9Sstevel@tonic-gate void gl_limit_history(GetLine *gl, int max_lines)
9474*7c478bd9Sstevel@tonic-gate {
9475*7c478bd9Sstevel@tonic-gate   if(gl) {
9476*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
9477*7c478bd9Sstevel@tonic-gate /*
9478*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
9479*7c478bd9Sstevel@tonic-gate  */
9480*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
9481*7c478bd9Sstevel@tonic-gate /*
9482*7c478bd9Sstevel@tonic-gate  * Apply the limit while signals are blocked.
9483*7c478bd9Sstevel@tonic-gate  */
9484*7c478bd9Sstevel@tonic-gate     _glh_limit_history(gl->glh, max_lines);
9485*7c478bd9Sstevel@tonic-gate /*
9486*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9487*7c478bd9Sstevel@tonic-gate  */
9488*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
9489*7c478bd9Sstevel@tonic-gate   };
9490*7c478bd9Sstevel@tonic-gate }
9491*7c478bd9Sstevel@tonic-gate 
9492*7c478bd9Sstevel@tonic-gate /*.......................................................................
9493*7c478bd9Sstevel@tonic-gate  * Discard either all historical lines, or just those associated with the
9494*7c478bd9Sstevel@tonic-gate  * current history group.
9495*7c478bd9Sstevel@tonic-gate  *
9496*7c478bd9Sstevel@tonic-gate  * Input:
9497*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The resource object of gl_get_line().
9498*7c478bd9Sstevel@tonic-gate  *  all_groups  int    If true, clear all of the history. If false,
9499*7c478bd9Sstevel@tonic-gate  *                     clear only the stored lines associated with the
9500*7c478bd9Sstevel@tonic-gate  *                     currently selected history group.
9501*7c478bd9Sstevel@tonic-gate  */
9502*7c478bd9Sstevel@tonic-gate void gl_clear_history(GetLine *gl, int all_groups)
9503*7c478bd9Sstevel@tonic-gate {
9504*7c478bd9Sstevel@tonic-gate   if(gl) {
9505*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
9506*7c478bd9Sstevel@tonic-gate /*
9507*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
9508*7c478bd9Sstevel@tonic-gate  */
9509*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
9510*7c478bd9Sstevel@tonic-gate /*
9511*7c478bd9Sstevel@tonic-gate  * Clear the history buffer while signals are blocked.
9512*7c478bd9Sstevel@tonic-gate  */
9513*7c478bd9Sstevel@tonic-gate     _glh_clear_history(gl->glh, all_groups);
9514*7c478bd9Sstevel@tonic-gate /*
9515*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9516*7c478bd9Sstevel@tonic-gate  */
9517*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
9518*7c478bd9Sstevel@tonic-gate   };
9519*7c478bd9Sstevel@tonic-gate }
9520*7c478bd9Sstevel@tonic-gate 
9521*7c478bd9Sstevel@tonic-gate /*.......................................................................
9522*7c478bd9Sstevel@tonic-gate  * Temporarily enable or disable the gl_get_line() history mechanism.
9523*7c478bd9Sstevel@tonic-gate  *
9524*7c478bd9Sstevel@tonic-gate  * Input:
9525*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The resource object of gl_get_line().
9526*7c478bd9Sstevel@tonic-gate  *  enable      int    If true, turn on the history mechanism. If
9527*7c478bd9Sstevel@tonic-gate  *                     false, disable it.
9528*7c478bd9Sstevel@tonic-gate  */
9529*7c478bd9Sstevel@tonic-gate void gl_toggle_history(GetLine *gl, int enable)
9530*7c478bd9Sstevel@tonic-gate {
9531*7c478bd9Sstevel@tonic-gate   if(gl) {
9532*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
9533*7c478bd9Sstevel@tonic-gate /*
9534*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
9535*7c478bd9Sstevel@tonic-gate  */
9536*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
9537*7c478bd9Sstevel@tonic-gate /*
9538*7c478bd9Sstevel@tonic-gate  * Change the history recording mode while signals are blocked.
9539*7c478bd9Sstevel@tonic-gate  */
9540*7c478bd9Sstevel@tonic-gate     _glh_toggle_history(gl->glh, enable);
9541*7c478bd9Sstevel@tonic-gate /*
9542*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9543*7c478bd9Sstevel@tonic-gate  */
9544*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
9545*7c478bd9Sstevel@tonic-gate   };
9546*7c478bd9Sstevel@tonic-gate }
9547*7c478bd9Sstevel@tonic-gate 
9548*7c478bd9Sstevel@tonic-gate /*.......................................................................
9549*7c478bd9Sstevel@tonic-gate  * Lookup a history line by its sequential number of entry in the
9550*7c478bd9Sstevel@tonic-gate  * history buffer.
9551*7c478bd9Sstevel@tonic-gate  *
9552*7c478bd9Sstevel@tonic-gate  * Input:
9553*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of gl_get_line().
9554*7c478bd9Sstevel@tonic-gate  *  id      unsigned long    The identification number of the line to
9555*7c478bd9Sstevel@tonic-gate  *                           be returned, where 0 denotes the first line
9556*7c478bd9Sstevel@tonic-gate  *                           that was entered in the history list, and
9557*7c478bd9Sstevel@tonic-gate  *                           each subsequently added line has a number
9558*7c478bd9Sstevel@tonic-gate  *                           one greater than the previous one. For
9559*7c478bd9Sstevel@tonic-gate  *                           the range of lines currently in the list,
9560*7c478bd9Sstevel@tonic-gate  *                           see the gl_range_of_history() function.
9561*7c478bd9Sstevel@tonic-gate  * Input/Output:
9562*7c478bd9Sstevel@tonic-gate  *  line    GlHistoryLine *  A pointer to the variable in which to
9563*7c478bd9Sstevel@tonic-gate  *                           return the details of the line.
9564*7c478bd9Sstevel@tonic-gate  * Output:
9565*7c478bd9Sstevel@tonic-gate  *  return            int    0 - The line is no longer in the history
9566*7c478bd9Sstevel@tonic-gate  *                               list, and *line has not been changed.
9567*7c478bd9Sstevel@tonic-gate  *                           1 - The requested line can be found in
9568*7c478bd9Sstevel@tonic-gate  *                               *line. Note that line->line is part
9569*7c478bd9Sstevel@tonic-gate  *                               of the history buffer, so a
9570*7c478bd9Sstevel@tonic-gate  *                               private copy should be made if you
9571*7c478bd9Sstevel@tonic-gate  *                               wish to use it after subsequent calls
9572*7c478bd9Sstevel@tonic-gate  *                               to any functions that take *gl as an
9573*7c478bd9Sstevel@tonic-gate  *                               argument.
9574*7c478bd9Sstevel@tonic-gate  */
9575*7c478bd9Sstevel@tonic-gate int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line)
9576*7c478bd9Sstevel@tonic-gate {
9577*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
9578*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of this function */
9579*7c478bd9Sstevel@tonic-gate /*
9580*7c478bd9Sstevel@tonic-gate  * Check the arguments.
9581*7c478bd9Sstevel@tonic-gate  */
9582*7c478bd9Sstevel@tonic-gate   if(!gl)
9583*7c478bd9Sstevel@tonic-gate     return 0;
9584*7c478bd9Sstevel@tonic-gate /*
9585*7c478bd9Sstevel@tonic-gate  * Block all signals while modifying the contents of gl.
9586*7c478bd9Sstevel@tonic-gate  */
9587*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
9588*7c478bd9Sstevel@tonic-gate     return 1;
9589*7c478bd9Sstevel@tonic-gate /*
9590*7c478bd9Sstevel@tonic-gate  * Perform the lookup while signals are blocked.
9591*7c478bd9Sstevel@tonic-gate  */
9592*7c478bd9Sstevel@tonic-gate   status = _glh_lookup_history(gl->glh, (GlhLineID) id, &line->line,
9593*7c478bd9Sstevel@tonic-gate 			       &line->group, &line->timestamp);
9594*7c478bd9Sstevel@tonic-gate   if(status)
9595*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
9596*7c478bd9Sstevel@tonic-gate /*
9597*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9598*7c478bd9Sstevel@tonic-gate  */
9599*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
9600*7c478bd9Sstevel@tonic-gate   return status;
9601*7c478bd9Sstevel@tonic-gate }
9602*7c478bd9Sstevel@tonic-gate 
9603*7c478bd9Sstevel@tonic-gate /*.......................................................................
9604*7c478bd9Sstevel@tonic-gate  * Query the state of the history list. Note that any of the input/output
9605*7c478bd9Sstevel@tonic-gate  * pointers can be specified as NULL.
9606*7c478bd9Sstevel@tonic-gate  *
9607*7c478bd9Sstevel@tonic-gate  * Input:
9608*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of gl_get_line().
9609*7c478bd9Sstevel@tonic-gate  * Input/Output:
9610*7c478bd9Sstevel@tonic-gate  *  state  GlHistoryState *  A pointer to the variable in which to record
9611*7c478bd9Sstevel@tonic-gate  *                           the return values.
9612*7c478bd9Sstevel@tonic-gate  */
9613*7c478bd9Sstevel@tonic-gate void gl_state_of_history(GetLine *gl, GlHistoryState *state)
9614*7c478bd9Sstevel@tonic-gate {
9615*7c478bd9Sstevel@tonic-gate   if(gl && state) {
9616*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
9617*7c478bd9Sstevel@tonic-gate /*
9618*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
9619*7c478bd9Sstevel@tonic-gate  */
9620*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
9621*7c478bd9Sstevel@tonic-gate /*
9622*7c478bd9Sstevel@tonic-gate  * Lookup the status while signals are blocked.
9623*7c478bd9Sstevel@tonic-gate  */
9624*7c478bd9Sstevel@tonic-gate     _glh_state_of_history(gl->glh, &state->enabled, &state->group,
9625*7c478bd9Sstevel@tonic-gate 			  &state->max_lines);
9626*7c478bd9Sstevel@tonic-gate /*
9627*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9628*7c478bd9Sstevel@tonic-gate  */
9629*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
9630*7c478bd9Sstevel@tonic-gate   };
9631*7c478bd9Sstevel@tonic-gate }
9632*7c478bd9Sstevel@tonic-gate 
9633*7c478bd9Sstevel@tonic-gate /*.......................................................................
9634*7c478bd9Sstevel@tonic-gate  * Query the number and range of lines in the history buffer.
9635*7c478bd9Sstevel@tonic-gate  *
9636*7c478bd9Sstevel@tonic-gate  * Input:
9637*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of gl_get_line().
9638*7c478bd9Sstevel@tonic-gate  *  range  GlHistoryRange *  A pointer to the variable in which to record
9639*7c478bd9Sstevel@tonic-gate  *                           the return values. If range->nline=0, the
9640*7c478bd9Sstevel@tonic-gate  *                           range of lines will be given as 0-0.
9641*7c478bd9Sstevel@tonic-gate  */
9642*7c478bd9Sstevel@tonic-gate void gl_range_of_history(GetLine *gl, GlHistoryRange *range)
9643*7c478bd9Sstevel@tonic-gate {
9644*7c478bd9Sstevel@tonic-gate   if(gl && range) {
9645*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
9646*7c478bd9Sstevel@tonic-gate /*
9647*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
9648*7c478bd9Sstevel@tonic-gate  */
9649*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
9650*7c478bd9Sstevel@tonic-gate /*
9651*7c478bd9Sstevel@tonic-gate  * Lookup the information while signals are blocked.
9652*7c478bd9Sstevel@tonic-gate  */
9653*7c478bd9Sstevel@tonic-gate     _glh_range_of_history(gl->glh, &range->oldest, &range->newest,
9654*7c478bd9Sstevel@tonic-gate 			  &range->nlines);
9655*7c478bd9Sstevel@tonic-gate /*
9656*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9657*7c478bd9Sstevel@tonic-gate  */
9658*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
9659*7c478bd9Sstevel@tonic-gate   };
9660*7c478bd9Sstevel@tonic-gate }
9661*7c478bd9Sstevel@tonic-gate 
9662*7c478bd9Sstevel@tonic-gate /*.......................................................................
9663*7c478bd9Sstevel@tonic-gate  * Return the size of the history buffer and the amount of the
9664*7c478bd9Sstevel@tonic-gate  * buffer that is currently in use.
9665*7c478bd9Sstevel@tonic-gate  *
9666*7c478bd9Sstevel@tonic-gate  * Input:
9667*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The gl_get_line() resource object.
9668*7c478bd9Sstevel@tonic-gate  * Input/Output:
9669*7c478bd9Sstevel@tonic-gate  *  GlHistorySize size *  A pointer to the variable in which to return
9670*7c478bd9Sstevel@tonic-gate  *                        the results.
9671*7c478bd9Sstevel@tonic-gate  */
9672*7c478bd9Sstevel@tonic-gate void gl_size_of_history(GetLine *gl, GlHistorySize *size)
9673*7c478bd9Sstevel@tonic-gate {
9674*7c478bd9Sstevel@tonic-gate   if(gl && size) {
9675*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
9676*7c478bd9Sstevel@tonic-gate /*
9677*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
9678*7c478bd9Sstevel@tonic-gate  */
9679*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
9680*7c478bd9Sstevel@tonic-gate /*
9681*7c478bd9Sstevel@tonic-gate  * Lookup the information while signals are blocked.
9682*7c478bd9Sstevel@tonic-gate  */
9683*7c478bd9Sstevel@tonic-gate     _glh_size_of_history(gl->glh, &size->size, &size->used);
9684*7c478bd9Sstevel@tonic-gate /*
9685*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9686*7c478bd9Sstevel@tonic-gate  */
9687*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
9688*7c478bd9Sstevel@tonic-gate   };
9689*7c478bd9Sstevel@tonic-gate }
9690*7c478bd9Sstevel@tonic-gate 
9691*7c478bd9Sstevel@tonic-gate /*.......................................................................
9692*7c478bd9Sstevel@tonic-gate  * This is the action function that lists the contents of the history
9693*7c478bd9Sstevel@tonic-gate  * list.
9694*7c478bd9Sstevel@tonic-gate  */
9695*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_list_history)
9696*7c478bd9Sstevel@tonic-gate {
9697*7c478bd9Sstevel@tonic-gate /*
9698*7c478bd9Sstevel@tonic-gate  * Start a new line.
9699*7c478bd9Sstevel@tonic-gate  */
9700*7c478bd9Sstevel@tonic-gate   if(gl_start_newline(gl, 1))
9701*7c478bd9Sstevel@tonic-gate     return 1;
9702*7c478bd9Sstevel@tonic-gate /*
9703*7c478bd9Sstevel@tonic-gate  * List history lines that belong to the current group.
9704*7c478bd9Sstevel@tonic-gate  */
9705*7c478bd9Sstevel@tonic-gate   _glh_show_history(gl->glh, gl_write_fn, gl, "%N  %T   %H\r\n", 0,
9706*7c478bd9Sstevel@tonic-gate 		    count<=1 ? -1 : count);
9707*7c478bd9Sstevel@tonic-gate /*
9708*7c478bd9Sstevel@tonic-gate  * Arrange for the input line to be redisplayed.
9709*7c478bd9Sstevel@tonic-gate  */
9710*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
9711*7c478bd9Sstevel@tonic-gate   return 0;
9712*7c478bd9Sstevel@tonic-gate }
9713*7c478bd9Sstevel@tonic-gate 
9714*7c478bd9Sstevel@tonic-gate /*.......................................................................
9715*7c478bd9Sstevel@tonic-gate  * Specify whether text that users type should be displayed or hidden.
9716*7c478bd9Sstevel@tonic-gate  * In the latter case, only the prompt is displayed, and the final
9717*7c478bd9Sstevel@tonic-gate  * input line is not archived in the history list.
9718*7c478bd9Sstevel@tonic-gate  *
9719*7c478bd9Sstevel@tonic-gate  * Input:
9720*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The gl_get_line() resource object.
9721*7c478bd9Sstevel@tonic-gate  *  enable         int     0 - Disable echoing.
9722*7c478bd9Sstevel@tonic-gate  *                         1 - Enable echoing.
9723*7c478bd9Sstevel@tonic-gate  *                        -1 - Just query the mode without changing it.
9724*7c478bd9Sstevel@tonic-gate  * Output:
9725*7c478bd9Sstevel@tonic-gate  *  return         int    The echoing disposition that was in effect
9726*7c478bd9Sstevel@tonic-gate  *                        before this function was called:
9727*7c478bd9Sstevel@tonic-gate  *                         0 - Echoing was disabled.
9728*7c478bd9Sstevel@tonic-gate  *                         1 - Echoing was enabled.
9729*7c478bd9Sstevel@tonic-gate  */
9730*7c478bd9Sstevel@tonic-gate int gl_echo_mode(GetLine *gl, int enable)
9731*7c478bd9Sstevel@tonic-gate {
9732*7c478bd9Sstevel@tonic-gate   if(gl) {
9733*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
9734*7c478bd9Sstevel@tonic-gate     int was_echoing; /* The echoing disposition on entry to this function */
9735*7c478bd9Sstevel@tonic-gate /*
9736*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
9737*7c478bd9Sstevel@tonic-gate  */
9738*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
9739*7c478bd9Sstevel@tonic-gate /*
9740*7c478bd9Sstevel@tonic-gate  * Install the new disposition while signals are blocked.
9741*7c478bd9Sstevel@tonic-gate  */
9742*7c478bd9Sstevel@tonic-gate     was_echoing = gl->echo;
9743*7c478bd9Sstevel@tonic-gate     if(enable >= 0)
9744*7c478bd9Sstevel@tonic-gate       gl->echo = enable;
9745*7c478bd9Sstevel@tonic-gate /*
9746*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9747*7c478bd9Sstevel@tonic-gate  */
9748*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
9749*7c478bd9Sstevel@tonic-gate /*
9750*7c478bd9Sstevel@tonic-gate  * Return the original echoing disposition.
9751*7c478bd9Sstevel@tonic-gate  */
9752*7c478bd9Sstevel@tonic-gate     return was_echoing;
9753*7c478bd9Sstevel@tonic-gate   };
9754*7c478bd9Sstevel@tonic-gate   return 1;
9755*7c478bd9Sstevel@tonic-gate }
9756*7c478bd9Sstevel@tonic-gate 
9757*7c478bd9Sstevel@tonic-gate /*.......................................................................
9758*7c478bd9Sstevel@tonic-gate  * Display the prompt.
9759*7c478bd9Sstevel@tonic-gate  *
9760*7c478bd9Sstevel@tonic-gate  * Input:
9761*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
9762*7c478bd9Sstevel@tonic-gate  * Output:
9763*7c478bd9Sstevel@tonic-gate  *  return         int    0 - OK.
9764*7c478bd9Sstevel@tonic-gate  *                        1 - Error.
9765*7c478bd9Sstevel@tonic-gate  */
9766*7c478bd9Sstevel@tonic-gate static int gl_display_prompt(GetLine *gl)
9767*7c478bd9Sstevel@tonic-gate {
9768*7c478bd9Sstevel@tonic-gate   const char *pptr;       /* A pointer into gl->prompt[] */
9769*7c478bd9Sstevel@tonic-gate   unsigned old_attr=0;    /* The current text display attributes */
9770*7c478bd9Sstevel@tonic-gate   unsigned new_attr=0;    /* The requested text display attributes */
9771*7c478bd9Sstevel@tonic-gate /*
9772*7c478bd9Sstevel@tonic-gate  * Temporarily switch to echoing output characters.
9773*7c478bd9Sstevel@tonic-gate  */
9774*7c478bd9Sstevel@tonic-gate   int kept_echo = gl->echo;
9775*7c478bd9Sstevel@tonic-gate   gl->echo = 1;
9776*7c478bd9Sstevel@tonic-gate /*
9777*7c478bd9Sstevel@tonic-gate  * In case the screen got messed up, send a carriage return to
9778*7c478bd9Sstevel@tonic-gate  * put the cursor at the beginning of the current terminal line.
9779*7c478bd9Sstevel@tonic-gate  */
9780*7c478bd9Sstevel@tonic-gate   if(gl_print_control_sequence(gl, 1, gl->bol))
9781*7c478bd9Sstevel@tonic-gate     return 1;
9782*7c478bd9Sstevel@tonic-gate /*
9783*7c478bd9Sstevel@tonic-gate  * Mark the line as partially displayed.
9784*7c478bd9Sstevel@tonic-gate  */
9785*7c478bd9Sstevel@tonic-gate   gl->displayed = 1;
9786*7c478bd9Sstevel@tonic-gate /*
9787*7c478bd9Sstevel@tonic-gate  * Write the prompt, using the currently selected prompt style.
9788*7c478bd9Sstevel@tonic-gate  */
9789*7c478bd9Sstevel@tonic-gate   switch(gl->prompt_style) {
9790*7c478bd9Sstevel@tonic-gate   case GL_LITERAL_PROMPT:
9791*7c478bd9Sstevel@tonic-gate     if(gl_print_string(gl, gl->prompt, '\0'))
9792*7c478bd9Sstevel@tonic-gate       return 1;
9793*7c478bd9Sstevel@tonic-gate     break;
9794*7c478bd9Sstevel@tonic-gate   case GL_FORMAT_PROMPT:
9795*7c478bd9Sstevel@tonic-gate     for(pptr=gl->prompt; *pptr; pptr++) {
9796*7c478bd9Sstevel@tonic-gate /*
9797*7c478bd9Sstevel@tonic-gate  * Does the latest character appear to be the start of a directive?
9798*7c478bd9Sstevel@tonic-gate  */
9799*7c478bd9Sstevel@tonic-gate       if(*pptr == '%') {
9800*7c478bd9Sstevel@tonic-gate /*
9801*7c478bd9Sstevel@tonic-gate  * Check for and act on attribute changing directives.
9802*7c478bd9Sstevel@tonic-gate  */
9803*7c478bd9Sstevel@tonic-gate 	switch(pptr[1]) {
9804*7c478bd9Sstevel@tonic-gate /*
9805*7c478bd9Sstevel@tonic-gate  * Add or remove a text attribute from the new set of attributes.
9806*7c478bd9Sstevel@tonic-gate  */
9807*7c478bd9Sstevel@tonic-gate 	case 'B': case 'U': case 'S': case 'P': case 'F': case 'V':
9808*7c478bd9Sstevel@tonic-gate 	case 'b': case 'u': case 's': case 'p': case 'f': case 'v':
9809*7c478bd9Sstevel@tonic-gate 	  switch(*++pptr) {
9810*7c478bd9Sstevel@tonic-gate 	  case 'B':           /* Switch to a bold font */
9811*7c478bd9Sstevel@tonic-gate 	    new_attr |= GL_TXT_BOLD;
9812*7c478bd9Sstevel@tonic-gate 	    break;
9813*7c478bd9Sstevel@tonic-gate 	  case 'b':           /* Switch to a non-bold font */
9814*7c478bd9Sstevel@tonic-gate 	    new_attr &= ~GL_TXT_BOLD;
9815*7c478bd9Sstevel@tonic-gate 	    break;
9816*7c478bd9Sstevel@tonic-gate 	  case 'U':           /* Start underlining */
9817*7c478bd9Sstevel@tonic-gate 	    new_attr |= GL_TXT_UNDERLINE;
9818*7c478bd9Sstevel@tonic-gate 	    break;
9819*7c478bd9Sstevel@tonic-gate 	  case 'u':           /* Stop underlining */
9820*7c478bd9Sstevel@tonic-gate 	    new_attr &= ~GL_TXT_UNDERLINE;
9821*7c478bd9Sstevel@tonic-gate 	    break;
9822*7c478bd9Sstevel@tonic-gate 	  case 'S':           /* Start highlighting */
9823*7c478bd9Sstevel@tonic-gate 	    new_attr |= GL_TXT_STANDOUT;
9824*7c478bd9Sstevel@tonic-gate 	    break;
9825*7c478bd9Sstevel@tonic-gate 	  case 's':           /* Stop highlighting */
9826*7c478bd9Sstevel@tonic-gate 	    new_attr &= ~GL_TXT_STANDOUT;
9827*7c478bd9Sstevel@tonic-gate 	    break;
9828*7c478bd9Sstevel@tonic-gate 	  case 'P':           /* Switch to a pale font */
9829*7c478bd9Sstevel@tonic-gate 	    new_attr |= GL_TXT_DIM;
9830*7c478bd9Sstevel@tonic-gate 	    break;
9831*7c478bd9Sstevel@tonic-gate 	  case 'p':           /* Switch to a non-pale font */
9832*7c478bd9Sstevel@tonic-gate 	    new_attr &= ~GL_TXT_DIM;
9833*7c478bd9Sstevel@tonic-gate 	    break;
9834*7c478bd9Sstevel@tonic-gate 	  case 'F':           /* Switch to a flashing font */
9835*7c478bd9Sstevel@tonic-gate 	    new_attr |= GL_TXT_BLINK;
9836*7c478bd9Sstevel@tonic-gate 	    break;
9837*7c478bd9Sstevel@tonic-gate 	  case 'f':           /* Switch to a steady font */
9838*7c478bd9Sstevel@tonic-gate 	    new_attr &= ~GL_TXT_BLINK;
9839*7c478bd9Sstevel@tonic-gate 	    break;
9840*7c478bd9Sstevel@tonic-gate 	  case 'V':           /* Switch to reverse video */
9841*7c478bd9Sstevel@tonic-gate 	    new_attr |= GL_TXT_REVERSE;
9842*7c478bd9Sstevel@tonic-gate 	    break;
9843*7c478bd9Sstevel@tonic-gate 	  case 'v':           /* Switch out of reverse video */
9844*7c478bd9Sstevel@tonic-gate 	    new_attr &= ~GL_TXT_REVERSE;
9845*7c478bd9Sstevel@tonic-gate 	    break;
9846*7c478bd9Sstevel@tonic-gate 	  };
9847*7c478bd9Sstevel@tonic-gate 	  continue;
9848*7c478bd9Sstevel@tonic-gate /*
9849*7c478bd9Sstevel@tonic-gate  * A literal % is represented by %%. Skip the leading %.
9850*7c478bd9Sstevel@tonic-gate  */
9851*7c478bd9Sstevel@tonic-gate 	case '%':
9852*7c478bd9Sstevel@tonic-gate 	  pptr++;
9853*7c478bd9Sstevel@tonic-gate 	  break;
9854*7c478bd9Sstevel@tonic-gate 	};
9855*7c478bd9Sstevel@tonic-gate       };
9856*7c478bd9Sstevel@tonic-gate /*
9857*7c478bd9Sstevel@tonic-gate  * Many terminals, when asked to turn off a single text attribute, turn
9858*7c478bd9Sstevel@tonic-gate  * them all off, so the portable way to turn one off individually is to
9859*7c478bd9Sstevel@tonic-gate  * explicitly turn them all off, then specify those that we want from
9860*7c478bd9Sstevel@tonic-gate  * scratch.
9861*7c478bd9Sstevel@tonic-gate  */
9862*7c478bd9Sstevel@tonic-gate       if(old_attr & ~new_attr) {
9863*7c478bd9Sstevel@tonic-gate 	if(gl_print_control_sequence(gl, 1, gl->text_attr_off))
9864*7c478bd9Sstevel@tonic-gate 	  return 1;
9865*7c478bd9Sstevel@tonic-gate 	old_attr = 0;
9866*7c478bd9Sstevel@tonic-gate       };
9867*7c478bd9Sstevel@tonic-gate /*
9868*7c478bd9Sstevel@tonic-gate  * Install new text attributes?
9869*7c478bd9Sstevel@tonic-gate  */
9870*7c478bd9Sstevel@tonic-gate       if(new_attr != old_attr) {
9871*7c478bd9Sstevel@tonic-gate 	if(new_attr & GL_TXT_BOLD && !(old_attr & GL_TXT_BOLD) &&
9872*7c478bd9Sstevel@tonic-gate 	   gl_print_control_sequence(gl, 1, gl->bold))
9873*7c478bd9Sstevel@tonic-gate 	  return 1;
9874*7c478bd9Sstevel@tonic-gate 	if(new_attr & GL_TXT_UNDERLINE && !(old_attr & GL_TXT_UNDERLINE) &&
9875*7c478bd9Sstevel@tonic-gate 	   gl_print_control_sequence(gl, 1, gl->underline))
9876*7c478bd9Sstevel@tonic-gate 	  return 1;
9877*7c478bd9Sstevel@tonic-gate 	if(new_attr & GL_TXT_STANDOUT && !(old_attr & GL_TXT_STANDOUT) &&
9878*7c478bd9Sstevel@tonic-gate 	   gl_print_control_sequence(gl, 1, gl->standout))
9879*7c478bd9Sstevel@tonic-gate 	  return 1;
9880*7c478bd9Sstevel@tonic-gate 	if(new_attr & GL_TXT_DIM && !(old_attr & GL_TXT_DIM) &&
9881*7c478bd9Sstevel@tonic-gate 	   gl_print_control_sequence(gl, 1, gl->dim))
9882*7c478bd9Sstevel@tonic-gate 	  return 1;
9883*7c478bd9Sstevel@tonic-gate 	if(new_attr & GL_TXT_REVERSE && !(old_attr & GL_TXT_REVERSE) &&
9884*7c478bd9Sstevel@tonic-gate 	   gl_print_control_sequence(gl, 1, gl->reverse))
9885*7c478bd9Sstevel@tonic-gate 	  return 1;
9886*7c478bd9Sstevel@tonic-gate 	if(new_attr & GL_TXT_BLINK && !(old_attr & GL_TXT_BLINK) &&
9887*7c478bd9Sstevel@tonic-gate 	   gl_print_control_sequence(gl, 1, gl->blink))
9888*7c478bd9Sstevel@tonic-gate 	  return 1;
9889*7c478bd9Sstevel@tonic-gate 	old_attr = new_attr;
9890*7c478bd9Sstevel@tonic-gate       };
9891*7c478bd9Sstevel@tonic-gate /*
9892*7c478bd9Sstevel@tonic-gate  * Display the latest character.
9893*7c478bd9Sstevel@tonic-gate  */
9894*7c478bd9Sstevel@tonic-gate       if(gl_print_char(gl, *pptr, pptr[1]))
9895*7c478bd9Sstevel@tonic-gate 	return 1;
9896*7c478bd9Sstevel@tonic-gate     };
9897*7c478bd9Sstevel@tonic-gate /*
9898*7c478bd9Sstevel@tonic-gate  * Turn off all text attributes now that we have finished drawing
9899*7c478bd9Sstevel@tonic-gate  * the prompt.
9900*7c478bd9Sstevel@tonic-gate  */
9901*7c478bd9Sstevel@tonic-gate     if(gl_print_control_sequence(gl, 1, gl->text_attr_off))
9902*7c478bd9Sstevel@tonic-gate       return 1;
9903*7c478bd9Sstevel@tonic-gate     break;
9904*7c478bd9Sstevel@tonic-gate   };
9905*7c478bd9Sstevel@tonic-gate /*
9906*7c478bd9Sstevel@tonic-gate  * Restore the original echo mode.
9907*7c478bd9Sstevel@tonic-gate  */
9908*7c478bd9Sstevel@tonic-gate   gl->echo = kept_echo;
9909*7c478bd9Sstevel@tonic-gate /*
9910*7c478bd9Sstevel@tonic-gate  * The prompt has now been displayed at least once.
9911*7c478bd9Sstevel@tonic-gate  */
9912*7c478bd9Sstevel@tonic-gate   gl->prompt_changed = 0;
9913*7c478bd9Sstevel@tonic-gate   return 0;
9914*7c478bd9Sstevel@tonic-gate }
9915*7c478bd9Sstevel@tonic-gate 
9916*7c478bd9Sstevel@tonic-gate /*.......................................................................
9917*7c478bd9Sstevel@tonic-gate  * This function can be called from gl_get_line() callbacks to have
9918*7c478bd9Sstevel@tonic-gate  * the prompt changed when they return. It has no effect if gl_get_line()
9919*7c478bd9Sstevel@tonic-gate  * is not currently being invoked.
9920*7c478bd9Sstevel@tonic-gate  *
9921*7c478bd9Sstevel@tonic-gate  * Input:
9922*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
9923*7c478bd9Sstevel@tonic-gate  *  prompt  const char *  The new prompt.
9924*7c478bd9Sstevel@tonic-gate  */
9925*7c478bd9Sstevel@tonic-gate void gl_replace_prompt(GetLine *gl, const char *prompt)
9926*7c478bd9Sstevel@tonic-gate {
9927*7c478bd9Sstevel@tonic-gate   if(gl) {
9928*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
9929*7c478bd9Sstevel@tonic-gate /*
9930*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
9931*7c478bd9Sstevel@tonic-gate  */
9932*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
9933*7c478bd9Sstevel@tonic-gate /*
9934*7c478bd9Sstevel@tonic-gate  * Replace the prompt.
9935*7c478bd9Sstevel@tonic-gate  */
9936*7c478bd9Sstevel@tonic-gate     _gl_replace_prompt(gl, prompt);
9937*7c478bd9Sstevel@tonic-gate /*
9938*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
9939*7c478bd9Sstevel@tonic-gate  */
9940*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
9941*7c478bd9Sstevel@tonic-gate   };
9942*7c478bd9Sstevel@tonic-gate }
9943*7c478bd9Sstevel@tonic-gate 
9944*7c478bd9Sstevel@tonic-gate /*.......................................................................
9945*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_replace_prompt() function. It
9946*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
9947*7c478bd9Sstevel@tonic-gate  * delivery of signals.
9948*7c478bd9Sstevel@tonic-gate  */
9949*7c478bd9Sstevel@tonic-gate static void _gl_replace_prompt(GetLine *gl, const char *prompt)
9950*7c478bd9Sstevel@tonic-gate {
9951*7c478bd9Sstevel@tonic-gate   size_t size;
9952*7c478bd9Sstevel@tonic-gate 
9953*7c478bd9Sstevel@tonic-gate /*
9954*7c478bd9Sstevel@tonic-gate  * Substitute an empty prompt?
9955*7c478bd9Sstevel@tonic-gate  */
9956*7c478bd9Sstevel@tonic-gate   if(!prompt)
9957*7c478bd9Sstevel@tonic-gate     prompt = "";
9958*7c478bd9Sstevel@tonic-gate /*
9959*7c478bd9Sstevel@tonic-gate  * Gaurd against aliasing between prompt and gl->prompt.
9960*7c478bd9Sstevel@tonic-gate  */
9961*7c478bd9Sstevel@tonic-gate   if(gl->prompt != prompt) {
9962*7c478bd9Sstevel@tonic-gate /*
9963*7c478bd9Sstevel@tonic-gate  * Get the length of the new prompt string.
9964*7c478bd9Sstevel@tonic-gate  */
9965*7c478bd9Sstevel@tonic-gate     size_t slen = strlen(prompt);
9966*7c478bd9Sstevel@tonic-gate /*
9967*7c478bd9Sstevel@tonic-gate  * If needed, allocate a new buffer for the prompt string.
9968*7c478bd9Sstevel@tonic-gate  */
9969*7c478bd9Sstevel@tonic-gate     size = sizeof(char) * (slen + 1);
9970*7c478bd9Sstevel@tonic-gate     if(!gl->prompt || slen > strlen(gl->prompt)) {
9971*7c478bd9Sstevel@tonic-gate       char *new_prompt = gl->prompt ? realloc(gl->prompt, size) : malloc(size);
9972*7c478bd9Sstevel@tonic-gate       if(!new_prompt)
9973*7c478bd9Sstevel@tonic-gate 	return;
9974*7c478bd9Sstevel@tonic-gate       gl->prompt = new_prompt;
9975*7c478bd9Sstevel@tonic-gate     };
9976*7c478bd9Sstevel@tonic-gate /*
9977*7c478bd9Sstevel@tonic-gate  * Make a copy of the new prompt.
9978*7c478bd9Sstevel@tonic-gate  */
9979*7c478bd9Sstevel@tonic-gate     strlcpy(gl->prompt, prompt, size);
9980*7c478bd9Sstevel@tonic-gate   };
9981*7c478bd9Sstevel@tonic-gate /*
9982*7c478bd9Sstevel@tonic-gate  * Record the statistics of the new prompt.
9983*7c478bd9Sstevel@tonic-gate  */
9984*7c478bd9Sstevel@tonic-gate   gl->prompt_len = gl_displayed_prompt_width(gl);
9985*7c478bd9Sstevel@tonic-gate   gl->prompt_changed = 1;
9986*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
9987*7c478bd9Sstevel@tonic-gate   return;
9988*7c478bd9Sstevel@tonic-gate }
9989*7c478bd9Sstevel@tonic-gate 
9990*7c478bd9Sstevel@tonic-gate /*.......................................................................
9991*7c478bd9Sstevel@tonic-gate  * Work out the length of the current prompt on the terminal, according
9992*7c478bd9Sstevel@tonic-gate  * to the current prompt formatting style.
9993*7c478bd9Sstevel@tonic-gate  *
9994*7c478bd9Sstevel@tonic-gate  * Input:
9995*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The resource object of this library.
9996*7c478bd9Sstevel@tonic-gate  * Output:
9997*7c478bd9Sstevel@tonic-gate  *  return       int    The number of displayed characters.
9998*7c478bd9Sstevel@tonic-gate  */
9999*7c478bd9Sstevel@tonic-gate static int gl_displayed_prompt_width(GetLine *gl)
10000*7c478bd9Sstevel@tonic-gate {
10001*7c478bd9Sstevel@tonic-gate   int slen=0;         /* The displayed number of characters */
10002*7c478bd9Sstevel@tonic-gate   const char *pptr;   /* A pointer into prompt[] */
10003*7c478bd9Sstevel@tonic-gate /*
10004*7c478bd9Sstevel@tonic-gate  * The length differs according to the prompt display style.
10005*7c478bd9Sstevel@tonic-gate  */
10006*7c478bd9Sstevel@tonic-gate   switch(gl->prompt_style) {
10007*7c478bd9Sstevel@tonic-gate   case GL_LITERAL_PROMPT:
10008*7c478bd9Sstevel@tonic-gate     return gl_displayed_string_width(gl, gl->prompt, -1, 0);
10009*7c478bd9Sstevel@tonic-gate     break;
10010*7c478bd9Sstevel@tonic-gate   case GL_FORMAT_PROMPT:
10011*7c478bd9Sstevel@tonic-gate /*
10012*7c478bd9Sstevel@tonic-gate  * Add up the length of the displayed string, while filtering out
10013*7c478bd9Sstevel@tonic-gate  * attribute directives.
10014*7c478bd9Sstevel@tonic-gate  */
10015*7c478bd9Sstevel@tonic-gate     for(pptr=gl->prompt; *pptr; pptr++) {
10016*7c478bd9Sstevel@tonic-gate /*
10017*7c478bd9Sstevel@tonic-gate  * Does the latest character appear to be the start of a directive?
10018*7c478bd9Sstevel@tonic-gate  */
10019*7c478bd9Sstevel@tonic-gate       if(*pptr == '%') {
10020*7c478bd9Sstevel@tonic-gate /*
10021*7c478bd9Sstevel@tonic-gate  * Check for and skip attribute changing directives.
10022*7c478bd9Sstevel@tonic-gate  */
10023*7c478bd9Sstevel@tonic-gate 	switch(pptr[1]) {
10024*7c478bd9Sstevel@tonic-gate 	case 'B': case 'b': case 'U': case 'u': case 'S': case 's':
10025*7c478bd9Sstevel@tonic-gate 	  pptr++;
10026*7c478bd9Sstevel@tonic-gate 	  continue;
10027*7c478bd9Sstevel@tonic-gate /*
10028*7c478bd9Sstevel@tonic-gate  * A literal % is represented by %%. Skip the leading %.
10029*7c478bd9Sstevel@tonic-gate  */
10030*7c478bd9Sstevel@tonic-gate 	case '%':
10031*7c478bd9Sstevel@tonic-gate 	  pptr++;
10032*7c478bd9Sstevel@tonic-gate 	  break;
10033*7c478bd9Sstevel@tonic-gate 	};
10034*7c478bd9Sstevel@tonic-gate       };
10035*7c478bd9Sstevel@tonic-gate       slen += gl_displayed_char_width(gl, *pptr, slen);
10036*7c478bd9Sstevel@tonic-gate     };
10037*7c478bd9Sstevel@tonic-gate     break;
10038*7c478bd9Sstevel@tonic-gate   };
10039*7c478bd9Sstevel@tonic-gate   return slen;
10040*7c478bd9Sstevel@tonic-gate }
10041*7c478bd9Sstevel@tonic-gate 
10042*7c478bd9Sstevel@tonic-gate /*.......................................................................
10043*7c478bd9Sstevel@tonic-gate  * Specify whether to heed text attribute directives within prompt
10044*7c478bd9Sstevel@tonic-gate  * strings.
10045*7c478bd9Sstevel@tonic-gate  *
10046*7c478bd9Sstevel@tonic-gate  * Input:
10047*7c478bd9Sstevel@tonic-gate  *  gl           GetLine *  The resource object of gl_get_line().
10048*7c478bd9Sstevel@tonic-gate  *  style  GlPromptStyle    The style of prompt (see the definition of
10049*7c478bd9Sstevel@tonic-gate  *                          GlPromptStyle in libtecla.h for details).
10050*7c478bd9Sstevel@tonic-gate  */
10051*7c478bd9Sstevel@tonic-gate void gl_prompt_style(GetLine *gl, GlPromptStyle style)
10052*7c478bd9Sstevel@tonic-gate {
10053*7c478bd9Sstevel@tonic-gate   if(gl) {
10054*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
10055*7c478bd9Sstevel@tonic-gate /*
10056*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
10057*7c478bd9Sstevel@tonic-gate  */
10058*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
10059*7c478bd9Sstevel@tonic-gate /*
10060*7c478bd9Sstevel@tonic-gate  * Install the new style in gl while signals are blocked.
10061*7c478bd9Sstevel@tonic-gate  */
10062*7c478bd9Sstevel@tonic-gate     if(style != gl->prompt_style) {
10063*7c478bd9Sstevel@tonic-gate       gl->prompt_style = style;
10064*7c478bd9Sstevel@tonic-gate       gl->prompt_len = gl_displayed_prompt_width(gl);
10065*7c478bd9Sstevel@tonic-gate       gl->prompt_changed = 1;
10066*7c478bd9Sstevel@tonic-gate       gl_queue_redisplay(gl);
10067*7c478bd9Sstevel@tonic-gate     };
10068*7c478bd9Sstevel@tonic-gate /*
10069*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
10070*7c478bd9Sstevel@tonic-gate  */
10071*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
10072*7c478bd9Sstevel@tonic-gate   };
10073*7c478bd9Sstevel@tonic-gate }
10074*7c478bd9Sstevel@tonic-gate 
10075*7c478bd9Sstevel@tonic-gate /*.......................................................................
10076*7c478bd9Sstevel@tonic-gate  * Tell gl_get_line() how to respond to a given signal. This can be used
10077*7c478bd9Sstevel@tonic-gate  * both to override the default responses to signals that gl_get_line()
10078*7c478bd9Sstevel@tonic-gate  * normally catches and to add new signals to the list that are to be
10079*7c478bd9Sstevel@tonic-gate  * caught.
10080*7c478bd9Sstevel@tonic-gate  *
10081*7c478bd9Sstevel@tonic-gate  * Input:
10082*7c478bd9Sstevel@tonic-gate  *  gl           GetLine *  The resource object of gl_get_line().
10083*7c478bd9Sstevel@tonic-gate  *  signo            int    The number of the signal to be caught.
10084*7c478bd9Sstevel@tonic-gate  *  flags       unsigned    A bitwise union of GlSignalFlags enumerators.
10085*7c478bd9Sstevel@tonic-gate  *  after  GlAfterSignal    What to do after the application's signal
10086*7c478bd9Sstevel@tonic-gate  *                          handler has been called.
10087*7c478bd9Sstevel@tonic-gate  *  errno_value      int    The value to set errno to.
10088*7c478bd9Sstevel@tonic-gate  * Output:
10089*7c478bd9Sstevel@tonic-gate  *  return           int    0 - OK.
10090*7c478bd9Sstevel@tonic-gate  *                          1 - Error.
10091*7c478bd9Sstevel@tonic-gate  */
10092*7c478bd9Sstevel@tonic-gate int gl_trap_signal(GetLine *gl, int signo, unsigned flags,
10093*7c478bd9Sstevel@tonic-gate 		   GlAfterSignal after, int errno_value)
10094*7c478bd9Sstevel@tonic-gate {
10095*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
10096*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of this function */
10097*7c478bd9Sstevel@tonic-gate /*
10098*7c478bd9Sstevel@tonic-gate  * Check the arguments.
10099*7c478bd9Sstevel@tonic-gate  */
10100*7c478bd9Sstevel@tonic-gate   if(!gl) {
10101*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
10102*7c478bd9Sstevel@tonic-gate     return 1;
10103*7c478bd9Sstevel@tonic-gate   };
10104*7c478bd9Sstevel@tonic-gate /*
10105*7c478bd9Sstevel@tonic-gate  * Block all signals while modifying the contents of gl.
10106*7c478bd9Sstevel@tonic-gate  */
10107*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
10108*7c478bd9Sstevel@tonic-gate     return 1;
10109*7c478bd9Sstevel@tonic-gate /*
10110*7c478bd9Sstevel@tonic-gate  * Perform the modification while signals are blocked.
10111*7c478bd9Sstevel@tonic-gate  */
10112*7c478bd9Sstevel@tonic-gate   status = _gl_trap_signal(gl, signo, flags, after, errno_value);
10113*7c478bd9Sstevel@tonic-gate /*
10114*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
10115*7c478bd9Sstevel@tonic-gate  */
10116*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
10117*7c478bd9Sstevel@tonic-gate   return status;
10118*7c478bd9Sstevel@tonic-gate }
10119*7c478bd9Sstevel@tonic-gate 
10120*7c478bd9Sstevel@tonic-gate /*.......................................................................
10121*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_trap_signal() function. It
10122*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
10123*7c478bd9Sstevel@tonic-gate  * delivery of signals.
10124*7c478bd9Sstevel@tonic-gate  */
10125*7c478bd9Sstevel@tonic-gate static int _gl_trap_signal(GetLine *gl, int signo, unsigned flags,
10126*7c478bd9Sstevel@tonic-gate 			   GlAfterSignal after, int errno_value)
10127*7c478bd9Sstevel@tonic-gate {
10128*7c478bd9Sstevel@tonic-gate   GlSignalNode *sig;
10129*7c478bd9Sstevel@tonic-gate /*
10130*7c478bd9Sstevel@tonic-gate  * Complain if an attempt is made to trap untrappable signals.
10131*7c478bd9Sstevel@tonic-gate  * These would otherwise cause errors later in gl_mask_signals().
10132*7c478bd9Sstevel@tonic-gate  */
10133*7c478bd9Sstevel@tonic-gate   if(0
10134*7c478bd9Sstevel@tonic-gate #ifdef SIGKILL
10135*7c478bd9Sstevel@tonic-gate      || signo==SIGKILL
10136*7c478bd9Sstevel@tonic-gate #endif
10137*7c478bd9Sstevel@tonic-gate #ifdef SIGBLOCK
10138*7c478bd9Sstevel@tonic-gate      || signo==SIGBLOCK
10139*7c478bd9Sstevel@tonic-gate #endif
10140*7c478bd9Sstevel@tonic-gate      ) {
10141*7c478bd9Sstevel@tonic-gate     return 1;
10142*7c478bd9Sstevel@tonic-gate   };
10143*7c478bd9Sstevel@tonic-gate /*
10144*7c478bd9Sstevel@tonic-gate  * See if the signal has already been registered.
10145*7c478bd9Sstevel@tonic-gate  */
10146*7c478bd9Sstevel@tonic-gate   for(sig=gl->sigs; sig && sig->signo != signo; sig = sig->next)
10147*7c478bd9Sstevel@tonic-gate     ;
10148*7c478bd9Sstevel@tonic-gate /*
10149*7c478bd9Sstevel@tonic-gate  * If the signal hasn't already been registered, allocate a node for
10150*7c478bd9Sstevel@tonic-gate  * it.
10151*7c478bd9Sstevel@tonic-gate  */
10152*7c478bd9Sstevel@tonic-gate   if(!sig) {
10153*7c478bd9Sstevel@tonic-gate     sig = (GlSignalNode *) _new_FreeListNode(gl->sig_mem);
10154*7c478bd9Sstevel@tonic-gate     if(!sig)
10155*7c478bd9Sstevel@tonic-gate       return 1;
10156*7c478bd9Sstevel@tonic-gate /*
10157*7c478bd9Sstevel@tonic-gate  * Add the new node to the head of the list.
10158*7c478bd9Sstevel@tonic-gate  */
10159*7c478bd9Sstevel@tonic-gate     sig->next = gl->sigs;
10160*7c478bd9Sstevel@tonic-gate     gl->sigs = sig;
10161*7c478bd9Sstevel@tonic-gate /*
10162*7c478bd9Sstevel@tonic-gate  * Record the signal number.
10163*7c478bd9Sstevel@tonic-gate  */
10164*7c478bd9Sstevel@tonic-gate     sig->signo = signo;
10165*7c478bd9Sstevel@tonic-gate /*
10166*7c478bd9Sstevel@tonic-gate  * Create a signal set that includes just this signal.
10167*7c478bd9Sstevel@tonic-gate  */
10168*7c478bd9Sstevel@tonic-gate     sigemptyset(&sig->proc_mask);
10169*7c478bd9Sstevel@tonic-gate     if(sigaddset(&sig->proc_mask, signo) == -1) {
10170*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "sigaddset error", END_ERR_MSG);
10171*7c478bd9Sstevel@tonic-gate       sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig);
10172*7c478bd9Sstevel@tonic-gate       return 1;
10173*7c478bd9Sstevel@tonic-gate     };
10174*7c478bd9Sstevel@tonic-gate /*
10175*7c478bd9Sstevel@tonic-gate  * Add the signal to the bit-mask of signals being trapped.
10176*7c478bd9Sstevel@tonic-gate  */
10177*7c478bd9Sstevel@tonic-gate     sigaddset(&gl->all_signal_set, signo);
10178*7c478bd9Sstevel@tonic-gate   };
10179*7c478bd9Sstevel@tonic-gate /*
10180*7c478bd9Sstevel@tonic-gate  * Record the new signal attributes.
10181*7c478bd9Sstevel@tonic-gate  */
10182*7c478bd9Sstevel@tonic-gate   sig->flags = flags;
10183*7c478bd9Sstevel@tonic-gate   sig->after = after;
10184*7c478bd9Sstevel@tonic-gate   sig->errno_value = errno_value;
10185*7c478bd9Sstevel@tonic-gate   return 0;
10186*7c478bd9Sstevel@tonic-gate }
10187*7c478bd9Sstevel@tonic-gate 
10188*7c478bd9Sstevel@tonic-gate /*.......................................................................
10189*7c478bd9Sstevel@tonic-gate  * Remove a signal from the list of signals that gl_get_line() traps.
10190*7c478bd9Sstevel@tonic-gate  *
10191*7c478bd9Sstevel@tonic-gate  * Input:
10192*7c478bd9Sstevel@tonic-gate  *  gl           GetLine *  The resource object of gl_get_line().
10193*7c478bd9Sstevel@tonic-gate  *  signo            int    The number of the signal to be ignored.
10194*7c478bd9Sstevel@tonic-gate  * Output:
10195*7c478bd9Sstevel@tonic-gate  *  return           int    0 - OK.
10196*7c478bd9Sstevel@tonic-gate  *                          1 - Error.
10197*7c478bd9Sstevel@tonic-gate  */
10198*7c478bd9Sstevel@tonic-gate int gl_ignore_signal(GetLine *gl, int signo)
10199*7c478bd9Sstevel@tonic-gate {
10200*7c478bd9Sstevel@tonic-gate   GlSignalNode *sig;  /* The gl->sigs list node of the specified signal */
10201*7c478bd9Sstevel@tonic-gate   GlSignalNode *prev; /* The node that precedes sig in the list */
10202*7c478bd9Sstevel@tonic-gate   sigset_t oldset;    /* The signals that were blocked on entry to this */
10203*7c478bd9Sstevel@tonic-gate                       /*  function. */
10204*7c478bd9Sstevel@tonic-gate /*
10205*7c478bd9Sstevel@tonic-gate  * Check the arguments.
10206*7c478bd9Sstevel@tonic-gate  */
10207*7c478bd9Sstevel@tonic-gate   if(!gl) {
10208*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
10209*7c478bd9Sstevel@tonic-gate     return 1;
10210*7c478bd9Sstevel@tonic-gate   };
10211*7c478bd9Sstevel@tonic-gate /*
10212*7c478bd9Sstevel@tonic-gate  * Block all signals while modifying the contents of gl.
10213*7c478bd9Sstevel@tonic-gate  */
10214*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
10215*7c478bd9Sstevel@tonic-gate     return 1;
10216*7c478bd9Sstevel@tonic-gate /*
10217*7c478bd9Sstevel@tonic-gate  * Find the node of the gl->sigs list which records the disposition
10218*7c478bd9Sstevel@tonic-gate  * of the specified signal.
10219*7c478bd9Sstevel@tonic-gate  */
10220*7c478bd9Sstevel@tonic-gate   for(prev=NULL,sig=gl->sigs; sig && sig->signo != signo;
10221*7c478bd9Sstevel@tonic-gate       prev=sig,sig=sig->next)
10222*7c478bd9Sstevel@tonic-gate     ;
10223*7c478bd9Sstevel@tonic-gate   if(sig) {
10224*7c478bd9Sstevel@tonic-gate /*
10225*7c478bd9Sstevel@tonic-gate  * Remove the node from the list.
10226*7c478bd9Sstevel@tonic-gate  */
10227*7c478bd9Sstevel@tonic-gate     if(prev)
10228*7c478bd9Sstevel@tonic-gate       prev->next = sig->next;
10229*7c478bd9Sstevel@tonic-gate     else
10230*7c478bd9Sstevel@tonic-gate       gl->sigs = sig->next;
10231*7c478bd9Sstevel@tonic-gate /*
10232*7c478bd9Sstevel@tonic-gate  * Return the node to the freelist.
10233*7c478bd9Sstevel@tonic-gate  */
10234*7c478bd9Sstevel@tonic-gate     sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig);
10235*7c478bd9Sstevel@tonic-gate /*
10236*7c478bd9Sstevel@tonic-gate  * Remove the signal from the bit-mask union of signals being trapped.
10237*7c478bd9Sstevel@tonic-gate  */
10238*7c478bd9Sstevel@tonic-gate     sigdelset(&gl->all_signal_set, signo);
10239*7c478bd9Sstevel@tonic-gate   };
10240*7c478bd9Sstevel@tonic-gate /*
10241*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
10242*7c478bd9Sstevel@tonic-gate  */
10243*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
10244*7c478bd9Sstevel@tonic-gate   return 0;
10245*7c478bd9Sstevel@tonic-gate }
10246*7c478bd9Sstevel@tonic-gate 
10247*7c478bd9Sstevel@tonic-gate /*.......................................................................
10248*7c478bd9Sstevel@tonic-gate  * This function is called when an input line has been completed. It
10249*7c478bd9Sstevel@tonic-gate  * appends the specified newline character, terminates the line,
10250*7c478bd9Sstevel@tonic-gate  * records the line in the history buffer if appropriate, and positions
10251*7c478bd9Sstevel@tonic-gate  * the terminal cursor at the start of the next line.
10252*7c478bd9Sstevel@tonic-gate  *
10253*7c478bd9Sstevel@tonic-gate  * Input:
10254*7c478bd9Sstevel@tonic-gate  *  gl           GetLine *  The resource object of gl_get_line().
10255*7c478bd9Sstevel@tonic-gate  *  newline_char     int    The newline character to add to the end
10256*7c478bd9Sstevel@tonic-gate  *                          of the line.
10257*7c478bd9Sstevel@tonic-gate  * Output:
10258*7c478bd9Sstevel@tonic-gate  *  return           int    0 - OK.
10259*7c478bd9Sstevel@tonic-gate  *                          1 - Error.
10260*7c478bd9Sstevel@tonic-gate  */
10261*7c478bd9Sstevel@tonic-gate static int gl_line_ended(GetLine *gl, int newline_char)
10262*7c478bd9Sstevel@tonic-gate {
10263*7c478bd9Sstevel@tonic-gate /*
10264*7c478bd9Sstevel@tonic-gate  * If the newline character is printable, display it at the end of
10265*7c478bd9Sstevel@tonic-gate  * the line, and add it to the input line buffer.
10266*7c478bd9Sstevel@tonic-gate  */
10267*7c478bd9Sstevel@tonic-gate   if(isprint((int)(unsigned char) newline_char)) {
10268*7c478bd9Sstevel@tonic-gate     if(gl_end_of_line(gl, 1, NULL) || gl_add_char_to_line(gl, newline_char))
10269*7c478bd9Sstevel@tonic-gate       return 1;
10270*7c478bd9Sstevel@tonic-gate   } else {
10271*7c478bd9Sstevel@tonic-gate /*
10272*7c478bd9Sstevel@tonic-gate  * Otherwise just append a newline character to the input line buffer.
10273*7c478bd9Sstevel@tonic-gate  */
10274*7c478bd9Sstevel@tonic-gate     newline_char = '\n';
10275*7c478bd9Sstevel@tonic-gate     gl_buffer_char(gl, newline_char, gl->ntotal);
10276*7c478bd9Sstevel@tonic-gate   };
10277*7c478bd9Sstevel@tonic-gate /*
10278*7c478bd9Sstevel@tonic-gate  * Add the line to the history buffer if it was entered with a
10279*7c478bd9Sstevel@tonic-gate  * newline character.
10280*7c478bd9Sstevel@tonic-gate  */
10281*7c478bd9Sstevel@tonic-gate   if(gl->echo && gl->automatic_history && newline_char=='\n')
10282*7c478bd9Sstevel@tonic-gate     (void) _gl_append_history(gl, gl->line);
10283*7c478bd9Sstevel@tonic-gate /*
10284*7c478bd9Sstevel@tonic-gate  * Except when depending on the system-provided line editing, start a new
10285*7c478bd9Sstevel@tonic-gate  * line after the end of the line that has just been entered.
10286*7c478bd9Sstevel@tonic-gate  */
10287*7c478bd9Sstevel@tonic-gate   if(gl->editor != GL_NO_EDITOR && gl_start_newline(gl, 1))
10288*7c478bd9Sstevel@tonic-gate     return 1;
10289*7c478bd9Sstevel@tonic-gate /*
10290*7c478bd9Sstevel@tonic-gate  * Record the successful return status.
10291*7c478bd9Sstevel@tonic-gate  */
10292*7c478bd9Sstevel@tonic-gate   gl_record_status(gl, GLR_NEWLINE, 0);
10293*7c478bd9Sstevel@tonic-gate /*
10294*7c478bd9Sstevel@tonic-gate  * Attempt to flush any pending output.
10295*7c478bd9Sstevel@tonic-gate  */
10296*7c478bd9Sstevel@tonic-gate   (void) gl_flush_output(gl);
10297*7c478bd9Sstevel@tonic-gate /*
10298*7c478bd9Sstevel@tonic-gate  * The next call to gl_get_line() will write the prompt for a new line
10299*7c478bd9Sstevel@tonic-gate  * (or continue the above flush if incomplete), so if we manage to
10300*7c478bd9Sstevel@tonic-gate  * flush the terminal now, report that we are waiting to write to the
10301*7c478bd9Sstevel@tonic-gate  * terminal.
10302*7c478bd9Sstevel@tonic-gate  */
10303*7c478bd9Sstevel@tonic-gate   gl->pending_io = GLP_WRITE;
10304*7c478bd9Sstevel@tonic-gate   return 0;
10305*7c478bd9Sstevel@tonic-gate }
10306*7c478bd9Sstevel@tonic-gate 
10307*7c478bd9Sstevel@tonic-gate /*.......................................................................
10308*7c478bd9Sstevel@tonic-gate  * Return the last signal that was caught by the most recent call to
10309*7c478bd9Sstevel@tonic-gate  * gl_get_line(), or -1 if no signals were caught. This is useful if
10310*7c478bd9Sstevel@tonic-gate  * gl_get_line() returns errno=EINTR and you need to find out what signal
10311*7c478bd9Sstevel@tonic-gate  * caused it to abort.
10312*7c478bd9Sstevel@tonic-gate  *
10313*7c478bd9Sstevel@tonic-gate  * Input:
10314*7c478bd9Sstevel@tonic-gate  *  gl           GetLine *  The resource object of gl_get_line().
10315*7c478bd9Sstevel@tonic-gate  * Output:
10316*7c478bd9Sstevel@tonic-gate  *  return           int    The last signal caught by the most recent
10317*7c478bd9Sstevel@tonic-gate  *                          call to gl_get_line(), or -1 if no signals
10318*7c478bd9Sstevel@tonic-gate  *                          were caught.
10319*7c478bd9Sstevel@tonic-gate  */
10320*7c478bd9Sstevel@tonic-gate int gl_last_signal(GetLine *gl)
10321*7c478bd9Sstevel@tonic-gate {
10322*7c478bd9Sstevel@tonic-gate   int signo = -1;   /* The requested signal number */
10323*7c478bd9Sstevel@tonic-gate   if(gl) {
10324*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
10325*7c478bd9Sstevel@tonic-gate /*
10326*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
10327*7c478bd9Sstevel@tonic-gate  */
10328*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
10329*7c478bd9Sstevel@tonic-gate /*
10330*7c478bd9Sstevel@tonic-gate  * Access gl now that signals are blocked.
10331*7c478bd9Sstevel@tonic-gate  */
10332*7c478bd9Sstevel@tonic-gate     signo = gl->last_signal;
10333*7c478bd9Sstevel@tonic-gate /*
10334*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
10335*7c478bd9Sstevel@tonic-gate  */
10336*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
10337*7c478bd9Sstevel@tonic-gate   };
10338*7c478bd9Sstevel@tonic-gate   return signo;
10339*7c478bd9Sstevel@tonic-gate }
10340*7c478bd9Sstevel@tonic-gate 
10341*7c478bd9Sstevel@tonic-gate /*.......................................................................
10342*7c478bd9Sstevel@tonic-gate  * Prepare to edit a new line.
10343*7c478bd9Sstevel@tonic-gate  *
10344*7c478bd9Sstevel@tonic-gate  * Input:
10345*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of this library.
10346*7c478bd9Sstevel@tonic-gate  *  prompt        char *  The prompt to prefix the line with, or NULL to
10347*7c478bd9Sstevel@tonic-gate  *                        use the same prompt that was used by the previous
10348*7c478bd9Sstevel@tonic-gate  *                        line.
10349*7c478bd9Sstevel@tonic-gate  *  start_line    char *  The initial contents of the input line, or NULL
10350*7c478bd9Sstevel@tonic-gate  *                        if it should start out empty.
10351*7c478bd9Sstevel@tonic-gate  *  start_pos      int    If start_line isn't NULL, this specifies the
10352*7c478bd9Sstevel@tonic-gate  *                        index of the character over which the cursor
10353*7c478bd9Sstevel@tonic-gate  *                        should initially be positioned within the line.
10354*7c478bd9Sstevel@tonic-gate  *                        If you just want it to follow the last character
10355*7c478bd9Sstevel@tonic-gate  *                        of the line, send -1.
10356*7c478bd9Sstevel@tonic-gate  * Output:
10357*7c478bd9Sstevel@tonic-gate  *  return    int    0 - OK.
10358*7c478bd9Sstevel@tonic-gate  *                   1 - Error.
10359*7c478bd9Sstevel@tonic-gate  */
10360*7c478bd9Sstevel@tonic-gate static int gl_present_line(GetLine *gl, const char *prompt,
10361*7c478bd9Sstevel@tonic-gate 			   const char *start_line, int start_pos)
10362*7c478bd9Sstevel@tonic-gate {
10363*7c478bd9Sstevel@tonic-gate /*
10364*7c478bd9Sstevel@tonic-gate  * Reset the properties of the line.
10365*7c478bd9Sstevel@tonic-gate  */
10366*7c478bd9Sstevel@tonic-gate   gl_reset_input_line(gl);
10367*7c478bd9Sstevel@tonic-gate /*
10368*7c478bd9Sstevel@tonic-gate  * Record the new prompt and its displayed width.
10369*7c478bd9Sstevel@tonic-gate  */
10370*7c478bd9Sstevel@tonic-gate   if(prompt)
10371*7c478bd9Sstevel@tonic-gate     _gl_replace_prompt(gl, prompt);
10372*7c478bd9Sstevel@tonic-gate /*
10373*7c478bd9Sstevel@tonic-gate  * Reset the history search pointers.
10374*7c478bd9Sstevel@tonic-gate  */
10375*7c478bd9Sstevel@tonic-gate   if(_glh_cancel_search(gl->glh)) {
10376*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
10377*7c478bd9Sstevel@tonic-gate     return 1;
10378*7c478bd9Sstevel@tonic-gate   };
10379*7c478bd9Sstevel@tonic-gate /*
10380*7c478bd9Sstevel@tonic-gate  * If the previous line was entered via the repeat-history action,
10381*7c478bd9Sstevel@tonic-gate  * preload the specified history line.
10382*7c478bd9Sstevel@tonic-gate  */
10383*7c478bd9Sstevel@tonic-gate   if(gl->preload_history) {
10384*7c478bd9Sstevel@tonic-gate     gl->preload_history = 0;
10385*7c478bd9Sstevel@tonic-gate     if(gl->preload_id) {
10386*7c478bd9Sstevel@tonic-gate       if(_glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen+1)) {
10387*7c478bd9Sstevel@tonic-gate 	gl_update_buffer(gl);          /* Compute gl->ntotal etc.. */
10388*7c478bd9Sstevel@tonic-gate 	gl->buff_curpos = gl->ntotal;
10389*7c478bd9Sstevel@tonic-gate       } else {
10390*7c478bd9Sstevel@tonic-gate 	gl_truncate_buffer(gl, 0);
10391*7c478bd9Sstevel@tonic-gate       };
10392*7c478bd9Sstevel@tonic-gate       gl->preload_id = 0;
10393*7c478bd9Sstevel@tonic-gate     };
10394*7c478bd9Sstevel@tonic-gate /*
10395*7c478bd9Sstevel@tonic-gate  * Present a specified initial line?
10396*7c478bd9Sstevel@tonic-gate  */
10397*7c478bd9Sstevel@tonic-gate   } else if(start_line) {
10398*7c478bd9Sstevel@tonic-gate     char *cptr;      /* A pointer into gl->line[] */
10399*7c478bd9Sstevel@tonic-gate /*
10400*7c478bd9Sstevel@tonic-gate  * Measure the length of the starting line.
10401*7c478bd9Sstevel@tonic-gate  */
10402*7c478bd9Sstevel@tonic-gate     int start_len = strlen(start_line);
10403*7c478bd9Sstevel@tonic-gate /*
10404*7c478bd9Sstevel@tonic-gate  * If the length of the line is greater than the available space,
10405*7c478bd9Sstevel@tonic-gate  * truncate it.
10406*7c478bd9Sstevel@tonic-gate  */
10407*7c478bd9Sstevel@tonic-gate     if(start_len > gl->linelen)
10408*7c478bd9Sstevel@tonic-gate       start_len = gl->linelen;
10409*7c478bd9Sstevel@tonic-gate /*
10410*7c478bd9Sstevel@tonic-gate  * Load the line into the buffer.
10411*7c478bd9Sstevel@tonic-gate  */
10412*7c478bd9Sstevel@tonic-gate     if(start_line != gl->line)
10413*7c478bd9Sstevel@tonic-gate       gl_buffer_string(gl, start_line, start_len, 0);
10414*7c478bd9Sstevel@tonic-gate /*
10415*7c478bd9Sstevel@tonic-gate  * Strip off any trailing newline and carriage return characters.
10416*7c478bd9Sstevel@tonic-gate  */
10417*7c478bd9Sstevel@tonic-gate     for(cptr=gl->line + gl->ntotal - 1; cptr >= gl->line &&
10418*7c478bd9Sstevel@tonic-gate 	(*cptr=='\n' || *cptr=='\r'); cptr--,gl->ntotal--)
10419*7c478bd9Sstevel@tonic-gate       ;
10420*7c478bd9Sstevel@tonic-gate     gl_truncate_buffer(gl, gl->ntotal < 0 ? 0 : gl->ntotal);
10421*7c478bd9Sstevel@tonic-gate /*
10422*7c478bd9Sstevel@tonic-gate  * Where should the cursor be placed within the line?
10423*7c478bd9Sstevel@tonic-gate  */
10424*7c478bd9Sstevel@tonic-gate     if(start_pos < 0 || start_pos > gl->ntotal) {
10425*7c478bd9Sstevel@tonic-gate       if(gl_place_cursor(gl, gl->ntotal))
10426*7c478bd9Sstevel@tonic-gate 	return 1;
10427*7c478bd9Sstevel@tonic-gate     } else {
10428*7c478bd9Sstevel@tonic-gate       if(gl_place_cursor(gl, start_pos))
10429*7c478bd9Sstevel@tonic-gate 	return 1;
10430*7c478bd9Sstevel@tonic-gate     };
10431*7c478bd9Sstevel@tonic-gate /*
10432*7c478bd9Sstevel@tonic-gate  * Clear the input line?
10433*7c478bd9Sstevel@tonic-gate  */
10434*7c478bd9Sstevel@tonic-gate   } else {
10435*7c478bd9Sstevel@tonic-gate     gl_truncate_buffer(gl, 0);
10436*7c478bd9Sstevel@tonic-gate   };
10437*7c478bd9Sstevel@tonic-gate /*
10438*7c478bd9Sstevel@tonic-gate  * Arrange for the line to be displayed by gl_flush_output().
10439*7c478bd9Sstevel@tonic-gate  */
10440*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
10441*7c478bd9Sstevel@tonic-gate /*
10442*7c478bd9Sstevel@tonic-gate  * Update the display.
10443*7c478bd9Sstevel@tonic-gate  */
10444*7c478bd9Sstevel@tonic-gate   return gl_flush_output(gl);
10445*7c478bd9Sstevel@tonic-gate }
10446*7c478bd9Sstevel@tonic-gate 
10447*7c478bd9Sstevel@tonic-gate /*.......................................................................
10448*7c478bd9Sstevel@tonic-gate  * Reset all line input parameters for a new input line.
10449*7c478bd9Sstevel@tonic-gate  *
10450*7c478bd9Sstevel@tonic-gate  * Input:
10451*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The line editor resource object.
10452*7c478bd9Sstevel@tonic-gate  */
10453*7c478bd9Sstevel@tonic-gate static void gl_reset_input_line(GetLine *gl)
10454*7c478bd9Sstevel@tonic-gate {
10455*7c478bd9Sstevel@tonic-gate   gl->ntotal = 0;
10456*7c478bd9Sstevel@tonic-gate   gl->line[0] = '\0';
10457*7c478bd9Sstevel@tonic-gate   gl->buff_curpos = 0;
10458*7c478bd9Sstevel@tonic-gate   gl->term_curpos = 0;
10459*7c478bd9Sstevel@tonic-gate   gl->term_len = 0;
10460*7c478bd9Sstevel@tonic-gate   gl->insert_curpos = 0;
10461*7c478bd9Sstevel@tonic-gate   gl->number = -1;
10462*7c478bd9Sstevel@tonic-gate   gl->displayed = 0;
10463*7c478bd9Sstevel@tonic-gate   gl->endline = 0;
10464*7c478bd9Sstevel@tonic-gate   gl->redisplay = 0;
10465*7c478bd9Sstevel@tonic-gate   gl->postpone = 0;
10466*7c478bd9Sstevel@tonic-gate   gl->nbuf = 0;
10467*7c478bd9Sstevel@tonic-gate   gl->nread = 0;
10468*7c478bd9Sstevel@tonic-gate   gl->vi.command = 0;
10469*7c478bd9Sstevel@tonic-gate   gl->vi.undo.line[0] = '\0';
10470*7c478bd9Sstevel@tonic-gate   gl->vi.undo.ntotal = 0;
10471*7c478bd9Sstevel@tonic-gate   gl->vi.undo.buff_curpos = 0;
10472*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.action.fn = 0;
10473*7c478bd9Sstevel@tonic-gate   gl->vi.repeat.action.data = 0;
10474*7c478bd9Sstevel@tonic-gate   gl->last_signal = -1;
10475*7c478bd9Sstevel@tonic-gate }
10476*7c478bd9Sstevel@tonic-gate 
10477*7c478bd9Sstevel@tonic-gate /*.......................................................................
10478*7c478bd9Sstevel@tonic-gate  * Print an informational message to the terminal, after starting a new
10479*7c478bd9Sstevel@tonic-gate  * line.
10480*7c478bd9Sstevel@tonic-gate  *
10481*7c478bd9Sstevel@tonic-gate  * Input:
10482*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The line editor resource object.
10483*7c478bd9Sstevel@tonic-gate  *  ...  const char *  Zero or more strings to be printed.
10484*7c478bd9Sstevel@tonic-gate  *  ...        void *  The last argument must always be GL_END_INFO.
10485*7c478bd9Sstevel@tonic-gate  * Output:
10486*7c478bd9Sstevel@tonic-gate  *  return      int    0 - OK.
10487*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
10488*7c478bd9Sstevel@tonic-gate  */
10489*7c478bd9Sstevel@tonic-gate static int gl_print_info(GetLine *gl, ...)
10490*7c478bd9Sstevel@tonic-gate {
10491*7c478bd9Sstevel@tonic-gate   va_list ap;     /* The variable argument list */
10492*7c478bd9Sstevel@tonic-gate   const char *s;  /* The string being printed */
10493*7c478bd9Sstevel@tonic-gate   int waserr = 0; /* True after an error */
10494*7c478bd9Sstevel@tonic-gate /*
10495*7c478bd9Sstevel@tonic-gate  * Only display output when echoing is on.
10496*7c478bd9Sstevel@tonic-gate  */
10497*7c478bd9Sstevel@tonic-gate   if(gl->echo) {
10498*7c478bd9Sstevel@tonic-gate /*
10499*7c478bd9Sstevel@tonic-gate  * Skip to the start of the next empty line before displaying the message.
10500*7c478bd9Sstevel@tonic-gate  */
10501*7c478bd9Sstevel@tonic-gate     if(gl_start_newline(gl, 1))
10502*7c478bd9Sstevel@tonic-gate       return 1;
10503*7c478bd9Sstevel@tonic-gate /*
10504*7c478bd9Sstevel@tonic-gate  * Display the list of provided messages.
10505*7c478bd9Sstevel@tonic-gate  */
10506*7c478bd9Sstevel@tonic-gate     va_start(ap, gl);
10507*7c478bd9Sstevel@tonic-gate     while(!waserr && (s = va_arg(ap, const char *)) != GL_END_INFO)
10508*7c478bd9Sstevel@tonic-gate       waserr = gl_print_raw_string(gl, 1, s, -1);
10509*7c478bd9Sstevel@tonic-gate     va_end(ap);
10510*7c478bd9Sstevel@tonic-gate /*
10511*7c478bd9Sstevel@tonic-gate  * Start a newline.
10512*7c478bd9Sstevel@tonic-gate  */
10513*7c478bd9Sstevel@tonic-gate     waserr = waserr || gl_print_raw_string(gl, 1, "\n\r", -1);
10514*7c478bd9Sstevel@tonic-gate /*
10515*7c478bd9Sstevel@tonic-gate  * Arrange for the input line to be redrawn.
10516*7c478bd9Sstevel@tonic-gate  */
10517*7c478bd9Sstevel@tonic-gate     gl_queue_redisplay(gl);
10518*7c478bd9Sstevel@tonic-gate   };
10519*7c478bd9Sstevel@tonic-gate   return waserr;
10520*7c478bd9Sstevel@tonic-gate }
10521*7c478bd9Sstevel@tonic-gate 
10522*7c478bd9Sstevel@tonic-gate /*.......................................................................
10523*7c478bd9Sstevel@tonic-gate  * Go to the start of the next empty line, ready to output miscellaneous
10524*7c478bd9Sstevel@tonic-gate  * text to the screen.
10525*7c478bd9Sstevel@tonic-gate  *
10526*7c478bd9Sstevel@tonic-gate  * Note that when async-signal safety is required, the 'buffered'
10527*7c478bd9Sstevel@tonic-gate  * argument must be 0.
10528*7c478bd9Sstevel@tonic-gate  *
10529*7c478bd9Sstevel@tonic-gate  * Input:
10530*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *  The line editor resource object.
10531*7c478bd9Sstevel@tonic-gate  *  buffered        int    If true, used buffered I/O when writing to
10532*7c478bd9Sstevel@tonic-gate  *                         the terminal. Otherwise use async-signal-safe
10533*7c478bd9Sstevel@tonic-gate  *                         unbuffered I/O.
10534*7c478bd9Sstevel@tonic-gate  * Output:
10535*7c478bd9Sstevel@tonic-gate  *  return          int    0 - OK.
10536*7c478bd9Sstevel@tonic-gate  *                         1 - Error.
10537*7c478bd9Sstevel@tonic-gate  */
10538*7c478bd9Sstevel@tonic-gate static int gl_start_newline(GetLine *gl, int buffered)
10539*7c478bd9Sstevel@tonic-gate {
10540*7c478bd9Sstevel@tonic-gate   int waserr = 0;  /* True after any I/O error */
10541*7c478bd9Sstevel@tonic-gate /*
10542*7c478bd9Sstevel@tonic-gate  * Move the cursor to the start of the terminal line that follows the
10543*7c478bd9Sstevel@tonic-gate  * last line of the partially enterred line. In order that this
10544*7c478bd9Sstevel@tonic-gate  * function remain async-signal safe when write_fn is signal safe, we
10545*7c478bd9Sstevel@tonic-gate  * can't call our normal output functions, since they call tputs(),
10546*7c478bd9Sstevel@tonic-gate  * who's signal saftey isn't defined. Fortunately, we can simply use
10547*7c478bd9Sstevel@tonic-gate  * \r and \n to move the cursor to the right place.
10548*7c478bd9Sstevel@tonic-gate  */
10549*7c478bd9Sstevel@tonic-gate   if(gl->displayed) {   /* Is an input line currently displayed? */
10550*7c478bd9Sstevel@tonic-gate /*
10551*7c478bd9Sstevel@tonic-gate  * On which terminal lines are the cursor and the last character of the
10552*7c478bd9Sstevel@tonic-gate  * input line?
10553*7c478bd9Sstevel@tonic-gate  */
10554*7c478bd9Sstevel@tonic-gate     int curs_line = gl->term_curpos / gl->ncolumn;
10555*7c478bd9Sstevel@tonic-gate     int last_line = gl->term_len / gl->ncolumn;
10556*7c478bd9Sstevel@tonic-gate /*
10557*7c478bd9Sstevel@tonic-gate  * Move the cursor to the start of the line that follows the last
10558*7c478bd9Sstevel@tonic-gate  * terminal line that is occupied by the input line.
10559*7c478bd9Sstevel@tonic-gate  */
10560*7c478bd9Sstevel@tonic-gate     for( ; curs_line < last_line + 1; curs_line++)
10561*7c478bd9Sstevel@tonic-gate       waserr = waserr || gl_print_raw_string(gl, buffered, "\n", 1);
10562*7c478bd9Sstevel@tonic-gate     waserr = waserr || gl_print_raw_string(gl, buffered, "\r", 1);
10563*7c478bd9Sstevel@tonic-gate /*
10564*7c478bd9Sstevel@tonic-gate  * Mark the line as no longer displayed.
10565*7c478bd9Sstevel@tonic-gate  */
10566*7c478bd9Sstevel@tonic-gate     gl_line_erased(gl);
10567*7c478bd9Sstevel@tonic-gate   };
10568*7c478bd9Sstevel@tonic-gate   return waserr;
10569*7c478bd9Sstevel@tonic-gate }
10570*7c478bd9Sstevel@tonic-gate 
10571*7c478bd9Sstevel@tonic-gate /*.......................................................................
10572*7c478bd9Sstevel@tonic-gate  * The callback through which all terminal output is routed.
10573*7c478bd9Sstevel@tonic-gate  * This simply appends characters to a queue buffer, which is
10574*7c478bd9Sstevel@tonic-gate  * subsequently flushed to the output channel by gl_flush_output().
10575*7c478bd9Sstevel@tonic-gate  *
10576*7c478bd9Sstevel@tonic-gate  * Input:
10577*7c478bd9Sstevel@tonic-gate  *  data     void *  The pointer to a GetLine line editor resource object
10578*7c478bd9Sstevel@tonic-gate  *                   cast to (void *).
10579*7c478bd9Sstevel@tonic-gate  *  s  const char *  The string to be written.
10580*7c478bd9Sstevel@tonic-gate  *  n         int    The number of characters to write from s[].
10581*7c478bd9Sstevel@tonic-gate  * Output:
10582*7c478bd9Sstevel@tonic-gate  *  return    int    The number of characters written. This will always
10583*7c478bd9Sstevel@tonic-gate  *                   be equal to 'n' unless an error occurs.
10584*7c478bd9Sstevel@tonic-gate  */
10585*7c478bd9Sstevel@tonic-gate static GL_WRITE_FN(gl_write_fn)
10586*7c478bd9Sstevel@tonic-gate {
10587*7c478bd9Sstevel@tonic-gate   GetLine *gl = (GetLine *) data;
10588*7c478bd9Sstevel@tonic-gate   int ndone = _glq_append_chars(gl->cq, s, n, gl->flush_fn, gl);
10589*7c478bd9Sstevel@tonic-gate   if(ndone != n)
10590*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glq_last_error(gl->cq), END_ERR_MSG);
10591*7c478bd9Sstevel@tonic-gate   return ndone;
10592*7c478bd9Sstevel@tonic-gate }
10593*7c478bd9Sstevel@tonic-gate 
10594*7c478bd9Sstevel@tonic-gate /*.......................................................................
10595*7c478bd9Sstevel@tonic-gate  * Ask gl_get_line() what caused it to return.
10596*7c478bd9Sstevel@tonic-gate  *
10597*7c478bd9Sstevel@tonic-gate  * Input:
10598*7c478bd9Sstevel@tonic-gate  *  gl             GetLine *  The line editor resource object.
10599*7c478bd9Sstevel@tonic-gate  * Output:
10600*7c478bd9Sstevel@tonic-gate  *  return  GlReturnStatus    The return status of the last call to
10601*7c478bd9Sstevel@tonic-gate  *                            gl_get_line().
10602*7c478bd9Sstevel@tonic-gate  */
10603*7c478bd9Sstevel@tonic-gate GlReturnStatus gl_return_status(GetLine *gl)
10604*7c478bd9Sstevel@tonic-gate {
10605*7c478bd9Sstevel@tonic-gate   GlReturnStatus rtn_status = GLR_ERROR;   /* The requested status */
10606*7c478bd9Sstevel@tonic-gate   if(gl) {
10607*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
10608*7c478bd9Sstevel@tonic-gate /*
10609*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
10610*7c478bd9Sstevel@tonic-gate  */
10611*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
10612*7c478bd9Sstevel@tonic-gate /*
10613*7c478bd9Sstevel@tonic-gate  * Access gl while signals are blocked.
10614*7c478bd9Sstevel@tonic-gate  */
10615*7c478bd9Sstevel@tonic-gate     rtn_status = gl->rtn_status;
10616*7c478bd9Sstevel@tonic-gate /*
10617*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
10618*7c478bd9Sstevel@tonic-gate  */
10619*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
10620*7c478bd9Sstevel@tonic-gate   };
10621*7c478bd9Sstevel@tonic-gate   return rtn_status;
10622*7c478bd9Sstevel@tonic-gate }
10623*7c478bd9Sstevel@tonic-gate 
10624*7c478bd9Sstevel@tonic-gate /*.......................................................................
10625*7c478bd9Sstevel@tonic-gate  * In non-blocking server-I/O mode, this function should be called
10626*7c478bd9Sstevel@tonic-gate  * from the application's external event loop to see what type of
10627*7c478bd9Sstevel@tonic-gate  * terminal I/O is being waited for by gl_get_line(), and thus what
10628*7c478bd9Sstevel@tonic-gate  * direction of I/O to wait for with select() or poll().
10629*7c478bd9Sstevel@tonic-gate  *
10630*7c478bd9Sstevel@tonic-gate  * Input:
10631*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *  The resource object of gl_get_line().
10632*7c478bd9Sstevel@tonic-gate  * Output:
10633*7c478bd9Sstevel@tonic-gate  *  return  GlPendingIO    The type of pending I/O being waited for.
10634*7c478bd9Sstevel@tonic-gate  */
10635*7c478bd9Sstevel@tonic-gate GlPendingIO gl_pending_io(GetLine *gl)
10636*7c478bd9Sstevel@tonic-gate {
10637*7c478bd9Sstevel@tonic-gate   GlPendingIO pending_io = GLP_WRITE;   /* The requested information */
10638*7c478bd9Sstevel@tonic-gate   if(gl) {
10639*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
10640*7c478bd9Sstevel@tonic-gate /*
10641*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
10642*7c478bd9Sstevel@tonic-gate  */
10643*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
10644*7c478bd9Sstevel@tonic-gate /*
10645*7c478bd9Sstevel@tonic-gate  * Access gl while signals are blocked.
10646*7c478bd9Sstevel@tonic-gate  */
10647*7c478bd9Sstevel@tonic-gate     pending_io = gl->pending_io;
10648*7c478bd9Sstevel@tonic-gate /*
10649*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
10650*7c478bd9Sstevel@tonic-gate  */
10651*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
10652*7c478bd9Sstevel@tonic-gate   };
10653*7c478bd9Sstevel@tonic-gate   return pending_io;
10654*7c478bd9Sstevel@tonic-gate }
10655*7c478bd9Sstevel@tonic-gate 
10656*7c478bd9Sstevel@tonic-gate /*.......................................................................
10657*7c478bd9Sstevel@tonic-gate  * In server mode, this function configures the terminal for non-blocking
10658*7c478bd9Sstevel@tonic-gate  * raw terminal I/O. In normal I/O mode it does nothing.
10659*7c478bd9Sstevel@tonic-gate  *
10660*7c478bd9Sstevel@tonic-gate  * Callers of this function must be careful to trap all signals that
10661*7c478bd9Sstevel@tonic-gate  * terminate or suspend the program, and call gl_normal_io()
10662*7c478bd9Sstevel@tonic-gate  * from the corresponding signal handlers in order to restore the
10663*7c478bd9Sstevel@tonic-gate  * terminal to its original settings before the program is terminated
10664*7c478bd9Sstevel@tonic-gate  * or suspended. They should also trap the SIGCONT signal to detect
10665*7c478bd9Sstevel@tonic-gate  * when the program resumes, and ensure that its signal handler
10666*7c478bd9Sstevel@tonic-gate  * call gl_raw_io() to redisplay the line and resume editing.
10667*7c478bd9Sstevel@tonic-gate  *
10668*7c478bd9Sstevel@tonic-gate  * This function is async signal safe.
10669*7c478bd9Sstevel@tonic-gate  *
10670*7c478bd9Sstevel@tonic-gate  * Input:
10671*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The line editor resource object.
10672*7c478bd9Sstevel@tonic-gate  * Output:
10673*7c478bd9Sstevel@tonic-gate  *  return      int    0 - OK.
10674*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
10675*7c478bd9Sstevel@tonic-gate  */
10676*7c478bd9Sstevel@tonic-gate int gl_raw_io(GetLine *gl)
10677*7c478bd9Sstevel@tonic-gate {
10678*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
10679*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_raw_io() */
10680*7c478bd9Sstevel@tonic-gate /*
10681*7c478bd9Sstevel@tonic-gate  * Check the arguments.
10682*7c478bd9Sstevel@tonic-gate  */
10683*7c478bd9Sstevel@tonic-gate   if(!gl) {
10684*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
10685*7c478bd9Sstevel@tonic-gate     return 1;
10686*7c478bd9Sstevel@tonic-gate   };
10687*7c478bd9Sstevel@tonic-gate /*
10688*7c478bd9Sstevel@tonic-gate  * Block all signals.
10689*7c478bd9Sstevel@tonic-gate  */
10690*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
10691*7c478bd9Sstevel@tonic-gate     return 1;
10692*7c478bd9Sstevel@tonic-gate /*
10693*7c478bd9Sstevel@tonic-gate  * Don't allow applications to switch into raw mode unless in server mode.
10694*7c478bd9Sstevel@tonic-gate  */
10695*7c478bd9Sstevel@tonic-gate   if(gl->io_mode != GL_SERVER_MODE) {
10696*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "Can't switch to raw I/O unless in server mode",
10697*7c478bd9Sstevel@tonic-gate 		    END_ERR_MSG);
10698*7c478bd9Sstevel@tonic-gate     errno = EPERM;
10699*7c478bd9Sstevel@tonic-gate     status = 1;
10700*7c478bd9Sstevel@tonic-gate   } else {
10701*7c478bd9Sstevel@tonic-gate /*
10702*7c478bd9Sstevel@tonic-gate  * Execute the private body of the function while signals are blocked.
10703*7c478bd9Sstevel@tonic-gate  */
10704*7c478bd9Sstevel@tonic-gate     status = _gl_raw_io(gl, 1);
10705*7c478bd9Sstevel@tonic-gate   };
10706*7c478bd9Sstevel@tonic-gate /*
10707*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
10708*7c478bd9Sstevel@tonic-gate  */
10709*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
10710*7c478bd9Sstevel@tonic-gate   return status;
10711*7c478bd9Sstevel@tonic-gate }
10712*7c478bd9Sstevel@tonic-gate 
10713*7c478bd9Sstevel@tonic-gate /*.......................................................................
10714*7c478bd9Sstevel@tonic-gate  * This is the private body of the public function, gl_raw_io().
10715*7c478bd9Sstevel@tonic-gate  * It assumes that the caller has checked its arguments and blocked the
10716*7c478bd9Sstevel@tonic-gate  * delivery of signals.
10717*7c478bd9Sstevel@tonic-gate  *
10718*7c478bd9Sstevel@tonic-gate  * This function is async signal safe.
10719*7c478bd9Sstevel@tonic-gate  */
10720*7c478bd9Sstevel@tonic-gate static int _gl_raw_io(GetLine *gl, int redisplay)
10721*7c478bd9Sstevel@tonic-gate {
10722*7c478bd9Sstevel@tonic-gate /*
10723*7c478bd9Sstevel@tonic-gate  * If we are already in the correct mode, do nothing.
10724*7c478bd9Sstevel@tonic-gate  */
10725*7c478bd9Sstevel@tonic-gate   if(gl->raw_mode)
10726*7c478bd9Sstevel@tonic-gate     return 0;
10727*7c478bd9Sstevel@tonic-gate /*
10728*7c478bd9Sstevel@tonic-gate  * Switch the terminal to raw mode.
10729*7c478bd9Sstevel@tonic-gate  */
10730*7c478bd9Sstevel@tonic-gate   if(gl->is_term && gl_raw_terminal_mode(gl))
10731*7c478bd9Sstevel@tonic-gate     return 1;
10732*7c478bd9Sstevel@tonic-gate /*
10733*7c478bd9Sstevel@tonic-gate  * Switch to non-blocking I/O mode?
10734*7c478bd9Sstevel@tonic-gate  */
10735*7c478bd9Sstevel@tonic-gate   if(gl->io_mode==GL_SERVER_MODE &&
10736*7c478bd9Sstevel@tonic-gate      (gl_nonblocking_io(gl, gl->input_fd) ||
10737*7c478bd9Sstevel@tonic-gate       gl_nonblocking_io(gl, gl->output_fd) ||
10738*7c478bd9Sstevel@tonic-gate       (gl->file_fp && gl_nonblocking_io(gl, fileno(gl->file_fp))))) {
10739*7c478bd9Sstevel@tonic-gate     if(gl->is_term)
10740*7c478bd9Sstevel@tonic-gate       gl_restore_terminal_attributes(gl);
10741*7c478bd9Sstevel@tonic-gate     return 1;
10742*7c478bd9Sstevel@tonic-gate   };
10743*7c478bd9Sstevel@tonic-gate /*
10744*7c478bd9Sstevel@tonic-gate  * If an input line is being entered, arrange for it to be
10745*7c478bd9Sstevel@tonic-gate  * displayed.
10746*7c478bd9Sstevel@tonic-gate  */
10747*7c478bd9Sstevel@tonic-gate   if(redisplay) {
10748*7c478bd9Sstevel@tonic-gate     gl->postpone = 0;
10749*7c478bd9Sstevel@tonic-gate     gl_queue_redisplay(gl);
10750*7c478bd9Sstevel@tonic-gate   };
10751*7c478bd9Sstevel@tonic-gate   return 0;
10752*7c478bd9Sstevel@tonic-gate }
10753*7c478bd9Sstevel@tonic-gate 
10754*7c478bd9Sstevel@tonic-gate /*.......................................................................
10755*7c478bd9Sstevel@tonic-gate  * Restore the terminal to the state that it had when
10756*7c478bd9Sstevel@tonic-gate  * gl_raw_io() was last called. After calling
10757*7c478bd9Sstevel@tonic-gate  * gl_raw_io(), this function must be called before
10758*7c478bd9Sstevel@tonic-gate  * terminating or suspending the program, and before attempting other
10759*7c478bd9Sstevel@tonic-gate  * uses of the terminal from within the program. See gl_raw_io()
10760*7c478bd9Sstevel@tonic-gate  * for more details.
10761*7c478bd9Sstevel@tonic-gate  *
10762*7c478bd9Sstevel@tonic-gate  * Input:
10763*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The line editor resource object.
10764*7c478bd9Sstevel@tonic-gate  * Output:
10765*7c478bd9Sstevel@tonic-gate  *  return      int    0 - OK.
10766*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
10767*7c478bd9Sstevel@tonic-gate  */
10768*7c478bd9Sstevel@tonic-gate int gl_normal_io(GetLine *gl)
10769*7c478bd9Sstevel@tonic-gate {
10770*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
10771*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_normal_io() */
10772*7c478bd9Sstevel@tonic-gate /*
10773*7c478bd9Sstevel@tonic-gate  * Check the arguments.
10774*7c478bd9Sstevel@tonic-gate  */
10775*7c478bd9Sstevel@tonic-gate   if(!gl) {
10776*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
10777*7c478bd9Sstevel@tonic-gate     return 1;
10778*7c478bd9Sstevel@tonic-gate   };
10779*7c478bd9Sstevel@tonic-gate /*
10780*7c478bd9Sstevel@tonic-gate  * Block all signals.
10781*7c478bd9Sstevel@tonic-gate  */
10782*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
10783*7c478bd9Sstevel@tonic-gate     return 1;
10784*7c478bd9Sstevel@tonic-gate /*
10785*7c478bd9Sstevel@tonic-gate  * Execute the private body of the function while signals are blocked.
10786*7c478bd9Sstevel@tonic-gate  */
10787*7c478bd9Sstevel@tonic-gate   status = _gl_normal_io(gl);
10788*7c478bd9Sstevel@tonic-gate /*
10789*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
10790*7c478bd9Sstevel@tonic-gate  */
10791*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
10792*7c478bd9Sstevel@tonic-gate   return status;
10793*7c478bd9Sstevel@tonic-gate }
10794*7c478bd9Sstevel@tonic-gate 
10795*7c478bd9Sstevel@tonic-gate /*.......................................................................
10796*7c478bd9Sstevel@tonic-gate  * This is the private body of the public function, gl_normal_io().
10797*7c478bd9Sstevel@tonic-gate  * It assumes that the caller has checked its arguments and blocked the
10798*7c478bd9Sstevel@tonic-gate  * delivery of signals.
10799*7c478bd9Sstevel@tonic-gate  */
10800*7c478bd9Sstevel@tonic-gate static int _gl_normal_io(GetLine *gl)
10801*7c478bd9Sstevel@tonic-gate {
10802*7c478bd9Sstevel@tonic-gate /*
10803*7c478bd9Sstevel@tonic-gate  * If we are already in normal mode, do nothing.
10804*7c478bd9Sstevel@tonic-gate  */
10805*7c478bd9Sstevel@tonic-gate   if(!gl->raw_mode)
10806*7c478bd9Sstevel@tonic-gate     return 0;
10807*7c478bd9Sstevel@tonic-gate /*
10808*7c478bd9Sstevel@tonic-gate  * Postpone subsequent redisplays until after _gl_raw_io(gl, 1)
10809*7c478bd9Sstevel@tonic-gate  * is next called.
10810*7c478bd9Sstevel@tonic-gate  */
10811*7c478bd9Sstevel@tonic-gate   gl->postpone = 1;
10812*7c478bd9Sstevel@tonic-gate /*
10813*7c478bd9Sstevel@tonic-gate  * Switch back to blocking I/O. Note that this is essential to do
10814*7c478bd9Sstevel@tonic-gate  * here, because when using non-blocking I/O, the terminal output
10815*7c478bd9Sstevel@tonic-gate  * buffering code can't always make room for new output without calling
10816*7c478bd9Sstevel@tonic-gate  * malloc(), and a call to malloc() would mean that this function
10817*7c478bd9Sstevel@tonic-gate  * couldn't safely be called from signal handlers.
10818*7c478bd9Sstevel@tonic-gate  */
10819*7c478bd9Sstevel@tonic-gate   if(gl->io_mode==GL_SERVER_MODE &&
10820*7c478bd9Sstevel@tonic-gate      (gl_blocking_io(gl, gl->input_fd) ||
10821*7c478bd9Sstevel@tonic-gate       gl_blocking_io(gl, gl->output_fd) ||
10822*7c478bd9Sstevel@tonic-gate       (gl->file_fp && gl_blocking_io(gl, fileno(gl->file_fp)))))
10823*7c478bd9Sstevel@tonic-gate     return 1;
10824*7c478bd9Sstevel@tonic-gate /*
10825*7c478bd9Sstevel@tonic-gate  * Move the cursor to the next empty terminal line. Note that
10826*7c478bd9Sstevel@tonic-gate  * unbuffered I/O is requested, to ensure that gl_start_newline() be
10827*7c478bd9Sstevel@tonic-gate  * async-signal-safe.
10828*7c478bd9Sstevel@tonic-gate  */
10829*7c478bd9Sstevel@tonic-gate   if(gl->is_term && gl_start_newline(gl, 0))
10830*7c478bd9Sstevel@tonic-gate     return 1;
10831*7c478bd9Sstevel@tonic-gate /*
10832*7c478bd9Sstevel@tonic-gate  * Switch the terminal to normal mode.
10833*7c478bd9Sstevel@tonic-gate  */
10834*7c478bd9Sstevel@tonic-gate   if(gl->is_term && gl_restore_terminal_attributes(gl)) {
10835*7c478bd9Sstevel@tonic-gate /*
10836*7c478bd9Sstevel@tonic-gate  * On error, revert to non-blocking I/O if needed, so that on failure
10837*7c478bd9Sstevel@tonic-gate  * we remain in raw mode.
10838*7c478bd9Sstevel@tonic-gate  */
10839*7c478bd9Sstevel@tonic-gate     if(gl->io_mode==GL_SERVER_MODE) {
10840*7c478bd9Sstevel@tonic-gate       gl_nonblocking_io(gl, gl->input_fd);
10841*7c478bd9Sstevel@tonic-gate       gl_nonblocking_io(gl, gl->output_fd);
10842*7c478bd9Sstevel@tonic-gate       if(gl->file_fp)
10843*7c478bd9Sstevel@tonic-gate 	gl_nonblocking_io(gl, fileno(gl->file_fp));
10844*7c478bd9Sstevel@tonic-gate     };
10845*7c478bd9Sstevel@tonic-gate     return 1;
10846*7c478bd9Sstevel@tonic-gate   };
10847*7c478bd9Sstevel@tonic-gate   return 0;
10848*7c478bd9Sstevel@tonic-gate }
10849*7c478bd9Sstevel@tonic-gate 
10850*7c478bd9Sstevel@tonic-gate /*.......................................................................
10851*7c478bd9Sstevel@tonic-gate  * This function allows you to install an additional completion
10852*7c478bd9Sstevel@tonic-gate  * action, or to change the completion function of an existing
10853*7c478bd9Sstevel@tonic-gate  * one. This should be called before the first call to gl_get_line()
10854*7c478bd9Sstevel@tonic-gate  * so that the name of the action be defined before the user's
10855*7c478bd9Sstevel@tonic-gate  * configuration file is read.
10856*7c478bd9Sstevel@tonic-gate  *
10857*7c478bd9Sstevel@tonic-gate  * Input:
10858*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of the command-line input
10859*7c478bd9Sstevel@tonic-gate  *                           module.
10860*7c478bd9Sstevel@tonic-gate  *  data             void *  This is passed to match_fn() whenever it is
10861*7c478bd9Sstevel@tonic-gate  *                           called. It could, for example, point to a
10862*7c478bd9Sstevel@tonic-gate  *                           symbol table that match_fn() would look up
10863*7c478bd9Sstevel@tonic-gate  *                           matches in.
10864*7c478bd9Sstevel@tonic-gate  *  match_fn   CplMatchFn *  The function that will identify the prefix
10865*7c478bd9Sstevel@tonic-gate  *                           to be completed from the input line, and
10866*7c478bd9Sstevel@tonic-gate  *                           report matching symbols.
10867*7c478bd9Sstevel@tonic-gate  *  list_only         int    If non-zero, install an action that only lists
10868*7c478bd9Sstevel@tonic-gate  *                           possible completions, rather than attempting
10869*7c478bd9Sstevel@tonic-gate  *                           to perform the completion.
10870*7c478bd9Sstevel@tonic-gate  *  name       const char *  The name with which users can refer to the
10871*7c478bd9Sstevel@tonic-gate  *                           binding in tecla configuration files.
10872*7c478bd9Sstevel@tonic-gate  *  keyseq     const char *  Either NULL, or a key sequence with which
10873*7c478bd9Sstevel@tonic-gate  *                           to invoke the binding. This should be
10874*7c478bd9Sstevel@tonic-gate  *                           specified in the same manner as key-sequences
10875*7c478bd9Sstevel@tonic-gate  *                           in tecla configuration files (eg. "M-^I").
10876*7c478bd9Sstevel@tonic-gate  * Output:
10877*7c478bd9Sstevel@tonic-gate  *  return            int    0 - OK.
10878*7c478bd9Sstevel@tonic-gate  *                           1 - Error.
10879*7c478bd9Sstevel@tonic-gate  */
10880*7c478bd9Sstevel@tonic-gate int gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn,
10881*7c478bd9Sstevel@tonic-gate 			 int list_only, const char *name, const char *keyseq)
10882*7c478bd9Sstevel@tonic-gate {
10883*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
10884*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_completion_action() */
10885*7c478bd9Sstevel@tonic-gate /*
10886*7c478bd9Sstevel@tonic-gate  * Check the arguments.
10887*7c478bd9Sstevel@tonic-gate  */
10888*7c478bd9Sstevel@tonic-gate   if(!gl || !name || !match_fn) {
10889*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
10890*7c478bd9Sstevel@tonic-gate     return 1;
10891*7c478bd9Sstevel@tonic-gate   };
10892*7c478bd9Sstevel@tonic-gate /*
10893*7c478bd9Sstevel@tonic-gate  * Block all signals.
10894*7c478bd9Sstevel@tonic-gate  */
10895*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
10896*7c478bd9Sstevel@tonic-gate     return 1;
10897*7c478bd9Sstevel@tonic-gate /*
10898*7c478bd9Sstevel@tonic-gate  * Install the new action while signals are blocked.
10899*7c478bd9Sstevel@tonic-gate  */
10900*7c478bd9Sstevel@tonic-gate   status = _gl_completion_action(gl, data, match_fn, list_only, name, keyseq);
10901*7c478bd9Sstevel@tonic-gate /*
10902*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
10903*7c478bd9Sstevel@tonic-gate  */
10904*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
10905*7c478bd9Sstevel@tonic-gate   return status;
10906*7c478bd9Sstevel@tonic-gate }
10907*7c478bd9Sstevel@tonic-gate 
10908*7c478bd9Sstevel@tonic-gate /*.......................................................................
10909*7c478bd9Sstevel@tonic-gate  * This is the private body of the public function, gl_completion_action().
10910*7c478bd9Sstevel@tonic-gate  * It assumes that the caller has checked its arguments and blocked the
10911*7c478bd9Sstevel@tonic-gate  * delivery of signals.
10912*7c478bd9Sstevel@tonic-gate  */
10913*7c478bd9Sstevel@tonic-gate static int _gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn,
10914*7c478bd9Sstevel@tonic-gate 				 int list_only, const char *name,
10915*7c478bd9Sstevel@tonic-gate 				 const char *keyseq)
10916*7c478bd9Sstevel@tonic-gate {
10917*7c478bd9Sstevel@tonic-gate   KtKeyFn *current_fn;      /* An existing action function */
10918*7c478bd9Sstevel@tonic-gate   void *current_data;       /* The action-function callback data */
10919*7c478bd9Sstevel@tonic-gate /*
10920*7c478bd9Sstevel@tonic-gate  * Which action function is desired?
10921*7c478bd9Sstevel@tonic-gate  */
10922*7c478bd9Sstevel@tonic-gate   KtKeyFn *action_fn = list_only ? gl_list_completions : gl_complete_word;
10923*7c478bd9Sstevel@tonic-gate /*
10924*7c478bd9Sstevel@tonic-gate  * Is there already an action of the specified name?
10925*7c478bd9Sstevel@tonic-gate  */
10926*7c478bd9Sstevel@tonic-gate   if(_kt_lookup_action(gl->bindings, name, &current_fn, &current_data) == 0) {
10927*7c478bd9Sstevel@tonic-gate /*
10928*7c478bd9Sstevel@tonic-gate  * If the action has the same type as the one being requested,
10929*7c478bd9Sstevel@tonic-gate  * simply change the contents of its GlCplCallback callback data.
10930*7c478bd9Sstevel@tonic-gate  */
10931*7c478bd9Sstevel@tonic-gate     if(current_fn == action_fn) {
10932*7c478bd9Sstevel@tonic-gate       GlCplCallback *cb = (GlCplCallback *) current_data;
10933*7c478bd9Sstevel@tonic-gate       cb->fn = match_fn;
10934*7c478bd9Sstevel@tonic-gate       cb->data = data;
10935*7c478bd9Sstevel@tonic-gate     } else {
10936*7c478bd9Sstevel@tonic-gate       errno = EINVAL;
10937*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err,
10938*7c478bd9Sstevel@tonic-gate         "Illegal attempt to change the type of an existing completion action",
10939*7c478bd9Sstevel@tonic-gate         END_ERR_MSG);
10940*7c478bd9Sstevel@tonic-gate       return 1;
10941*7c478bd9Sstevel@tonic-gate     };
10942*7c478bd9Sstevel@tonic-gate /*
10943*7c478bd9Sstevel@tonic-gate  * No existing action has the specified name.
10944*7c478bd9Sstevel@tonic-gate  */
10945*7c478bd9Sstevel@tonic-gate   } else {
10946*7c478bd9Sstevel@tonic-gate /*
10947*7c478bd9Sstevel@tonic-gate  * Allocate a new GlCplCallback callback object.
10948*7c478bd9Sstevel@tonic-gate  */
10949*7c478bd9Sstevel@tonic-gate     GlCplCallback *cb = (GlCplCallback *) _new_FreeListNode(gl->cpl_mem);
10950*7c478bd9Sstevel@tonic-gate     if(!cb) {
10951*7c478bd9Sstevel@tonic-gate       errno = ENOMEM;
10952*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "Insufficient memory to add completion action",
10953*7c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
10954*7c478bd9Sstevel@tonic-gate       return 1;
10955*7c478bd9Sstevel@tonic-gate     };
10956*7c478bd9Sstevel@tonic-gate /*
10957*7c478bd9Sstevel@tonic-gate  * Record the completion callback data.
10958*7c478bd9Sstevel@tonic-gate  */
10959*7c478bd9Sstevel@tonic-gate     cb->fn = match_fn;
10960*7c478bd9Sstevel@tonic-gate     cb->data = data;
10961*7c478bd9Sstevel@tonic-gate /*
10962*7c478bd9Sstevel@tonic-gate  * Attempt to register the new action.
10963*7c478bd9Sstevel@tonic-gate  */
10964*7c478bd9Sstevel@tonic-gate     if(_kt_set_action(gl->bindings, name, action_fn, cb)) {
10965*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG);
10966*7c478bd9Sstevel@tonic-gate       _del_FreeListNode(gl->cpl_mem, (void *) cb);
10967*7c478bd9Sstevel@tonic-gate       return 1;
10968*7c478bd9Sstevel@tonic-gate     };
10969*7c478bd9Sstevel@tonic-gate   };
10970*7c478bd9Sstevel@tonic-gate /*
10971*7c478bd9Sstevel@tonic-gate  * Bind the action to a given key-sequence?
10972*7c478bd9Sstevel@tonic-gate  */
10973*7c478bd9Sstevel@tonic-gate   if(keyseq && _kt_set_keybinding(gl->bindings, KTB_NORM, keyseq, name)) {
10974*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG);
10975*7c478bd9Sstevel@tonic-gate     return 1;
10976*7c478bd9Sstevel@tonic-gate   };
10977*7c478bd9Sstevel@tonic-gate   return 0;
10978*7c478bd9Sstevel@tonic-gate }
10979*7c478bd9Sstevel@tonic-gate 
10980*7c478bd9Sstevel@tonic-gate /*.......................................................................
10981*7c478bd9Sstevel@tonic-gate  * Register an application-provided function as an action function.
10982*7c478bd9Sstevel@tonic-gate  * This should preferably be called before the first call to gl_get_line()
10983*7c478bd9Sstevel@tonic-gate  * so that the name of the action becomes defined before the user's
10984*7c478bd9Sstevel@tonic-gate  * configuration file is read.
10985*7c478bd9Sstevel@tonic-gate  *
10986*7c478bd9Sstevel@tonic-gate  * Input:
10987*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of the command-line input
10988*7c478bd9Sstevel@tonic-gate  *                           module.
10989*7c478bd9Sstevel@tonic-gate  *  data             void *  Arbitrary application-specific callback
10990*7c478bd9Sstevel@tonic-gate  *                           data to be passed to the callback
10991*7c478bd9Sstevel@tonic-gate  *                           function, fn().
10992*7c478bd9Sstevel@tonic-gate  *  fn         GlActionFn *  The application-specific function that
10993*7c478bd9Sstevel@tonic-gate  *                           implements the action. This will be invoked
10994*7c478bd9Sstevel@tonic-gate  *                           whenever the user presses any
10995*7c478bd9Sstevel@tonic-gate  *                           key-sequence which is bound to this action.
10996*7c478bd9Sstevel@tonic-gate  *  name       const char *  The name with which users can refer to the
10997*7c478bd9Sstevel@tonic-gate  *                           binding in tecla configuration files.
10998*7c478bd9Sstevel@tonic-gate  *  keyseq     const char *  The key sequence with which to invoke
10999*7c478bd9Sstevel@tonic-gate  *                           the binding. This should be specified in the
11000*7c478bd9Sstevel@tonic-gate  *                           same manner as key-sequences in tecla
11001*7c478bd9Sstevel@tonic-gate  *                           configuration files (eg. "M-^I").
11002*7c478bd9Sstevel@tonic-gate  * Output:
11003*7c478bd9Sstevel@tonic-gate  *  return            int    0 - OK.
11004*7c478bd9Sstevel@tonic-gate  *                           1 - Error.
11005*7c478bd9Sstevel@tonic-gate  */
11006*7c478bd9Sstevel@tonic-gate int gl_register_action(GetLine *gl, void *data, GlActionFn *fn,
11007*7c478bd9Sstevel@tonic-gate                        const char *name, const char *keyseq)
11008*7c478bd9Sstevel@tonic-gate {
11009*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
11010*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_register_action() */
11011*7c478bd9Sstevel@tonic-gate /*
11012*7c478bd9Sstevel@tonic-gate  * Check the arguments.
11013*7c478bd9Sstevel@tonic-gate  */
11014*7c478bd9Sstevel@tonic-gate   if(!gl || !name || !fn) {
11015*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
11016*7c478bd9Sstevel@tonic-gate     return 1;
11017*7c478bd9Sstevel@tonic-gate   };
11018*7c478bd9Sstevel@tonic-gate /*
11019*7c478bd9Sstevel@tonic-gate  * Block all signals.
11020*7c478bd9Sstevel@tonic-gate  */
11021*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
11022*7c478bd9Sstevel@tonic-gate     return 1;
11023*7c478bd9Sstevel@tonic-gate /*
11024*7c478bd9Sstevel@tonic-gate  * Install the new action while signals are blocked.
11025*7c478bd9Sstevel@tonic-gate  */
11026*7c478bd9Sstevel@tonic-gate   status = _gl_register_action(gl, data, fn, name, keyseq);
11027*7c478bd9Sstevel@tonic-gate /*
11028*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
11029*7c478bd9Sstevel@tonic-gate  */
11030*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
11031*7c478bd9Sstevel@tonic-gate   return status;
11032*7c478bd9Sstevel@tonic-gate }
11033*7c478bd9Sstevel@tonic-gate 
11034*7c478bd9Sstevel@tonic-gate /*.......................................................................
11035*7c478bd9Sstevel@tonic-gate  * This is the private body of the public function, gl_register_action().
11036*7c478bd9Sstevel@tonic-gate  * It assumes that the caller has checked its arguments and blocked the
11037*7c478bd9Sstevel@tonic-gate  * delivery of signals.
11038*7c478bd9Sstevel@tonic-gate  */
11039*7c478bd9Sstevel@tonic-gate static int _gl_register_action(GetLine *gl, void *data, GlActionFn *fn,
11040*7c478bd9Sstevel@tonic-gate 			       const char *name, const char *keyseq)
11041*7c478bd9Sstevel@tonic-gate {
11042*7c478bd9Sstevel@tonic-gate   KtKeyFn *current_fn;      /* An existing action function */
11043*7c478bd9Sstevel@tonic-gate   void *current_data;       /* The action-function callback data */
11044*7c478bd9Sstevel@tonic-gate /*
11045*7c478bd9Sstevel@tonic-gate  * Get the action function which actually runs the application-provided
11046*7c478bd9Sstevel@tonic-gate  * function.
11047*7c478bd9Sstevel@tonic-gate  */
11048*7c478bd9Sstevel@tonic-gate   KtKeyFn *action_fn = gl_run_external_action;
11049*7c478bd9Sstevel@tonic-gate /*
11050*7c478bd9Sstevel@tonic-gate  * Is there already an action of the specified name?
11051*7c478bd9Sstevel@tonic-gate  */
11052*7c478bd9Sstevel@tonic-gate   if(_kt_lookup_action(gl->bindings, name, &current_fn, &current_data) == 0) {
11053*7c478bd9Sstevel@tonic-gate /*
11054*7c478bd9Sstevel@tonic-gate  * If the action has the same type as the one being requested,
11055*7c478bd9Sstevel@tonic-gate  * simply change the contents of its GlCplCallback callback data.
11056*7c478bd9Sstevel@tonic-gate  */
11057*7c478bd9Sstevel@tonic-gate     if(current_fn == action_fn) {
11058*7c478bd9Sstevel@tonic-gate       GlExternalAction *a = (GlExternalAction *) current_data;
11059*7c478bd9Sstevel@tonic-gate       a->fn = fn;
11060*7c478bd9Sstevel@tonic-gate       a->data = data;
11061*7c478bd9Sstevel@tonic-gate     } else {
11062*7c478bd9Sstevel@tonic-gate       errno = EINVAL;
11063*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err,
11064*7c478bd9Sstevel@tonic-gate         "Illegal attempt to change the type of an existing action",
11065*7c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
11066*7c478bd9Sstevel@tonic-gate       return 1;
11067*7c478bd9Sstevel@tonic-gate     };
11068*7c478bd9Sstevel@tonic-gate /*
11069*7c478bd9Sstevel@tonic-gate  * No existing action has the specified name.
11070*7c478bd9Sstevel@tonic-gate  */
11071*7c478bd9Sstevel@tonic-gate   } else {
11072*7c478bd9Sstevel@tonic-gate /*
11073*7c478bd9Sstevel@tonic-gate  * Allocate a new GlCplCallback callback object.
11074*7c478bd9Sstevel@tonic-gate  */
11075*7c478bd9Sstevel@tonic-gate     GlExternalAction *a =
11076*7c478bd9Sstevel@tonic-gate       (GlExternalAction *) _new_FreeListNode(gl->ext_act_mem);
11077*7c478bd9Sstevel@tonic-gate     if(!a) {
11078*7c478bd9Sstevel@tonic-gate       errno = ENOMEM;
11079*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "Insufficient memory to add completion action",
11080*7c478bd9Sstevel@tonic-gate 		      END_ERR_MSG);
11081*7c478bd9Sstevel@tonic-gate       return 1;
11082*7c478bd9Sstevel@tonic-gate     };
11083*7c478bd9Sstevel@tonic-gate /*
11084*7c478bd9Sstevel@tonic-gate  * Record the completion callback data.
11085*7c478bd9Sstevel@tonic-gate  */
11086*7c478bd9Sstevel@tonic-gate     a->fn = fn;
11087*7c478bd9Sstevel@tonic-gate     a->data = data;
11088*7c478bd9Sstevel@tonic-gate /*
11089*7c478bd9Sstevel@tonic-gate  * Attempt to register the new action.
11090*7c478bd9Sstevel@tonic-gate  */
11091*7c478bd9Sstevel@tonic-gate     if(_kt_set_action(gl->bindings, name, action_fn, a)) {
11092*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG);
11093*7c478bd9Sstevel@tonic-gate       _del_FreeListNode(gl->cpl_mem, (void *) a);
11094*7c478bd9Sstevel@tonic-gate       return 1;
11095*7c478bd9Sstevel@tonic-gate     };
11096*7c478bd9Sstevel@tonic-gate   };
11097*7c478bd9Sstevel@tonic-gate /*
11098*7c478bd9Sstevel@tonic-gate  * Bind the action to a given key-sequence?
11099*7c478bd9Sstevel@tonic-gate  */
11100*7c478bd9Sstevel@tonic-gate   if(keyseq && _kt_set_keybinding(gl->bindings, KTB_NORM, keyseq, name)) {
11101*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG);
11102*7c478bd9Sstevel@tonic-gate     return 1;
11103*7c478bd9Sstevel@tonic-gate   };
11104*7c478bd9Sstevel@tonic-gate   return 0;
11105*7c478bd9Sstevel@tonic-gate }
11106*7c478bd9Sstevel@tonic-gate 
11107*7c478bd9Sstevel@tonic-gate /*.......................................................................
11108*7c478bd9Sstevel@tonic-gate  * Invoke an action function previously registered by a call to
11109*7c478bd9Sstevel@tonic-gate  * gl_register_action().
11110*7c478bd9Sstevel@tonic-gate  */
11111*7c478bd9Sstevel@tonic-gate static KT_KEY_FN(gl_run_external_action)
11112*7c478bd9Sstevel@tonic-gate {
11113*7c478bd9Sstevel@tonic-gate   GlAfterAction status;  /* The return value of the action function */
11114*7c478bd9Sstevel@tonic-gate /*
11115*7c478bd9Sstevel@tonic-gate  * Get the container of the action function and associated callback data.
11116*7c478bd9Sstevel@tonic-gate  */
11117*7c478bd9Sstevel@tonic-gate   GlExternalAction *a = (GlExternalAction *) data;
11118*7c478bd9Sstevel@tonic-gate /*
11119*7c478bd9Sstevel@tonic-gate  * Invoke the action function.
11120*7c478bd9Sstevel@tonic-gate  */
11121*7c478bd9Sstevel@tonic-gate   status = a->fn(gl, a->data, count, gl->buff_curpos, gl->line);
11122*7c478bd9Sstevel@tonic-gate /*
11123*7c478bd9Sstevel@tonic-gate  * If the callback took us out of raw (possibly non-blocking) input
11124*7c478bd9Sstevel@tonic-gate  * mode, restore this mode, and queue a redisplay of the input line.
11125*7c478bd9Sstevel@tonic-gate  */
11126*7c478bd9Sstevel@tonic-gate   if(_gl_raw_io(gl, 1))
11127*7c478bd9Sstevel@tonic-gate     return 1;
11128*7c478bd9Sstevel@tonic-gate /*
11129*7c478bd9Sstevel@tonic-gate  * Finally, check to see what the action function wants us to do next.
11130*7c478bd9Sstevel@tonic-gate  */
11131*7c478bd9Sstevel@tonic-gate   switch(status) {
11132*7c478bd9Sstevel@tonic-gate   default:
11133*7c478bd9Sstevel@tonic-gate   case GLA_ABORT:
11134*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_ERROR, errno);
11135*7c478bd9Sstevel@tonic-gate     return 1;
11136*7c478bd9Sstevel@tonic-gate     break;
11137*7c478bd9Sstevel@tonic-gate   case GLA_RETURN:
11138*7c478bd9Sstevel@tonic-gate     return gl_newline(gl, 1, NULL);
11139*7c478bd9Sstevel@tonic-gate     break;
11140*7c478bd9Sstevel@tonic-gate   case GLA_CONTINUE:
11141*7c478bd9Sstevel@tonic-gate     break;
11142*7c478bd9Sstevel@tonic-gate   };
11143*7c478bd9Sstevel@tonic-gate   return 0;
11144*7c478bd9Sstevel@tonic-gate }
11145*7c478bd9Sstevel@tonic-gate 
11146*7c478bd9Sstevel@tonic-gate /*.......................................................................
11147*7c478bd9Sstevel@tonic-gate  * In server-I/O mode the terminal is left in raw mode between calls
11148*7c478bd9Sstevel@tonic-gate  * to gl_get_line(), so it is necessary for the application to install
11149*7c478bd9Sstevel@tonic-gate  * terminal restoring signal handlers for signals that could terminate
11150*7c478bd9Sstevel@tonic-gate  * or suspend the process, plus a terminal reconfiguration handler to
11151*7c478bd9Sstevel@tonic-gate  * be called when a process resumption signal is received, and finally
11152*7c478bd9Sstevel@tonic-gate  * a handler to be called when a terminal-resize signal is received.
11153*7c478bd9Sstevel@tonic-gate  *
11154*7c478bd9Sstevel@tonic-gate  * Since there are many signals that by default terminate or suspend
11155*7c478bd9Sstevel@tonic-gate  * processes, and different systems support different sub-sets of
11156*7c478bd9Sstevel@tonic-gate  * these signals, this function provides a convenient wrapper around
11157*7c478bd9Sstevel@tonic-gate  * sigaction() for assigning the specified handlers to all appropriate
11158*7c478bd9Sstevel@tonic-gate  * signals. It also arranges that when any one of these signals is
11159*7c478bd9Sstevel@tonic-gate  * being handled, all other catchable signals are blocked. This is
11160*7c478bd9Sstevel@tonic-gate  * necessary so that the specified signal handlers can safely call
11161*7c478bd9Sstevel@tonic-gate  * gl_raw_io(), gl_normal_io() and gl_update_size() without
11162*7c478bd9Sstevel@tonic-gate  * reentrancy issues.
11163*7c478bd9Sstevel@tonic-gate  *
11164*7c478bd9Sstevel@tonic-gate  * Input:
11165*7c478bd9Sstevel@tonic-gate  *  term_handler  void (*)(int)  The signal handler to invoke when
11166*7c478bd9Sstevel@tonic-gate  *                               a process terminating signal is
11167*7c478bd9Sstevel@tonic-gate  *                               received.
11168*7c478bd9Sstevel@tonic-gate  *  susp_handler  void (*)(int)  The signal handler to invoke when
11169*7c478bd9Sstevel@tonic-gate  *                               a process suspending signal is
11170*7c478bd9Sstevel@tonic-gate  *                               received.
11171*7c478bd9Sstevel@tonic-gate  *  cont_handler  void (*)(int)  The signal handler to invoke when
11172*7c478bd9Sstevel@tonic-gate  *                               a process resumption signal is
11173*7c478bd9Sstevel@tonic-gate  *                               received (ie. SIGCONT).
11174*7c478bd9Sstevel@tonic-gate  *  size_handler  void (*)(int)  The signal handler to invoke when
11175*7c478bd9Sstevel@tonic-gate  *                               a terminal-resize signal (ie. SIGWINCH)
11176*7c478bd9Sstevel@tonic-gate  *                               is received.
11177*7c478bd9Sstevel@tonic-gate  * Output:
11178*7c478bd9Sstevel@tonic-gate  *  return                  int  0 - OK.
11179*7c478bd9Sstevel@tonic-gate  *                               1 - Error.
11180*7c478bd9Sstevel@tonic-gate  */
11181*7c478bd9Sstevel@tonic-gate int gl_tty_signals(void (*term_handler)(int), void (*susp_handler)(int),
11182*7c478bd9Sstevel@tonic-gate 		   void (*cont_handler)(int), void (*size_handler)(int))
11183*7c478bd9Sstevel@tonic-gate {
11184*7c478bd9Sstevel@tonic-gate   int i;
11185*7c478bd9Sstevel@tonic-gate /*
11186*7c478bd9Sstevel@tonic-gate  * Search for signals of the specified classes, and assign the
11187*7c478bd9Sstevel@tonic-gate  * associated signal handler to them.
11188*7c478bd9Sstevel@tonic-gate  */
11189*7c478bd9Sstevel@tonic-gate   for(i=0; i<sizeof(gl_signal_list)/sizeof(gl_signal_list[0]); i++) {
11190*7c478bd9Sstevel@tonic-gate     const struct GlDefSignal *sig = gl_signal_list + i;
11191*7c478bd9Sstevel@tonic-gate     if(sig->attr & GLSA_SUSP) {
11192*7c478bd9Sstevel@tonic-gate       if(gl_set_tty_signal(sig->signo, term_handler))
11193*7c478bd9Sstevel@tonic-gate 	return 1;
11194*7c478bd9Sstevel@tonic-gate     } else if(sig->attr & GLSA_TERM) {
11195*7c478bd9Sstevel@tonic-gate       if(gl_set_tty_signal(sig->signo, susp_handler))
11196*7c478bd9Sstevel@tonic-gate 	return 1;
11197*7c478bd9Sstevel@tonic-gate     } else if(sig->attr & GLSA_CONT) {
11198*7c478bd9Sstevel@tonic-gate       if(gl_set_tty_signal(sig->signo, cont_handler))
11199*7c478bd9Sstevel@tonic-gate 	return 1;
11200*7c478bd9Sstevel@tonic-gate     } else if(sig->attr & GLSA_SIZE) {
11201*7c478bd9Sstevel@tonic-gate       if(gl_set_tty_signal(sig->signo, size_handler))
11202*7c478bd9Sstevel@tonic-gate 	return 1;
11203*7c478bd9Sstevel@tonic-gate     };
11204*7c478bd9Sstevel@tonic-gate   };
11205*7c478bd9Sstevel@tonic-gate   return 0;
11206*7c478bd9Sstevel@tonic-gate }
11207*7c478bd9Sstevel@tonic-gate 
11208*7c478bd9Sstevel@tonic-gate /*.......................................................................
11209*7c478bd9Sstevel@tonic-gate  * This is a private function of gl_tty_signals(). It installs a given
11210*7c478bd9Sstevel@tonic-gate  * signal handler, and arranges that when that signal handler is being
11211*7c478bd9Sstevel@tonic-gate  * invoked other signals are blocked. The latter is important to allow
11212*7c478bd9Sstevel@tonic-gate  * functions like gl_normal_io(), gl_raw_io() and gl_update_size()
11213*7c478bd9Sstevel@tonic-gate  * to be called from signal handlers.
11214*7c478bd9Sstevel@tonic-gate  *
11215*7c478bd9Sstevel@tonic-gate  * Input:
11216*7c478bd9Sstevel@tonic-gate  *  signo     int           The signal to be trapped.
11217*7c478bd9Sstevel@tonic-gate  *  handler  void (*)(int)  The signal handler to assign to the signal.
11218*7c478bd9Sstevel@tonic-gate  */
11219*7c478bd9Sstevel@tonic-gate static int gl_set_tty_signal(int signo, void (*handler)(int))
11220*7c478bd9Sstevel@tonic-gate {
11221*7c478bd9Sstevel@tonic-gate   SigAction act;   /* The signal handler configuation */
11222*7c478bd9Sstevel@tonic-gate /*
11223*7c478bd9Sstevel@tonic-gate  * Arrange to block all trappable signals except the one that is being
11224*7c478bd9Sstevel@tonic-gate  * assigned (the trapped signal will be blocked automatically by the
11225*7c478bd9Sstevel@tonic-gate  * system).
11226*7c478bd9Sstevel@tonic-gate  */
11227*7c478bd9Sstevel@tonic-gate   gl_list_trappable_signals(&act.sa_mask);
11228*7c478bd9Sstevel@tonic-gate   sigdelset(&act.sa_mask, signo);
11229*7c478bd9Sstevel@tonic-gate /*
11230*7c478bd9Sstevel@tonic-gate  * Assign the signal handler.
11231*7c478bd9Sstevel@tonic-gate  */
11232*7c478bd9Sstevel@tonic-gate   act.sa_handler = handler;
11233*7c478bd9Sstevel@tonic-gate /*
11234*7c478bd9Sstevel@tonic-gate  * There is only one portable signal handling flag, and it isn't
11235*7c478bd9Sstevel@tonic-gate  * relevant to us, so don't specify any flags.
11236*7c478bd9Sstevel@tonic-gate  */
11237*7c478bd9Sstevel@tonic-gate   act.sa_flags = 0;
11238*7c478bd9Sstevel@tonic-gate /*
11239*7c478bd9Sstevel@tonic-gate  * Register the signal handler.
11240*7c478bd9Sstevel@tonic-gate  */
11241*7c478bd9Sstevel@tonic-gate   if(sigaction(signo, &act, NULL))
11242*7c478bd9Sstevel@tonic-gate     return 1;
11243*7c478bd9Sstevel@tonic-gate   return 0;
11244*7c478bd9Sstevel@tonic-gate }
11245*7c478bd9Sstevel@tonic-gate 
11246*7c478bd9Sstevel@tonic-gate /*.......................................................................
11247*7c478bd9Sstevel@tonic-gate  * Display a left-justified string over multiple terminal lines,
11248*7c478bd9Sstevel@tonic-gate  * taking account of the current width of the terminal. Optional
11249*7c478bd9Sstevel@tonic-gate  * indentation and an optional prefix string can be specified to be
11250*7c478bd9Sstevel@tonic-gate  * displayed at the start of each new terminal line used. Similarly,
11251*7c478bd9Sstevel@tonic-gate  * an optional suffix can be specified to be displayed at the end of
11252*7c478bd9Sstevel@tonic-gate  * each terminal line.  If needed, a single paragraph can be broken
11253*7c478bd9Sstevel@tonic-gate  * across multiple calls.  Note that literal newlines in the input
11254*7c478bd9Sstevel@tonic-gate  * string can be used to force a newline at any point and that you
11255*7c478bd9Sstevel@tonic-gate  * should use this feature to explicitly end all paragraphs, including
11256*7c478bd9Sstevel@tonic-gate  * at the end of the last string that you write. Note that when a new
11257*7c478bd9Sstevel@tonic-gate  * line is started between two words that are separated by spaces,
11258*7c478bd9Sstevel@tonic-gate  * those spaces are not output, whereas when a new line is started
11259*7c478bd9Sstevel@tonic-gate  * because a newline character was found in the string, only the
11260*7c478bd9Sstevel@tonic-gate  * spaces before the newline character are discarded.
11261*7c478bd9Sstevel@tonic-gate  *
11262*7c478bd9Sstevel@tonic-gate  * Input:
11263*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
11264*7c478bd9Sstevel@tonic-gate  *  indentation    int    The number of spaces of indentation to write
11265*7c478bd9Sstevel@tonic-gate  *                        at the beginning of each new terminal line.
11266*7c478bd9Sstevel@tonic-gate  *  prefix  const char *  An optional prefix string to write after the
11267*7c478bd9Sstevel@tonic-gate  *                        indentation margin at the start of each new
11268*7c478bd9Sstevel@tonic-gate  *                        terminal line. You can specify NULL if no
11269*7c478bd9Sstevel@tonic-gate  *                        prefix is required.
11270*7c478bd9Sstevel@tonic-gate  *  suffix  const char *  An optional suffix string to draw at the end
11271*7c478bd9Sstevel@tonic-gate  *                        of the terminal line. Spaces will be added
11272*7c478bd9Sstevel@tonic-gate  *                        where necessary to ensure that the suffix ends
11273*7c478bd9Sstevel@tonic-gate  *                        in the last column of the terminal line. If
11274*7c478bd9Sstevel@tonic-gate  *                        no suffix is desired, specify NULL.
11275*7c478bd9Sstevel@tonic-gate  *  fill_char      int    The padding character to use when indenting
11276*7c478bd9Sstevel@tonic-gate  *                        the line or padding up to the suffix.
11277*7c478bd9Sstevel@tonic-gate  *  def_width      int    If the terminal width isn't known, such as when
11278*7c478bd9Sstevel@tonic-gate  *                        writing to a pipe or redirecting to a file,
11279*7c478bd9Sstevel@tonic-gate  *                        this number specifies what width to assume.
11280*7c478bd9Sstevel@tonic-gate  *  start          int    The number of characters already written to
11281*7c478bd9Sstevel@tonic-gate  *                        the start of the current terminal line. This
11282*7c478bd9Sstevel@tonic-gate  *                        is primarily used to allow individual
11283*7c478bd9Sstevel@tonic-gate  *                        paragraphs to be written over multiple calls
11284*7c478bd9Sstevel@tonic-gate  *                        to this function, but can also be used to
11285*7c478bd9Sstevel@tonic-gate  *                        allow you to start the first line of a
11286*7c478bd9Sstevel@tonic-gate  *                        paragraph with a different prefix or
11287*7c478bd9Sstevel@tonic-gate  *                        indentation than those specified above.
11288*7c478bd9Sstevel@tonic-gate  *  string  const char *  The string to be written.
11289*7c478bd9Sstevel@tonic-gate  * Output:
11290*7c478bd9Sstevel@tonic-gate  *  return         int    On error -1 is returned. Otherwise the
11291*7c478bd9Sstevel@tonic-gate  *                        return value is the terminal column index at
11292*7c478bd9Sstevel@tonic-gate  *                        which the cursor was left after writing the
11293*7c478bd9Sstevel@tonic-gate  *                        final word in the string. Successful return
11294*7c478bd9Sstevel@tonic-gate  *                        values can thus be passed verbatim to the
11295*7c478bd9Sstevel@tonic-gate  *                        'start' arguments of subsequent calls to
11296*7c478bd9Sstevel@tonic-gate  *                        gl_display_text() to allow the printing of a
11297*7c478bd9Sstevel@tonic-gate  *                        paragraph to be broken across multiple calls
11298*7c478bd9Sstevel@tonic-gate  *                        to gl_display_text().
11299*7c478bd9Sstevel@tonic-gate  */
11300*7c478bd9Sstevel@tonic-gate int gl_display_text(GetLine *gl, int indentation, const char *prefix,
11301*7c478bd9Sstevel@tonic-gate 		    const char *suffix, int fill_char,
11302*7c478bd9Sstevel@tonic-gate 		    int def_width, int start, const char *string)
11303*7c478bd9Sstevel@tonic-gate {
11304*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
11305*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_completion_action() */
11306*7c478bd9Sstevel@tonic-gate /*
11307*7c478bd9Sstevel@tonic-gate  * Check the arguments?
11308*7c478bd9Sstevel@tonic-gate  */
11309*7c478bd9Sstevel@tonic-gate   if(!gl || !string) {
11310*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
11311*7c478bd9Sstevel@tonic-gate     return -1;
11312*7c478bd9Sstevel@tonic-gate   };
11313*7c478bd9Sstevel@tonic-gate /*
11314*7c478bd9Sstevel@tonic-gate  * Block all signals.
11315*7c478bd9Sstevel@tonic-gate  */
11316*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
11317*7c478bd9Sstevel@tonic-gate     return -1;
11318*7c478bd9Sstevel@tonic-gate /*
11319*7c478bd9Sstevel@tonic-gate  * Display the text while signals are blocked.
11320*7c478bd9Sstevel@tonic-gate  */
11321*7c478bd9Sstevel@tonic-gate   status = _io_display_text(_io_write_stdio, gl->output_fp, indentation,
11322*7c478bd9Sstevel@tonic-gate 			    prefix, suffix, fill_char,
11323*7c478bd9Sstevel@tonic-gate 			    gl->ncolumn > 0 ? gl->ncolumn : def_width,
11324*7c478bd9Sstevel@tonic-gate 			    start, string);
11325*7c478bd9Sstevel@tonic-gate /*
11326*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
11327*7c478bd9Sstevel@tonic-gate  */
11328*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
11329*7c478bd9Sstevel@tonic-gate   return status;
11330*7c478bd9Sstevel@tonic-gate }
11331*7c478bd9Sstevel@tonic-gate 
11332*7c478bd9Sstevel@tonic-gate /*.......................................................................
11333*7c478bd9Sstevel@tonic-gate  * Block all of the signals that we are currently trapping.
11334*7c478bd9Sstevel@tonic-gate  *
11335*7c478bd9Sstevel@tonic-gate  * Input:
11336*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *   The resource object of gl_get_line().
11337*7c478bd9Sstevel@tonic-gate  * Input/Output:
11338*7c478bd9Sstevel@tonic-gate  *  oldset   sigset_t *   The superseded process signal mask
11339*7c478bd9Sstevel@tonic-gate  *                        will be return in *oldset unless oldset is
11340*7c478bd9Sstevel@tonic-gate  *                        NULL.
11341*7c478bd9Sstevel@tonic-gate  * Output:
11342*7c478bd9Sstevel@tonic-gate  *  return        int     0 - OK.
11343*7c478bd9Sstevel@tonic-gate  *                        1 - Error.
11344*7c478bd9Sstevel@tonic-gate  */
11345*7c478bd9Sstevel@tonic-gate static int gl_mask_signals(GetLine *gl, sigset_t *oldset)
11346*7c478bd9Sstevel@tonic-gate {
11347*7c478bd9Sstevel@tonic-gate /*
11348*7c478bd9Sstevel@tonic-gate  * Block all signals in all_signal_set, along with any others that are
11349*7c478bd9Sstevel@tonic-gate  * already blocked by the application.
11350*7c478bd9Sstevel@tonic-gate  */
11351*7c478bd9Sstevel@tonic-gate   if(sigprocmask(SIG_BLOCK, &gl->all_signal_set, oldset) >= 0) {
11352*7c478bd9Sstevel@tonic-gate     gl->signals_masked = 1;
11353*7c478bd9Sstevel@tonic-gate     return 0;
11354*7c478bd9Sstevel@tonic-gate   };
11355*7c478bd9Sstevel@tonic-gate /*
11356*7c478bd9Sstevel@tonic-gate  * On error attempt to query the current process signal mask, so
11357*7c478bd9Sstevel@tonic-gate  * that oldset be the correct process signal mask to restore later
11358*7c478bd9Sstevel@tonic-gate  * if the caller of this function ignores the error return value.
11359*7c478bd9Sstevel@tonic-gate  */
11360*7c478bd9Sstevel@tonic-gate   if(oldset)
11361*7c478bd9Sstevel@tonic-gate     (void) sigprocmask(SIG_SETMASK, NULL, oldset);
11362*7c478bd9Sstevel@tonic-gate   gl->signals_masked = 0;
11363*7c478bd9Sstevel@tonic-gate   return 1;
11364*7c478bd9Sstevel@tonic-gate }
11365*7c478bd9Sstevel@tonic-gate 
11366*7c478bd9Sstevel@tonic-gate /*.......................................................................
11367*7c478bd9Sstevel@tonic-gate  * Restore a process signal mask that was previously returned via the
11368*7c478bd9Sstevel@tonic-gate  * oldset argument of gl_mask_signals().
11369*7c478bd9Sstevel@tonic-gate  *
11370*7c478bd9Sstevel@tonic-gate  * Input:
11371*7c478bd9Sstevel@tonic-gate  *  gl        GetLine *   The resource object of gl_get_line().
11372*7c478bd9Sstevel@tonic-gate  * Input/Output:
11373*7c478bd9Sstevel@tonic-gate  *  oldset   sigset_t *   The process signal mask to be restored.
11374*7c478bd9Sstevel@tonic-gate  * Output:
11375*7c478bd9Sstevel@tonic-gate  *  return        int     0 - OK.
11376*7c478bd9Sstevel@tonic-gate  *                        1 - Error.
11377*7c478bd9Sstevel@tonic-gate  */
11378*7c478bd9Sstevel@tonic-gate static int gl_unmask_signals(GetLine *gl, sigset_t *oldset)
11379*7c478bd9Sstevel@tonic-gate {
11380*7c478bd9Sstevel@tonic-gate   gl->signals_masked = 0;
11381*7c478bd9Sstevel@tonic-gate   return sigprocmask(SIG_SETMASK, oldset, NULL) < 0;
11382*7c478bd9Sstevel@tonic-gate }
11383*7c478bd9Sstevel@tonic-gate 
11384*7c478bd9Sstevel@tonic-gate /*.......................................................................
11385*7c478bd9Sstevel@tonic-gate  * Arrange to temporarily catch the signals marked in gl->use_signal_set.
11386*7c478bd9Sstevel@tonic-gate  *
11387*7c478bd9Sstevel@tonic-gate  * Input:
11388*7c478bd9Sstevel@tonic-gate  *  gl        GetLine *   The resource object of gl_get_line().
11389*7c478bd9Sstevel@tonic-gate  * Output:
11390*7c478bd9Sstevel@tonic-gate  *  return        int     0 - OK.
11391*7c478bd9Sstevel@tonic-gate  *                        1 - Error.
11392*7c478bd9Sstevel@tonic-gate  */
11393*7c478bd9Sstevel@tonic-gate static int gl_catch_signals(GetLine *gl)
11394*7c478bd9Sstevel@tonic-gate {
11395*7c478bd9Sstevel@tonic-gate   return sigprocmask(SIG_UNBLOCK, &gl->use_signal_set, NULL) < 0;
11396*7c478bd9Sstevel@tonic-gate }
11397*7c478bd9Sstevel@tonic-gate 
11398*7c478bd9Sstevel@tonic-gate /*.......................................................................
11399*7c478bd9Sstevel@tonic-gate  * Select the I/O mode to be used by gl_get_line().
11400*7c478bd9Sstevel@tonic-gate  *
11401*7c478bd9Sstevel@tonic-gate  * Input:
11402*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
11403*7c478bd9Sstevel@tonic-gate  *  mode      GlIOMode    The I/O mode to establish.
11404*7c478bd9Sstevel@tonic-gate  * Output:
11405*7c478bd9Sstevel@tonic-gate  *  return         int    0 - OK.
11406*7c478bd9Sstevel@tonic-gate  *                        1 - Error.
11407*7c478bd9Sstevel@tonic-gate  */
11408*7c478bd9Sstevel@tonic-gate int gl_io_mode(GetLine *gl, GlIOMode mode)
11409*7c478bd9Sstevel@tonic-gate {
11410*7c478bd9Sstevel@tonic-gate   sigset_t oldset; /* The signals that were blocked on entry to this function */
11411*7c478bd9Sstevel@tonic-gate   int status;      /* The return status of _gl_io_mode() */
11412*7c478bd9Sstevel@tonic-gate /*
11413*7c478bd9Sstevel@tonic-gate  * Check the arguments.
11414*7c478bd9Sstevel@tonic-gate  */
11415*7c478bd9Sstevel@tonic-gate   if(!gl) {
11416*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
11417*7c478bd9Sstevel@tonic-gate     return 1;
11418*7c478bd9Sstevel@tonic-gate   };
11419*7c478bd9Sstevel@tonic-gate /*
11420*7c478bd9Sstevel@tonic-gate  * Check that the requested mode is known.
11421*7c478bd9Sstevel@tonic-gate  */
11422*7c478bd9Sstevel@tonic-gate   switch(mode) {
11423*7c478bd9Sstevel@tonic-gate   case GL_NORMAL_MODE:
11424*7c478bd9Sstevel@tonic-gate   case GL_SERVER_MODE:
11425*7c478bd9Sstevel@tonic-gate     break;
11426*7c478bd9Sstevel@tonic-gate   default:
11427*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
11428*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "Unknown gl_get_line() I/O mode requested.",
11429*7c478bd9Sstevel@tonic-gate 		    END_ERR_MSG);
11430*7c478bd9Sstevel@tonic-gate     return 1;
11431*7c478bd9Sstevel@tonic-gate   };
11432*7c478bd9Sstevel@tonic-gate /*
11433*7c478bd9Sstevel@tonic-gate  * Block all signals.
11434*7c478bd9Sstevel@tonic-gate  */
11435*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
11436*7c478bd9Sstevel@tonic-gate     return 1;
11437*7c478bd9Sstevel@tonic-gate /*
11438*7c478bd9Sstevel@tonic-gate  * Invoke the private body of this function.
11439*7c478bd9Sstevel@tonic-gate  */
11440*7c478bd9Sstevel@tonic-gate   status = _gl_io_mode(gl, mode);
11441*7c478bd9Sstevel@tonic-gate /*
11442*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
11443*7c478bd9Sstevel@tonic-gate  */
11444*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
11445*7c478bd9Sstevel@tonic-gate   return status;
11446*7c478bd9Sstevel@tonic-gate }
11447*7c478bd9Sstevel@tonic-gate 
11448*7c478bd9Sstevel@tonic-gate /*.......................................................................
11449*7c478bd9Sstevel@tonic-gate  * This is the private body of the public function, gl_io_mode().
11450*7c478bd9Sstevel@tonic-gate  * It assumes that the caller has checked its arguments and blocked the
11451*7c478bd9Sstevel@tonic-gate  * delivery of signals.
11452*7c478bd9Sstevel@tonic-gate  */
11453*7c478bd9Sstevel@tonic-gate static int _gl_io_mode(GetLine *gl, GlIOMode mode)
11454*7c478bd9Sstevel@tonic-gate {
11455*7c478bd9Sstevel@tonic-gate /*
11456*7c478bd9Sstevel@tonic-gate  * Are we already in the specified mode?
11457*7c478bd9Sstevel@tonic-gate  */
11458*7c478bd9Sstevel@tonic-gate   if(mode == gl->io_mode)
11459*7c478bd9Sstevel@tonic-gate     return 0;
11460*7c478bd9Sstevel@tonic-gate /*
11461*7c478bd9Sstevel@tonic-gate  * First revert to normal I/O in the current I/O mode.
11462*7c478bd9Sstevel@tonic-gate  */
11463*7c478bd9Sstevel@tonic-gate   _gl_normal_io(gl);
11464*7c478bd9Sstevel@tonic-gate /*
11465*7c478bd9Sstevel@tonic-gate  * Record the new mode.
11466*7c478bd9Sstevel@tonic-gate  */
11467*7c478bd9Sstevel@tonic-gate   gl->io_mode = mode;
11468*7c478bd9Sstevel@tonic-gate /*
11469*7c478bd9Sstevel@tonic-gate  * Perform any actions needed by the new mode.
11470*7c478bd9Sstevel@tonic-gate  */
11471*7c478bd9Sstevel@tonic-gate   if(mode==GL_SERVER_MODE) {
11472*7c478bd9Sstevel@tonic-gate     if(_gl_raw_io(gl, 1))
11473*7c478bd9Sstevel@tonic-gate       return 1;
11474*7c478bd9Sstevel@tonic-gate   };
11475*7c478bd9Sstevel@tonic-gate   return 0;
11476*7c478bd9Sstevel@tonic-gate }
11477*7c478bd9Sstevel@tonic-gate 
11478*7c478bd9Sstevel@tonic-gate /*.......................................................................
11479*7c478bd9Sstevel@tonic-gate  * Return extra information (ie. in addition to that provided by errno)
11480*7c478bd9Sstevel@tonic-gate  * about the last error to occur in either gl_get_line() or its
11481*7c478bd9Sstevel@tonic-gate  * associated public functions.
11482*7c478bd9Sstevel@tonic-gate  *
11483*7c478bd9Sstevel@tonic-gate  * Input:
11484*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
11485*7c478bd9Sstevel@tonic-gate  * Input/Output:
11486*7c478bd9Sstevel@tonic-gate  *  buff          char *  An optional output buffer. Note that if the
11487*7c478bd9Sstevel@tonic-gate  *                        calling application calls any gl_*()
11488*7c478bd9Sstevel@tonic-gate  *                        functions from signal handlers, it should
11489*7c478bd9Sstevel@tonic-gate  *                        provide a buffer here, so that a copy of
11490*7c478bd9Sstevel@tonic-gate  *                        the latest error message can safely be made
11491*7c478bd9Sstevel@tonic-gate  *                        while signals are blocked.
11492*7c478bd9Sstevel@tonic-gate  *  n           size_t    The allocated size of buff[].
11493*7c478bd9Sstevel@tonic-gate  * Output:
11494*7c478bd9Sstevel@tonic-gate  *  return  const char *  A pointer to the error message. This will
11495*7c478bd9Sstevel@tonic-gate  *                        be the buff argument, unless buff==NULL, in
11496*7c478bd9Sstevel@tonic-gate  *                        which case it will be a pointer to an
11497*7c478bd9Sstevel@tonic-gate  *                        internal error buffer. In the latter case,
11498*7c478bd9Sstevel@tonic-gate  *                        note that the contents of the returned buffer
11499*7c478bd9Sstevel@tonic-gate  *                        will change on subsequent calls to any gl_*()
11500*7c478bd9Sstevel@tonic-gate  *                        functions.
11501*7c478bd9Sstevel@tonic-gate  */
11502*7c478bd9Sstevel@tonic-gate const char *gl_error_message(GetLine *gl, char *buff, size_t n)
11503*7c478bd9Sstevel@tonic-gate {
11504*7c478bd9Sstevel@tonic-gate   if(!gl) {
11505*7c478bd9Sstevel@tonic-gate     static const char *msg = "NULL GetLine argument";
11506*7c478bd9Sstevel@tonic-gate     if(buff) {
11507*7c478bd9Sstevel@tonic-gate       strncpy(buff, msg, n);
11508*7c478bd9Sstevel@tonic-gate       buff[n-1] = '\0';
11509*7c478bd9Sstevel@tonic-gate     } else {
11510*7c478bd9Sstevel@tonic-gate       return msg;
11511*7c478bd9Sstevel@tonic-gate     };
11512*7c478bd9Sstevel@tonic-gate   } else if(buff) {
11513*7c478bd9Sstevel@tonic-gate     sigset_t oldset; /* The signals that were blocked on entry to this block */
11514*7c478bd9Sstevel@tonic-gate /*
11515*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
11516*7c478bd9Sstevel@tonic-gate  */
11517*7c478bd9Sstevel@tonic-gate     gl_mask_signals(gl, &oldset);
11518*7c478bd9Sstevel@tonic-gate /*
11519*7c478bd9Sstevel@tonic-gate  * Copy the error message into the specified buffer.
11520*7c478bd9Sstevel@tonic-gate  */
11521*7c478bd9Sstevel@tonic-gate     if(buff && n > 0) {
11522*7c478bd9Sstevel@tonic-gate       strncpy(buff, _err_get_msg(gl->err), n);
11523*7c478bd9Sstevel@tonic-gate       buff[n-1] = '\0';
11524*7c478bd9Sstevel@tonic-gate     };
11525*7c478bd9Sstevel@tonic-gate /*
11526*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
11527*7c478bd9Sstevel@tonic-gate  */
11528*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &oldset);
11529*7c478bd9Sstevel@tonic-gate   } else {
11530*7c478bd9Sstevel@tonic-gate     return _err_get_msg(gl->err);
11531*7c478bd9Sstevel@tonic-gate   };
11532*7c478bd9Sstevel@tonic-gate   return buff;
11533*7c478bd9Sstevel@tonic-gate }
11534*7c478bd9Sstevel@tonic-gate 
11535*7c478bd9Sstevel@tonic-gate /*.......................................................................
11536*7c478bd9Sstevel@tonic-gate  * Return the signal mask used by gl_get_line(). This is the set of
11537*7c478bd9Sstevel@tonic-gate  * signals that gl_get_line() is currently configured to trap.
11538*7c478bd9Sstevel@tonic-gate  *
11539*7c478bd9Sstevel@tonic-gate  * Input:
11540*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *  The resource object of gl_get_line().
11541*7c478bd9Sstevel@tonic-gate  * Input/Output:
11542*7c478bd9Sstevel@tonic-gate  *  set       sigset_t *  The set of signals will be returned in *set,
11543*7c478bd9Sstevel@tonic-gate  *                        in the form of a signal process mask, as
11544*7c478bd9Sstevel@tonic-gate  *                        used by sigaction(), sigprocmask(),
11545*7c478bd9Sstevel@tonic-gate  *                        sigpending(), sigsuspend(), sigsetjmp() and
11546*7c478bd9Sstevel@tonic-gate  *                        other standard POSIX signal-aware
11547*7c478bd9Sstevel@tonic-gate  *                        functions.
11548*7c478bd9Sstevel@tonic-gate  * Output:
11549*7c478bd9Sstevel@tonic-gate  *  return         int    0 - OK.
11550*7c478bd9Sstevel@tonic-gate  *                        1 - Error (examine errno for reason).
11551*7c478bd9Sstevel@tonic-gate  */
11552*7c478bd9Sstevel@tonic-gate int gl_list_signals(GetLine *gl, sigset_t *set)
11553*7c478bd9Sstevel@tonic-gate {
11554*7c478bd9Sstevel@tonic-gate /*
11555*7c478bd9Sstevel@tonic-gate  * Check the arguments.
11556*7c478bd9Sstevel@tonic-gate  */
11557*7c478bd9Sstevel@tonic-gate   if(!gl || !set) {
11558*7c478bd9Sstevel@tonic-gate     if(gl)
11559*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG);
11560*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
11561*7c478bd9Sstevel@tonic-gate     return 1;
11562*7c478bd9Sstevel@tonic-gate   };
11563*7c478bd9Sstevel@tonic-gate /*
11564*7c478bd9Sstevel@tonic-gate  * Copy the signal mask into *set.
11565*7c478bd9Sstevel@tonic-gate  */
11566*7c478bd9Sstevel@tonic-gate   memcpy(set, &gl->all_signal_set, sizeof(*set));
11567*7c478bd9Sstevel@tonic-gate   return 0;
11568*7c478bd9Sstevel@tonic-gate }
11569*7c478bd9Sstevel@tonic-gate 
11570*7c478bd9Sstevel@tonic-gate /*.......................................................................
11571*7c478bd9Sstevel@tonic-gate  * By default, gl_get_line() doesn't trap signals that are blocked
11572*7c478bd9Sstevel@tonic-gate  * when it is called. This default can be changed either on a
11573*7c478bd9Sstevel@tonic-gate  * per-signal basis by calling gl_trap_signal(), or on a global basis
11574*7c478bd9Sstevel@tonic-gate  * by calling this function. What this function does is add the
11575*7c478bd9Sstevel@tonic-gate  * GLS_UNBLOCK_SIG flag to all signals that are currently configured
11576*7c478bd9Sstevel@tonic-gate  * to be trapped by gl_get_line(), such that when subsequent calls to
11577*7c478bd9Sstevel@tonic-gate  * gl_get_line() wait for I/O, these signals are temporarily
11578*7c478bd9Sstevel@tonic-gate  * unblocked. This behavior is useful in non-blocking server-I/O mode,
11579*7c478bd9Sstevel@tonic-gate  * where it is used to avoid race conditions related to handling these
11580*7c478bd9Sstevel@tonic-gate  * signals externally to gl_get_line(). See the demonstration code in
11581*7c478bd9Sstevel@tonic-gate  * demo3.c, or the gl_handle_signal() man page for further
11582*7c478bd9Sstevel@tonic-gate  * information.
11583*7c478bd9Sstevel@tonic-gate  *
11584*7c478bd9Sstevel@tonic-gate  * Input:
11585*7c478bd9Sstevel@tonic-gate  *  gl         GetLine *   The resource object of gl_get_line().
11586*7c478bd9Sstevel@tonic-gate  */
11587*7c478bd9Sstevel@tonic-gate void gl_catch_blocked(GetLine *gl)
11588*7c478bd9Sstevel@tonic-gate {
11589*7c478bd9Sstevel@tonic-gate   sigset_t oldset;    /* The process signal mask to restore */
11590*7c478bd9Sstevel@tonic-gate   GlSignalNode *sig;  /* A signal node in gl->sigs */
11591*7c478bd9Sstevel@tonic-gate /*
11592*7c478bd9Sstevel@tonic-gate  * Check the arguments.
11593*7c478bd9Sstevel@tonic-gate  */
11594*7c478bd9Sstevel@tonic-gate   if(!gl) {
11595*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
11596*7c478bd9Sstevel@tonic-gate     return;
11597*7c478bd9Sstevel@tonic-gate   };
11598*7c478bd9Sstevel@tonic-gate /*
11599*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals while we modify the contents of gl.
11600*7c478bd9Sstevel@tonic-gate  */
11601*7c478bd9Sstevel@tonic-gate   gl_mask_signals(gl, &oldset);
11602*7c478bd9Sstevel@tonic-gate /*
11603*7c478bd9Sstevel@tonic-gate  * Add the GLS_UNBLOCK_SIG flag to all configured signals.
11604*7c478bd9Sstevel@tonic-gate  */
11605*7c478bd9Sstevel@tonic-gate   for(sig=gl->sigs; sig; sig=sig->next)
11606*7c478bd9Sstevel@tonic-gate     sig->flags |= GLS_UNBLOCK_SIG;
11607*7c478bd9Sstevel@tonic-gate /*
11608*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask that was superseded by the call
11609*7c478bd9Sstevel@tonic-gate  * to gl_mask_signals().
11610*7c478bd9Sstevel@tonic-gate  */
11611*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
11612*7c478bd9Sstevel@tonic-gate   return;
11613*7c478bd9Sstevel@tonic-gate }
11614*7c478bd9Sstevel@tonic-gate 
11615*7c478bd9Sstevel@tonic-gate /*.......................................................................
11616*7c478bd9Sstevel@tonic-gate  * Respond to signals who's default effects have important
11617*7c478bd9Sstevel@tonic-gate  * consequences to gl_get_line(). This is intended for use in
11618*7c478bd9Sstevel@tonic-gate  * non-blocking server mode, where the external event loop is
11619*7c478bd9Sstevel@tonic-gate  * responsible for catching signals. Signals that are handled include
11620*7c478bd9Sstevel@tonic-gate  * those that by default terminate or suspend the process, and the
11621*7c478bd9Sstevel@tonic-gate  * signal that indicates that the terminal size has changed. Note that
11622*7c478bd9Sstevel@tonic-gate  * this function is not signal safe and should thus not be called from
11623*7c478bd9Sstevel@tonic-gate  * a signal handler itself. See the gl_io_mode() man page for how it
11624*7c478bd9Sstevel@tonic-gate  * should be used.
11625*7c478bd9Sstevel@tonic-gate  *
11626*7c478bd9Sstevel@tonic-gate  * In the case of signals that by default terminate or suspend
11627*7c478bd9Sstevel@tonic-gate  * processes, command-line editing will be suspended, the terminal
11628*7c478bd9Sstevel@tonic-gate  * returned to a usable state, then the default disposition of the
11629*7c478bd9Sstevel@tonic-gate  * signal restored and the signal resent, in order to suspend or
11630*7c478bd9Sstevel@tonic-gate  * terminate the process.  If the process subsequently resumes,
11631*7c478bd9Sstevel@tonic-gate  * command-line editing is resumed.
11632*7c478bd9Sstevel@tonic-gate  *
11633*7c478bd9Sstevel@tonic-gate  * In the case of signals that indicate that the terminal has been
11634*7c478bd9Sstevel@tonic-gate  * resized, the new size will be queried, and any input line that is
11635*7c478bd9Sstevel@tonic-gate  * being edited will be redrawn to fit the new dimensions of the
11636*7c478bd9Sstevel@tonic-gate  * terminal.
11637*7c478bd9Sstevel@tonic-gate  *
11638*7c478bd9Sstevel@tonic-gate  * Input:
11639*7c478bd9Sstevel@tonic-gate  *  signo    int    The number of the signal to respond to.
11640*7c478bd9Sstevel@tonic-gate  *  gl   GetLine *  The first element of an array of 'ngl' GetLine
11641*7c478bd9Sstevel@tonic-gate  *                  objects.
11642*7c478bd9Sstevel@tonic-gate  *  ngl      int    The number of elements in the gl[] array. Normally
11643*7c478bd9Sstevel@tonic-gate  *                  this will be one.
11644*7c478bd9Sstevel@tonic-gate  */
11645*7c478bd9Sstevel@tonic-gate void gl_handle_signal(int signo, GetLine *gl, int ngl)
11646*7c478bd9Sstevel@tonic-gate {
11647*7c478bd9Sstevel@tonic-gate   int attr;             /* The attributes of the specified signal */
11648*7c478bd9Sstevel@tonic-gate   sigset_t all_signals; /* The set of trappable signals */
11649*7c478bd9Sstevel@tonic-gate   sigset_t oldset;      /* The process signal mask to restore */
11650*7c478bd9Sstevel@tonic-gate   int i;
11651*7c478bd9Sstevel@tonic-gate /*
11652*7c478bd9Sstevel@tonic-gate  * NULL operation?
11653*7c478bd9Sstevel@tonic-gate  */
11654*7c478bd9Sstevel@tonic-gate   if(ngl < 1 || !gl)
11655*7c478bd9Sstevel@tonic-gate     return;
11656*7c478bd9Sstevel@tonic-gate /*
11657*7c478bd9Sstevel@tonic-gate  * Look up the default attributes of the specified signal.
11658*7c478bd9Sstevel@tonic-gate  */
11659*7c478bd9Sstevel@tonic-gate   attr = gl_classify_signal(signo);
11660*7c478bd9Sstevel@tonic-gate /*
11661*7c478bd9Sstevel@tonic-gate  * If the signal isn't known, we are done.
11662*7c478bd9Sstevel@tonic-gate  */
11663*7c478bd9Sstevel@tonic-gate   if(!attr)
11664*7c478bd9Sstevel@tonic-gate     return;
11665*7c478bd9Sstevel@tonic-gate /*
11666*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals while we modify the gl objects.
11667*7c478bd9Sstevel@tonic-gate  */
11668*7c478bd9Sstevel@tonic-gate   gl_list_trappable_signals(&all_signals);
11669*7c478bd9Sstevel@tonic-gate   sigprocmask(SIG_BLOCK, &all_signals, &oldset);
11670*7c478bd9Sstevel@tonic-gate /*
11671*7c478bd9Sstevel@tonic-gate  * Suspend or terminate the process?
11672*7c478bd9Sstevel@tonic-gate  */
11673*7c478bd9Sstevel@tonic-gate   if(attr & (GLSA_SUSP | GLSA_TERM)) {
11674*7c478bd9Sstevel@tonic-gate     gl_suspend_process(signo, gl, ngl);
11675*7c478bd9Sstevel@tonic-gate /*
11676*7c478bd9Sstevel@tonic-gate  * Resize the terminal? Note that ioctl() isn't defined as being
11677*7c478bd9Sstevel@tonic-gate  * signal safe, so we can't call gl_update_size() here. However,
11678*7c478bd9Sstevel@tonic-gate  * gl_get_line() checks for resizes on each call, so simply arrange
11679*7c478bd9Sstevel@tonic-gate  * for the application's event loop to call gl_get_line() as soon as
11680*7c478bd9Sstevel@tonic-gate  * it becomes possible to write to the terminal. Note that if the
11681*7c478bd9Sstevel@tonic-gate  * caller is calling select() or poll when this happens, these functions
11682*7c478bd9Sstevel@tonic-gate  * get interrupted, since a signal has been caught.
11683*7c478bd9Sstevel@tonic-gate  */
11684*7c478bd9Sstevel@tonic-gate   } else if(attr & GLSA_SIZE) {
11685*7c478bd9Sstevel@tonic-gate     for(i=0; i<ngl; i++)
11686*7c478bd9Sstevel@tonic-gate       gl[i].pending_io = GLP_WRITE;
11687*7c478bd9Sstevel@tonic-gate   };
11688*7c478bd9Sstevel@tonic-gate /*
11689*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask that was superseded by the call
11690*7c478bd9Sstevel@tonic-gate  * to gl_mask_signals().
11691*7c478bd9Sstevel@tonic-gate  */
11692*7c478bd9Sstevel@tonic-gate   sigprocmask(SIG_SETMASK, &oldset, NULL);
11693*7c478bd9Sstevel@tonic-gate   return;
11694*7c478bd9Sstevel@tonic-gate }
11695*7c478bd9Sstevel@tonic-gate 
11696*7c478bd9Sstevel@tonic-gate /*.......................................................................
11697*7c478bd9Sstevel@tonic-gate  * Respond to an externally caught process suspension or
11698*7c478bd9Sstevel@tonic-gate  * termination signal.
11699*7c478bd9Sstevel@tonic-gate  *
11700*7c478bd9Sstevel@tonic-gate  * After restoring the terminal to a usable state, suspend or
11701*7c478bd9Sstevel@tonic-gate  * terminate the calling process, using the original signal with its
11702*7c478bd9Sstevel@tonic-gate  * default disposition restored to do so. If the process subsequently
11703*7c478bd9Sstevel@tonic-gate  * resumes, resume editing any input lines that were being entered.
11704*7c478bd9Sstevel@tonic-gate  *
11705*7c478bd9Sstevel@tonic-gate  * Input:
11706*7c478bd9Sstevel@tonic-gate  *  signo    int    The signal number to suspend the process with. Note
11707*7c478bd9Sstevel@tonic-gate  *                  that the default disposition of this signal will be
11708*7c478bd9Sstevel@tonic-gate  *                  restored before the signal is sent, so provided
11709*7c478bd9Sstevel@tonic-gate  *                  that the default disposition of this signal is to
11710*7c478bd9Sstevel@tonic-gate  *                  either suspend or terminate the application,
11711*7c478bd9Sstevel@tonic-gate  *                  that is what wil happen, regardless of what signal
11712*7c478bd9Sstevel@tonic-gate  *                  handler is currently assigned to this signal.
11713*7c478bd9Sstevel@tonic-gate  *  gl   GetLine *  The first element of an array of 'ngl' GetLine objects
11714*7c478bd9Sstevel@tonic-gate  *                  whose terminals should be restored to a sane state
11715*7c478bd9Sstevel@tonic-gate  *                  while the application is suspended.
11716*7c478bd9Sstevel@tonic-gate  *  ngl      int    The number of elements in the gl[] array.
11717*7c478bd9Sstevel@tonic-gate  */
11718*7c478bd9Sstevel@tonic-gate static void gl_suspend_process(int signo, GetLine *gl, int ngl)
11719*7c478bd9Sstevel@tonic-gate {
11720*7c478bd9Sstevel@tonic-gate   sigset_t only_signo;          /* A signal set containing just signo */
11721*7c478bd9Sstevel@tonic-gate   sigset_t oldset;              /* The signal mask on entry to this function */
11722*7c478bd9Sstevel@tonic-gate   sigset_t all_signals;         /* A signal set containing all signals */
11723*7c478bd9Sstevel@tonic-gate   struct sigaction old_action;  /* The current signal handler */
11724*7c478bd9Sstevel@tonic-gate   struct sigaction def_action;  /* The default signal handler */
11725*7c478bd9Sstevel@tonic-gate   int i;
11726*7c478bd9Sstevel@tonic-gate /*
11727*7c478bd9Sstevel@tonic-gate  * Create a signal mask containing the signal that was trapped.
11728*7c478bd9Sstevel@tonic-gate  */
11729*7c478bd9Sstevel@tonic-gate   sigemptyset(&only_signo);
11730*7c478bd9Sstevel@tonic-gate   sigaddset(&only_signo, signo);
11731*7c478bd9Sstevel@tonic-gate /*
11732*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals.
11733*7c478bd9Sstevel@tonic-gate  */
11734*7c478bd9Sstevel@tonic-gate   gl_list_trappable_signals(&all_signals);
11735*7c478bd9Sstevel@tonic-gate   sigprocmask(SIG_BLOCK, &all_signals, &oldset);
11736*7c478bd9Sstevel@tonic-gate /*
11737*7c478bd9Sstevel@tonic-gate  * Restore the terminal to a usable state.
11738*7c478bd9Sstevel@tonic-gate  */
11739*7c478bd9Sstevel@tonic-gate   for(i=0; i<ngl; i++) {
11740*7c478bd9Sstevel@tonic-gate     GetLine *obj = gl + i;
11741*7c478bd9Sstevel@tonic-gate     if(obj->raw_mode) {
11742*7c478bd9Sstevel@tonic-gate       _gl_normal_io(obj);
11743*7c478bd9Sstevel@tonic-gate       if(!obj->raw_mode)        /* Check that gl_normal_io() succeded */
11744*7c478bd9Sstevel@tonic-gate 	obj->raw_mode = -1;     /* Flag raw mode as needing to be restored */
11745*7c478bd9Sstevel@tonic-gate     };
11746*7c478bd9Sstevel@tonic-gate   };
11747*7c478bd9Sstevel@tonic-gate /*
11748*7c478bd9Sstevel@tonic-gate  * Restore the system default disposition of the signal that we
11749*7c478bd9Sstevel@tonic-gate  * caught.  Note that this signal is currently blocked. Note that we
11750*7c478bd9Sstevel@tonic-gate  * don't use memcpy() to copy signal sets here, because the signal safety
11751*7c478bd9Sstevel@tonic-gate  * of memcpy() is undefined.
11752*7c478bd9Sstevel@tonic-gate  */
11753*7c478bd9Sstevel@tonic-gate   def_action.sa_handler = SIG_DFL;
11754*7c478bd9Sstevel@tonic-gate   {
11755*7c478bd9Sstevel@tonic-gate     char *orig = (char *) &all_signals;
11756*7c478bd9Sstevel@tonic-gate     char *dest = (char *) &def_action.sa_mask;
11757*7c478bd9Sstevel@tonic-gate     for(i=0; i<sizeof(sigset_t); i++)
11758*7c478bd9Sstevel@tonic-gate       *dest++ = *orig++;
11759*7c478bd9Sstevel@tonic-gate   };
11760*7c478bd9Sstevel@tonic-gate   sigaction(signo, &def_action, &old_action);
11761*7c478bd9Sstevel@tonic-gate /*
11762*7c478bd9Sstevel@tonic-gate  * Resend the signal, and unblock it so that it gets delivered to
11763*7c478bd9Sstevel@tonic-gate  * the application. This will invoke the default action of this signal.
11764*7c478bd9Sstevel@tonic-gate  */
11765*7c478bd9Sstevel@tonic-gate   raise(signo);
11766*7c478bd9Sstevel@tonic-gate   sigprocmask(SIG_UNBLOCK, &only_signo, NULL);
11767*7c478bd9Sstevel@tonic-gate /*
11768*7c478bd9Sstevel@tonic-gate  * If the process resumes again, it will resume here.
11769*7c478bd9Sstevel@tonic-gate  * Block the signal again, then restore our signal handler.
11770*7c478bd9Sstevel@tonic-gate  */
11771*7c478bd9Sstevel@tonic-gate   sigprocmask(SIG_BLOCK, &only_signo, NULL);
11772*7c478bd9Sstevel@tonic-gate   sigaction(signo, &old_action, NULL);
11773*7c478bd9Sstevel@tonic-gate /*
11774*7c478bd9Sstevel@tonic-gate  * Resume command-line editing.
11775*7c478bd9Sstevel@tonic-gate  */
11776*7c478bd9Sstevel@tonic-gate   for(i=0; i<ngl; i++) {
11777*7c478bd9Sstevel@tonic-gate     GetLine *obj = gl + i;
11778*7c478bd9Sstevel@tonic-gate     if(obj->raw_mode == -1) { /* Did we flag the need to restore raw mode? */
11779*7c478bd9Sstevel@tonic-gate       obj->raw_mode = 0;      /* gl_raw_io() does nothing unless raw_mode==0 */
11780*7c478bd9Sstevel@tonic-gate       _gl_raw_io(obj, 1);
11781*7c478bd9Sstevel@tonic-gate     };
11782*7c478bd9Sstevel@tonic-gate   };
11783*7c478bd9Sstevel@tonic-gate /*
11784*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask to the way it was when this function
11785*7c478bd9Sstevel@tonic-gate  * was called.
11786*7c478bd9Sstevel@tonic-gate  */
11787*7c478bd9Sstevel@tonic-gate   sigprocmask(SIG_SETMASK, &oldset, NULL);
11788*7c478bd9Sstevel@tonic-gate   return;
11789*7c478bd9Sstevel@tonic-gate }
11790*7c478bd9Sstevel@tonic-gate 
11791*7c478bd9Sstevel@tonic-gate /*.......................................................................
11792*7c478bd9Sstevel@tonic-gate  * Return the information about the default attributes of a given signal.
11793*7c478bd9Sstevel@tonic-gate  * The attributes that are returned are as defined by the standards that
11794*7c478bd9Sstevel@tonic-gate  * created them, including POSIX, SVR4 and 4.3+BSD, and are taken from a
11795*7c478bd9Sstevel@tonic-gate  * table in Richard Steven's book, "Advanced programming in the UNIX
11796*7c478bd9Sstevel@tonic-gate  * environment".
11797*7c478bd9Sstevel@tonic-gate  *
11798*7c478bd9Sstevel@tonic-gate  * Input:
11799*7c478bd9Sstevel@tonic-gate  *  signo        int   The signal to be characterized.
11800*7c478bd9Sstevel@tonic-gate  * Output:
11801*7c478bd9Sstevel@tonic-gate  *  return       int   A bitwise union of GlSigAttr enumerators, or 0
11802*7c478bd9Sstevel@tonic-gate  *                     if the signal isn't known.
11803*7c478bd9Sstevel@tonic-gate  */
11804*7c478bd9Sstevel@tonic-gate static int gl_classify_signal(int signo)
11805*7c478bd9Sstevel@tonic-gate {
11806*7c478bd9Sstevel@tonic-gate   int i;
11807*7c478bd9Sstevel@tonic-gate /*
11808*7c478bd9Sstevel@tonic-gate  * Search for the specified signal in the gl_signal_list[] table.
11809*7c478bd9Sstevel@tonic-gate  */
11810*7c478bd9Sstevel@tonic-gate   for(i=0; i<sizeof(gl_signal_list)/sizeof(gl_signal_list[0]); i++) {
11811*7c478bd9Sstevel@tonic-gate     const struct GlDefSignal *sig = gl_signal_list + i;
11812*7c478bd9Sstevel@tonic-gate     if(sig->signo == signo)
11813*7c478bd9Sstevel@tonic-gate       return sig->attr;
11814*7c478bd9Sstevel@tonic-gate   };
11815*7c478bd9Sstevel@tonic-gate /*
11816*7c478bd9Sstevel@tonic-gate  * Signal not known.
11817*7c478bd9Sstevel@tonic-gate  */
11818*7c478bd9Sstevel@tonic-gate   return 0;
11819*7c478bd9Sstevel@tonic-gate }
11820*7c478bd9Sstevel@tonic-gate 
11821*7c478bd9Sstevel@tonic-gate /*.......................................................................
11822*7c478bd9Sstevel@tonic-gate  * When in non-blocking server mode, this function can be used to abandon
11823*7c478bd9Sstevel@tonic-gate  * the current incompletely entered input line, and prepare to start
11824*7c478bd9Sstevel@tonic-gate  * editing a new line on the next call to gl_get_line().
11825*7c478bd9Sstevel@tonic-gate  *
11826*7c478bd9Sstevel@tonic-gate  * Input:
11827*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *  The line editor resource object.
11828*7c478bd9Sstevel@tonic-gate  */
11829*7c478bd9Sstevel@tonic-gate void gl_abandon_line(GetLine *gl)
11830*7c478bd9Sstevel@tonic-gate {
11831*7c478bd9Sstevel@tonic-gate   sigset_t oldset;    /* The process signal mask to restore */
11832*7c478bd9Sstevel@tonic-gate /*
11833*7c478bd9Sstevel@tonic-gate  * Check the arguments.
11834*7c478bd9Sstevel@tonic-gate  */
11835*7c478bd9Sstevel@tonic-gate   if(!gl) {
11836*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
11837*7c478bd9Sstevel@tonic-gate     return;
11838*7c478bd9Sstevel@tonic-gate   };
11839*7c478bd9Sstevel@tonic-gate /*
11840*7c478bd9Sstevel@tonic-gate  * Temporarily block all signals while we modify the contents of gl.
11841*7c478bd9Sstevel@tonic-gate  */
11842*7c478bd9Sstevel@tonic-gate   gl_mask_signals(gl, &oldset);
11843*7c478bd9Sstevel@tonic-gate /*
11844*7c478bd9Sstevel@tonic-gate  * Mark the input line as discarded.
11845*7c478bd9Sstevel@tonic-gate  */
11846*7c478bd9Sstevel@tonic-gate   _gl_abandon_line(gl);
11847*7c478bd9Sstevel@tonic-gate /*
11848*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask that was superseded by the call
11849*7c478bd9Sstevel@tonic-gate  * to gl_mask_signals().
11850*7c478bd9Sstevel@tonic-gate  */
11851*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
11852*7c478bd9Sstevel@tonic-gate   return;
11853*7c478bd9Sstevel@tonic-gate }
11854*7c478bd9Sstevel@tonic-gate 
11855*7c478bd9Sstevel@tonic-gate /*.......................................................................
11856*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_abandon_line() function. It
11857*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
11858*7c478bd9Sstevel@tonic-gate  * delivery of signals.
11859*7c478bd9Sstevel@tonic-gate  */
11860*7c478bd9Sstevel@tonic-gate void _gl_abandon_line(GetLine *gl)
11861*7c478bd9Sstevel@tonic-gate {
11862*7c478bd9Sstevel@tonic-gate   gl->endline = 1;
11863*7c478bd9Sstevel@tonic-gate   gl->pending_io = GLP_WRITE;
11864*7c478bd9Sstevel@tonic-gate }
11865*7c478bd9Sstevel@tonic-gate 
11866*7c478bd9Sstevel@tonic-gate /*.......................................................................
11867*7c478bd9Sstevel@tonic-gate  * How many characters are needed to write a number as an octal string?
11868*7c478bd9Sstevel@tonic-gate  *
11869*7c478bd9Sstevel@tonic-gate  * Input:
11870*7c478bd9Sstevel@tonic-gate  *  num   unsigned   The to be measured.
11871*7c478bd9Sstevel@tonic-gate  * Output:
11872*7c478bd9Sstevel@tonic-gate  *  return     int   The number of characters needed.
11873*7c478bd9Sstevel@tonic-gate  */
11874*7c478bd9Sstevel@tonic-gate static int gl_octal_width(unsigned num)
11875*7c478bd9Sstevel@tonic-gate {
11876*7c478bd9Sstevel@tonic-gate   int n;    /* The number of characters needed to render the number */
11877*7c478bd9Sstevel@tonic-gate   for(n=1; num /= 8; n++)
11878*7c478bd9Sstevel@tonic-gate     ;
11879*7c478bd9Sstevel@tonic-gate   return n;
11880*7c478bd9Sstevel@tonic-gate }
11881*7c478bd9Sstevel@tonic-gate 
11882*7c478bd9Sstevel@tonic-gate /*.......................................................................
11883*7c478bd9Sstevel@tonic-gate  * Tell gl_get_line() the current terminal size. Note that this is only
11884*7c478bd9Sstevel@tonic-gate  * necessary on systems where changes in terminal size aren't reported
11885*7c478bd9Sstevel@tonic-gate  * via SIGWINCH.
11886*7c478bd9Sstevel@tonic-gate  *
11887*7c478bd9Sstevel@tonic-gate  * Input:
11888*7c478bd9Sstevel@tonic-gate  *  gl            GetLine *  The resource object of gl_get_line().
11889*7c478bd9Sstevel@tonic-gate  *  ncolumn           int    The number of columns in the terminal.
11890*7c478bd9Sstevel@tonic-gate  *  nline             int    The number of lines in the terminal.
11891*7c478bd9Sstevel@tonic-gate  * Output:
11892*7c478bd9Sstevel@tonic-gate  *  return            int    0 - OK.
11893*7c478bd9Sstevel@tonic-gate  *                           1 - Error.
11894*7c478bd9Sstevel@tonic-gate  */
11895*7c478bd9Sstevel@tonic-gate int gl_set_term_size(GetLine *gl, int ncolumn, int nline)
11896*7c478bd9Sstevel@tonic-gate {
11897*7c478bd9Sstevel@tonic-gate   sigset_t oldset;      /* The signals that were blocked on entry */
11898*7c478bd9Sstevel@tonic-gate                         /*  to this function */
11899*7c478bd9Sstevel@tonic-gate   int status;           /* The return status */
11900*7c478bd9Sstevel@tonic-gate /*
11901*7c478bd9Sstevel@tonic-gate  * Block all signals while accessing gl.
11902*7c478bd9Sstevel@tonic-gate  */
11903*7c478bd9Sstevel@tonic-gate   gl_mask_signals(gl, &oldset);
11904*7c478bd9Sstevel@tonic-gate /*
11905*7c478bd9Sstevel@tonic-gate  * Install the new terminal size.
11906*7c478bd9Sstevel@tonic-gate  */
11907*7c478bd9Sstevel@tonic-gate   status = _gl_set_term_size(gl, ncolumn, nline);
11908*7c478bd9Sstevel@tonic-gate /*
11909*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
11910*7c478bd9Sstevel@tonic-gate  */
11911*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
11912*7c478bd9Sstevel@tonic-gate   return status;
11913*7c478bd9Sstevel@tonic-gate }
11914*7c478bd9Sstevel@tonic-gate 
11915*7c478bd9Sstevel@tonic-gate /*.......................................................................
11916*7c478bd9Sstevel@tonic-gate  * This is the private body of the gl_set_term_size() function. It
11917*7c478bd9Sstevel@tonic-gate  * assumes that the caller has checked its arguments and blocked the
11918*7c478bd9Sstevel@tonic-gate  * delivery of signals.
11919*7c478bd9Sstevel@tonic-gate  */
11920*7c478bd9Sstevel@tonic-gate static int _gl_set_term_size(GetLine *gl, int ncolumn, int nline)
11921*7c478bd9Sstevel@tonic-gate {
11922*7c478bd9Sstevel@tonic-gate /*
11923*7c478bd9Sstevel@tonic-gate  * Check the arguments.
11924*7c478bd9Sstevel@tonic-gate  */
11925*7c478bd9Sstevel@tonic-gate   if(!gl) {
11926*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
11927*7c478bd9Sstevel@tonic-gate     return 1;
11928*7c478bd9Sstevel@tonic-gate   };
11929*7c478bd9Sstevel@tonic-gate /*
11930*7c478bd9Sstevel@tonic-gate  * Reject non-sensical dimensions.
11931*7c478bd9Sstevel@tonic-gate  */
11932*7c478bd9Sstevel@tonic-gate   if(ncolumn <= 0 || nline <= 0) {
11933*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, "Invalid terminal size", END_ERR_MSG);
11934*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
11935*7c478bd9Sstevel@tonic-gate     return 1;
11936*7c478bd9Sstevel@tonic-gate   };
11937*7c478bd9Sstevel@tonic-gate /*
11938*7c478bd9Sstevel@tonic-gate  * Install the new dimensions in the terminal driver if possible, so
11939*7c478bd9Sstevel@tonic-gate  * that future calls to gl_query_size() get the new value.
11940*7c478bd9Sstevel@tonic-gate  */
11941*7c478bd9Sstevel@tonic-gate #ifdef TIOCSWINSZ
11942*7c478bd9Sstevel@tonic-gate   if(gl->is_term) {
11943*7c478bd9Sstevel@tonic-gate     struct winsize size;
11944*7c478bd9Sstevel@tonic-gate     size.ws_row = nline;
11945*7c478bd9Sstevel@tonic-gate     size.ws_col = ncolumn;
11946*7c478bd9Sstevel@tonic-gate     size.ws_xpixel = 0;
11947*7c478bd9Sstevel@tonic-gate     size.ws_ypixel = 0;
11948*7c478bd9Sstevel@tonic-gate     if(ioctl(gl->output_fd, TIOCSWINSZ, &size) == -1) {
11949*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "Can't change terminal size", END_ERR_MSG);
11950*7c478bd9Sstevel@tonic-gate       return 1;
11951*7c478bd9Sstevel@tonic-gate     };
11952*7c478bd9Sstevel@tonic-gate   };
11953*7c478bd9Sstevel@tonic-gate #endif
11954*7c478bd9Sstevel@tonic-gate /*
11955*7c478bd9Sstevel@tonic-gate  * If an input line is in the process of being edited, redisplay it to
11956*7c478bd9Sstevel@tonic-gate  * accomodate the new dimensions, and record the new dimensions in
11957*7c478bd9Sstevel@tonic-gate  * gl->nline and gl->ncolumn.
11958*7c478bd9Sstevel@tonic-gate  */
11959*7c478bd9Sstevel@tonic-gate   return gl_handle_tty_resize(gl, ncolumn, nline);
11960*7c478bd9Sstevel@tonic-gate }
11961*7c478bd9Sstevel@tonic-gate 
11962*7c478bd9Sstevel@tonic-gate /*.......................................................................
11963*7c478bd9Sstevel@tonic-gate  * Record a character in the input line buffer at a given position.
11964*7c478bd9Sstevel@tonic-gate  *
11965*7c478bd9Sstevel@tonic-gate  * Input:
11966*7c478bd9Sstevel@tonic-gate  *  gl    GetLine *   The resource object of gl_get_line().
11967*7c478bd9Sstevel@tonic-gate  *  c        char     The character to be recorded.
11968*7c478bd9Sstevel@tonic-gate  *  bufpos    int     The index in the buffer at which to record the
11969*7c478bd9Sstevel@tonic-gate  *                    character.
11970*7c478bd9Sstevel@tonic-gate  * Output:
11971*7c478bd9Sstevel@tonic-gate  *  return    int     0 - OK.
11972*7c478bd9Sstevel@tonic-gate  *                    1 - Insufficient room.
11973*7c478bd9Sstevel@tonic-gate  */
11974*7c478bd9Sstevel@tonic-gate static int gl_buffer_char(GetLine *gl, char c, int bufpos)
11975*7c478bd9Sstevel@tonic-gate {
11976*7c478bd9Sstevel@tonic-gate /*
11977*7c478bd9Sstevel@tonic-gate  * Guard against buffer overruns.
11978*7c478bd9Sstevel@tonic-gate  */
11979*7c478bd9Sstevel@tonic-gate   if(bufpos >= gl->linelen)
11980*7c478bd9Sstevel@tonic-gate     return 1;
11981*7c478bd9Sstevel@tonic-gate /*
11982*7c478bd9Sstevel@tonic-gate  * Record the new character.
11983*7c478bd9Sstevel@tonic-gate  */
11984*7c478bd9Sstevel@tonic-gate   gl->line[bufpos] = c;
11985*7c478bd9Sstevel@tonic-gate /*
11986*7c478bd9Sstevel@tonic-gate  * If the new character was placed beyond the end of the current input
11987*7c478bd9Sstevel@tonic-gate  * line, update gl->ntotal to reflect the increased number of characters
11988*7c478bd9Sstevel@tonic-gate  * that are in gl->line, and terminate the string.
11989*7c478bd9Sstevel@tonic-gate  */
11990*7c478bd9Sstevel@tonic-gate   if(bufpos >= gl->ntotal) {
11991*7c478bd9Sstevel@tonic-gate     gl->ntotal = bufpos+1;
11992*7c478bd9Sstevel@tonic-gate     gl->line[gl->ntotal] = '\0';
11993*7c478bd9Sstevel@tonic-gate   };
11994*7c478bd9Sstevel@tonic-gate   return 0;
11995*7c478bd9Sstevel@tonic-gate }
11996*7c478bd9Sstevel@tonic-gate 
11997*7c478bd9Sstevel@tonic-gate /*.......................................................................
11998*7c478bd9Sstevel@tonic-gate  * Copy a given string into the input buffer, overwriting the current
11999*7c478bd9Sstevel@tonic-gate  * contents.
12000*7c478bd9Sstevel@tonic-gate  *
12001*7c478bd9Sstevel@tonic-gate  * Input:
12002*7c478bd9Sstevel@tonic-gate  *  gl    GetLine *   The resource object of gl_get_line().
12003*7c478bd9Sstevel@tonic-gate  *  s  const char *   The string to be recorded.
12004*7c478bd9Sstevel@tonic-gate  *  n         int     The number of characters to be copied from the
12005*7c478bd9Sstevel@tonic-gate  *                    string.
12006*7c478bd9Sstevel@tonic-gate  *  bufpos    int     The index in the buffer at which to place the
12007*7c478bd9Sstevel@tonic-gate  *                    the first character of the string.
12008*7c478bd9Sstevel@tonic-gate  * Output:
12009*7c478bd9Sstevel@tonic-gate  *  return    int     0 - OK.
12010*7c478bd9Sstevel@tonic-gate  *                    1 - String truncated to fit.
12011*7c478bd9Sstevel@tonic-gate  */
12012*7c478bd9Sstevel@tonic-gate static int gl_buffer_string(GetLine *gl, const char *s, int n, int bufpos)
12013*7c478bd9Sstevel@tonic-gate {
12014*7c478bd9Sstevel@tonic-gate   int nnew;  /* The number of characters actually recorded */
12015*7c478bd9Sstevel@tonic-gate   int i;
12016*7c478bd9Sstevel@tonic-gate /*
12017*7c478bd9Sstevel@tonic-gate  * How many of the characters will fit within the buffer?
12018*7c478bd9Sstevel@tonic-gate  */
12019*7c478bd9Sstevel@tonic-gate   nnew = bufpos + n <= gl->linelen ? n : (gl->linelen - bufpos);
12020*7c478bd9Sstevel@tonic-gate /*
12021*7c478bd9Sstevel@tonic-gate  * Record the first nnew characters of s[] in the buffer.
12022*7c478bd9Sstevel@tonic-gate  */
12023*7c478bd9Sstevel@tonic-gate   for(i=0; i<nnew; i++)
12024*7c478bd9Sstevel@tonic-gate     gl_buffer_char(gl, s[i], bufpos + i);
12025*7c478bd9Sstevel@tonic-gate /*
12026*7c478bd9Sstevel@tonic-gate  * Was the string truncated?
12027*7c478bd9Sstevel@tonic-gate  */
12028*7c478bd9Sstevel@tonic-gate   return nnew < n;
12029*7c478bd9Sstevel@tonic-gate }
12030*7c478bd9Sstevel@tonic-gate 
12031*7c478bd9Sstevel@tonic-gate /*.......................................................................
12032*7c478bd9Sstevel@tonic-gate  * Make room in the input buffer for a string to be inserted. This
12033*7c478bd9Sstevel@tonic-gate  * involves moving the characters that follow a specified point, towards
12034*7c478bd9Sstevel@tonic-gate  * the end of the buffer.
12035*7c478bd9Sstevel@tonic-gate  *
12036*7c478bd9Sstevel@tonic-gate  * Input:
12037*7c478bd9Sstevel@tonic-gate  *  gl    GetLine *   The resource object of gl_get_line().
12038*7c478bd9Sstevel@tonic-gate  *  start     int     The index of the first character to be moved.
12039*7c478bd9Sstevel@tonic-gate  *  n         int     The width of the gap.
12040*7c478bd9Sstevel@tonic-gate  * Output:
12041*7c478bd9Sstevel@tonic-gate  *  return    int     0 - OK.
12042*7c478bd9Sstevel@tonic-gate  *                    1 - Insufficient room.
12043*7c478bd9Sstevel@tonic-gate  */
12044*7c478bd9Sstevel@tonic-gate static int gl_make_gap_in_buffer(GetLine *gl, int start, int n)
12045*7c478bd9Sstevel@tonic-gate {
12046*7c478bd9Sstevel@tonic-gate /*
12047*7c478bd9Sstevel@tonic-gate  * Ensure that the buffer has sufficient space.
12048*7c478bd9Sstevel@tonic-gate  */
12049*7c478bd9Sstevel@tonic-gate   if(gl->ntotal + n > gl->linelen)
12050*7c478bd9Sstevel@tonic-gate     return 1;
12051*7c478bd9Sstevel@tonic-gate /*
12052*7c478bd9Sstevel@tonic-gate  * Move everything including and beyond the character at 'start'
12053*7c478bd9Sstevel@tonic-gate  * towards the end of the string.
12054*7c478bd9Sstevel@tonic-gate  */
12055*7c478bd9Sstevel@tonic-gate   memmove(gl->line + start + n, gl->line + start, gl->ntotal - start + 1);
12056*7c478bd9Sstevel@tonic-gate /*
12057*7c478bd9Sstevel@tonic-gate  * Update the recorded size of the line.
12058*7c478bd9Sstevel@tonic-gate  */
12059*7c478bd9Sstevel@tonic-gate   gl->ntotal += n;
12060*7c478bd9Sstevel@tonic-gate   return 1;
12061*7c478bd9Sstevel@tonic-gate }
12062*7c478bd9Sstevel@tonic-gate 
12063*7c478bd9Sstevel@tonic-gate /*.......................................................................
12064*7c478bd9Sstevel@tonic-gate  * Remove a given number of characters from the input buffer. This
12065*7c478bd9Sstevel@tonic-gate  * involves moving the characters that follow the removed characters to
12066*7c478bd9Sstevel@tonic-gate  * where the removed sub-string started in the input buffer.
12067*7c478bd9Sstevel@tonic-gate  *
12068*7c478bd9Sstevel@tonic-gate  * Input:
12069*7c478bd9Sstevel@tonic-gate  *  gl    GetLine *   The resource object of gl_get_line().
12070*7c478bd9Sstevel@tonic-gate  *  start     int     The first character to be removed.
12071*7c478bd9Sstevel@tonic-gate  *  n         int     The number of characters to remove.
12072*7c478bd9Sstevel@tonic-gate  */
12073*7c478bd9Sstevel@tonic-gate static void gl_remove_from_buffer(GetLine *gl, int start, int n)
12074*7c478bd9Sstevel@tonic-gate {
12075*7c478bd9Sstevel@tonic-gate   memmove(gl->line + start, gl->line + start + n, gl->ntotal - start - n + 1);
12076*7c478bd9Sstevel@tonic-gate /*
12077*7c478bd9Sstevel@tonic-gate  * Update the recorded size of the line.
12078*7c478bd9Sstevel@tonic-gate  */
12079*7c478bd9Sstevel@tonic-gate   gl->ntotal -= n;
12080*7c478bd9Sstevel@tonic-gate }
12081*7c478bd9Sstevel@tonic-gate 
12082*7c478bd9Sstevel@tonic-gate /*.......................................................................
12083*7c478bd9Sstevel@tonic-gate  * Truncate the string in the input line buffer after a given number of
12084*7c478bd9Sstevel@tonic-gate  * characters.
12085*7c478bd9Sstevel@tonic-gate  *
12086*7c478bd9Sstevel@tonic-gate  * Input:
12087*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *   The resource object of gl_get_line().
12088*7c478bd9Sstevel@tonic-gate  *  n            int     The new length of the line.
12089*7c478bd9Sstevel@tonic-gate  * Output:
12090*7c478bd9Sstevel@tonic-gate  *  return       int     0 - OK.
12091*7c478bd9Sstevel@tonic-gate  *                       1 - n > gl->linelen.
12092*7c478bd9Sstevel@tonic-gate  */
12093*7c478bd9Sstevel@tonic-gate static int gl_truncate_buffer(GetLine *gl, int n)
12094*7c478bd9Sstevel@tonic-gate {
12095*7c478bd9Sstevel@tonic-gate   if(n > gl->linelen)
12096*7c478bd9Sstevel@tonic-gate     return 1;
12097*7c478bd9Sstevel@tonic-gate   gl->line[n] = '\0';
12098*7c478bd9Sstevel@tonic-gate   gl->ntotal = n;
12099*7c478bd9Sstevel@tonic-gate   return 0;
12100*7c478bd9Sstevel@tonic-gate }
12101*7c478bd9Sstevel@tonic-gate 
12102*7c478bd9Sstevel@tonic-gate /*.......................................................................
12103*7c478bd9Sstevel@tonic-gate  * When the contents of gl->line[] are changed without calling any of the
12104*7c478bd9Sstevel@tonic-gate  * gl_ buffer manipulation functions, this function must be called to
12105*7c478bd9Sstevel@tonic-gate  * compute the length of this string, and ancillary information.
12106*7c478bd9Sstevel@tonic-gate  *
12107*7c478bd9Sstevel@tonic-gate  * Input:
12108*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *   The resource object of gl_get_line().
12109*7c478bd9Sstevel@tonic-gate  */
12110*7c478bd9Sstevel@tonic-gate static void gl_update_buffer(GetLine *gl)
12111*7c478bd9Sstevel@tonic-gate {
12112*7c478bd9Sstevel@tonic-gate   int len;  /* The length of the line */
12113*7c478bd9Sstevel@tonic-gate /*
12114*7c478bd9Sstevel@tonic-gate  * Measure the length of the input line.
12115*7c478bd9Sstevel@tonic-gate  */
12116*7c478bd9Sstevel@tonic-gate   for(len=0; len <= gl->linelen && gl->line[len]; len++)
12117*7c478bd9Sstevel@tonic-gate     ;
12118*7c478bd9Sstevel@tonic-gate /*
12119*7c478bd9Sstevel@tonic-gate  * Just in case the string wasn't correctly terminated, do so here.
12120*7c478bd9Sstevel@tonic-gate  */
12121*7c478bd9Sstevel@tonic-gate   gl->line[len] = '\0';
12122*7c478bd9Sstevel@tonic-gate /*
12123*7c478bd9Sstevel@tonic-gate  * Record the number of characters that are now in gl->line[].
12124*7c478bd9Sstevel@tonic-gate  */
12125*7c478bd9Sstevel@tonic-gate   gl->ntotal = len;
12126*7c478bd9Sstevel@tonic-gate /*
12127*7c478bd9Sstevel@tonic-gate  * Ensure that the cursor stays within the bounds of the modified
12128*7c478bd9Sstevel@tonic-gate  * input line.
12129*7c478bd9Sstevel@tonic-gate  */
12130*7c478bd9Sstevel@tonic-gate   if(gl->buff_curpos > gl->ntotal)
12131*7c478bd9Sstevel@tonic-gate     gl->buff_curpos = gl->ntotal;
12132*7c478bd9Sstevel@tonic-gate /*
12133*7c478bd9Sstevel@tonic-gate  * Arrange for the input line to be redrawn.
12134*7c478bd9Sstevel@tonic-gate  */
12135*7c478bd9Sstevel@tonic-gate   gl_queue_redisplay(gl);
12136*7c478bd9Sstevel@tonic-gate   return;
12137*7c478bd9Sstevel@tonic-gate }
12138*7c478bd9Sstevel@tonic-gate 
12139*7c478bd9Sstevel@tonic-gate /*.......................................................................
12140*7c478bd9Sstevel@tonic-gate  * Erase the displayed input line, including its prompt, and leave the
12141*7c478bd9Sstevel@tonic-gate  * cursor where the erased line started. Note that to allow this
12142*7c478bd9Sstevel@tonic-gate  * function to be used when responding to a terminal resize, this
12143*7c478bd9Sstevel@tonic-gate  * function is designed to work even if the horizontal cursor position
12144*7c478bd9Sstevel@tonic-gate  * doesn't match the internally recorded position.
12145*7c478bd9Sstevel@tonic-gate  *
12146*7c478bd9Sstevel@tonic-gate  * Input:
12147*7c478bd9Sstevel@tonic-gate  *  gl      GetLine *   The resource object of gl_get_line().
12148*7c478bd9Sstevel@tonic-gate  * Output:
12149*7c478bd9Sstevel@tonic-gate  *  return      int     0 - OK.
12150*7c478bd9Sstevel@tonic-gate  *                      1 - Error.
12151*7c478bd9Sstevel@tonic-gate  */
12152*7c478bd9Sstevel@tonic-gate static int gl_erase_line(GetLine *gl)
12153*7c478bd9Sstevel@tonic-gate {
12154*7c478bd9Sstevel@tonic-gate /*
12155*7c478bd9Sstevel@tonic-gate  * Is a line currently displayed?
12156*7c478bd9Sstevel@tonic-gate  */
12157*7c478bd9Sstevel@tonic-gate   if(gl->displayed) {
12158*7c478bd9Sstevel@tonic-gate /*
12159*7c478bd9Sstevel@tonic-gate  * Relative the the start of the input line, which terminal line of
12160*7c478bd9Sstevel@tonic-gate  * the current input line is the cursor currently on?
12161*7c478bd9Sstevel@tonic-gate  */
12162*7c478bd9Sstevel@tonic-gate     int cursor_line = gl->term_curpos / gl->ncolumn;
12163*7c478bd9Sstevel@tonic-gate /*
12164*7c478bd9Sstevel@tonic-gate  * Move the cursor to the start of the line.
12165*7c478bd9Sstevel@tonic-gate  */
12166*7c478bd9Sstevel@tonic-gate     for( ; cursor_line > 0; cursor_line--) {
12167*7c478bd9Sstevel@tonic-gate       if(gl_print_control_sequence(gl, 1, gl->up))
12168*7c478bd9Sstevel@tonic-gate 	return 1;
12169*7c478bd9Sstevel@tonic-gate     };
12170*7c478bd9Sstevel@tonic-gate     if(gl_print_control_sequence(gl, 1, gl->bol))
12171*7c478bd9Sstevel@tonic-gate       return 1;
12172*7c478bd9Sstevel@tonic-gate /*
12173*7c478bd9Sstevel@tonic-gate  * Clear from the start of the line to the end of the terminal.
12174*7c478bd9Sstevel@tonic-gate  */
12175*7c478bd9Sstevel@tonic-gate     if(gl_print_control_sequence(gl, gl->nline, gl->clear_eod))
12176*7c478bd9Sstevel@tonic-gate       return 1;
12177*7c478bd9Sstevel@tonic-gate /*
12178*7c478bd9Sstevel@tonic-gate  * Mark the line as no longer displayed.
12179*7c478bd9Sstevel@tonic-gate  */
12180*7c478bd9Sstevel@tonic-gate     gl_line_erased(gl);
12181*7c478bd9Sstevel@tonic-gate   };
12182*7c478bd9Sstevel@tonic-gate   return 0;
12183*7c478bd9Sstevel@tonic-gate }
12184*7c478bd9Sstevel@tonic-gate 
12185*7c478bd9Sstevel@tonic-gate /*.......................................................................
12186*7c478bd9Sstevel@tonic-gate  * Arrange for the input line to be redisplayed by gl_flush_output(),
12187*7c478bd9Sstevel@tonic-gate  * as soon as the output queue becomes empty.
12188*7c478bd9Sstevel@tonic-gate  *
12189*7c478bd9Sstevel@tonic-gate  * Input:
12190*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *   The resource object of gl_get_line().
12191*7c478bd9Sstevel@tonic-gate  */
12192*7c478bd9Sstevel@tonic-gate static void gl_queue_redisplay(GetLine *gl)
12193*7c478bd9Sstevel@tonic-gate {
12194*7c478bd9Sstevel@tonic-gate   gl->redisplay = 1;
12195*7c478bd9Sstevel@tonic-gate   gl->pending_io = GLP_WRITE;
12196*7c478bd9Sstevel@tonic-gate }
12197*7c478bd9Sstevel@tonic-gate 
12198*7c478bd9Sstevel@tonic-gate /*.......................................................................
12199*7c478bd9Sstevel@tonic-gate  * Truncate the displayed input line starting from the current
12200*7c478bd9Sstevel@tonic-gate  * terminal cursor position, and leave the cursor at the end of the
12201*7c478bd9Sstevel@tonic-gate  * truncated line. The input-line buffer is not affected.
12202*7c478bd9Sstevel@tonic-gate  *
12203*7c478bd9Sstevel@tonic-gate  * Input:
12204*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *   The resource object of gl_get_line().
12205*7c478bd9Sstevel@tonic-gate  * Output:
12206*7c478bd9Sstevel@tonic-gate  *  return     int     0 - OK.
12207*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
12208*7c478bd9Sstevel@tonic-gate  */
12209*7c478bd9Sstevel@tonic-gate static int gl_truncate_display(GetLine *gl)
12210*7c478bd9Sstevel@tonic-gate {
12211*7c478bd9Sstevel@tonic-gate /*
12212*7c478bd9Sstevel@tonic-gate  * Keep a record of the current terminal cursor position.
12213*7c478bd9Sstevel@tonic-gate  */
12214*7c478bd9Sstevel@tonic-gate   int term_curpos = gl->term_curpos;
12215*7c478bd9Sstevel@tonic-gate /*
12216*7c478bd9Sstevel@tonic-gate  * First clear from the cursor to the end of the current input line.
12217*7c478bd9Sstevel@tonic-gate  */
12218*7c478bd9Sstevel@tonic-gate   if(gl_print_control_sequence(gl, 1, gl->clear_eol))
12219*7c478bd9Sstevel@tonic-gate     return 1;
12220*7c478bd9Sstevel@tonic-gate /*
12221*7c478bd9Sstevel@tonic-gate  * If there is more than one line displayed, go to the start of the
12222*7c478bd9Sstevel@tonic-gate  * next line and clear from there to the end of the display. Note that
12223*7c478bd9Sstevel@tonic-gate  * we can't use clear_eod to do the whole job of clearing from the
12224*7c478bd9Sstevel@tonic-gate  * current cursor position to the end of the terminal because
12225*7c478bd9Sstevel@tonic-gate  * clear_eod is only defined when used at the start of a terminal line
12226*7c478bd9Sstevel@tonic-gate  * (eg. with gnome terminals, clear_eod clears from the start of the
12227*7c478bd9Sstevel@tonic-gate  * current terminal line, rather than from the current cursor
12228*7c478bd9Sstevel@tonic-gate  * position).
12229*7c478bd9Sstevel@tonic-gate  */
12230*7c478bd9Sstevel@tonic-gate   if(gl->term_len / gl->ncolumn > gl->term_curpos / gl->ncolumn) {
12231*7c478bd9Sstevel@tonic-gate     if(gl_print_control_sequence(gl, 1, gl->down) ||
12232*7c478bd9Sstevel@tonic-gate        gl_print_control_sequence(gl, 1, gl->bol) ||
12233*7c478bd9Sstevel@tonic-gate        gl_print_control_sequence(gl, gl->nline, gl->clear_eod))
12234*7c478bd9Sstevel@tonic-gate       return 1;
12235*7c478bd9Sstevel@tonic-gate /*
12236*7c478bd9Sstevel@tonic-gate  * Where is the cursor now?
12237*7c478bd9Sstevel@tonic-gate  */
12238*7c478bd9Sstevel@tonic-gate     gl->term_curpos = gl->ncolumn * (term_curpos / gl->ncolumn + 1);
12239*7c478bd9Sstevel@tonic-gate /*
12240*7c478bd9Sstevel@tonic-gate  * Restore the cursor position.
12241*7c478bd9Sstevel@tonic-gate  */
12242*7c478bd9Sstevel@tonic-gate     gl_set_term_curpos(gl, term_curpos);
12243*7c478bd9Sstevel@tonic-gate   };
12244*7c478bd9Sstevel@tonic-gate /*
12245*7c478bd9Sstevel@tonic-gate  * Update the recorded position of the final character.
12246*7c478bd9Sstevel@tonic-gate  */
12247*7c478bd9Sstevel@tonic-gate   gl->term_len = gl->term_curpos;
12248*7c478bd9Sstevel@tonic-gate   return 0;
12249*7c478bd9Sstevel@tonic-gate }
12250*7c478bd9Sstevel@tonic-gate 
12251*7c478bd9Sstevel@tonic-gate /*.......................................................................
12252*7c478bd9Sstevel@tonic-gate  * Return the set of all trappable signals.
12253*7c478bd9Sstevel@tonic-gate  *
12254*7c478bd9Sstevel@tonic-gate  * Input:
12255*7c478bd9Sstevel@tonic-gate  *  signals   sigset_t *  The set of signals will be recorded in
12256*7c478bd9Sstevel@tonic-gate  *                        *signals.
12257*7c478bd9Sstevel@tonic-gate  */
12258*7c478bd9Sstevel@tonic-gate static void gl_list_trappable_signals(sigset_t *signals)
12259*7c478bd9Sstevel@tonic-gate {
12260*7c478bd9Sstevel@tonic-gate /*
12261*7c478bd9Sstevel@tonic-gate  * Start with the set of all signals.
12262*7c478bd9Sstevel@tonic-gate  */
12263*7c478bd9Sstevel@tonic-gate   sigfillset(signals);
12264*7c478bd9Sstevel@tonic-gate /*
12265*7c478bd9Sstevel@tonic-gate  * Remove un-trappable signals from this set.
12266*7c478bd9Sstevel@tonic-gate  */
12267*7c478bd9Sstevel@tonic-gate #ifdef SIGKILL
12268*7c478bd9Sstevel@tonic-gate   sigdelset(signals, SIGKILL);
12269*7c478bd9Sstevel@tonic-gate #endif
12270*7c478bd9Sstevel@tonic-gate #ifdef SIGSTOP
12271*7c478bd9Sstevel@tonic-gate   sigdelset(signals, SIGSTOP);
12272*7c478bd9Sstevel@tonic-gate #endif
12273*7c478bd9Sstevel@tonic-gate }
12274*7c478bd9Sstevel@tonic-gate 
12275*7c478bd9Sstevel@tonic-gate /*.......................................................................
12276*7c478bd9Sstevel@tonic-gate  * Read an input line from a non-interactive input stream.
12277*7c478bd9Sstevel@tonic-gate  *
12278*7c478bd9Sstevel@tonic-gate  * Input:
12279*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *   The resource object of gl_get_line().
12280*7c478bd9Sstevel@tonic-gate  * Output:
12281*7c478bd9Sstevel@tonic-gate  *  return     int     0 - OK
12282*7c478bd9Sstevel@tonic-gate  *                     1 - Error.
12283*7c478bd9Sstevel@tonic-gate  */
12284*7c478bd9Sstevel@tonic-gate static int gl_read_stream_line(GetLine *gl)
12285*7c478bd9Sstevel@tonic-gate {
12286*7c478bd9Sstevel@tonic-gate   char c = '\0'; /* The latest character read from fp */
12287*7c478bd9Sstevel@tonic-gate /*
12288*7c478bd9Sstevel@tonic-gate  * Record the fact that we are about to read input.
12289*7c478bd9Sstevel@tonic-gate  */
12290*7c478bd9Sstevel@tonic-gate   gl->pending_io = GLP_READ;
12291*7c478bd9Sstevel@tonic-gate /*
12292*7c478bd9Sstevel@tonic-gate  * If we are starting a new line, reset the line-input parameters.
12293*7c478bd9Sstevel@tonic-gate  */
12294*7c478bd9Sstevel@tonic-gate   if(gl->endline)
12295*7c478bd9Sstevel@tonic-gate     gl_reset_input_line(gl);
12296*7c478bd9Sstevel@tonic-gate /*
12297*7c478bd9Sstevel@tonic-gate  * Read one character at a time.
12298*7c478bd9Sstevel@tonic-gate  */
12299*7c478bd9Sstevel@tonic-gate   while(gl->ntotal < gl->linelen && c != '\n') {
12300*7c478bd9Sstevel@tonic-gate /*
12301*7c478bd9Sstevel@tonic-gate  * Attempt to read one more character.
12302*7c478bd9Sstevel@tonic-gate  */
12303*7c478bd9Sstevel@tonic-gate     switch(gl_read_input(gl, &c)) {
12304*7c478bd9Sstevel@tonic-gate     case GL_READ_OK:
12305*7c478bd9Sstevel@tonic-gate       break;
12306*7c478bd9Sstevel@tonic-gate     case GL_READ_EOF:        /* Reached end-of-file? */
12307*7c478bd9Sstevel@tonic-gate /*
12308*7c478bd9Sstevel@tonic-gate  * If any characters were read before the end-of-file condition,
12309*7c478bd9Sstevel@tonic-gate  * interpolate a newline character, so that the caller sees a
12310*7c478bd9Sstevel@tonic-gate  * properly terminated line. Otherwise return an end-of-file
12311*7c478bd9Sstevel@tonic-gate  * condition.
12312*7c478bd9Sstevel@tonic-gate  */
12313*7c478bd9Sstevel@tonic-gate       if(gl->ntotal > 0) {
12314*7c478bd9Sstevel@tonic-gate 	c = '\n';
12315*7c478bd9Sstevel@tonic-gate       } else {
12316*7c478bd9Sstevel@tonic-gate 	gl_record_status(gl, GLR_EOF, 0);
12317*7c478bd9Sstevel@tonic-gate 	return 1;
12318*7c478bd9Sstevel@tonic-gate       };
12319*7c478bd9Sstevel@tonic-gate       break;
12320*7c478bd9Sstevel@tonic-gate     case GL_READ_BLOCKED:    /* Input blocked? */
12321*7c478bd9Sstevel@tonic-gate       gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO);
12322*7c478bd9Sstevel@tonic-gate       return 1;
12323*7c478bd9Sstevel@tonic-gate       break;
12324*7c478bd9Sstevel@tonic-gate     case GL_READ_ERROR:     /* I/O error? */
12325*7c478bd9Sstevel@tonic-gate       return 1;
12326*7c478bd9Sstevel@tonic-gate       break;
12327*7c478bd9Sstevel@tonic-gate     };
12328*7c478bd9Sstevel@tonic-gate /*
12329*7c478bd9Sstevel@tonic-gate  * Append the character to the line buffer.
12330*7c478bd9Sstevel@tonic-gate  */
12331*7c478bd9Sstevel@tonic-gate     if(gl_buffer_char(gl, c, gl->ntotal))
12332*7c478bd9Sstevel@tonic-gate       return 1;
12333*7c478bd9Sstevel@tonic-gate   };
12334*7c478bd9Sstevel@tonic-gate /*
12335*7c478bd9Sstevel@tonic-gate  * Was the end of the input line reached before running out of buffer space?
12336*7c478bd9Sstevel@tonic-gate  */
12337*7c478bd9Sstevel@tonic-gate   gl->endline = (c == '\n');
12338*7c478bd9Sstevel@tonic-gate   return 0;
12339*7c478bd9Sstevel@tonic-gate }
12340*7c478bd9Sstevel@tonic-gate 
12341*7c478bd9Sstevel@tonic-gate /*.......................................................................
12342*7c478bd9Sstevel@tonic-gate  * Read a single character from a non-interactive input stream.
12343*7c478bd9Sstevel@tonic-gate  *
12344*7c478bd9Sstevel@tonic-gate  * Input:
12345*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *   The resource object of gl_get_line().
12346*7c478bd9Sstevel@tonic-gate  * Output:
12347*7c478bd9Sstevel@tonic-gate  *  return     int     The character, or EOF on error.
12348*7c478bd9Sstevel@tonic-gate  */
12349*7c478bd9Sstevel@tonic-gate static int gl_read_stream_char(GetLine *gl)
12350*7c478bd9Sstevel@tonic-gate {
12351*7c478bd9Sstevel@tonic-gate   char c = '\0';    /* The latest character read from fp */
12352*7c478bd9Sstevel@tonic-gate   int retval = EOF; /* The return value of this function */
12353*7c478bd9Sstevel@tonic-gate /*
12354*7c478bd9Sstevel@tonic-gate  * Arrange to discard any incomplete input line.
12355*7c478bd9Sstevel@tonic-gate  */
12356*7c478bd9Sstevel@tonic-gate   _gl_abandon_line(gl);
12357*7c478bd9Sstevel@tonic-gate /*
12358*7c478bd9Sstevel@tonic-gate  * Record the fact that we are about to read input.
12359*7c478bd9Sstevel@tonic-gate  */
12360*7c478bd9Sstevel@tonic-gate   gl->pending_io = GLP_READ;
12361*7c478bd9Sstevel@tonic-gate /*
12362*7c478bd9Sstevel@tonic-gate  * Attempt to read one more character.
12363*7c478bd9Sstevel@tonic-gate  */
12364*7c478bd9Sstevel@tonic-gate   switch(gl_read_input(gl, &c)) {
12365*7c478bd9Sstevel@tonic-gate   case GL_READ_OK:      /* Success */
12366*7c478bd9Sstevel@tonic-gate     retval = c;
12367*7c478bd9Sstevel@tonic-gate     break;
12368*7c478bd9Sstevel@tonic-gate   case GL_READ_BLOCKED: /* The read blocked */
12369*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO);
12370*7c478bd9Sstevel@tonic-gate     retval = EOF;  /* Failure */
12371*7c478bd9Sstevel@tonic-gate     break;
12372*7c478bd9Sstevel@tonic-gate   case GL_READ_EOF:     /* End of file reached */
12373*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_EOF, 0);
12374*7c478bd9Sstevel@tonic-gate     retval = EOF;  /* Failure */
12375*7c478bd9Sstevel@tonic-gate     break;
12376*7c478bd9Sstevel@tonic-gate   case GL_READ_ERROR:
12377*7c478bd9Sstevel@tonic-gate     retval = EOF;  /* Failure */
12378*7c478bd9Sstevel@tonic-gate     break;
12379*7c478bd9Sstevel@tonic-gate   };
12380*7c478bd9Sstevel@tonic-gate   return retval;
12381*7c478bd9Sstevel@tonic-gate }
12382*7c478bd9Sstevel@tonic-gate 
12383*7c478bd9Sstevel@tonic-gate /*.......................................................................
12384*7c478bd9Sstevel@tonic-gate  * Bind a key sequence to a given action.
12385*7c478bd9Sstevel@tonic-gate  *
12386*7c478bd9Sstevel@tonic-gate  * Input:
12387*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *   The resource object of gl_get_line().
12388*7c478bd9Sstevel@tonic-gate  *  origin  GlKeyOrigin     The originator of the key binding.
12389*7c478bd9Sstevel@tonic-gate  *  key      const char *   The key-sequence to be bound (or unbound).
12390*7c478bd9Sstevel@tonic-gate  *  action   const char *   The name of the action to bind the key to,
12391*7c478bd9Sstevel@tonic-gate  *                          or either NULL or "" to unbind the
12392*7c478bd9Sstevel@tonic-gate  *                          key-sequence.
12393*7c478bd9Sstevel@tonic-gate  * Output:
12394*7c478bd9Sstevel@tonic-gate  *  return          int     0 - OK
12395*7c478bd9Sstevel@tonic-gate  *                          1 - Error.
12396*7c478bd9Sstevel@tonic-gate  */
12397*7c478bd9Sstevel@tonic-gate int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin, const char *keyseq,
12398*7c478bd9Sstevel@tonic-gate 		   const char *action)
12399*7c478bd9Sstevel@tonic-gate {
12400*7c478bd9Sstevel@tonic-gate   KtBinder binder;  /* The private internal equivalent of 'origin' */
12401*7c478bd9Sstevel@tonic-gate /*
12402*7c478bd9Sstevel@tonic-gate  * Check the arguments.
12403*7c478bd9Sstevel@tonic-gate  */
12404*7c478bd9Sstevel@tonic-gate   if(!gl || !keyseq) {
12405*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
12406*7c478bd9Sstevel@tonic-gate     if(gl)
12407*7c478bd9Sstevel@tonic-gate       _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG);
12408*7c478bd9Sstevel@tonic-gate     return 1;
12409*7c478bd9Sstevel@tonic-gate   };
12410*7c478bd9Sstevel@tonic-gate /*
12411*7c478bd9Sstevel@tonic-gate  * An empty action string requests that the key-sequence be unbound.
12412*7c478bd9Sstevel@tonic-gate  * This is indicated to _kt_set_keybinding() by passing a NULL action
12413*7c478bd9Sstevel@tonic-gate  * string, so convert an empty string to a NULL action pointer.
12414*7c478bd9Sstevel@tonic-gate  */
12415*7c478bd9Sstevel@tonic-gate   if(action && *action=='\0')
12416*7c478bd9Sstevel@tonic-gate     action = NULL;
12417*7c478bd9Sstevel@tonic-gate /*
12418*7c478bd9Sstevel@tonic-gate  * Translate the public originator enumeration to the private equivalent.
12419*7c478bd9Sstevel@tonic-gate  */
12420*7c478bd9Sstevel@tonic-gate   binder = origin==GL_USER_KEY ? KTB_USER : KTB_NORM;
12421*7c478bd9Sstevel@tonic-gate /*
12422*7c478bd9Sstevel@tonic-gate  * Bind the action to a given key-sequence?
12423*7c478bd9Sstevel@tonic-gate  */
12424*7c478bd9Sstevel@tonic-gate   if(keyseq && _kt_set_keybinding(gl->bindings, binder, keyseq, action)) {
12425*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG);
12426*7c478bd9Sstevel@tonic-gate     return 1;
12427*7c478bd9Sstevel@tonic-gate   };
12428*7c478bd9Sstevel@tonic-gate   return 0;
12429*7c478bd9Sstevel@tonic-gate }
12430*7c478bd9Sstevel@tonic-gate 
12431*7c478bd9Sstevel@tonic-gate /*.......................................................................
12432*7c478bd9Sstevel@tonic-gate  * This is the public wrapper around the gl_clear_termina() function.
12433*7c478bd9Sstevel@tonic-gate  * It clears the terminal and leaves the cursor at the home position.
12434*7c478bd9Sstevel@tonic-gate  * In server I/O mode, the next call to gl_get_line() will also
12435*7c478bd9Sstevel@tonic-gate  * redisplay the current input line.
12436*7c478bd9Sstevel@tonic-gate  *
12437*7c478bd9Sstevel@tonic-gate  * Input:
12438*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *   The resource object of gl_get_line().
12439*7c478bd9Sstevel@tonic-gate  * Output:
12440*7c478bd9Sstevel@tonic-gate  *  return          int     0 - OK.
12441*7c478bd9Sstevel@tonic-gate  *                          1 - Error.
12442*7c478bd9Sstevel@tonic-gate  */
12443*7c478bd9Sstevel@tonic-gate int gl_erase_terminal(GetLine *gl)
12444*7c478bd9Sstevel@tonic-gate {
12445*7c478bd9Sstevel@tonic-gate   sigset_t oldset;      /* The signals that were blocked on entry */
12446*7c478bd9Sstevel@tonic-gate                         /*  to this function */
12447*7c478bd9Sstevel@tonic-gate   int status;           /* The return status */
12448*7c478bd9Sstevel@tonic-gate /*
12449*7c478bd9Sstevel@tonic-gate  * Block all signals while accessing gl.
12450*7c478bd9Sstevel@tonic-gate  */
12451*7c478bd9Sstevel@tonic-gate   gl_mask_signals(gl, &oldset);
12452*7c478bd9Sstevel@tonic-gate /*
12453*7c478bd9Sstevel@tonic-gate  * Clear the terminal.
12454*7c478bd9Sstevel@tonic-gate  */
12455*7c478bd9Sstevel@tonic-gate   status = gl_clear_screen(gl, 1, NULL);
12456*7c478bd9Sstevel@tonic-gate /*
12457*7c478bd9Sstevel@tonic-gate  * Attempt to flush the clear-screen control codes to the terminal.
12458*7c478bd9Sstevel@tonic-gate  * If this doesn't complete the job, the next call to gl_get_line()
12459*7c478bd9Sstevel@tonic-gate  * will.
12460*7c478bd9Sstevel@tonic-gate  */
12461*7c478bd9Sstevel@tonic-gate   (void) gl_flush_output(gl);
12462*7c478bd9Sstevel@tonic-gate /*
12463*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask before returning.
12464*7c478bd9Sstevel@tonic-gate  */
12465*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
12466*7c478bd9Sstevel@tonic-gate   return status;
12467*7c478bd9Sstevel@tonic-gate }
12468*7c478bd9Sstevel@tonic-gate 
12469*7c478bd9Sstevel@tonic-gate /*.......................................................................
12470*7c478bd9Sstevel@tonic-gate  * This function must be called by any function that erases the input
12471*7c478bd9Sstevel@tonic-gate  * line.
12472*7c478bd9Sstevel@tonic-gate  *
12473*7c478bd9Sstevel@tonic-gate  * Input:
12474*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *   The resource object of gl_get_line().
12475*7c478bd9Sstevel@tonic-gate  */
12476*7c478bd9Sstevel@tonic-gate static void gl_line_erased(GetLine *gl)
12477*7c478bd9Sstevel@tonic-gate {
12478*7c478bd9Sstevel@tonic-gate   gl->displayed = 0;
12479*7c478bd9Sstevel@tonic-gate   gl->term_curpos = 0;
12480*7c478bd9Sstevel@tonic-gate   gl->term_len = 0;
12481*7c478bd9Sstevel@tonic-gate }
12482*7c478bd9Sstevel@tonic-gate 
12483*7c478bd9Sstevel@tonic-gate /*.......................................................................
12484*7c478bd9Sstevel@tonic-gate  * Append a specified line to the history list.
12485*7c478bd9Sstevel@tonic-gate  *
12486*7c478bd9Sstevel@tonic-gate  * Input:
12487*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *   The resource object of gl_get_line().
12488*7c478bd9Sstevel@tonic-gate  *  line     const char *   The line to be added.
12489*7c478bd9Sstevel@tonic-gate  * Output:
12490*7c478bd9Sstevel@tonic-gate  *  return          int     0 - OK.
12491*7c478bd9Sstevel@tonic-gate  *                          1 - Error.
12492*7c478bd9Sstevel@tonic-gate  */
12493*7c478bd9Sstevel@tonic-gate int gl_append_history(GetLine *gl, const char *line)
12494*7c478bd9Sstevel@tonic-gate {
12495*7c478bd9Sstevel@tonic-gate   sigset_t oldset;      /* The signals that were blocked on entry */
12496*7c478bd9Sstevel@tonic-gate                         /*  to this function */
12497*7c478bd9Sstevel@tonic-gate   int status;           /* The return status */
12498*7c478bd9Sstevel@tonic-gate /*
12499*7c478bd9Sstevel@tonic-gate  * Check the arguments.
12500*7c478bd9Sstevel@tonic-gate  */
12501*7c478bd9Sstevel@tonic-gate   if(!gl || !line) {
12502*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
12503*7c478bd9Sstevel@tonic-gate     return 1;
12504*7c478bd9Sstevel@tonic-gate   };
12505*7c478bd9Sstevel@tonic-gate /*
12506*7c478bd9Sstevel@tonic-gate  * Block all signals.
12507*7c478bd9Sstevel@tonic-gate  */
12508*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
12509*7c478bd9Sstevel@tonic-gate     return 1;
12510*7c478bd9Sstevel@tonic-gate /*
12511*7c478bd9Sstevel@tonic-gate  * Execute the private body of the function while signals are blocked.
12512*7c478bd9Sstevel@tonic-gate  */
12513*7c478bd9Sstevel@tonic-gate   status = _gl_append_history(gl, line);
12514*7c478bd9Sstevel@tonic-gate /*
12515*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
12516*7c478bd9Sstevel@tonic-gate  */
12517*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
12518*7c478bd9Sstevel@tonic-gate   return status;
12519*7c478bd9Sstevel@tonic-gate }
12520*7c478bd9Sstevel@tonic-gate 
12521*7c478bd9Sstevel@tonic-gate /*.......................................................................
12522*7c478bd9Sstevel@tonic-gate  * This is the private body of the public function, gl_append_history().
12523*7c478bd9Sstevel@tonic-gate  * It assumes that the caller has checked its arguments and blocked the
12524*7c478bd9Sstevel@tonic-gate  * delivery of signals.
12525*7c478bd9Sstevel@tonic-gate  */
12526*7c478bd9Sstevel@tonic-gate static int _gl_append_history(GetLine *gl, const char *line)
12527*7c478bd9Sstevel@tonic-gate {
12528*7c478bd9Sstevel@tonic-gate   int status =_glh_add_history(gl->glh, line, 0);
12529*7c478bd9Sstevel@tonic-gate   if(status)
12530*7c478bd9Sstevel@tonic-gate     _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG);
12531*7c478bd9Sstevel@tonic-gate   return status;
12532*7c478bd9Sstevel@tonic-gate }
12533*7c478bd9Sstevel@tonic-gate 
12534*7c478bd9Sstevel@tonic-gate /*.......................................................................
12535*7c478bd9Sstevel@tonic-gate  * Enable or disable the automatic addition of newly entered lines to the
12536*7c478bd9Sstevel@tonic-gate  * history list.
12537*7c478bd9Sstevel@tonic-gate  *
12538*7c478bd9Sstevel@tonic-gate  * Input:
12539*7c478bd9Sstevel@tonic-gate  *  gl          GetLine *   The resource object of gl_get_line().
12540*7c478bd9Sstevel@tonic-gate  *  enable          int     If true, subsequently entered lines will
12541*7c478bd9Sstevel@tonic-gate  *                          automatically be added to the history list
12542*7c478bd9Sstevel@tonic-gate  *                          before they are returned to the caller of
12543*7c478bd9Sstevel@tonic-gate  *                          gl_get_line(). If 0, the choice of how and
12544*7c478bd9Sstevel@tonic-gate  *                          when to archive lines in the history list,
12545*7c478bd9Sstevel@tonic-gate  *                          is left up to the calling application, which
12546*7c478bd9Sstevel@tonic-gate  *                          can do so via calls to gl_append_history().
12547*7c478bd9Sstevel@tonic-gate  * Output:
12548*7c478bd9Sstevel@tonic-gate  *  return          int     0 - OK.
12549*7c478bd9Sstevel@tonic-gate  *                          1 - Error.
12550*7c478bd9Sstevel@tonic-gate  */
12551*7c478bd9Sstevel@tonic-gate int gl_automatic_history(GetLine *gl, int enable)
12552*7c478bd9Sstevel@tonic-gate {
12553*7c478bd9Sstevel@tonic-gate   sigset_t oldset;      /* The signals that were blocked on entry */
12554*7c478bd9Sstevel@tonic-gate                         /*  to this function */
12555*7c478bd9Sstevel@tonic-gate /*
12556*7c478bd9Sstevel@tonic-gate  * Check the arguments.
12557*7c478bd9Sstevel@tonic-gate  */
12558*7c478bd9Sstevel@tonic-gate   if(!gl) {
12559*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
12560*7c478bd9Sstevel@tonic-gate     return 1;
12561*7c478bd9Sstevel@tonic-gate   };
12562*7c478bd9Sstevel@tonic-gate /*
12563*7c478bd9Sstevel@tonic-gate  * Block all signals.
12564*7c478bd9Sstevel@tonic-gate  */
12565*7c478bd9Sstevel@tonic-gate   if(gl_mask_signals(gl, &oldset))
12566*7c478bd9Sstevel@tonic-gate     return 1;
12567*7c478bd9Sstevel@tonic-gate /*
12568*7c478bd9Sstevel@tonic-gate  * Execute the private body of the function while signals are blocked.
12569*7c478bd9Sstevel@tonic-gate  */
12570*7c478bd9Sstevel@tonic-gate   gl->automatic_history = enable;
12571*7c478bd9Sstevel@tonic-gate /*
12572*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask.
12573*7c478bd9Sstevel@tonic-gate  */
12574*7c478bd9Sstevel@tonic-gate   gl_unmask_signals(gl, &oldset);
12575*7c478bd9Sstevel@tonic-gate   return 0;
12576*7c478bd9Sstevel@tonic-gate }
12577*7c478bd9Sstevel@tonic-gate 
12578*7c478bd9Sstevel@tonic-gate /*.......................................................................
12579*7c478bd9Sstevel@tonic-gate  * This is a public function that reads a single uninterpretted
12580*7c478bd9Sstevel@tonic-gate  * character from the user, without displaying anything.
12581*7c478bd9Sstevel@tonic-gate  *
12582*7c478bd9Sstevel@tonic-gate  * Input:
12583*7c478bd9Sstevel@tonic-gate  *  gl     GetLine *  A resource object previously returned by
12584*7c478bd9Sstevel@tonic-gate  *                    new_GetLine().
12585*7c478bd9Sstevel@tonic-gate  * Output:
12586*7c478bd9Sstevel@tonic-gate  *  return     int    The character that was read, or EOF if the read
12587*7c478bd9Sstevel@tonic-gate  *                    had to be aborted (in which case you can call
12588*7c478bd9Sstevel@tonic-gate  *                    gl_return_status() to find out why).
12589*7c478bd9Sstevel@tonic-gate  */
12590*7c478bd9Sstevel@tonic-gate int gl_read_char(GetLine *gl)
12591*7c478bd9Sstevel@tonic-gate {
12592*7c478bd9Sstevel@tonic-gate   int retval;   /* The return value of _gl_read_char() */
12593*7c478bd9Sstevel@tonic-gate /*
12594*7c478bd9Sstevel@tonic-gate  * This function can be called from application callback functions,
12595*7c478bd9Sstevel@tonic-gate  * so check whether signals have already been masked, so that we don't
12596*7c478bd9Sstevel@tonic-gate  * do it again, and overwrite gl->old_signal_set.
12597*7c478bd9Sstevel@tonic-gate  */
12598*7c478bd9Sstevel@tonic-gate   int was_masked = gl->signals_masked;
12599*7c478bd9Sstevel@tonic-gate /*
12600*7c478bd9Sstevel@tonic-gate  * Check the arguments.
12601*7c478bd9Sstevel@tonic-gate  */
12602*7c478bd9Sstevel@tonic-gate   if(!gl) {
12603*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
12604*7c478bd9Sstevel@tonic-gate     return EOF;
12605*7c478bd9Sstevel@tonic-gate   };
12606*7c478bd9Sstevel@tonic-gate /*
12607*7c478bd9Sstevel@tonic-gate  * Temporarily block all of the signals that we have been asked to trap.
12608*7c478bd9Sstevel@tonic-gate  */
12609*7c478bd9Sstevel@tonic-gate   if(!was_masked && gl_mask_signals(gl, &gl->old_signal_set))
12610*7c478bd9Sstevel@tonic-gate     return EOF;
12611*7c478bd9Sstevel@tonic-gate /*
12612*7c478bd9Sstevel@tonic-gate  * Perform the character reading task.
12613*7c478bd9Sstevel@tonic-gate  */
12614*7c478bd9Sstevel@tonic-gate   retval = _gl_read_char(gl);
12615*7c478bd9Sstevel@tonic-gate /*
12616*7c478bd9Sstevel@tonic-gate  * Restore the process signal mask to how it was when this function was
12617*7c478bd9Sstevel@tonic-gate  * first called.
12618*7c478bd9Sstevel@tonic-gate  */
12619*7c478bd9Sstevel@tonic-gate   if(!was_masked)
12620*7c478bd9Sstevel@tonic-gate     gl_unmask_signals(gl, &gl->old_signal_set);
12621*7c478bd9Sstevel@tonic-gate   return retval;
12622*7c478bd9Sstevel@tonic-gate }
12623*7c478bd9Sstevel@tonic-gate 
12624*7c478bd9Sstevel@tonic-gate /*.......................................................................
12625*7c478bd9Sstevel@tonic-gate  * This is the main body of the public function gl_read_char().
12626*7c478bd9Sstevel@tonic-gate  */
12627*7c478bd9Sstevel@tonic-gate static int _gl_read_char(GetLine *gl)
12628*7c478bd9Sstevel@tonic-gate {
12629*7c478bd9Sstevel@tonic-gate   int retval = EOF;  /* The return value */
12630*7c478bd9Sstevel@tonic-gate   int waserr = 0;    /* True if an error occurs */
12631*7c478bd9Sstevel@tonic-gate   char c;            /* The character read */
12632*7c478bd9Sstevel@tonic-gate /*
12633*7c478bd9Sstevel@tonic-gate  * This function can be called from application callback functions,
12634*7c478bd9Sstevel@tonic-gate  * so check whether signals have already been overriden, so that we don't
12635*7c478bd9Sstevel@tonic-gate  * overwrite the preserved signal handlers with gl_get_line()s. Also
12636*7c478bd9Sstevel@tonic-gate  * record whether we are currently in raw I/O mode or not, so that this
12637*7c478bd9Sstevel@tonic-gate  * can be left in the same state on leaving this function.
12638*7c478bd9Sstevel@tonic-gate  */
12639*7c478bd9Sstevel@tonic-gate   int was_overriden = gl->signals_overriden;
12640*7c478bd9Sstevel@tonic-gate   int was_raw = gl->raw_mode;
12641*7c478bd9Sstevel@tonic-gate /*
12642*7c478bd9Sstevel@tonic-gate  * Also keep a record of the direction of any I/O that gl_get_line()
12643*7c478bd9Sstevel@tonic-gate  * is awaiting, so that we can restore this status on return.
12644*7c478bd9Sstevel@tonic-gate  */
12645*7c478bd9Sstevel@tonic-gate   GlPendingIO old_pending_io = gl->pending_io;
12646*7c478bd9Sstevel@tonic-gate /*
12647*7c478bd9Sstevel@tonic-gate  * Assume that this call will successfully complete the input operation
12648*7c478bd9Sstevel@tonic-gate  * until proven otherwise.
12649*7c478bd9Sstevel@tonic-gate  */
12650*7c478bd9Sstevel@tonic-gate   gl_clear_status(gl);
12651*7c478bd9Sstevel@tonic-gate /*
12652*7c478bd9Sstevel@tonic-gate  * If this is the first call to this function or gl_get_line(),
12653*7c478bd9Sstevel@tonic-gate  * since new_GetLine(), complete any postponed configuration.
12654*7c478bd9Sstevel@tonic-gate  */
12655*7c478bd9Sstevel@tonic-gate   if(!gl->configured) {
12656*7c478bd9Sstevel@tonic-gate     (void) _gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE);
12657*7c478bd9Sstevel@tonic-gate     gl->configured = 1;
12658*7c478bd9Sstevel@tonic-gate   };
12659*7c478bd9Sstevel@tonic-gate /*
12660*7c478bd9Sstevel@tonic-gate  * Before installing our signal handler functions, record the fact
12661*7c478bd9Sstevel@tonic-gate  * that there are no pending signals.
12662*7c478bd9Sstevel@tonic-gate  */
12663*7c478bd9Sstevel@tonic-gate   gl_pending_signal = -1;
12664*7c478bd9Sstevel@tonic-gate /*
12665*7c478bd9Sstevel@tonic-gate  * Temporarily override the signal handlers of the calling program,
12666*7c478bd9Sstevel@tonic-gate  * so that we can intercept signals that would leave the terminal
12667*7c478bd9Sstevel@tonic-gate  * in a bad state.
12668*7c478bd9Sstevel@tonic-gate  */
12669*7c478bd9Sstevel@tonic-gate   if(!was_overriden)
12670*7c478bd9Sstevel@tonic-gate     waserr = gl_override_signal_handlers(gl);
12671*7c478bd9Sstevel@tonic-gate /*
12672*7c478bd9Sstevel@tonic-gate  * After recording the current terminal settings, switch the terminal
12673*7c478bd9Sstevel@tonic-gate  * into raw input mode, without redisplaying any partially entered input
12674*7c478bd9Sstevel@tonic-gate  * line.
12675*7c478bd9Sstevel@tonic-gate  */
12676*7c478bd9Sstevel@tonic-gate   if(!was_raw)
12677*7c478bd9Sstevel@tonic-gate     waserr = waserr || _gl_raw_io(gl, 0);
12678*7c478bd9Sstevel@tonic-gate /*
12679*7c478bd9Sstevel@tonic-gate  * Attempt to read the line. This will require more than one attempt if
12680*7c478bd9Sstevel@tonic-gate  * either a current temporary input file is opened by gl_get_input_line()
12681*7c478bd9Sstevel@tonic-gate  * or the end of a temporary input file is reached by gl_read_stream_line().
12682*7c478bd9Sstevel@tonic-gate  */
12683*7c478bd9Sstevel@tonic-gate   while(!waserr) {
12684*7c478bd9Sstevel@tonic-gate /*
12685*7c478bd9Sstevel@tonic-gate  * Read a line from a non-interactive stream?
12686*7c478bd9Sstevel@tonic-gate  */
12687*7c478bd9Sstevel@tonic-gate     if(gl->file_fp || !gl->is_term) {
12688*7c478bd9Sstevel@tonic-gate       retval = gl_read_stream_char(gl);
12689*7c478bd9Sstevel@tonic-gate       if(retval != EOF) {            /* Success? */
12690*7c478bd9Sstevel@tonic-gate 	break;
12691*7c478bd9Sstevel@tonic-gate       } else if(gl->file_fp) {  /* End of temporary input file? */
12692*7c478bd9Sstevel@tonic-gate 	gl_revert_input(gl);
12693*7c478bd9Sstevel@tonic-gate 	gl_record_status(gl, GLR_NEWLINE, 0);
12694*7c478bd9Sstevel@tonic-gate       } else {                  /* An error? */
12695*7c478bd9Sstevel@tonic-gate 	waserr = 1;
12696*7c478bd9Sstevel@tonic-gate 	break;
12697*7c478bd9Sstevel@tonic-gate       };
12698*7c478bd9Sstevel@tonic-gate     };
12699*7c478bd9Sstevel@tonic-gate /*
12700*7c478bd9Sstevel@tonic-gate  * Read from the terminal? Note that the above if() block may have
12701*7c478bd9Sstevel@tonic-gate  * changed gl->file_fp, so it is necessary to retest it here, rather
12702*7c478bd9Sstevel@tonic-gate  * than using an else statement.
12703*7c478bd9Sstevel@tonic-gate  */
12704*7c478bd9Sstevel@tonic-gate     if(!gl->file_fp && gl->is_term) {
12705*7c478bd9Sstevel@tonic-gate /*
12706*7c478bd9Sstevel@tonic-gate  * Flush any pending output to the terminal before waiting
12707*7c478bd9Sstevel@tonic-gate  * for the user to type a character.
12708*7c478bd9Sstevel@tonic-gate  */
12709*7c478bd9Sstevel@tonic-gate       if(_glq_char_count(gl->cq) > 0 && gl_flush_output(gl)) {
12710*7c478bd9Sstevel@tonic-gate 	retval = EOF;
12711*7c478bd9Sstevel@tonic-gate /*
12712*7c478bd9Sstevel@tonic-gate  * Read one character. Don't append it to the key buffer, since
12713*7c478bd9Sstevel@tonic-gate  * this would subseuqnely appear as bogus input to the line editor.
12714*7c478bd9Sstevel@tonic-gate  */
12715*7c478bd9Sstevel@tonic-gate       } else if(gl_read_terminal(gl, 0, &c) == 0) {
12716*7c478bd9Sstevel@tonic-gate /*
12717*7c478bd9Sstevel@tonic-gate  * Record the character for return.
12718*7c478bd9Sstevel@tonic-gate  */
12719*7c478bd9Sstevel@tonic-gate 	retval = c;
12720*7c478bd9Sstevel@tonic-gate /*
12721*7c478bd9Sstevel@tonic-gate  * In this mode, count each character as being a new key-sequence.
12722*7c478bd9Sstevel@tonic-gate  */
12723*7c478bd9Sstevel@tonic-gate 	gl->keyseq_count++;
12724*7c478bd9Sstevel@tonic-gate /*
12725*7c478bd9Sstevel@tonic-gate  * Delete the character that was read, from the key-press buffer.
12726*7c478bd9Sstevel@tonic-gate  */
12727*7c478bd9Sstevel@tonic-gate 	gl_discard_chars(gl, 1);
12728*7c478bd9Sstevel@tonic-gate       };
12729*7c478bd9Sstevel@tonic-gate       if(retval==EOF)
12730*7c478bd9Sstevel@tonic-gate 	waserr = 1;
12731*7c478bd9Sstevel@tonic-gate       else
12732*7c478bd9Sstevel@tonic-gate 	break;
12733*7c478bd9Sstevel@tonic-gate     };
12734*7c478bd9Sstevel@tonic-gate   };
12735*7c478bd9Sstevel@tonic-gate /*
12736*7c478bd9Sstevel@tonic-gate  * If an error occurred, but gl->rtn_status is still set to
12737*7c478bd9Sstevel@tonic-gate  * GLR_NEWLINE, change the status to GLR_ERROR. Otherwise
12738*7c478bd9Sstevel@tonic-gate  * leave it at whatever specific value was assigned by the function
12739*7c478bd9Sstevel@tonic-gate  * that aborted input. This means that only functions that trap
12740*7c478bd9Sstevel@tonic-gate  * non-generic errors have to remember to update gl->rtn_status
12741*7c478bd9Sstevel@tonic-gate  * themselves.
12742*7c478bd9Sstevel@tonic-gate  */
12743*7c478bd9Sstevel@tonic-gate   if(waserr && gl->rtn_status == GLR_NEWLINE)
12744*7c478bd9Sstevel@tonic-gate     gl_record_status(gl, GLR_ERROR, errno);
12745*7c478bd9Sstevel@tonic-gate /*
12746*7c478bd9Sstevel@tonic-gate  * Restore terminal settings, if they were changed by this function.
12747*7c478bd9Sstevel@tonic-gate  */
12748*7c478bd9Sstevel@tonic-gate   if(!was_raw && gl->io_mode != GL_SERVER_MODE)
12749*7c478bd9Sstevel@tonic-gate     _gl_normal_io(gl);
12750*7c478bd9Sstevel@tonic-gate /*
12751*7c478bd9Sstevel@tonic-gate  * Restore the signal handlers, if they were overriden by this function.
12752*7c478bd9Sstevel@tonic-gate  */
12753*7c478bd9Sstevel@tonic-gate   if(!was_overriden)
12754*7c478bd9Sstevel@tonic-gate     gl_restore_signal_handlers(gl);
12755*7c478bd9Sstevel@tonic-gate /*
12756*7c478bd9Sstevel@tonic-gate  * If this function gets aborted early, the errno value associated
12757*7c478bd9Sstevel@tonic-gate  * with the event that caused this to happen is recorded in
12758*7c478bd9Sstevel@tonic-gate  * gl->rtn_errno. Since errno may have been overwritten by cleanup
12759*7c478bd9Sstevel@tonic-gate  * functions after this, restore its value to the value that it had
12760*7c478bd9Sstevel@tonic-gate  * when the error condition occured, so that the caller can examine it
12761*7c478bd9Sstevel@tonic-gate  * to find out what happened.
12762*7c478bd9Sstevel@tonic-gate  */
12763*7c478bd9Sstevel@tonic-gate   errno = gl->rtn_errno;
12764*7c478bd9Sstevel@tonic-gate /*
12765*7c478bd9Sstevel@tonic-gate  * Error conditions are signalled to the caller, by setting the returned
12766*7c478bd9Sstevel@tonic-gate  * character to EOF.
12767*7c478bd9Sstevel@tonic-gate  */
12768*7c478bd9Sstevel@tonic-gate   if(gl->rtn_status != GLR_NEWLINE)
12769*7c478bd9Sstevel@tonic-gate     retval = EOF;
12770*7c478bd9Sstevel@tonic-gate /*
12771*7c478bd9Sstevel@tonic-gate  * Restore the indication of what direction of I/O gl_get_line()
12772*7c478bd9Sstevel@tonic-gate  * was awaiting before this call.
12773*7c478bd9Sstevel@tonic-gate  */
12774*7c478bd9Sstevel@tonic-gate   gl->pending_io = old_pending_io;
12775*7c478bd9Sstevel@tonic-gate /*
12776*7c478bd9Sstevel@tonic-gate  * Return the acquired character.
12777*7c478bd9Sstevel@tonic-gate  */
12778*7c478bd9Sstevel@tonic-gate   return retval;
12779*7c478bd9Sstevel@tonic-gate }
12780*7c478bd9Sstevel@tonic-gate 
12781*7c478bd9Sstevel@tonic-gate /*.......................................................................
12782*7c478bd9Sstevel@tonic-gate  * Reset the GetLine completion status. This function should be called
12783*7c478bd9Sstevel@tonic-gate  * at the start of gl_get_line(), gl_read_char() and gl_query_char()
12784*7c478bd9Sstevel@tonic-gate  * to discard the completion status and non-zero errno value of any
12785*7c478bd9Sstevel@tonic-gate  * preceding calls to these functions.
12786*7c478bd9Sstevel@tonic-gate  *
12787*7c478bd9Sstevel@tonic-gate  * Input:
12788*7c478bd9Sstevel@tonic-gate  *  gl       GetLine *  The resource object of this module.
12789*7c478bd9Sstevel@tonic-gate  */
12790*7c478bd9Sstevel@tonic-gate static void gl_clear_status(GetLine *gl)
12791*7c478bd9Sstevel@tonic-gate {
12792*7c478bd9Sstevel@tonic-gate   gl_record_status(gl, GLR_NEWLINE, 0);
12793*7c478bd9Sstevel@tonic-gate }
12794*7c478bd9Sstevel@tonic-gate 
12795*7c478bd9Sstevel@tonic-gate /*.......................................................................
12796*7c478bd9Sstevel@tonic-gate  * When an error or other event causes gl_get_line() to return, this
12797*7c478bd9Sstevel@tonic-gate  * function should be called to record information about what
12798*7c478bd9Sstevel@tonic-gate  * happened, including the value of errno and the value that
12799*7c478bd9Sstevel@tonic-gate  * gl_return_status() should return.
12800*7c478bd9Sstevel@tonic-gate  *
12801*7c478bd9Sstevel@tonic-gate  * Input:
12802*7c478bd9Sstevel@tonic-gate  *  gl                GetLine *  The resource object of this module.
12803*7c478bd9Sstevel@tonic-gate  *  rtn_status GlReturnStatus    The completion status. To clear a
12804*7c478bd9Sstevel@tonic-gate  *                               previous abnormal completion status,
12805*7c478bd9Sstevel@tonic-gate  *                               specify GLR_NEWLINE (this is what
12806*7c478bd9Sstevel@tonic-gate  *                               gl_clear_status() does).
12807*7c478bd9Sstevel@tonic-gate  *  rtn_errno             int    The associated value of errno.
12808*7c478bd9Sstevel@tonic-gate  */
12809*7c478bd9Sstevel@tonic-gate static void gl_record_status(GetLine *gl, GlReturnStatus rtn_status,
12810*7c478bd9Sstevel@tonic-gate 			     int rtn_errno)
12811*7c478bd9Sstevel@tonic-gate {
12812*7c478bd9Sstevel@tonic-gate /*
12813*7c478bd9Sstevel@tonic-gate  * If rtn_status==GLR_NEWLINE, then this resets the completion status, so we
12814*7c478bd9Sstevel@tonic-gate  * should always heed this. Otherwise, only record the first abnormal
12815*7c478bd9Sstevel@tonic-gate  * condition that occurs after such a reset.
12816*7c478bd9Sstevel@tonic-gate  */
12817*7c478bd9Sstevel@tonic-gate   if(rtn_status == GLR_NEWLINE || gl->rtn_status == GLR_NEWLINE) {
12818*7c478bd9Sstevel@tonic-gate     gl->rtn_status = rtn_status;
12819*7c478bd9Sstevel@tonic-gate     gl->rtn_errno = rtn_errno;
12820*7c478bd9Sstevel@tonic-gate   };
12821*7c478bd9Sstevel@tonic-gate }
12822*7c478bd9Sstevel@tonic-gate 
12823