summaryrefslogtreecommitdiff
path: root/term/munge.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1995-08-31 20:01:44 +0000
committerMichael I. Bushnell <mib@gnu.org>1995-08-31 20:01:44 +0000
commit941c81aa67cdaf9c63f4835e61d33c182db5237b (patch)
tree05f62a9fc70a5eb5cf58d54bd98ffe11ee7cb8f7 /term/munge.c
parent8832237df37b394f990e0746f2702f6400d1359e (diff)
Initial revision
Diffstat (limited to 'term/munge.c')
-rw-r--r--term/munge.c725
1 files changed, 725 insertions, 0 deletions
diff --git a/term/munge.c b/term/munge.c
new file mode 100644
index 00000000..31f0bdee
--- /dev/null
+++ b/term/munge.c
@@ -0,0 +1,725 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "term.h"
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Number of characters in the rawq which have been
+ echoed continuously without intervening output. */
+int echo_qsize;
+
+/* Where the output_psize was when echo_qsize was last 0. */
+int echo_pstart;
+
+/* Actually drop character onto output queue. This should be the
+ only place where we actually enqueue characters on the output queue;
+ it is responsible for keeping track of cursor positions. */
+inline void
+poutput (int c)
+{
+ if (termflags & FLUSH_OUTPUT)
+ return; /* never mind */
+
+ if ((c >= ' ') && (c < '\177'))
+ output_psize++;
+ else if (c == '\r')
+ output_psize = 0;
+ else if (c == '\t')
+ output_psize += (output_psize + 8) % 8;
+ else if (c == '\b')
+ output_psize--;
+
+ enqueue (&outputq, c);
+}
+
+/* Place C on output queue, doing normal output processing.
+ This is used for write, never for echo. */
+void
+output_character (int c)
+{
+ int oflag = termstate.c_oflag;
+
+ /* One might think we should turn of INHDERASE here, but, no
+ in U*x it is only turned off by echoed characters.
+ See echo_char in input.c. */
+ if (oflag & OPOST)
+ {
+ /* Characters we write specially */
+ if ((oflag & ONLCR) && c == '\n')
+ {
+ poutput ('\r');
+ poutput ('\n');
+ }
+ else if ((oflag & OXTABS) && c == '\t')
+ {
+ poutput (' ');
+ while (output_psize % 8)
+ poutput (' ');
+ }
+ else if ((oflag & ONOEOT) && c == CHAR_EOT)
+ ;
+ else if ((oflag & OTILDE) && c == '~')
+ {
+ poutput ('\\');
+ poutput ('`');
+ }
+ else if ((oflag & OLCASE) && isalpha (c))
+ {
+ if (isupper (c))
+ poutput ('\\');
+ else
+ c = toupper (c);
+ poutput (c);
+ }
+ else
+ poutput (c);
+ }
+ else
+ poutput (c);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+}
+
+/* Report the width of character C as printed by output_character,
+ if output_psize were at LOC. . */
+int
+output_width (int c, int loc)
+{
+ int oflag = termstate.c_oflag;
+
+ if (oflag & OPOST)
+ {
+ if ((oflag & OTILDE) && c == '~')
+ return 2;
+ if ((oflag & OLCASE) && isalpha (c) && isupper (c))
+ return 2;
+ }
+ if (c == '\t')
+ {
+ int n = loc + 1;
+ while (n % 8)
+ n++;
+ return n;
+ }
+ if ((c >= ' ') && (c < '\177'))
+ return 1;
+ return 0;
+}
+
+
+
+/* For ICANON mode, this holds the edited line. */
+struct queue *rawq;
+
+/* For each character in this table, if the element is set, then
+ the character is EVEN */
+char const char_parity[] =
+{
+ 1, 0, 0, 1, 0, 1, 1, 0, /* nul - bel */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* bs - si */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* dle - etb */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* can - us */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* sp - ' */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* ( - / */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* 0 - 7 */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* 8 - ? */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* @ - G */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* H - O */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* P - W */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* X - _ */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* ` - g */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* h - o */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* p - w */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* x - del */
+};
+#define checkevenpar(c) (((c)&0x80) \
+ ? !char_parity[(c)&0x7f] \
+ : char_parity[(c)&0x7f])
+#define checkoddpar(c) (((c)&0x80) \
+ ? char_parity[(c)&0x7f] \
+ : !char_parity[(c)&0x7f])
+
+
+
+/* These functions are used by both echo and erase code */
+
+/* Tell if we should echo a character at all */
+static inline int
+echo_p (char c, int quoted)
+{
+ return ((termstate.c_lflag & ECHO)
+ || (c == '\n' && (termstate.c_lflag & ECHONL) && !quoted));
+}
+
+/* Tell if this character deserves double-width cntrl processing */
+static inline int
+echo_double (char c, int quoted)
+{
+ return (iscntrl (c) && (termstate.c_lflag & ECHOCTL)
+ && !((c == '\n' || c == '\t') && !quoted));
+}
+
+/* Do a single C-h SPC C-h sequence */
+static inline void
+write_erase_sequence ()
+{
+ poutput ('\b');
+ poutput (' ');
+ poutput ('\b');
+}
+
+/* Echo a single character to the output */
+/* If this is an echo of a character which is being hard-erased,
+ set hderase. If this is a newline or tab which should not receive
+ their normal special processing, set quoted. */
+static void
+echo_char (char c, int hderase, int quoted)
+{
+ echo_qsize++;
+
+ if (echo_p (c, quoted))
+ {
+ if (!hderase && (termflags & INSIDE_HDERASE))
+ {
+ poutput ('/');
+ termflags &= ~INSIDE_HDERASE;
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+ }
+
+ if (hderase && !(termflags & INSIDE_HDERASE))
+ {
+ poutput ('\\');
+ termflags |= INSIDE_HDERASE;
+ }
+
+ /* Control characters that should use caret-letter */
+ if (echo_double (c, quoted))
+ {
+ poutput ('^');
+ poutput (c + ('A' - CHAR_SOH));
+ }
+ else
+ poutput (c);
+ }
+}
+
+
+/* Re-echo the current rawq preceded by the VREPRINT char. */
+static inline void
+reprint_line ()
+{
+ short *cp;
+
+ if (termstate.c_cc[VREPRINT] != _POSIX_VDISABLE)
+ echo_char (termstate.c_cc[VREPRINT], 0, 0);
+ else
+ echo_char (CHAR_DC3, 0, 0);
+ echo_char ('\n', 0, 0);
+
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+
+ for (cp = rawq->cs; cp != rawq->ce; cp++)
+ echo_char (unquote_char (*cp), 0, char_quoted_p (*cp));
+}
+
+/* Erase a single character off the end of the rawq, and delete
+ it from the screen appropriately. Set ERASE_CHAR if this
+ is being done by the VERASE character, to that character. */
+static void
+erase_1 (char erase_char)
+{
+ int quoted;
+ char c;
+ quoted_char cq;
+
+ if (qsize (rawq) == 0)
+ return;
+
+ cq = queue_erase (rawq);
+ c = unquote_char (cq);
+ quoted = char_quoted_p (cq);
+
+ if (!echo_p (c, quoted))
+ return;
+
+ /* The code for WERASE knows that we echo erase_char iff
+ !ECHOPRT && !ECHO. */
+
+ if (echo_qsize--)
+ {
+ if (termstate.c_lflag & ECHOPRT)
+ echo_char (c, 1, quoted);
+ else if (!(termstate.c_lflag & ECHOE) && erase_char)
+ echo_char (erase_char, 0, 0);
+ else
+ {
+ int nerase;
+
+ if (echo_double (c, quoted))
+ nerase = 2;
+ else if (c == '\t')
+ {
+ quoted_char *cp;
+ int loc = echo_pstart;
+
+ for (cp = rawq->ce - echo_qsize; cp != rawq->ce; cp++)
+ loc += (echo_double (unquote_char (*cp), char_quoted_p (*cp))
+ ? 2
+ : output_width (*cp, loc));
+ nerase = output_psize - loc;
+ }
+ else
+ nerase = output_width (c, output_psize);
+
+ while (nerase--)
+ write_erase_sequence ();
+ }
+ if (echo_qsize == 0)
+ assert (echo_pstart == output_psize);
+ }
+ else
+ reprint_line ();
+}
+
+
+/* Place newly input character C on the input queue. If all remaining
+ pending input characters should be dropped, return 1; else return
+ 0. */
+int
+input_character (int c)
+{
+ int lflag = termstate.c_lflag;
+ int iflag = termstate.c_iflag;
+ int cflag = termstate.c_cflag;
+ cc_t *cc = termstate.c_cc;
+ struct queue **qp = (lflag & ICANON) ? &rawq : &inputq;
+ int flush = 0;
+
+ /* Handle parity errors */
+ if ((iflag & INPCK)
+ && ((cflag & PARODD) ? checkoddpar (c) : checkevenpar (c)))
+ {
+ if (iflag & IGNPAR)
+ goto alldone;
+ else if (iflag & PARMRK)
+ {
+ enqueue_quote (qp, CHAR_USER_QUOTE);
+ enqueue_quote (qp, '\0');
+ enqueue_quote (qp, c);
+ goto alldone;
+ }
+ else
+ c = 0;
+ }
+
+ /* Check to see if we should send IXOFF */
+ if ((iflag & IXOFF)
+ && !qavail (*qp)
+ && (cc[VSTOP] != _POSIX_VDISABLE))
+ {
+ poutput (cc[VSTOP]);
+ termflags |= SENT_VSTOP;
+ }
+
+ /* Character mutations */
+ if (!(iflag & ISTRIP) && (iflag & PARMRK) && (c == CHAR_USER_QUOTE))
+ enqueue_quote (qp, CHAR_USER_QUOTE); /* cause doubling */
+
+ if (iflag & ISTRIP)
+ c &= 0x7f;
+
+ /* Handle LNEXT right away */
+ if (termflags & LAST_LNEXT)
+ {
+ enqueue_quote (qp, c);
+ echo_char (c, 0, 1);
+ termflags &= ~LAST_LNEXT;
+ }
+
+ /* Mutate ILCASE */
+ if ((iflag & ILCASE) && isalpha(c))
+ {
+ if (termflags & LAST_SLASH)
+ erase_1 (0); /* remove the slash from input */
+ else
+ c = isupper(c) ? tolower (c) : c;
+ }
+
+ /* IEXTEN control chars */
+ if (lflag & IEXTEN)
+ {
+ if (CCEQ (cc[VLNEXT], c))
+ {
+ if (lflag & ECHO)
+ {
+ poutput ('^');
+ poutput ('\b');
+ }
+ termflags |= LAST_LNEXT;
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VDISCARD], c))
+ {
+ if (termflags & FLUSH_OUTPUT)
+ termflags &= ~FLUSH_OUTPUT;
+ else
+ {
+ drop_output ();
+ termflags |= FLUSH_OUTPUT;
+ }
+ }
+ }
+
+ /* Signals */
+ if (lflag & ISIG)
+ {
+ if (CCEQ (cc[VINTR], c) || CCEQ (cc[VQUIT], c))
+ {
+ if (!(lflag & NOFLSH))
+ {
+ drop_output ();
+ flush = 1;
+ }
+ echo_char (c, 0, 0);
+ send_signal (CCEQ (cc[VINTR], c) ? SIGINT : SIGQUIT);
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VSUSP], c))
+ {
+ if (!(lflag & NOFLSH))
+ flush = 1;
+ echo_char (c, 0, 0);
+ send_signal (SIGTSTP);
+ goto alldone;
+ }
+ }
+
+ /* IXON */
+ if (iflag & IXON)
+ {
+ if (CCEQ (cc[VSTOP], c))
+ {
+ termflags |= USER_OUTPUT_SUSP;
+
+ if (!(CCEQ(cc[VSTART], c))) /* toggle if VSTART == VSTOP */
+ /* Alldone code always turns off USER_OUTPUT_SUSP. */
+ goto alldone;
+
+ return flush;
+ }
+ if (CCEQ (cc[VSTART], c))
+ goto alldone;
+ }
+
+ /* Newline and carriage-return frobbing */
+ if (c == '\r')
+ {
+ if (iflag & ICRNL)
+ c = '\n';
+ else if (iflag & IGNCR)
+ goto alldone;
+ }
+ else if ((c == '\n') && (iflag & INLCR))
+ c = '\r';
+
+ /* Canonical mode processing */
+ if (lflag & ICANON)
+ {
+ if (CCEQ (cc[VERASE], c))
+ {
+ if (qsize(rawq))
+ erase_1 (c);
+ if (!(termflags & LAST_SLASH)
+ || !(lflag & IEXTEN))
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VKILL], c))
+ {
+ if (!(termflags & LAST_SLASH)
+ || !(lflag & IEXTEN))
+ {
+ if ((lflag & ECHOKE) && !(lflag & ECHOPRT)
+ && (echo_qsize == qsize (rawq)))
+ {
+ while (output_psize > echo_pstart)
+ write_erase_sequence ();
+ }
+ else
+ {
+ echo_char (c, 0, 0);
+ if ((lflag & ECHOK) || (lflag & ECHOKE))
+ echo_char ('\n', 0, 0);
+ }
+ clear_queue (rawq);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+ termflags &= ~(LAST_SLASH|LAST_LNEXT|INSIDE_HDERASE);
+ goto alldone;
+ }
+ else
+ erase_1 (0); /* remove \ */
+ }
+
+ if (CCEQ (cc[VWERASE], c))
+ {
+ /* If we are not going to echo the erase, then
+ echo a WERASE character right now. (If we
+ passed it to erase_1; it would echo it multiple
+ times.) */
+ if (!(lflag & (ECHOPRT|ECHOE)))
+ echo_char (cc[VWERASE], 0, 1);
+
+ /* Erase whitespace */
+ while (qsize (rawq) && isblank (unquote_char (rawq->ce[-1])))
+ erase_1 (0);
+
+ /* Erase word. */
+ if (lflag & ALTWERASE)
+ /* For ALTWERASE, we erase back to the first blank */
+ while (qsize (rawq) && !isblank (unquote_char (rawq->ce[-1])))
+ erase_1 (0);
+ else
+ /* For regular WERASE, we erase back to the first nonalpha/_ */
+ while (qsize (rawq) && !isblank (unquote_char (rawq->ce[-1]))
+ && (isalnum (unquote_char (rawq->ce[-1]))
+ || (unquote_char (rawq->ce[-1]) != '_')))
+ erase_1 (0);
+
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VREPRINT], c) && (lflag & IEXTEN))
+ {
+ reprint_line ();
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VSTATUS], c) && (lflag & ISIG) && (lflag & IEXTEN))
+ {
+ send_signal (SIGINFO);
+ goto alldone;
+ }
+ }
+
+ /* Now we have a character intended as input. See if it will fit. */
+ if (!qavail (*qp))
+ {
+ if (iflag & IMAXBEL)
+ poutput ('\a');
+ else
+ {
+ /* Drop everything */
+ drop_output ();
+ flush = 1;
+ }
+ goto alldone;
+ }
+
+ /* Echo */
+ echo_char (c, 0, 0);
+ if (CCEQ (cc[VEOF], c) && (lflag & ECHO))
+ {
+ /* Special bizzare echo processing for VEOF character. */
+ int n;
+ n = echo_double (c, 0) ? 2 : output_width (c, output_psize);
+ while (n--)
+ poutput ('\b');
+ }
+
+ /* Put character on input queue */
+ enqueue (qp, c);
+
+ /* Check for break characters in canonical input processing */
+ if (lflag & ICANON)
+ {
+ if (CCEQ (cc[VEOL], c)
+ || CCEQ (cc[VEOL2], c)
+ || CCEQ (cc[VEOF], c)
+ || c == '\n')
+ /* Make input available */
+ while (qsize (rawq))
+ enqueue (&inputq, dequeue (rawq));
+ }
+
+alldone:
+ /* Restart output */
+ if ((iflag & IXANY) || (CCEQ (cc[VSTART], c)))
+ termflags &= ~USER_OUTPUT_SUSP;
+ start_output ();
+
+ return flush;
+}
+
+
+/* This is called by the lower half when a break is received. */
+void
+input_break ()
+{
+ struct queue **qp = termstate.c_lflag & ICANON ? &rawq : &inputq;
+
+ /* Don't do anything if IGNBRK is set. */
+ if (termstate.c_iflag & IGNBRK)
+ return;
+
+ /* If BRKINT is set, then flush queues and send SIGINT. */
+ if (termstate.c_iflag & BRKINT)
+ {
+ drop_output ();
+ /* XXX drop pending input How?? */
+ send_signal (SIGINT);
+ return;
+ }
+
+ /* A break is then read as a null byte; marked specially if PARMRK is set. */
+ if (termstate.c_iflag & PARMRK)
+ {
+ enqueue_quote (qp, CHAR_USER_QUOTE);
+ enqueue_quote (qp, '\0');
+ }
+ enqueue_quote (qp, '\0');
+}
+
+/* Called when a character is recived with a framing error. */
+void
+input_framing_error (int c)
+{
+ struct queue **qp = termstate.c_lflag & ICANON ? &rawq : &inputq;
+
+ /* Ignore it if IGNPAR is set. */
+ if (termstate.c_iflag & IGNPAR)
+ return;
+
+ /* If PARMRK is set, pass it specially marked. */
+ if (termstate.c_iflag & PARMRK)
+ {
+ enqueue_quote (qp, CHAR_USER_QUOTE);
+ enqueue_quote (qp, '\0');
+ enqueue_quote (qp, c);
+ }
+ else
+ /* Otherwise, it looks like a null. */
+ enqueue_quote (qp, '\0');
+}
+
+/* Copy the characters in RAWQ to the end of INPUTQ and clear RAWQ. */
+void
+copy_rawq ()
+{
+ while (qsize (rawq))
+ enqueue (&inputq, dequeue (rawq));
+}
+
+/* Process all the characters in INPUTQ as if they had just been read. */
+void
+rescan_inputq ()
+{
+ short *buf;
+ int i, n;
+
+ n = qsize (inputq);
+ buf = alloca (n * sizeof (quoted_char));
+ bcopy (inputq->cs, buf, n * sizeof (quoted_char));
+ clear_queue (inputq);
+
+ for (i = 0; i < n; i++)
+ input_character (unquote_char (buf[i]));
+}
+
+void
+drop_output ()
+{
+ clear_queue (outputq);
+ abandon_physical_output ();
+}
+
+error_t
+drain_output ()
+{
+ int cancel = 0;
+
+ while ((qsize (outputq) || pending_output_size ())
+ && (!(termflags & NO_CARRIER) || (termstate.c_cflag & CLOCAL))
+ && !cancel)
+ cancel = hurd_condition_wait (outputq->wait, &global_lock);
+
+ return cancel ? EINTR : 0;
+}
+
+/* Create and return a new queue. */
+struct queue *
+create_queue (int size, int lowat, int hiwat)
+{
+ struct queue *q;
+
+ q = malloc (sizeof (struct queue) + size * sizeof (quoted_char));
+ q->susp = 0;
+ q->lowat = lowat;
+ q->hiwat = hiwat;
+ q->cs = q->ce = q->array;
+ q->arraylen = size;
+ q->wait = malloc (sizeof (struct condition));
+ condition_init (q->wait);
+ return q;
+}
+
+/* Make Q able to have more characters added to it. */
+struct queue *
+reallocate_queue (struct queue *q)
+{
+ int len;
+ struct queue *newq;
+
+ len = qsize (q);
+
+ if (len < q->arraylen / 2)
+ {
+ /* Shift the characters to the front of
+ the queue. */
+ memmove (q->array, q->cs, len * sizeof (quoted_char));
+ q->cs = q->array;
+ q->ce = q->cs + len;
+ }
+ else
+ {
+ /* Make the queue twice as large. */
+ newq = malloc (sizeof (struct queue)
+ + q->arraylen * 2 * sizeof (quoted_char));
+ newq->susp = q->susp;
+ newq->lowat = q->lowat;
+ newq->hiwat = q->hiwat;
+ newq->cs = newq->array;
+ newq->ce = newq->array + len;
+ newq->arraylen = q->arraylen * 2;
+ newq->wait = q->wait;
+ memmove (newq->array, q->cs, len * sizeof (quoted_char));
+ free (q);
+ q = newq;
+ }
+ return q;
+}