Print this page
    
9589 ldterm_dosig() can send empty mblks downstream
Reviewed by: Dominik Hassler <hadfl@omniosce.org>
Reviewed by: Igor Kozhukhov <igor@dilos.org>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Approved by: Gordon Ross <gwr@nexenta.com>
9042 multiples of tty streams modules cause weirdness
Reviewed by: Randy Fishel <randyf@sibernet.com>
Reviewed by: Carlos Neira <cneirabustos@gmail.com>
Approved by: Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
NEX-16819 loader UEFI support
Includes work by Toomas Soome <tsoome@me.com>
Upstream commits:
    loader: pxe receive cleanup
    9475 libefi: Do not return only if ReceiveFilter
    installboot: should support efi system partition
    8931 boot1.efi: scan all display modes rather than
    loader: spinconsole updates
    loader: gfx experiment to try GOP Blt() function.
    sha1 build test
    loader: add sha1 hash calculation
    common/sha1: update for loader build
    loader: biosdisk rework
    uts: 32-bit kernel FB needs mapping in low memory
    uts: add diag-device
    uts: boot console mirror with diag-device
    uts: enable very early console on ttya
    kmdb: add diag-device as input/output device
    uts: test VGA memory exclusion from mapping
    uts: clear boot mapping and protect boot pages test
    uts: add dboot map debug printf
    uts: need to release FB pages in release_bootstrap()
    uts: add screenmap ioctl
    uts: update sys/queue.h
    loader: add illumos uts/common to include path
    loader: tem/gfx font cleanup
    loader: vbe checks
    uts: gfx_private set KD_TEXT when KD_RESETTEXT is
    uts: gfx 8-bit update
    loader: gfx 8-bit fix
    loader: always set media size from partition.
    uts: MB2 support for 32-bit kernel
    loader: x86 should have tem 80x25
    uts: x86 should have tem 80x25
    uts: font update
    loader: font update
    uts: tem attributes
    loader: tem.c comment added
    uts: use font module
    loader: add font module
    loader: build rules for new font setup
    uts: gfx_private update for new font structure
    uts: early boot update for new font structure
    uts: font update
    uts: font build rules update for new fonts
    uts: tem update to new font structure
    loader: module.c needs to include tem_impl.h
    uts: gfx_private 8x16 font rework
    uts: make font_lookup public
    loader: font rework
    uts: font rework
    9259 libefi: efi_alloc_and_read should check for PMBR
    uts: tem utf-8 support
    loader: implement tem utf-8 support
    loader: tem should be able to display UTF-8
    7784 uts: console input should support utf-8
    7796 uts: ldterm default to utf-8
    uts: do not reset serial console
    uts: set up colors even if tem is not console
    uts: add type for early boot properties
    uts: gfx_private experiment with drm and vga
    uts: gfx_private should use setmode drm callback.
    uts: identify FB types and set up gfx_private based
    loader: replace gop and vesa with framebuffer
    uts: boot needs simple tem to support mdb
    uts: boot_keyboard should emit esc sequences for
    uts: gfx_private FB showuld be written by line
    kmdb: set terminal window size
    uts: gfx_private needs to keep track of early boot FB
    pnglite: move pnglite to usr/src/common
    loader: gfx_fb
    ficl-sys: add gfx primitives
    loader: add illumos.png logo
    ficl: add fb-putimage
    loader: add png support
    loader: add alpha blending for gfx_fb
    loader: use term-drawrect for menu frame
    ficl: add simple gfx words
    uts: provide fb_info via fbgattr dev_specific array.
    uts: gfx_private add alpha blending
    uts: update sys/ascii.h
    uts: tem OSC support (incomplete)
    uts: implement env module support and use data from
    uts: tem get colors from early boot data
    loader: use crc32 from libstand (libz)
    loader: optimize for size
    loader: pass tem info to the environment
    loader: import tem for loader console
    loader: UEFI loader needs to set ISADIR based on
    loader: need UEFI32 support
    8918 loader.efi: add vesa edid support
    uts: tem_safe_pix_clear_prom_output() should only
    uts: tem_safe_pix_clear_entire_screen() should use
    uts: tem_safe_check_first_time() should query cursor
    uts: tem implement cls callback & visual_io v4
    uts: gfx_vgatext use block cursor for vgatext
    uts: gfx_private implement cls callback & visual_io
    uts: gfx_private bitmap framebuffer implementation
    uts: early start frame buffer console support
    uts: font functions should check the input char
    uts: font rendering should support 16/24/32bit depths
    uts: use smallest font as fallback default.
    uts: update terminal dimensions based on selected
    7834 uts: vgatext should use gfx_private
    uts: add spacing property to 8859-1.bdf
    terminfo: add underline for sun-color
    terminfo: sun-color has 16 colors
    uts: add font load callback type
    loader: do not repeat int13 calls with error 0x20 and
    8905 loader: add skein/edonr support
    8904 common/crypto: make skein and edonr loader
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Revert "NEX-16819 loader UEFI support"
This reverts commit ec06b9fc617b99234e538bf2e7e4d02a24993e0c.
Reverting due to failures in the zfs-tests and the sharefs-tests
NEX-16819 loader UEFI support
Includes work by Toomas Soome <tsoome@me.com>
Upstream commits:
    loader: pxe receive cleanup
    9475 libefi: Do not return only if ReceiveFilter
    installboot: should support efi system partition
    8931 boot1.efi: scan all display modes rather than
    loader: spinconsole updates
    loader: gfx experiment to try GOP Blt() function.
    sha1 build test
    loader: add sha1 hash calculation
    common/sha1: update for loader build
    loader: biosdisk rework
    uts: 32-bit kernel FB needs mapping in low memory
    uts: add diag-device
    uts: boot console mirror with diag-device
    uts: enable very early console on ttya
    kmdb: add diag-device as input/output device
    uts: test VGA memory exclusion from mapping
    uts: clear boot mapping and protect boot pages test
    uts: add dboot map debug printf
    uts: need to release FB pages in release_bootstrap()
    uts: add screenmap ioctl
    uts: update sys/queue.h
    loader: add illumos uts/common to include path
    loader: tem/gfx font cleanup
    loader: vbe checks
    uts: gfx_private set KD_TEXT when KD_RESETTEXT is
    uts: gfx 8-bit update
    loader: gfx 8-bit fix
    loader: always set media size from partition.
    uts: MB2 support for 32-bit kernel
    loader: x86 should have tem 80x25
    uts: x86 should have tem 80x25
    uts: font update
    loader: font update
    uts: tem attributes
    loader: tem.c comment added
    uts: use font module
    loader: add font module
    loader: build rules for new font setup
    uts: gfx_private update for new font structure
    uts: early boot update for new font structure
    uts: font update
    uts: font build rules update for new fonts
    uts: tem update to new font structure
    loader: module.c needs to include tem_impl.h
    uts: gfx_private 8x16 font rework
    uts: make font_lookup public
    loader: font rework
    uts: font rework
    libefi: efi_alloc_and_read should check for PMBR
    uts: tem utf-8 support
    loader: implement tem utf-8 support
    loader: tem should be able to display UTF-8
    7784 uts: console input should support utf-8
    7796 uts: ldterm default to utf-8
    uts: do not reset serial console
    uts: set up colors even if tem is not console
    uts: add type for early boot properties
    uts: gfx_private experiment with drm and vga
    uts: gfx_private should use setmode drm callback.
    uts: identify FB types and set up gfx_private based
    loader: replace gop and vesa with framebuffer
    uts: boot needs simple tem to support mdb
    uts: boot_keyboard should emit esc sequences for
    uts: gfx_private FB showuld be written by line
    kmdb: set terminal window size
    uts: gfx_private needs to keep track of early boot FB
    pnglite: move pnglite to usr/src/common
    loader: gfx_fb
    ficl-sys: add gfx primitives
    loader: add illumos.png logo
    ficl: add fb-putimage
    loader: add png support
    loader: add alpha blending for gfx_fb
    loader: use term-drawrect for menu frame
    ficl: add simple gfx words
    uts: provide fb_info via fbgattr dev_specific array.
    uts: gfx_private add alpha blending
    uts: update sys/ascii.h
    uts: tem OSC support (incomplete)
    uts: implement env module support and use data from
    uts: tem get colors from early boot data
    loader: use crc32 from libstand (libz)
    loader: optimize for size
    loader: pass tem info to the environment
    loader: import tem for loader console
    loader: UEFI loader needs to set ISADIR based on
    loader: need UEFI32 support
    8918 loader.efi: add vesa edid support
    uts: tem_safe_pix_clear_prom_output() should only
    uts: tem_safe_pix_clear_entire_screen() should use
    uts: tem_safe_check_first_time() should query cursor
    uts: tem implement cls callback & visual_io v4
    uts: gfx_vgatext use block cursor for vgatext
    uts: gfx_private implement cls callback & visual_io
    uts: gfx_private bitmap framebuffer implementation
    uts: early start frame buffer console support
    uts: font functions should check the input char
    uts: font rendering should support 16/24/32bit depths
    uts: use smallest font as fallback default.
    uts: update terminal dimensions based on selected
    7834 uts: vgatext should use gfx_private
    uts: add spacing property to 8859-1.bdf
    terminfo: add underline for sun-color
    terminfo: sun-color has 16 colors
    uts: add font load callback type
    loader: do not repeat int13 calls with error 0x20 and
    8905 loader: add skein/edonr support
    8904 common/crypto: make skein and edonr loader
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/io/ldterm.c
          +++ new/usr/src/uts/common/io/ldterm.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  
    | 
      ↓ open down ↓ | 
    14 lines elided | 
    
      ↑ open up ↑ | 
  
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23   23   * Use is subject to license terms.
  24   24   * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
       25 + * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  25   26   */
  26   27  
  27   28  /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T      */
  28   29  /* All Rights Reserved                                  */
  29   30  
  30   31  /*
  31   32   * University Copyright- Copyright (c) 1982, 1986, 1988
  32   33   * The Regents of the University of California
  33   34   * All Rights Reserved
  34   35   *
  35   36   * University Acknowledgment- Portions of this document are derived from
  36   37   * software developed by the University of California, Berkeley, and its
  37   38   * contributors.
  38   39   */
  39   40  
  40   41  /*
  41   42   * Standard Streams Terminal Line Discipline module.
  42   43   */
  43   44  
  44   45  #include <sys/param.h>
  45   46  #include <sys/types.h>
  46   47  #include <sys/termio.h>
  47   48  #include <sys/stream.h>
  48   49  #include <sys/conf.h>
  49   50  #include <sys/stropts.h>
  50   51  #include <sys/strsubr.h>
  51   52  #include <sys/strsun.h>
  52   53  #include <sys/strtty.h>
  53   54  #include <sys/signal.h>
  54   55  #include <sys/file.h>
  55   56  #include <sys/errno.h>
  56   57  #include <sys/debug.h>
  57   58  #include <sys/cmn_err.h>
  58   59  #include <sys/euc.h>
  59   60  #include <sys/eucioctl.h>
  60   61  #include <sys/csiioctl.h>
  61   62  #include <sys/ptms.h>
  62   63  #include <sys/ldterm.h>
  63   64  #include <sys/cred.h>
  64   65  #include <sys/ddi.h>
  65   66  #include <sys/sunddi.h>
  66   67  #include <sys/kmem.h>
  67   68  #include <sys/modctl.h>
  68   69  
  69   70  /* Time limit when draining during a close(9E) invoked by exit(2) */
  70   71  /* Can be set to zero to emulate the old, broken behavior */
  71   72  int ldterm_drain_limit = 15000000;
  72   73  
  73   74  /*
  74   75   * Character types.
  75   76   */
  76   77  #define ORDINARY        0
  77   78  #define CONTROL         1
  78   79  #define BACKSPACE       2
  79   80  #define NEWLINE         3
  80   81  #define TAB             4
  81   82  #define VTAB            5
  82   83  #define RETURN          6
  83   84  
  84   85  /*
  85   86   * The following for EUC handling:
  86   87   */
  87   88  #define T_SS2           7
  88   89  #define T_SS3           8
  89   90  
  90   91  /*
  91   92   * Table indicating character classes to tty driver.  In particular,
  92   93   * if the class is ORDINARY, then the character needs no special
  93   94   * processing on output.
  94   95   *
  95   96   * Characters in the C1 set are all considered CONTROL; this will
  96   97   * work with terminals that properly use the ANSI/ISO extensions,
  97   98   * but might cause distress with terminals that put graphics in
  98   99   * the range 0200-0237.  On the other hand, characters in that
  99  100   * range cause even greater distress to other UNIX terminal drivers....
 100  101   */
 101  102  
 102  103  static char typetab[256] = {
 103  104  /* 000 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 104  105  /* 004 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 105  106  /* 010 */       BACKSPACE,      TAB,            NEWLINE,        CONTROL,
 106  107  /* 014 */       VTAB,           RETURN,         CONTROL,        CONTROL,
 107  108  /* 020 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 108  109  /* 024 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 109  110  /* 030 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 110  111  /* 034 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 111  112  /* 040 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 112  113  /* 044 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 113  114  /* 050 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 114  115  /* 054 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 115  116  /* 060 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 116  117  /* 064 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 117  118  /* 070 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 118  119  /* 074 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 119  120  /* 100 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 120  121  /* 104 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 121  122  /* 110 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 122  123  /* 114 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 123  124  /* 120 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 124  125  /* 124 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 125  126  /* 130 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 126  127  /* 134 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 127  128  /* 140 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 128  129  /* 144 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 129  130  /* 150 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 130  131  /* 154 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 131  132  /* 160 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 132  133  /* 164 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 133  134  /* 170 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 134  135  /* 174 */       ORDINARY,       ORDINARY,       ORDINARY,       CONTROL,
 135  136  /* 200 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 136  137  /* 204 */       CONTROL,        CONTROL,        T_SS2,          T_SS3,
 137  138  /* 210 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 138  139  /* 214 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 139  140  /* 220 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 140  141  /* 224 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 141  142  /* 230 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 142  143  /* 234 */       CONTROL,        CONTROL,        CONTROL,        CONTROL,
 143  144  /* 240 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 144  145  /* 244 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 145  146  /* 250 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 146  147  /* 254 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 147  148  /* 260 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 148  149  /* 264 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 149  150  /* 270 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 150  151  /* 274 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 151  152  /* 300 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 152  153  /* 304 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 153  154  /* 310 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 154  155  /* 314 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 155  156  /* 320 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 156  157  /* 324 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 157  158  /* 330 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 158  159  /* 334 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 159  160  /* 340 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 160  161  /* 344 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 161  162  /* 350 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 162  163  /* 354 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 163  164  /* 360 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 164  165  /* 364 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 165  166  /* 370 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 166  167  /*
 167  168   * WARNING:  For EUC, 0xFF must be an ordinary character.  It is used with
 168  169   * single-byte EUC in some of the "ISO Latin Alphabet" codesets, and occupies
 169  170   * a screen position; in those ISO sets where that position isn't used, it
 170  171   * shouldn't make any difference.
 171  172   */
 172  173  /* 374 */       ORDINARY,       ORDINARY,       ORDINARY,       ORDINARY,
 173  174  };
 174  175  
 175  176  /*
 176  177   * Translation table for output without OLCUC.  All ORDINARY-class characters
 177  178   * translate to themselves.  All other characters have a zero in the table,
 178  179   * which stops the copying.
 179  180   */
 180  181  static unsigned char notrantab[256] = {
 181  182  /* 000 */       0,      0,      0,      0,      0,      0,      0,      0,
 182  183  /* 010 */       0,      0,      0,      0,      0,      0,      0,      0,
 183  184  /* 020 */       0,      0,      0,      0,      0,      0,      0,      0,
 184  185  /* 030 */       0,      0,      0,      0,      0,      0,      0,      0,
 185  186  /* 040 */       ' ',    '!',    '"',    '#',    '$',    '%',    '&',    '\'',
 186  187  /* 050 */       '(',    ')',    '*',    '+',    ',',    '-',    '.',    '/',
 187  188  /* 060 */       '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',
 188  189  /* 070 */       '8',    '9',    ':',    ';',    '<',    '=',    '>',    '?',
 189  190  /* 100 */       '@',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
 190  191  /* 110 */       'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
 191  192  /* 120 */       'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
 192  193  /* 130 */       'X',    'Y',    'Z',    '[',    '\\',   ']',    '^',    '_',
 193  194  /* 140 */       '`',    'a',    'b',    'c',    'd',    'e',    'f',    'g',
 194  195  /* 150 */       'h',    'i',    'j',    'k',    'l',    'm',    'n',    'o',
 195  196  /* 160 */       'p',    'q',    'r',    's',    't',    'u',    'v',    'w',
 196  197  /* 170 */       'x',    'y',    'z',    '{',    '|',    '}',    '~',    0,
 197  198  /* 200 */       0,      0,      0,      0,      0,      0,      0,      0,
 198  199  /* 210 */       0,      0,      0,      0,      0,      0,      0,      0,
 199  200  /* 220 */       0,      0,      0,      0,      0,      0,      0,      0,
 200  201  /* 230 */       0,      0,      0,      0,      0,      0,      0,      0,
 201  202  /* 240 */       0240,   0241,   0242,   0243,   0244,   0245,   0246,   0247,
 202  203  /* 250 */       0250,   0251,   0252,   0253,   0254,   0255,   0256,   0257,
 203  204  /* 260 */       0260,   0261,   0262,   0263,   0264,   0265,   0266,   0267,
 204  205  /* 270 */       0270,   0271,   0272,   0273,   0274,   0275,   0276,   0277,
 205  206  /* 300 */       0300,   0301,   0302,   0303,   0304,   0305,   0306,   0307,
 206  207  /* 310 */       0310,   0311,   0312,   0313,   0314,   0315,   0316,   0317,
 207  208  /* 320 */       0320,   0321,   0322,   0323,   0324,   0325,   0326,   0327,
 208  209  /* 330 */       0330,   0331,   0332,   0333,   0334,   0335,   0336,   0337,
 209  210  /* 340 */       0340,   0341,   0342,   0343,   0344,   0345,   0346,   0347,
 210  211  /* 350 */       0350,   0351,   0352,   0353,   0354,   0355,   0356,   0357,
 211  212  /* 360 */       0360,   0361,   0362,   0363,   0364,   0365,   0366,   0367,
 212  213  /*
 213  214   * WARNING: as for above ISO sets, \377 may be used.  Translate it to
 214  215   * itself.
 215  216   */
 216  217  /* 370 */       0370,   0371,   0372,   0373,   0374,   0375,   0376,   0377,
 217  218  };
 218  219  
 219  220  /*
 220  221   * Translation table for output with OLCUC.  All ORDINARY-class characters
 221  222   * translate to themselves, except for lower-case letters which translate
 222  223   * to their upper-case equivalents.  All other characters have a zero in
 223  224   * the table, which stops the copying.
 224  225   */
 225  226  static unsigned char lcuctab[256] = {
 226  227  /* 000 */       0,      0,      0,      0,      0,      0,      0,      0,
 227  228  /* 010 */       0,      0,      0,      0,      0,      0,      0,      0,
 228  229  /* 020 */       0,      0,      0,      0,      0,      0,      0,      0,
 229  230  /* 030 */       0,      0,      0,      0,      0,      0,      0,      0,
 230  231  /* 040 */       ' ',    '!',    '"',    '#',    '$',    '%',    '&',    '\'',
 231  232  /* 050 */       '(',    ')',    '*',    '+',    ',',    '-',    '.',    '/',
 232  233  /* 060 */       '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',
 233  234  /* 070 */       '8',    '9',    ':',    ';',    '<',    '=',    '>',    '?',
 234  235  /* 100 */       '@',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
 235  236  /* 110 */       'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
 236  237  /* 120 */       'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
 237  238  /* 130 */       'X',    'Y',    'Z',    '[',    '\\',   ']',    '^',    '_',
 238  239  /* 140 */       '`',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
 239  240  /* 150 */       'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
 240  241  /* 160 */       'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
 241  242  /* 170 */       'X',    'Y',    'Z',    '{',    '|',    '}',    '~',    0,
 242  243  /* 200 */       0,      0,      0,      0,      0,      0,      0,      0,
 243  244  /* 210 */       0,      0,      0,      0,      0,      0,      0,      0,
 244  245  /* 220 */       0,      0,      0,      0,      0,      0,      0,      0,
 245  246  /* 230 */       0,      0,      0,      0,      0,      0,      0,      0,
 246  247  /* 240 */       0240,   0241,   0242,   0243,   0244,   0245,   0246,   0247,
 247  248  /* 250 */       0250,   0251,   0252,   0253,   0254,   0255,   0256,   0257,
 248  249  /* 260 */       0260,   0261,   0262,   0263,   0264,   0265,   0266,   0267,
 249  250  /* 270 */       0270,   0271,   0272,   0273,   0274,   0275,   0276,   0277,
 250  251  /* 300 */       0300,   0301,   0302,   0303,   0304,   0305,   0306,   0307,
 251  252  /* 310 */       0310,   0311,   0312,   0313,   0314,   0315,   0316,   0317,
 252  253  /* 320 */       0320,   0321,   0322,   0323,   0324,   0325,   0326,   0327,
 253  254  /* 330 */       0330,   0331,   0332,   0333,   0334,   0335,   0336,   0337,
 254  255  /* 340 */       0340,   0341,   0342,   0343,   0344,   0345,   0346,   0347,
 255  256  /* 350 */       0350,   0351,   0352,   0353,   0354,   0355,   0356,   0357,
 256  257  /* 360 */       0360,   0361,   0362,   0363,   0364,   0365,   0366,   0367,
 257  258  /*
 258  259   * WARNING: as for above ISO sets, \377 may be used.  Translate it to
 259  260   * itself.
 260  261   */
 261  262  /* 370 */       0370,   0371,   0372,   0373,   0374,   0375,   0376,   0377,
 262  263  };
 263  264  
 264  265  /*
 265  266   * Input mapping table -- if an entry is non-zero, and XCASE is set,
 266  267   * when the corresponding character is typed preceded by "\" the escape
 267  268   * sequence is replaced by the table value.  Mostly used for
 268  269   * upper-case only terminals.
 269  270   */
 270  271  static char     imaptab[256] = {
 271  272  /* 000 */       0,      0,      0,      0,      0,      0,      0,      0,
 272  273  /* 010 */       0,      0,      0,      0,      0,      0,      0,      0,
 273  274  /* 020 */       0,      0,      0,      0,      0,      0,      0,      0,
 274  275  /* 030 */       0,      0,      0,      0,      0,      0,      0,      0,
 275  276  /* 040 */       0,      '|',    0,      0,      0,      0,      0,      '`',
 276  277  /* 050 */       '{',    '}',    0,      0,      0,      0,      0,      0,
 277  278  /* 060 */       0,      0,      0,      0,      0,      0,      0,      0,
 278  279  /* 070 */       0,      0,      0,      0,      0,      0,      0,      0,
 279  280  /* 100 */       0,      0,      0,      0,      0,      0,      0,      0,
 280  281  /* 110 */       0,      0,      0,      0,      0,      0,      0,      0,
 281  282  /* 120 */       0,      0,      0,      0,      0,      0,      0,      0,
 282  283  /* 130 */       0,      0,      0,      0,      '\\',   0,      '~',    0,
 283  284  /* 140 */       0,      'A',    'B',    'C',    'D',    'E',    'F',    'G',
 284  285  /* 150 */       'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
 285  286  /* 160 */       'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
 286  287  /* 170 */       'X',    'Y',    'Z',    0,      0,      0,      0,      0,
 287  288  /* 200-377 aren't mapped */
 288  289  };
 289  290  
 290  291  /*
 291  292   * Output mapping table -- if an entry is non-zero, and XCASE is set,
 292  293   * the corresponding character is printed as "\" followed by the table
 293  294   * value.  Mostly used for upper-case only terminals.
 294  295   */
 295  296  static char     omaptab[256] = {
 296  297  /* 000 */       0,      0,      0,      0,      0,      0,      0,      0,
 297  298  /* 010 */       0,      0,      0,      0,      0,      0,      0,      0,
 298  299  /* 020 */       0,      0,      0,      0,      0,      0,      0,      0,
 299  300  /* 030 */       0,      0,      0,      0,      0,      0,      0,      0,
 300  301  /* 040 */       0,      0,      0,      0,      0,      0,      0,      0,
 301  302  /* 050 */       0,      0,      0,      0,      0,      0,      0,      0,
 302  303  /* 060 */       0,      0,      0,      0,      0,      0,      0,      0,
 303  304  /* 070 */       0,      0,      0,      0,      0,      0,      0,      0,
 304  305  /* 100 */       0,      'A',    'B',    'C',    'D',    'E',    'F',    'G',
 305  306  /* 110 */       'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
 306  307  /* 120 */       'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
 307  308  /* 130 */       'X',    'Y',    'Z',    0,      0,      0,      0,      0,
 308  309  /* 140 */       '\'',   0,      0,      0,      0,      0,      0,      0,
 309  310  /* 150 */       0,      0,      0,      0,      0,      0,      0,      0,
 310  311  /* 160 */       0,      0,      0,      0,      0,      0,      0,      0,
 311  312  /* 170 */       0,      0,      0,      '(',    '!',    ')',    '^',    0,
 312  313  /* 200-377 aren't mapped */
 313  314  };
 314  315  
 315  316  /*
 316  317   * Translation table for TS_MEUC output without OLCUC.  All printing ASCII
 317  318   * characters translate to themselves.  All other _bytes_ have a zero in
 318  319   * the table, which stops the copying.  This and the following table exist
 319  320   * only so we can use the existing movtuc processing with or without OLCUC.
 320  321   * Maybe it speeds up something...because we can copy a block of characters
 321  322   * by only looking for zeros in the table.
 322  323   *
 323  324   * If we took the simple expedient of DISALLOWING "olcuc" with multi-byte
 324  325   * processing, we could rid ourselves of both these tables and save 512 bytes;
 325  326   * seriously, it doesn't make much sense to use olcuc with multi-byte, and
 326  327   * it will probably never be used.  Consideration should be given to disallowing
 327  328   * the combination TS_MEUC & OLCUC.
 328  329   */
 329  330  static unsigned char enotrantab[256] = {
 330  331  /* 000 */       0,      0,      0,      0,      0,      0,      0,      0,
 331  332  /* 010 */       0,      0,      0,      0,      0,      0,      0,      0,
 332  333  /* 020 */       0,      0,      0,      0,      0,      0,      0,      0,
 333  334  /* 030 */       0,      0,      0,      0,      0,      0,      0,      0,
 334  335  /* 040 */       ' ',    '!',    '"',    '#',    '$',    '%',    '&',    '\'',
 335  336  /* 050 */       '(',    ')',    '*',    '+',    ',',    '-',    '.',    '/',
 336  337  /* 060 */       '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',
 337  338  /* 070 */       '8',    '9',    ':',    ';',    '<',    '=',    '>',    '?',
 338  339  /* 100 */       '@',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
 339  340  /* 110 */       'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
 340  341  /* 120 */       'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
 341  342  /* 130 */       'X',    'Y',    'Z',    '[',    '\\',   ']',    '^',    '_',
 342  343  /* 140 */       '`',    'a',    'b',    'c',    'd',    'e',    'f',    'g',
 343  344  /* 150 */       'h',    'i',    'j',    'k',    'l',    'm',    'n',    'o',
 344  345  /* 160 */       'p',    'q',    'r',    's',    't',    'u',    'v',    'w',
 345  346  /* 170 */       'x',    'y',    'z',    '{',    '|',    '}',    '~',    0,
 346  347  /* 200 - 377 aren't mapped (they're stoppers). */
 347  348  };
 348  349  
 349  350  /*
 350  351   * Translation table for TS_MEUC output with OLCUC.  All printing ASCII
 351  352   * translate to themselves, except for lower-case letters which translate
 352  353   * to their upper-case equivalents.  All other bytes have a zero in
 353  354   * the table, which stops the copying.  Useless for ISO Latin Alphabet
 354  355   * translations, but *sigh* OLCUC is really only defined for ASCII anyway.
 355  356   * We only have this table so we can use the existing OLCUC processing with
 356  357   * TS_MEUC set (multi-byte mode).  Nobody would ever think of actually
 357  358   * _using_ it...would they?
 358  359   */
 359  360  static unsigned char elcuctab[256] = {
 360  361  /* 000 */       0,      0,      0,      0,      0,      0,      0,      0,
 361  362  /* 010 */       0,      0,      0,      0,      0,      0,      0,      0,
 362  363  /* 020 */       0,      0,      0,      0,      0,      0,      0,      0,
 363  364  /* 030 */       0,      0,      0,      0,      0,      0,      0,      0,
 364  365  /* 040 */       ' ',    '!',    '"',    '#',    '$',    '%',    '&',    '\'',
 365  366  /* 050 */       '(',    ')',    '*',    '+',    ',',    '-',    '.',    '/',
 366  367  /* 060 */       '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',
 367  368  /* 070 */       '8',    '9',    ':',    ';',    '<',    '=',    '>',    '?',
 368  369  /* 100 */       '@',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
 369  370  /* 110 */       'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
 370  371  /* 120 */       'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
 371  372  /* 130 */       'X',    'Y',    'Z',    '[',    '\\',   ']',    '^',    '_',
 372  373  /* 140 */       '`',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
 373  374  /* 150 */       'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
  
    | 
      ↓ open down ↓ | 
    339 lines elided | 
    
      ↑ open up ↑ | 
  
 374  375  /* 160 */       'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
 375  376  /* 170 */       'X',    'Y',    'Z',    '{',    '|',    '}',    '~',    0,
 376  377  /* 200 - 377 aren't mapped (they're stoppers). */
 377  378  };
 378  379  
 379  380  static struct streamtab ldtrinfo;
 380  381  
 381  382  static struct fmodsw fsw = {
 382  383          "ldterm",
 383  384          &ldtrinfo,
 384      -        D_MTQPAIR | D_MP
      385 +        D_MTQPAIR | D_MP | _D_SINGLE_INSTANCE
 385  386  };
 386  387  
 387  388  static struct modlstrmod modlstrmod = {
 388  389          &mod_strmodops, "terminal line discipline", &fsw
 389  390  };
 390  391  
 391  392  
 392  393  static struct modlinkage modlinkage = {
 393  394          MODREV_1, &modlstrmod, NULL
 394  395  };
 395  396  
 396  397  
 397  398  int
 398  399  _init(void)
 399  400  {
 400  401          return (mod_install(&modlinkage));
 401  402  }
 402  403  
 403  404  int
 404  405  _fini(void)
 405  406  {
 406  407          return (mod_remove(&modlinkage));
 407  408  }
 408  409  
 409  410  int
 410  411  _info(struct modinfo *modinfop)
 411  412  {
 412  413          return (mod_info(&modlinkage, modinfop));
 413  414  }
 414  415  
 415  416  
 416  417  static int      ldtermopen(queue_t *, dev_t *, int, int, cred_t *);
 417  418  static int      ldtermclose(queue_t *, int, cred_t *);
 418  419  static void     ldtermrput(queue_t *, mblk_t *);
 419  420  static void     ldtermrsrv(queue_t *);
 420  421  static int      ldtermrmsg(queue_t *, mblk_t *);
 421  422  static void     ldtermwput(queue_t *, mblk_t *);
 422  423  static void     ldtermwsrv(queue_t *);
 423  424  static int      ldtermwmsg(queue_t *, mblk_t *);
 424  425  static mblk_t   *ldterm_docanon(unsigned char, mblk_t *, size_t, queue_t *,
 425  426                                  ldtermstd_state_t *, int *);
 426  427  static int      ldterm_unget(ldtermstd_state_t *);
 427  428  static void     ldterm_trim(ldtermstd_state_t *);
 428  429  static void     ldterm_rubout(unsigned char, queue_t *, size_t,
 429  430                                  ldtermstd_state_t *);
 430  431  static int      ldterm_tabcols(ldtermstd_state_t *);
 431  432  static void     ldterm_erase(queue_t *, size_t, ldtermstd_state_t *);
 432  433  static void     ldterm_werase(queue_t *, size_t, ldtermstd_state_t *);
 433  434  static void     ldterm_kill(queue_t *, size_t, ldtermstd_state_t *);
 434  435  static void     ldterm_reprint(queue_t *, size_t, ldtermstd_state_t *);
 435  436  static mblk_t   *ldterm_dononcanon(mblk_t *, mblk_t *, size_t, queue_t *,
 436  437                                          ldtermstd_state_t *);
 437  438  static int      ldterm_echo(unsigned char, queue_t *, size_t,
 438  439                                  ldtermstd_state_t *);
 439  440  static void     ldterm_outchar(unsigned char, queue_t *, size_t,
 440  441                                  ldtermstd_state_t *);
 441  442  static void     ldterm_outstring(unsigned char *, int, queue_t *, size_t,
 442  443                                          ldtermstd_state_t *tp);
 443  444  static mblk_t   *newmsg(ldtermstd_state_t *);
 444  445  static void     ldterm_msg_upstream(queue_t *, ldtermstd_state_t *);
 445  446  static void     ldterm_wenable(void *);
 446  447  static mblk_t   *ldterm_output_msg(queue_t *, mblk_t *, mblk_t **,
 447  448                                  ldtermstd_state_t *, size_t, int);
 448  449  static void     ldterm_flush_output(unsigned char, queue_t *,
 449  450                                          ldtermstd_state_t *);
 450  451  static void     ldterm_dosig(queue_t *, int, unsigned char, int, int);
 451  452  static void     ldterm_do_ioctl(queue_t *, mblk_t *);
 452  453  static int      chgstropts(struct termios *, ldtermstd_state_t *, queue_t *);
 453  454  static void     ldterm_ioctl_reply(queue_t *, mblk_t *);
 454  455  static void     vmin_satisfied(queue_t *, ldtermstd_state_t *, int);
 455  456  static void     vmin_settimer(queue_t *);
 456  457  static void     vmin_timed_out(void *);
 457  458  static void     ldterm_adjust_modes(ldtermstd_state_t *);
 458  459  static void     ldterm_eucwarn(ldtermstd_state_t *);
 459  460  static void     cp_eucwioc(eucioc_t *, eucioc_t *, int);
 460  461  static int      ldterm_codeset(uchar_t, uchar_t);
 461  462  
 462  463  static void     ldterm_csi_erase(queue_t *, size_t, ldtermstd_state_t *);
 463  464  static void     ldterm_csi_werase(queue_t *, size_t, ldtermstd_state_t *);
 464  465  
 465  466  static uchar_t  ldterm_utf8_width(uchar_t *, int);
 466  467  
 467  468  /* Codeset type specific methods for EUC, PCCS, and, UTF-8 codeset types. */
 468  469  static int      __ldterm_dispwidth_euc(uchar_t, void *, int);
 469  470  static int      __ldterm_memwidth_euc(uchar_t, void *);
 470  471  
 471  472  static int      __ldterm_dispwidth_pccs(uchar_t, void *, int);
 472  473  static int      __ldterm_memwidth_pccs(uchar_t, void *);
 473  474  
 474  475  static int      __ldterm_dispwidth_utf8(uchar_t, void *, int);
 475  476  static int      __ldterm_memwidth_utf8(uchar_t, void *);
 476  477  
 477  478  static const ldterm_cs_methods_t cs_methods[LDTERM_CS_TYPE_MAX + 1] = {
 478  479          {
 479  480                  NULL,
 480  481                  NULL
 481  482          },
 482  483          {
 483  484                  __ldterm_dispwidth_euc,
 484  485                  __ldterm_memwidth_euc
 485  486          },
 486  487          {
 487  488                  __ldterm_dispwidth_pccs,
 488  489                  __ldterm_memwidth_pccs
 489  490          },
 490  491          {
 491  492                  __ldterm_dispwidth_utf8,
 492  493                  __ldterm_memwidth_utf8
 493  494          }
 494  495  };
 495  496  
 496  497  /*
 497  498   * The default codeset is presumably C locale's ISO 646 in EUC but
 498  499   * the data structure at below defined as the default codeset data also
 499  500   * support any single byte (EUC) locales.
 500  501   */
 501  502  static const ldterm_cs_data_t default_cs_data = {
 502  503          LDTERM_DATA_VERSION,
 503  504          LDTERM_CS_TYPE_EUC,
 504  505          (uchar_t)0,
 505  506          (uchar_t)0,
 506  507          (char *)NULL,
 507  508          {
 508  509                  '\0', '\0', '\0', '\0',
 509  510                  '\0', '\0', '\0', '\0',
 510  511                  '\0', '\0', '\0', '\0',
 511  512                  '\0', '\0', '\0', '\0',
 512  513                  '\0', '\0', '\0', '\0',
 513  514                  '\0', '\0', '\0', '\0',
 514  515                  '\0', '\0', '\0', '\0',
 515  516                  '\0', '\0', '\0', '\0',
 516  517                  '\0', '\0', '\0', '\0',
 517  518                  '\0', '\0', '\0', '\0'
 518  519          }
 519  520  };
 520  521  
 521  522  /*
 522  523   * The following tables are from either u8_textprep.c or uconv.c at
 523  524   * usr/src/common/unicode/. The tables are used to figure out corresponding
 524  525   * UTF-8 character byte lengths and also the validity of given character bytes.
 525  526   */
 526  527  extern const int8_t u8_number_of_bytes[];
 527  528  extern const uchar_t u8_masks_tbl[];
 528  529  extern const uint8_t u8_valid_min_2nd_byte[];
 529  530  extern const uint8_t u8_valid_max_2nd_byte[];
 530  531  
 531  532  /*
 532  533   * Unicode character width definition tables from uwidth.c:
 533  534   */
 534  535  extern const ldterm_unicode_data_cell_t ldterm_ucode[][16384];
 535  536  
 536  537  #ifdef LDDEBUG
 537  538  int     ldterm_debug = 0;
 538  539  #define DEBUG1(a)       if (ldterm_debug == 1) printf a
 539  540  #define DEBUG2(a)       if (ldterm_debug >= 2) printf a /* allocations */
 540  541  #define DEBUG3(a)       if (ldterm_debug >= 3) printf a /* M_CTL Stuff */
 541  542  #define DEBUG4(a)       if (ldterm_debug >= 4) printf a /* M_READ Stuff */
 542  543  #define DEBUG5(a)       if (ldterm_debug >= 5) printf a
 543  544  #define DEBUG6(a)       if (ldterm_debug >= 6) printf a
 544  545  #define DEBUG7(a)       if (ldterm_debug >= 7) printf a
 545  546  #else
 546  547  #define DEBUG1(a)
 547  548  #define DEBUG2(a)
 548  549  #define DEBUG3(a)
 549  550  #define DEBUG4(a)
 550  551  #define DEBUG5(a)
 551  552  #define DEBUG6(a)
 552  553  #define DEBUG7(a)
 553  554  #endif          /* LDDEBUG */
 554  555  
 555  556  
 556  557  /*
 557  558   * Since most of the buffering occurs either at the stream head or in
 558  559   * the "message currently being assembled" buffer, we have a
 559  560   * relatively small input queue, so that blockages above us get
 560  561   * reflected fairly quickly to the module below us.  We also have a
 561  562   * small maximum packet size, since you can put a message of that
 562  563   * size on an empty queue no matter how much bigger than the high
 563  564   * water mark it is.
 564  565   */
 565  566  static struct module_info ldtermmiinfo = {
 566  567          0x0bad,
 567  568          "ldterm",
 568  569          0,
 569  570          _TTY_BUFSIZ,
 570  571          _TTY_BUFSIZ,
 571  572          LOWAT
 572  573  };
 573  574  
 574  575  
 575  576  static struct qinit ldtermrinit = {
 576  577          (int (*)())ldtermrput,
 577  578          (int (*)())ldtermrsrv,
 578  579          ldtermopen,
 579  580          ldtermclose,
 580  581          NULL,
 581  582          &ldtermmiinfo
 582  583  };
 583  584  
 584  585  
 585  586  static struct module_info ldtermmoinfo = {
 586  587          0x0bad,
 587  588          "ldterm",
 588  589          0,
 589  590          INFPSZ,
 590  591          1,
 591  592          0
 592  593  };
 593  594  
 594  595  
 595  596  static struct qinit ldtermwinit = {
 596  597          (int (*)())ldtermwput,
 597  598          (int (*)())ldtermwsrv,
 598  599          ldtermopen,
 599  600          ldtermclose,
 600  601          NULL,
 601  602          &ldtermmoinfo
 602  603  };
 603  604  
 604  605  
 605  606  static struct streamtab ldtrinfo = {
 606  607          &ldtermrinit,
 607  608          &ldtermwinit,
 608  609          NULL,
 609  610          NULL
 610  611  };
 611  612  
 612  613  /*
 613  614   * Dummy qbufcall callback routine used by open and close.
 614  615   * The framework will wake up qwait_sig when we return from
 615  616   * this routine (as part of leaving the perimeters.)
 616  617   * (The framework enters the perimeters before calling the qbufcall() callback
 617  618   * and leaves the perimeters after the callback routine has executed. The
 618  619   * framework performs an implicit wakeup of any thread in qwait/qwait_sig
 619  620   * when it leaves the perimeter. See qwait(9E).)
 620  621   */
 621  622  /* ARGSUSED */
 622  623  static void
 623  624  dummy_callback(void *arg)
 624  625  {}
 625  626  
 626  627  
 627  628  static mblk_t *
 628  629  open_ioctl(queue_t *q, uint_t cmd)
 629  630  {
 630  631          mblk_t *mp;
 631  632          bufcall_id_t id;
 632  633          int retv;
 633  634  
 634  635          while ((mp = mkiocb(cmd)) == NULL) {
 635  636                  id = qbufcall(q, sizeof (struct iocblk), BPRI_MED,
 636  637                      dummy_callback, NULL);
 637  638                  retv = qwait_sig(q);
 638  639                  qunbufcall(q, id);
 639  640                  if (retv == 0)
 640  641                          break;
 641  642          }
 642  643          return (mp);
 643  644  }
 644  645  
 645  646  static mblk_t *
 646  647  open_mblk(queue_t *q, size_t len)
 647  648  {
 648  649          mblk_t *mp;
 649  650          bufcall_id_t id;
 650  651          int retv;
 651  652  
 652  653          while ((mp = allocb(len, BPRI_MED)) == NULL) {
 653  654                  id = qbufcall(q, len, BPRI_MED, dummy_callback, NULL);
 654  655                  retv = qwait_sig(q);
 655  656                  qunbufcall(q, id);
 656  657                  if (retv == 0)
 657  658                          break;
 658  659          }
 659  660          return (mp);
 660  661  }
 661  662  
 662  663  /*
 663  664   * Line discipline open.
 664  665   */
 665  666  /* ARGSUSED1 */
 666  667  static int
 667  668  ldtermopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
 668  669  {
 669  670          ldtermstd_state_t *tp;
 670  671          mblk_t *bp, *qryp;
 671  672          int len;
 672  673          struct stroptions *strop;
 673  674          struct termios *termiosp;
 674  675          queue_t *wq;
 675  676  
 676  677          if (q->q_ptr != NULL) {
 677  678                  return (0);     /* already attached */
 678  679          }
 679  680  
 680  681          tp = (ldtermstd_state_t *)kmem_zalloc(sizeof (ldtermstd_state_t),
 681  682              KM_SLEEP);
 682  683  
 683  684          /*
 684  685           * Get termios defaults.  These are stored as
 685  686           * a property in the "options" node.
 686  687           */
 687  688          if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), DDI_PROP_NOTPROM,
 688  689              "ttymodes", (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
 689  690              len == sizeof (struct termios)) {
 690  691                  tp->t_modes = *termiosp;
 691  692                  tp->t_amodes = *termiosp;
 692  693                  kmem_free(termiosp, len);
 693  694          } else {
 694  695                  /*
 695  696                   * Gack!  Whine about it.
 696  697                   */
 697  698                  cmn_err(CE_WARN, "ldterm: Couldn't get ttymodes property!");
 698  699          }
 699  700          bzero(&tp->t_dmodes, sizeof (struct termios));
 700  701  
 701  702          tp->t_state = 0;
 702  703  
 703  704          tp->t_line = 0;
 704  705          tp->t_col = 0;
 705  706  
 706  707          tp->t_rocount = 0;
 707  708          tp->t_rocol = 0;
 708  709  
 709  710          tp->t_message = NULL;
 710  711          tp->t_endmsg = NULL;
 711  712          tp->t_msglen = 0;
 712  713          tp->t_rd_request = 0;
 713  714  
 714  715          tp->t_echomp = NULL;
 715  716          tp->t_iocid = 0;
 716  717          tp->t_wbufcid = 0;
 717  718          tp->t_vtid = 0;
 718  719  
 719  720          q->q_ptr = (caddr_t)tp;
  
    | 
      ↓ open down ↓ | 
    325 lines elided | 
    
      ↑ open up ↑ | 
  
 720  721          WR(q)->q_ptr = (caddr_t)tp;
 721  722          /*
 722  723           * The following for EUC and also non-EUC codesets:
 723  724           */
 724  725          tp->t_codeset = tp->t_eucleft = tp->t_eucign = tp->t_scratch_len = 0;
 725  726          bzero(&tp->eucwioc, EUCSIZE);
 726  727          tp->eucwioc.eucw[0] = 1;        /* ASCII mem & screen width */
 727  728          tp->eucwioc.scrw[0] = 1;
 728  729          tp->t_maxeuc = 1;       /* the max len in bytes of an EUC char */
 729  730          tp->t_eucp = NULL;
 730      -        tp->t_eucp_mp = NULL;
 731      -        tp->t_eucwarn = 0;      /* no bad chars seen yet */
 732      -
 733      -        tp->t_csdata = default_cs_data;
 734  731          tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_EUC];
      732 +        tp->t_csdata = default_cs_data;
 735  733  
      734 +        /*
      735 +         * Try to switch to UTF-8 mode by allocating buffer for multibyte
      736 +         * chars, keep EUC if allocation fails.
      737 +         */
      738 +        if ((tp->t_eucp_mp = allocb(_TTY_BUFSIZ, BPRI_HI)) != NULL) {
      739 +                tp->t_eucp = tp->t_eucp_mp->b_rptr;
      740 +                tp->t_state = TS_MEUC;  /* Multibyte mode. */
      741 +                tp->t_maxeuc = 4; /* the max len in bytes of an UTF-8 char */
      742 +                tp->t_csdata.codeset_type = LDTERM_CS_TYPE_UTF8;
      743 +                tp->t_csdata.csinfo_num = 4;
      744 +                /* locale_name needs string length with terminating NUL */
      745 +                tp->t_csdata.locale_name = (char *)kmem_alloc(6, KM_SLEEP);
      746 +                (void) strcpy(tp->t_csdata.locale_name, "UTF-8");
      747 +                tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_UTF8];
      748 +        }
      749 +        tp->t_eucwarn = 0;      /* no bad chars seen yet */
      750 +
 736  751          qprocson(q);
 737  752  
 738  753          /*
 739  754           * Find out if the module below us does canonicalization; if
 740  755           * so, we won't do it ourselves.
 741  756           */
 742  757  
 743  758          if ((qryp = open_ioctl(q, MC_CANONQUERY)) == NULL)
 744  759                  goto open_abort;
 745  760  
 746  761          /*
 747  762           * Reformulate as an M_CTL message. The actual data will
 748  763           * be in the b_cont field.
 749  764           */
 750  765          qryp->b_datap->db_type = M_CTL;
 751  766          wq = OTHERQ(q);
 752  767          putnext(wq, qryp);
 753  768  
 754  769          /* allocate a TCSBRK ioctl in case we'll need it on close */
 755  770          if ((qryp = open_ioctl(q, TCSBRK)) == NULL)
 756  771                  goto open_abort;
 757  772          tp->t_drainmsg = qryp;
 758  773          if ((bp = open_mblk(q, sizeof (int))) == NULL)
 759  774                  goto open_abort;
 760  775          qryp->b_cont = bp;
 761  776  
 762  777          /*
 763  778           * Find out if the underlying driver supports proper POSIX close
 764  779           * semantics.  If not, we'll have to approximate it using TCSBRK.  If
 765  780           * it does, it will respond with MC_HAS_POSIX, and we'll catch that in
 766  781           * the ldtermrput routine.
 767  782           *
 768  783           * When the ldterm_drain_limit tunable is set to zero, we behave the
 769  784           * same as old ldterm: don't send this new message, and always use
 770  785           * TCSBRK during close.
 771  786           */
 772  787          if (ldterm_drain_limit != 0) {
 773  788                  if ((qryp = open_ioctl(q, MC_POSIXQUERY)) == NULL)
 774  789                          goto open_abort;
 775  790                  qryp->b_datap->db_type = M_CTL;
 776  791                  putnext(wq, qryp);
 777  792          }
 778  793  
 779  794          /* prepare to clear the water marks on close */
 780  795          if ((bp = open_mblk(q, sizeof (struct stroptions))) == NULL)
 781  796                  goto open_abort;
 782  797          tp->t_closeopts = bp;
 783  798  
 784  799          /*
 785  800           * Set the high-water and low-water marks on the stream head
 786  801           * to values appropriate for a terminal.  Also set the "vmin"
 787  802           * and "vtime" values to 1 and 0, turn on message-nondiscard
 788  803           * mode (as we're in ICANON mode), and turn on "old-style
 789  804           * NODELAY" mode.
 790  805           */
 791  806          if ((bp = open_mblk(q, sizeof (struct stroptions))) == NULL)
 792  807                  goto open_abort;
 793  808          strop = (struct stroptions *)bp->b_wptr;
 794  809          strop->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT|SO_NDELON|SO_ISTTY;
 795  810          strop->so_readopt = RMSGN;
 796  811          strop->so_hiwat = _TTY_BUFSIZ;
 797  812          strop->so_lowat = LOWAT;
 798  813          bp->b_wptr += sizeof (struct stroptions);
 799  814          bp->b_datap->db_type = M_SETOPTS;
 800  815          putnext(q, bp);
 801  816  
 802  817          return (0);             /* this can become a controlling TTY */
 803  818  
 804  819  open_abort:
 805  820          qprocsoff(q);
 806  821          q->q_ptr = NULL;
 807  822          WR(q)->q_ptr = NULL;
 808  823          freemsg(tp->t_closeopts);
 809  824          freemsg(tp->t_drainmsg);
 810  825          /* Dump the state structure */
 811  826          kmem_free(tp, sizeof (ldtermstd_state_t));
 812  827          return (EINTR);
 813  828  }
 814  829  
 815  830  struct close_timer {
 816  831          timeout_id_t id;
 817  832          ldtermstd_state_t *tp;
 818  833  };
 819  834  
 820  835  static void
 821  836  drain_timed_out(void *arg)
 822  837  {
 823  838          struct close_timer *ctp = arg;
 824  839  
 825  840          ctp->id = 0;
 826  841          ctp->tp->t_state &= ~TS_IOCWAIT;
 827  842  }
 828  843  
 829  844  /* ARGSUSED2 */
 830  845  static int
 831  846  ldtermclose(queue_t *q, int cflag, cred_t *crp)
 832  847  {
 833  848          ldtermstd_state_t *tp = (ldtermstd_state_t *)q->q_ptr;
 834  849          struct stroptions *strop;
 835  850          mblk_t *bp;
 836  851          struct close_timer cltimer;
 837  852  
 838  853          /*
 839  854           * If we have an outstanding vmin timeout, cancel it.
 840  855           */
 841  856          tp->t_state |= TS_CLOSE;
 842  857          if (tp->t_vtid != 0)
 843  858                  (void) quntimeout(q, tp->t_vtid);
 844  859          tp->t_vtid = 0;
 845  860  
 846  861          /*
 847  862           * Cancel outstanding qbufcall request.
 848  863           */
 849  864          if (tp->t_wbufcid != 0)
 850  865                  qunbufcall(q, tp->t_wbufcid);
 851  866  
 852  867          /*
 853  868           * Reset the high-water and low-water marks on the stream
 854  869           * head (?), turn on byte-stream mode, and turn off
 855  870           * "old-style NODELAY" mode.
 856  871           */
 857  872          bp = tp->t_closeopts;
 858  873          strop = (struct stroptions *)bp->b_wptr;
 859  874          strop->so_flags = SO_READOPT|SO_NDELOFF;
 860  875          strop->so_readopt = RNORM;
 861  876          bp->b_wptr += sizeof (struct stroptions);
 862  877          bp->b_datap->db_type = M_SETOPTS;
 863  878          putnext(q, bp);
 864  879  
 865  880          if (cflag & (FNDELAY|FNONBLOCK)) {
 866  881                  freemsg(tp->t_drainmsg);
 867  882          } else if ((bp = tp->t_drainmsg) != NULL) {
 868  883                  struct iocblk *iocb;
 869  884  
 870  885                  /*
 871  886                   * If the driver isn't known to have POSIX close semantics,
 872  887                   * then we have to emulate this the old way.  This is done by
 873  888                   * sending down TCSBRK,1 to drain the output and waiting for
 874  889                   * the reply.
 875  890                   */
 876  891                  iocb = (struct iocblk *)bp->b_rptr;
 877  892                  iocb->ioc_count = sizeof (int);
 878  893                  *(int *)bp->b_cont->b_rptr = 1;
 879  894                  bp->b_cont->b_wptr += sizeof (int);
 880  895                  tp->t_state |= TS_IOCWAIT;
 881  896                  tp->t_iocid = iocb->ioc_id;
 882  897                  if (!putq(WR(q), bp))
 883  898                          putnext(WR(q), bp);
 884  899  
 885  900                  /*
 886  901                   * If we're not able to receive signals at this point, then
 887  902                   * launch a timer.  This timer will prevent us from waiting
 888  903                   * forever for a signal that won't arrive.
 889  904                   */
 890  905                  cltimer.id = 0;
 891  906                  if (!ddi_can_receive_sig() && ldterm_drain_limit != 0) {
 892  907                          cltimer.tp = tp;
 893  908                          cltimer.id = qtimeout(q, drain_timed_out, &cltimer,
 894  909                              drv_usectohz(ldterm_drain_limit));
 895  910                  }
 896  911  
 897  912                  /*
 898  913                   * Note that the read side of ldterm and the qtimeout are
 899  914                   * protected by D_MTQPAIR, so no additional locking is needed
 900  915                   * here.
 901  916                   */
 902  917                  while (tp->t_state & TS_IOCWAIT) {
 903  918                          if (qwait_sig(q) == 0)
 904  919                                  break;
 905  920                  }
 906  921                  if (cltimer.id != 0)
 907  922                          (void) quntimeout(q, cltimer.id);
 908  923          }
 909  924  
 910  925          /*
 911  926           * From here to the end, the routine does not sleep and does not
 912  927           * reference STREAMS, so it's guaranteed to run to completion.
 913  928           */
 914  929  
 915  930          qprocsoff(q);
 916  931  
 917  932          freemsg(tp->t_message);
 918  933          freemsg(tp->t_eucp_mp);
 919  934  
 920  935          /* Dump the state structure, then unlink it */
 921  936          if (tp->t_csdata.locale_name != NULL)
 922  937                  kmem_free(tp->t_csdata.locale_name,
 923  938                      strlen(tp->t_csdata.locale_name) + 1);
 924  939          kmem_free(tp, sizeof (ldtermstd_state_t));
 925  940          q->q_ptr = NULL;
 926  941          return (0);
 927  942  }
 928  943  
 929  944  
 930  945  /*
 931  946   * Put procedure for input from driver end of stream (read queue).
 932  947   */
 933  948  static void
 934  949  ldtermrput(queue_t *q, mblk_t *mp)
 935  950  {
 936  951          ldtermstd_state_t *tp;
 937  952          unsigned char c;
 938  953          queue_t *wrq = WR(q);           /* write queue of ldterm mod */
 939  954          queue_t *nextq = q->q_next;     /* queue below us */
 940  955          mblk_t *bp;
 941  956          struct iocblk *qryp;
 942  957          unsigned char *readp;
 943  958          unsigned char *writep;
 944  959          struct termios *emodes;         /* effective modes set by driver */
 945  960          int dbtype;
 946  961  
 947  962          tp = (ldtermstd_state_t *)q->q_ptr;
 948  963          /*
 949  964           * We received our ack from the driver saying there is nothing left to
 950  965           * shovel out, so wake up the close routine.
 951  966           */
 952  967          dbtype = DB_TYPE(mp);
 953  968          if ((dbtype == M_IOCACK || dbtype == M_IOCNAK) &&
 954  969              (tp->t_state & (TS_CLOSE|TS_IOCWAIT)) == (TS_CLOSE|TS_IOCWAIT)) {
 955  970                  struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
 956  971  
 957  972                  if (iocp->ioc_id == tp->t_iocid) {
 958  973                          tp->t_state &= ~TS_IOCWAIT;
 959  974                          freemsg(mp);
 960  975                          return;
 961  976                  }
 962  977          }
 963  978  
 964  979          switch (dbtype) {
 965  980  
 966  981          default:
 967  982                  (void) putq(q, mp);
 968  983                  return;
 969  984  
 970  985                  /*
 971  986                   * Send these up unmolested
 972  987                   *
 973  988                   */
 974  989          case M_PCSIG:
 975  990          case M_SIG:
 976  991          case M_IOCNAK:
 977  992  
 978  993                  putnext(q, mp);
 979  994                  return;
 980  995  
 981  996          case M_IOCACK:
 982  997  
 983  998                  ldterm_ioctl_reply(q, mp);
 984  999                  return;
 985 1000  
 986 1001          case M_BREAK:
 987 1002  
 988 1003                  /*
 989 1004                   * Parity errors are sent up as M_BREAKS with single
 990 1005                   * character data (formerly handled in the driver)
 991 1006                   */
 992 1007                  if (mp->b_wptr - mp->b_rptr == 1) {
 993 1008                          /*
 994 1009                           * IGNPAR       PARMRK          RESULT
 995 1010                           * off          off             0
 996 1011                           * off          on              3 byte sequence
 997 1012                           * on           either          ignored
 998 1013                           */
 999 1014                          if (!(tp->t_amodes.c_iflag & IGNPAR)) {
1000 1015                                  mp->b_wptr = mp->b_rptr;
1001 1016                                  if (tp->t_amodes.c_iflag & PARMRK) {
1002 1017                                          unsigned char c;
1003 1018  
1004 1019                                          c = *mp->b_rptr;
1005 1020                                          freemsg(mp);
1006 1021                                          if ((mp = allocb(3, BPRI_HI)) == NULL) {
1007 1022                                                  cmn_err(CE_WARN,
1008 1023                                                      "ldtermrput: no blocks");
1009 1024                                                  return;
1010 1025                                          }
1011 1026                                          mp->b_datap->db_type = M_DATA;
1012 1027                                          *mp->b_wptr++ = (uchar_t)'\377';
1013 1028                                          *mp->b_wptr++ = '\0';
1014 1029                                          *mp->b_wptr++ = c;
1015 1030                                          putnext(q, mp);
1016 1031                                  } else {
1017 1032                                          mp->b_datap->db_type = M_DATA;
1018 1033                                          *mp->b_wptr++ = '\0';
1019 1034                                          putnext(q, mp);
1020 1035                                  }
1021 1036                          } else {
1022 1037                                  freemsg(mp);
1023 1038                          }
1024 1039                          return;
1025 1040                  }
1026 1041                  /*
1027 1042                   * We look at the apparent modes here instead of the
1028 1043                   * effective modes. Effective modes cannot be used if
1029 1044                   * IGNBRK, BRINT and PARMRK have been negotiated to
1030 1045                   * be handled by the driver. Since M_BREAK should be
1031 1046                   * sent upstream only if break processing was not
1032 1047                   * already done, it should be ok to use the apparent
1033 1048                   * modes.
1034 1049                   */
1035 1050  
1036 1051                  if (!(tp->t_amodes.c_iflag & IGNBRK)) {
1037 1052                          if (tp->t_amodes.c_iflag & BRKINT) {
1038 1053                                  ldterm_dosig(q, SIGINT, '\0', M_PCSIG, FLUSHRW);
1039 1054                                  freemsg(mp);
1040 1055                          } else if (tp->t_amodes.c_iflag & PARMRK) {
1041 1056                                  /*
1042 1057                                   * Send '\377','\0', '\0'.
1043 1058                                   */
1044 1059                                  freemsg(mp);
1045 1060                                  if ((mp = allocb(3, BPRI_HI)) == NULL) {
1046 1061                                          cmn_err(CE_WARN,
1047 1062                                              "ldtermrput: no blocks");
1048 1063                                          return;
1049 1064                                  }
1050 1065                                  mp->b_datap->db_type = M_DATA;
1051 1066                                  *mp->b_wptr++ = (uchar_t)'\377';
1052 1067                                  *mp->b_wptr++ = '\0';
1053 1068                                  *mp->b_wptr++ = '\0';
1054 1069                                  putnext(q, mp);
1055 1070                          } else {
1056 1071                                  /*
1057 1072                                   * Act as if a '\0' came in.
1058 1073                                   */
1059 1074                                  freemsg(mp);
1060 1075                                  if ((mp = allocb(1, BPRI_HI)) == NULL) {
1061 1076                                          cmn_err(CE_WARN,
1062 1077                                              "ldtermrput: no blocks");
1063 1078                                          return;
1064 1079                                  }
1065 1080                                  mp->b_datap->db_type = M_DATA;
1066 1081                                  *mp->b_wptr++ = '\0';
1067 1082                                  putnext(q, mp);
1068 1083                          }
1069 1084                  } else {
1070 1085                          freemsg(mp);
1071 1086                  }
1072 1087                  return;
1073 1088  
1074 1089          case M_CTL:
1075 1090                  DEBUG3(("ldtermrput: M_CTL received\n"));
1076 1091                  /*
1077 1092                   * The M_CTL has been standardized to look like an
1078 1093                   * M_IOCTL message.
1079 1094                   */
1080 1095  
1081 1096                  if ((mp->b_wptr - mp->b_rptr) != sizeof (struct iocblk)) {
1082 1097                          DEBUG3((
1083 1098                              "Non standard M_CTL received by ldterm module\n"));
1084 1099                          /* May be for someone else; pass it on */
1085 1100                          putnext(q, mp);
1086 1101                          return;
1087 1102                  }
1088 1103                  qryp = (struct iocblk *)mp->b_rptr;
1089 1104  
1090 1105                  switch (qryp->ioc_cmd) {
1091 1106  
1092 1107                  case MC_PART_CANON:
1093 1108  
1094 1109                          DEBUG3(("ldtermrput: M_CTL Query Reply\n"));
1095 1110                          if (!mp->b_cont) {
1096 1111                                  DEBUG3(("No information in Query Message\n"));
1097 1112                                  break;
1098 1113                          }
1099 1114                          if ((mp->b_cont->b_wptr - mp->b_cont->b_rptr) ==
1100 1115                              sizeof (struct termios)) {
1101 1116                                  DEBUG3(("ldtermrput: M_CTL GrandScheme\n"));
1102 1117                                  /* elaborate turning off scheme */
1103 1118                                  emodes = (struct termios *)mp->b_cont->b_rptr;
1104 1119                                  bcopy(emodes, &tp->t_dmodes,
1105 1120                                          sizeof (struct termios));
1106 1121                                  ldterm_adjust_modes(tp);
1107 1122                                  break;
1108 1123                          } else {
1109 1124                                  DEBUG3(("Incorrect query replysize\n"));
1110 1125                                  break;
1111 1126                          }
1112 1127  
1113 1128                  case MC_NO_CANON:
1114 1129                          tp->t_state |= TS_NOCANON;
1115 1130                          /*
1116 1131                           * Note: this is very nasty.  It's not clear
1117 1132                           * what the right thing to do with a partial
1118 1133                           * message is; We throw it out
1119 1134                           */
1120 1135                          if (tp->t_message != NULL) {
1121 1136                                  freemsg(tp->t_message);
1122 1137                                  tp->t_message = NULL;
1123 1138                                  tp->t_endmsg = NULL;
1124 1139                                  tp->t_msglen = 0;
1125 1140                                  tp->t_rocount = 0;
1126 1141                                  tp->t_rocol = 0;
1127 1142                                  if (tp->t_state & TS_MEUC) {
1128 1143                                          ASSERT(tp->t_eucp_mp);
1129 1144                                          tp->t_eucp = tp->t_eucp_mp->b_rptr;
1130 1145                                          tp->t_codeset = 0;
1131 1146                                          tp->t_eucleft = 0;
1132 1147                                  }
1133 1148                          }
1134 1149                          break;
1135 1150  
1136 1151                  case MC_DO_CANON:
1137 1152                          tp->t_state &= ~TS_NOCANON;
1138 1153                          break;
1139 1154  
1140 1155                  case MC_HAS_POSIX:
1141 1156                          /* no longer any reason to drain from ldterm */
1142 1157                          if (ldterm_drain_limit != 0) {
1143 1158                                  freemsg(tp->t_drainmsg);
1144 1159                                  tp->t_drainmsg = NULL;
1145 1160                          }
1146 1161                          break;
1147 1162  
1148 1163                  default:
1149 1164                          DEBUG3(("Unknown M_CTL Message\n"));
1150 1165                          break;
1151 1166                  }
1152 1167                  putnext(q, mp); /* In case anyone else has to see it */
1153 1168                  return;
1154 1169  
1155 1170          case M_FLUSH:
1156 1171                  /*
1157 1172                   * Flush everything we haven't looked at yet.
1158 1173                   */
1159 1174  
1160 1175                  if ((tp->t_state & TS_ISPTSTTY) && (*mp->b_rptr & FLUSHBAND))
1161 1176                          flushband(q, *(mp->b_rptr + 1), FLUSHDATA);
1162 1177                  else
1163 1178                          flushq(q, FLUSHDATA);
1164 1179  
1165 1180                  /*
1166 1181                   * Flush everything we have looked at.
1167 1182                   */
1168 1183                  freemsg(tp->t_message);
1169 1184                  tp->t_message = NULL;
1170 1185                  tp->t_endmsg = NULL;
1171 1186                  tp->t_msglen = 0;
1172 1187                  tp->t_rocount = 0;
1173 1188                  tp->t_rocol = 0;
1174 1189                  if (tp->t_state & TS_MEUC) {    /* EUC multi-byte */
1175 1190                          ASSERT(tp->t_eucp_mp);
1176 1191                          tp->t_eucp = tp->t_eucp_mp->b_rptr;
1177 1192                  }
1178 1193                  putnext(q, mp); /* pass it on */
1179 1194  
1180 1195                  /*
1181 1196                   * Relieve input flow control
1182 1197                   */
1183 1198                  if ((tp->t_modes.c_iflag & IXOFF) &&
1184 1199                      (tp->t_state & TS_TBLOCK) &&
1185 1200                      !(tp->t_state & TS_IFBLOCK) && q->q_count <= TTXOLO) {
1186 1201                          tp->t_state &= ~TS_TBLOCK;
1187 1202                          (void) putnextctl(wrq, M_STARTI);
1188 1203                          DEBUG1(("M_STARTI down\n"));
1189 1204                  }
1190 1205                  return;
1191 1206  
1192 1207          case M_DATA:
1193 1208                  break;
1194 1209          }
1195 1210          (void) drv_setparm(SYSRAWC, msgdsize(mp));
1196 1211  
1197 1212          /*
1198 1213           * Flow control: send "start input" message if blocked and
1199 1214           * our queue is below its low water mark.
1200 1215           */
1201 1216          if ((tp->t_modes.c_iflag & IXOFF) && (tp->t_state & TS_TBLOCK) &&
1202 1217              !(tp->t_state & TS_IFBLOCK) && q->q_count <= TTXOLO) {
1203 1218                  tp->t_state &= ~TS_TBLOCK;
1204 1219                  (void) putnextctl(wrq, M_STARTI);
1205 1220                  DEBUG1(("M_STARTI down\n"));
1206 1221          }
1207 1222          /*
1208 1223           * If somebody below us ("intelligent" communications
1209 1224           * board, pseudo-tty controlled by an editor) is doing
1210 1225           * canonicalization, don't scan it for special characters.
1211 1226           */
1212 1227          if (tp->t_state & TS_NOCANON) {
1213 1228                  (void) putq(q, mp);
1214 1229                  return;
1215 1230          }
1216 1231          bp = mp;
1217 1232  
1218 1233          do {
1219 1234                  readp = bp->b_rptr;
1220 1235                  writep = readp;
1221 1236                  if (tp->t_modes.c_iflag & (INLCR|IGNCR|ICRNL|IUCLC|IXON) ||
1222 1237                      tp->t_modes.c_lflag & (ISIG|ICANON)) {
1223 1238                          /*
1224 1239                           * We're doing some sort of non-trivial
1225 1240                           * processing of input; look at every
1226 1241                           * character.
1227 1242                           */
1228 1243                          while (readp < bp->b_wptr) {
1229 1244                                  c = *readp++;
1230 1245  
1231 1246                                  if (tp->t_modes.c_iflag & ISTRIP)
1232 1247                                          c &= 0177;
1233 1248  
1234 1249                                  /*
1235 1250                                   * First, check that this hasn't been
1236 1251                                   * escaped with the "literal next"
1237 1252                                   * character.
1238 1253                                   */
1239 1254                                  if (tp->t_state & TS_PLNCH) {
1240 1255                                          tp->t_state &= ~TS_PLNCH;
1241 1256                                          tp->t_modes.c_lflag &= ~FLUSHO;
1242 1257                                          *writep++ = c;
1243 1258                                          continue;
1244 1259                                  }
1245 1260                                  /*
1246 1261                                   * Setting a special character to NUL
1247 1262                                   * disables it, so if this character
1248 1263                                   * is NUL, it should not be compared
1249 1264                                   * with any of the special characters.
1250 1265                                   * It should, however, restart frozen
1251 1266                                   * output if IXON and IXANY are set.
1252 1267                                   */
1253 1268                                  if (c == _POSIX_VDISABLE) {
1254 1269                                          if (tp->t_modes.c_iflag & IXON &&
1255 1270                                              tp->t_state & TS_TTSTOP &&
1256 1271                                              tp->t_modes.c_lflag & IEXTEN &&
1257 1272                                              tp->t_modes.c_iflag & IXANY) {
1258 1273                                                  tp->t_state &=
1259 1274                                                      ~(TS_TTSTOP|TS_OFBLOCK);
1260 1275                                                  (void) putnextctl(wrq, M_START);
1261 1276                                          }
1262 1277                                          tp->t_modes.c_lflag &= ~FLUSHO;
1263 1278                                          *writep++ = c;
1264 1279                                          continue;
1265 1280                                  }
1266 1281                                  /*
1267 1282                                   * If stopped, start if you can; if
1268 1283                                   * running, stop if you must.
1269 1284                                   */
1270 1285                                  if (tp->t_modes.c_iflag & IXON) {
1271 1286                                          if (tp->t_state & TS_TTSTOP) {
1272 1287                                                  if (c ==
1273 1288                                                      tp->t_modes.c_cc[VSTART] ||
1274 1289                                                      (tp->t_modes.c_lflag &
1275 1290                                                      IEXTEN &&
1276 1291                                                      tp->t_modes.c_iflag &
1277 1292                                                      IXANY)) {
1278 1293                                                          tp->t_state &=
1279 1294                                                              ~(TS_TTSTOP |
1280 1295                                                              TS_OFBLOCK);
1281 1296                                                          (void) putnextctl(wrq,
1282 1297                                                              M_START);
1283 1298                                                  }
1284 1299                                          } else {
1285 1300                                                  if (c ==
1286 1301                                                      tp->t_modes.c_cc[VSTOP]) {
1287 1302                                                          tp->t_state |=
1288 1303                                                              TS_TTSTOP;
1289 1304                                                          (void) putnextctl(wrq,
1290 1305                                                              M_STOP);
1291 1306                                                  }
1292 1307                                          }
1293 1308                                          if (c == tp->t_modes.c_cc[VSTOP] ||
1294 1309                                              c == tp->t_modes.c_cc[VSTART])
1295 1310                                                  continue;
1296 1311                                  }
1297 1312                                  /*
1298 1313                                   * Check for "literal next" character
1299 1314                                   * and "flush output" character.
1300 1315                                   * Note that we omit checks for ISIG
1301 1316                                   * and ICANON, since the IEXTEN
1302 1317                                   * setting subsumes them.
1303 1318                                   */
1304 1319                                  if (tp->t_modes.c_lflag & IEXTEN) {
1305 1320                                          if (c == tp->t_modes.c_cc[VLNEXT]) {
1306 1321                                                  /*
1307 1322                                                   * Remember that we saw a
1308 1323                                                   * "literal next" while
1309 1324                                                   * scanning input, but leave
1310 1325                                                   * leave it in the message so
1311 1326                                                   * that the service routine
1312 1327                                                   * can see it too.
1313 1328                                                   */
1314 1329                                                  tp->t_state |= TS_PLNCH;
1315 1330                                                  tp->t_modes.c_lflag &= ~FLUSHO;
1316 1331                                                  *writep++ = c;
1317 1332                                                  continue;
1318 1333                                          }
1319 1334                                          if (c == tp->t_modes.c_cc[VDISCARD]) {
1320 1335                                                  ldterm_flush_output(c, wrq, tp);
1321 1336                                                  continue;
1322 1337                                          }
1323 1338                                  }
1324 1339                                  tp->t_modes.c_lflag &= ~FLUSHO;
1325 1340  
1326 1341                                  /*
1327 1342                                   * Check for signal-generating
1328 1343                                   * characters.
1329 1344                                   */
1330 1345                                  if (tp->t_modes.c_lflag & ISIG) {
1331 1346                                          if (c == tp->t_modes.c_cc[VINTR]) {
1332 1347                                                  ldterm_dosig(q, SIGINT, c,
1333 1348                                                      M_PCSIG, FLUSHRW);
1334 1349                                                  continue;
1335 1350                                          }
1336 1351                                          if (c == tp->t_modes.c_cc[VQUIT]) {
1337 1352                                                  ldterm_dosig(q, SIGQUIT, c,
1338 1353                                                      M_PCSIG, FLUSHRW);
1339 1354                                                  continue;
1340 1355                                          }
1341 1356                                          if (c == tp->t_modes.c_cc[VSWTCH]) {
1342 1357                                                  /*
1343 1358                                                   * Ancient SXT support; discard
1344 1359                                                   * character without action.
1345 1360                                                   */
1346 1361                                                  continue;
1347 1362                                          }
1348 1363                                          if (c == tp->t_modes.c_cc[VSUSP]) {
1349 1364                                                  ldterm_dosig(q, SIGTSTP, c,
1350 1365                                                      M_PCSIG, FLUSHRW);
1351 1366                                                  continue;
1352 1367                                          }
1353 1368                                          if ((tp->t_modes.c_lflag & IEXTEN) &&
1354 1369                                              (c == tp->t_modes.c_cc[VDSUSP])) {
1355 1370                                                  ldterm_dosig(q, SIGTSTP, c,
1356 1371                                                      M_SIG, 0);
1357 1372                                                  continue;
1358 1373                                          }
1359 1374  
1360 1375                                          /*
1361 1376                                           * Consumers do not expect the ^T to be
1362 1377                                           * echoed out when we generate a
1363 1378                                           * VSTATUS.
1364 1379                                           */
1365 1380                                          if (c == tp->t_modes.c_cc[VSTATUS]) {
1366 1381                                                  ldterm_dosig(q, SIGINFO, '\0',
1367 1382                                                      M_PCSIG, FLUSHRW);
1368 1383                                                  continue;
1369 1384                                          }
1370 1385                                  }
1371 1386                                  /*
1372 1387                                   * Throw away CR if IGNCR set, or
1373 1388                                   * turn it into NL if ICRNL set.
1374 1389                                   */
1375 1390                                  if (c == '\r') {
1376 1391                                          if (tp->t_modes.c_iflag & IGNCR)
1377 1392                                                  continue;
1378 1393                                          if (tp->t_modes.c_iflag & ICRNL)
1379 1394                                                  c = '\n';
1380 1395                                  } else {
1381 1396                                          /*
1382 1397                                           * Turn NL into CR if INLCR
1383 1398                                           * set.
1384 1399                                           */
1385 1400                                          if (c == '\n' &&
1386 1401                                              tp->t_modes.c_iflag & INLCR)
1387 1402                                                  c = '\r';
1388 1403                                  }
1389 1404  
1390 1405                                  /*
1391 1406                                   * Map upper case input to lower case
1392 1407                                   * if IUCLC flag set.
1393 1408                                   */
1394 1409                                  if (tp->t_modes.c_iflag & IUCLC &&
1395 1410                                      c >= 'A' && c <= 'Z')
1396 1411                                          c += 'a' - 'A';
1397 1412  
1398 1413                                  /*
1399 1414                                   * Put the possibly-transformed
1400 1415                                   * character back in the message.
1401 1416                                   */
1402 1417                                  *writep++ = c;
1403 1418                          }
1404 1419  
1405 1420                          /*
1406 1421                           * If we didn't copy some characters because
1407 1422                           * we were ignoring them, fix the size of the
1408 1423                           * data block by adjusting the write pointer.
1409 1424                           * XXX This may result in a zero-length
1410 1425                           * block; will this cause anybody gastric
1411 1426                           * distress?
1412 1427                           */
1413 1428                          bp->b_wptr -= (readp - writep);
1414 1429                  } else {
1415 1430                          /*
1416 1431                           * We won't be doing anything other than
1417 1432                           * possibly stripping the input.
1418 1433                           */
1419 1434                          if (tp->t_modes.c_iflag & ISTRIP) {
1420 1435                                  while (readp < bp->b_wptr)
1421 1436                                          *writep++ = *readp++ & 0177;
1422 1437                          }
1423 1438                          tp->t_modes.c_lflag &= ~FLUSHO;
1424 1439                  }
1425 1440  
1426 1441          } while ((bp = bp->b_cont) != NULL);    /* next block, if any */
1427 1442  
1428 1443          /*
1429 1444           * Queue the message for service procedure if the
1430 1445           * queue is not empty or canputnext() fails or
1431 1446           * tp->t_state & TS_RESCAN is true.
1432 1447           */
1433 1448  
1434 1449          if (q->q_first != NULL || !bcanputnext(q, mp->b_band) ||
1435 1450              (tp->t_state & TS_RESCAN))
1436 1451                  (void) putq(q, mp);
1437 1452          else
1438 1453                  (void) ldtermrmsg(q, mp);
1439 1454  
1440 1455          /*
1441 1456           * Flow control: send "stop input" message if our queue is
1442 1457           * approaching its high-water mark. The message will be
1443 1458           * dropped on the floor in the service procedure, if we
1444 1459           * cannot ship it up and we have had it upto our neck!
1445 1460           *
1446 1461           * Set QWANTW to ensure that the read queue service procedure
1447 1462           * gets run when nextq empties up again, so that it can
1448 1463           * unstop the input.
1449 1464           */
1450 1465          if ((tp->t_modes.c_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK) &&
1451 1466              q->q_count >= TTXOHI) {
1452 1467                  mutex_enter(QLOCK(nextq));
1453 1468                  nextq->q_flag |= QWANTW;
1454 1469                  mutex_exit(QLOCK(nextq));
1455 1470                  tp->t_state |= TS_TBLOCK;
1456 1471                  (void) putnextctl(wrq, M_STOPI);
1457 1472                  DEBUG1(("M_STOPI down\n"));
1458 1473          }
1459 1474  }
1460 1475  
1461 1476  
1462 1477  /*
1463 1478   * Line discipline input server processing.  Erase/kill and escape
1464 1479   * ('\') processing, gathering into messages, upper/lower case input
1465 1480   * mapping.
1466 1481   */
1467 1482  static void
1468 1483  ldtermrsrv(queue_t *q)
1469 1484  {
1470 1485          ldtermstd_state_t *tp;
1471 1486          mblk_t *mp;
1472 1487  
1473 1488          tp = (ldtermstd_state_t *)q->q_ptr;
1474 1489  
1475 1490          if (tp->t_state & TS_RESCAN) {
1476 1491                  /*
1477 1492                   * Canonicalization was turned on or off. Put the
1478 1493                   * message being assembled back in the input queue,
1479 1494                   * so that we rescan it.
1480 1495                   */
1481 1496                  if (tp->t_message != NULL) {
1482 1497                          DEBUG5(("RESCAN WAS SET; put back in q\n"));
1483 1498                          if (tp->t_msglen != 0)
1484 1499                                  (void) putbq(q, tp->t_message);
1485 1500                          else
1486 1501                                  freemsg(tp->t_message);
1487 1502                          tp->t_message = NULL;
1488 1503                          tp->t_endmsg = NULL;
1489 1504                          tp->t_msglen = 0;
1490 1505                  }
1491 1506                  if (tp->t_state & TS_MEUC) {
1492 1507                          ASSERT(tp->t_eucp_mp);
1493 1508                          tp->t_eucp = tp->t_eucp_mp->b_rptr;
1494 1509                          tp->t_codeset = 0;
1495 1510                          tp->t_eucleft = 0;
1496 1511                  }
1497 1512                  tp->t_state &= ~TS_RESCAN;
1498 1513          }
1499 1514  
1500 1515          while ((mp = getq(q)) != NULL) {
1501 1516                  if (!ldtermrmsg(q, mp))
1502 1517                          break;
1503 1518          }
1504 1519  
1505 1520          /*
1506 1521           * Flow control: send start message if blocked and our queue
1507 1522           * is below its low water mark.
1508 1523           */
1509 1524          if ((tp->t_modes.c_iflag & IXOFF) && (tp->t_state & TS_TBLOCK) &&
1510 1525              !(tp->t_state & TS_IFBLOCK) && q->q_count <= TTXOLO) {
1511 1526                  tp->t_state &= ~TS_TBLOCK;
1512 1527                  (void) putctl(WR(q), M_STARTI);
1513 1528          }
1514 1529  }
1515 1530  
1516 1531  /*
1517 1532   * This routine is called from both ldtermrput and ldtermrsrv to
1518 1533   * do the actual work of dealing with mp. Return 1 on sucesss and
1519 1534   * 0 on failure.
1520 1535   */
1521 1536  static int
1522 1537  ldtermrmsg(queue_t *q, mblk_t *mp)
1523 1538  {
1524 1539          unsigned char c;
1525 1540          int dofree;
1526 1541          int status = 1;
1527 1542          size_t   ebsize;
1528 1543          mblk_t *bp;
1529 1544          mblk_t *bpt;
1530 1545          ldtermstd_state_t *tp;
1531 1546  
1532 1547          bpt = NULL;
1533 1548  
1534 1549          tp = (ldtermstd_state_t *)q->q_ptr;
1535 1550  
1536 1551          if (mp->b_datap->db_type <= QPCTL && !bcanputnext(q, mp->b_band)) {
1537 1552                  /*
1538 1553                   * Stream head is flow controlled. If echo is
1539 1554                   * turned on, flush the read side or send a
1540 1555                   * bell down the line to stop input and
1541 1556                   * process the current message.
1542 1557                   * Otherwise(putbq) the user will not see any
1543 1558                   * response to to the typed input. Typically
1544 1559                   * happens if there is no reader process.
1545 1560                   * Note that you will loose the data in this
1546 1561                   * case if the data is coming too fast. There
1547 1562                   * is an assumption here that if ECHO is
1548 1563                   * turned on its some user typing the data on
1549 1564                   * a terminal and its not network.
1550 1565                   */
1551 1566                  if (tp->t_modes.c_lflag & ECHO) {
1552 1567                          if ((tp->t_modes.c_iflag & IMAXBEL) &&
1553 1568                              (tp->t_modes.c_lflag & ICANON)) {
1554 1569                                  freemsg(mp);
1555 1570                                  if (canputnext(WR(q)))
1556 1571                                          ldterm_outchar(CTRL('g'), WR(q), 4, tp);
1557 1572                                  status = 0;
1558 1573                                  goto echo;
1559 1574                          } else {
1560 1575                                  (void) putctl1(q, M_FLUSH, FLUSHR);
1561 1576                          }
1562 1577                  } else {
1563 1578                          (void) putbq(q, mp);
1564 1579                          status = 0;
1565 1580                          goto out;       /* read side is blocked */
1566 1581                  }
1567 1582          }
1568 1583          switch (mp->b_datap->db_type) {
1569 1584  
1570 1585          default:
1571 1586                  putnext(q, mp); /* pass it on */
1572 1587                  goto out;
1573 1588  
1574 1589          case M_HANGUP:
1575 1590                  /*
1576 1591                   * Flush everything we haven't looked at yet.
1577 1592                   */
1578 1593                  flushq(q, FLUSHDATA);
1579 1594  
1580 1595                  /*
1581 1596                   * Flush everything we have looked at.
1582 1597                   */
1583 1598                  freemsg(tp->t_message);
1584 1599                  tp->t_message = NULL;
1585 1600                  tp->t_endmsg = NULL;
1586 1601                  tp->t_msglen = 0;
1587 1602                  /*
1588 1603                   * XXX  should we set read request
1589 1604                   * tp->t_rd_request to NULL?
1590 1605                   */
1591 1606                  tp->t_rocount = 0;      /* if it hasn't been typed */
1592 1607                  tp->t_rocol = 0;        /* it hasn't been echoed :-) */
1593 1608                  if (tp->t_state & TS_MEUC) {
1594 1609                          ASSERT(tp->t_eucp_mp);
1595 1610                          tp->t_eucp = tp->t_eucp_mp->b_rptr;
1596 1611                  }
1597 1612                  /*
1598 1613                   * Restart output, since it's probably got
1599 1614                   * nowhere to go anyway, and we're probably
1600 1615                   * not going to see another ^Q for a while.
1601 1616                   */
1602 1617                  if (tp->t_state & TS_TTSTOP) {
1603 1618                          tp->t_state &= ~(TS_TTSTOP|TS_OFBLOCK);
1604 1619                          (void) putnextctl(WR(q), M_START);
1605 1620                  }
1606 1621                  /*
1607 1622                   * This message will travel up the read
1608 1623                   * queue, flushing as it goes, get turned
1609 1624                   * around at the stream head, and travel back
1610 1625                   * down the write queue, flushing as it goes.
1611 1626                   */
1612 1627                  (void) putnextctl1(q, M_FLUSH, FLUSHW);
1613 1628  
1614 1629                  /*
1615 1630                   * This message will travel down the write
1616 1631                   * queue, flushing as it goes, get turned
1617 1632                   * around at the driver, and travel back up
1618 1633                   * the read queue, flushing as it goes.
1619 1634                   */
1620 1635                  (void) putctl1(WR(q), M_FLUSH, FLUSHR);
1621 1636  
1622 1637                  /*
1623 1638                   * Now that that's done, we send a SIGCONT
1624 1639                   * upstream, followed by the M_HANGUP.
1625 1640                   */
1626 1641                  /* (void) putnextctl1(q, M_PCSIG, SIGCONT); */
1627 1642                  putnext(q, mp);
1628 1643                  goto out;
1629 1644  
1630 1645          case M_IOCACK:
1631 1646  
1632 1647                  /*
1633 1648                   * Augment whatever information the driver is
1634 1649                   * returning  with the information we supply.
1635 1650                   */
1636 1651                  ldterm_ioctl_reply(q, mp);
1637 1652                  goto out;
1638 1653  
1639 1654          case M_DATA:
1640 1655                  break;
1641 1656          }
1642 1657  
1643 1658          /*
1644 1659           * This is an M_DATA message.
1645 1660           */
1646 1661  
1647 1662          /*
1648 1663           * If somebody below us ("intelligent" communications
1649 1664           * board, pseudo-tty controlled by an editor) is
1650 1665           * doing canonicalization, don't scan it for special
1651 1666           * characters.
1652 1667           */
1653 1668          if (tp->t_state & TS_NOCANON) {
1654 1669                  putnext(q, mp);
1655 1670                  goto out;
1656 1671          }
1657 1672          bp = mp;
1658 1673  
1659 1674          if ((bpt = newmsg(tp)) != NULL) {
1660 1675                  mblk_t *bcont;
1661 1676  
1662 1677                  do {
1663 1678                          ASSERT(bp->b_wptr >= bp->b_rptr);
1664 1679                          ebsize = bp->b_wptr - bp->b_rptr;
1665 1680                          if (ebsize > EBSIZE)
1666 1681                                  ebsize = EBSIZE;
1667 1682                          bcont = bp->b_cont;
1668 1683                          if (CANON_MODE) {
1669 1684                                  /*
1670 1685                                   * By default, free the message once processed
1671 1686                                   */
1672 1687                                  dofree = 1;
1673 1688  
1674 1689                                  /*
1675 1690                                   * update sysinfo canch
1676 1691                                   * character. The value of
1677 1692                                   * canch may vary as compared
1678 1693                                   * to character tty
1679 1694                                   * implementation.
1680 1695                                   */
1681 1696                                  while (bp->b_rptr < bp->b_wptr) {
1682 1697                                          c = *bp->b_rptr++;
1683 1698                                          if ((bpt = ldterm_docanon(c,
1684 1699                                              bpt, ebsize, q, tp, &dofree)) ==
1685 1700                                              NULL)
1686 1701                                                  break;
1687 1702                                  }
1688 1703                                  /*
1689 1704                                   * Release this block or put back on queue.
1690 1705                                   */
1691 1706                                  if (dofree)
1692 1707                                          freeb(bp);
1693 1708                                  else {
1694 1709                                          (void) putbq(q, bp);
1695 1710                                          break;
1696 1711                                  }
1697 1712                          } else
1698 1713                                  bpt = ldterm_dononcanon(bp, bpt, ebsize, q, tp);
1699 1714                          if (bpt == NULL) {
1700 1715                                  cmn_err(CE_WARN,
1701 1716                                      "ldtermrsrv: out of blocks");
1702 1717                                  freemsg(bcont);
1703 1718                                  break;
1704 1719                          }
1705 1720                  } while ((bp = bcont) != NULL);
1706 1721          }
1707 1722  echo:
1708 1723          /*
1709 1724           * Send whatever we echoed downstream.
1710 1725           */
1711 1726          if (tp->t_echomp != NULL) {
1712 1727                  if (canputnext(WR(q)))
1713 1728                          putnext(WR(q), tp->t_echomp);
1714 1729                  else
1715 1730                          freemsg(tp->t_echomp);
1716 1731                  tp->t_echomp = NULL;
1717 1732          }
1718 1733  
1719 1734  out:
1720 1735          return (status);
1721 1736  }
1722 1737  
1723 1738  
1724 1739  /*
1725 1740   * Do canonical mode input; check whether this character is to be
1726 1741   * treated as a special character - if so, check whether it's equal
1727 1742   * to any of the special characters and handle it accordingly.
1728 1743   * Otherwise, just add it to the current line.
1729 1744   */
1730 1745  static mblk_t *
1731 1746  ldterm_docanon(uchar_t c, mblk_t *bpt, size_t ebsize, queue_t *q,
1732 1747      ldtermstd_state_t *tp, int *dofreep)
1733 1748  {
1734 1749          queue_t *wrq = WR(q);
1735 1750          int i;
1736 1751  
1737 1752          /*
1738 1753           * If the previous character was the "literal next"
1739 1754           * character, treat this character as regular input.
1740 1755           */
1741 1756          if (tp->t_state & TS_SLNCH)
1742 1757                  goto escaped;
1743 1758  
1744 1759          /*
1745 1760           * Setting a special character to NUL disables it, so if this
1746 1761           * character is NUL, it should not be compared with any of
1747 1762           * the special characters.
1748 1763           */
1749 1764          if (c == _POSIX_VDISABLE) {
1750 1765                  tp->t_state &= ~TS_QUOT;
1751 1766                  goto escaped;
1752 1767          }
1753 1768          /*
1754 1769           * If this character is the literal next character, echo it
1755 1770           * as '^', backspace over it, and record that fact.
1756 1771           */
1757 1772          if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VLNEXT]) {
1758 1773                  if (tp->t_modes.c_lflag & ECHO)
1759 1774                          ldterm_outstring((unsigned char *)"^\b", 2, wrq,
1760 1775                              ebsize, tp);
1761 1776                  tp->t_state |= TS_SLNCH;
1762 1777                  goto out;
1763 1778          }
1764 1779          /*
1765 1780           * Check for the editing character. If the display width of
1766 1781           * the last byte at the canonical buffer is not one and also
1767 1782           * smaller than or equal to UNKNOWN_WIDTH, the character at
1768 1783           * the end of the buffer is a multi-byte and/or multi-column
1769 1784           * character.
1770 1785           */
1771 1786          if (c == tp->t_modes.c_cc[VERASE] || c == tp->t_modes.c_cc[VERASE2]) {
1772 1787                  if (tp->t_state & TS_QUOT) {
1773 1788                          /*
1774 1789                           * Get rid of the backslash, and put the
1775 1790                           * erase character in its place.
1776 1791                           */
1777 1792                          ldterm_erase(wrq, ebsize, tp);
1778 1793                          bpt = tp->t_endmsg;
1779 1794                          goto escaped;
1780 1795                  } else {
1781 1796                          if ((tp->t_state & TS_MEUC) && tp->t_msglen &&
1782 1797                              (*(tp->t_eucp - 1) != 1 &&
1783 1798                              *(tp->t_eucp - 1) <= UNKNOWN_WIDTH))
1784 1799                                  ldterm_csi_erase(wrq, ebsize, tp);
1785 1800                          else
1786 1801                                  ldterm_erase(wrq, ebsize, tp);
1787 1802                          bpt = tp->t_endmsg;
1788 1803                          goto out;
1789 1804                  }
1790 1805          }
1791 1806          if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VWERASE]) {
1792 1807                  /*
1793 1808                   * Do "ASCII word" or "multibyte character token/chunk" erase.
1794 1809                   */
1795 1810                  if (tp->t_state & TS_MEUC)
1796 1811                          ldterm_csi_werase(wrq, ebsize, tp);
1797 1812                  else
1798 1813                          ldterm_werase(wrq, ebsize, tp);
1799 1814                  bpt = tp->t_endmsg;
1800 1815                  goto out;
1801 1816          }
1802 1817          if (c == tp->t_modes.c_cc[VKILL]) {
1803 1818                  if (tp->t_state & TS_QUOT) {
1804 1819                          /*
1805 1820                           * Get rid of the backslash, and put the kill
1806 1821                           * character in its place.
1807 1822                           */
1808 1823                          ldterm_erase(wrq, ebsize, tp);
1809 1824                          bpt = tp->t_endmsg;
1810 1825                          goto escaped;
1811 1826                  } else {
1812 1827                          ldterm_kill(wrq, ebsize, tp);
1813 1828                          bpt = tp->t_endmsg;
1814 1829                          goto out;
1815 1830                  }
1816 1831          }
1817 1832          if ((tp->t_modes.c_lflag & IEXTEN) && c == tp->t_modes.c_cc[VREPRINT]) {
1818 1833                  ldterm_reprint(wrq, ebsize, tp);
1819 1834                  goto out;
1820 1835          }
1821 1836          /*
1822 1837           * If the preceding character was a backslash: if the current
1823 1838           * character is an EOF, get rid of the backslash and treat
1824 1839           * the EOF as data; if we're in XCASE mode and the current
1825 1840           * character is part of a backslash-X escape sequence,
1826 1841           * process it; otherwise, just treat the current character
1827 1842           * normally.
1828 1843           */
1829 1844          if (tp->t_state & TS_QUOT) {
1830 1845                  tp->t_state &= ~TS_QUOT;
1831 1846                  if (c == tp->t_modes.c_cc[VEOF]) {
1832 1847                          /*
1833 1848                           * EOF character. Since it's escaped, get rid
1834 1849                           * of the backslash and put the EOF character
1835 1850                           * in its place.
1836 1851                           */
1837 1852                          ldterm_erase(wrq, ebsize, tp);
1838 1853                          bpt = tp->t_endmsg;
1839 1854                  } else {
1840 1855                          /*
1841 1856                           * If we're in XCASE mode, and the current
1842 1857                           * character is part of a backslash-X
1843 1858                           * sequence, get rid of the backslash and
1844 1859                           * replace the current character with what
1845 1860                           * that sequence maps to.
1846 1861                           */
1847 1862                          if ((tp->t_modes.c_lflag & XCASE) &&
1848 1863                              imaptab[c] != '\0') {
1849 1864                                  ldterm_erase(wrq, ebsize, tp);
1850 1865                                  bpt = tp->t_endmsg;
1851 1866                                  c = imaptab[c];
1852 1867                          }
1853 1868                  }
1854 1869          } else {
1855 1870                  /*
1856 1871                   * Previous character wasn't backslash; check whether
1857 1872                   * this was the EOF character.
1858 1873                   */
1859 1874                  if (c == tp->t_modes.c_cc[VEOF]) {
1860 1875                          /*
1861 1876                           * EOF character. Don't echo it unless
1862 1877                           * ECHOCTL is set, don't stuff it in the
1863 1878                           * current line, but send the line up the
1864 1879                           * stream.
1865 1880                           */
1866 1881                          if ((tp->t_modes.c_lflag & ECHOCTL) &&
1867 1882                              (tp->t_modes.c_lflag & IEXTEN) &&
1868 1883                              (tp->t_modes.c_lflag & ECHO)) {
1869 1884                                  i = ldterm_echo(c, wrq, ebsize, tp);
1870 1885                                  while (i > 0) {
1871 1886                                          ldterm_outchar('\b', wrq, ebsize, tp);
1872 1887                                          i--;
1873 1888                                  }
1874 1889                          }
1875 1890                          bpt->b_datap->db_type = M_DATA;
1876 1891                          ldterm_msg_upstream(q, tp);
1877 1892                          if (!canputnext(q)) {
1878 1893                                  bpt = NULL;
1879 1894                                  *dofreep = 0;
1880 1895                          } else {
1881 1896                                  bpt = newmsg(tp);
1882 1897                                  *dofreep = 1;
1883 1898                          }
1884 1899                          goto out;
1885 1900                  }
1886 1901          }
1887 1902  
1888 1903  escaped:
1889 1904          /*
1890 1905           * First, make sure we can fit one WHOLE multi-byte char in the
1891 1906           * buffer.  This is one place where we have overhead even if
1892 1907           * not in multi-byte mode; the overhead is subtracting
1893 1908           * tp->t_maxeuc from MAX_CANON before checking.
1894 1909           *
1895 1910           * Allows MAX_CANON bytes in the buffer before throwing awaying
1896 1911           * the the overflow of characters.
1897 1912           */
1898 1913          if ((tp->t_msglen > ((_TTY_BUFSIZ + 1) - (int)tp->t_maxeuc)) &&
1899 1914              !((tp->t_state & TS_MEUC) && tp->t_eucleft)) {
1900 1915  
1901 1916                  /*
1902 1917                   * Byte will cause line to overflow, or the next EUC
1903 1918                   * won't fit: Ring the bell or discard all input, and
1904 1919                   * don't save the byte away.
1905 1920                   */
1906 1921                  if (tp->t_modes.c_iflag & IMAXBEL) {
1907 1922                          if (canputnext(wrq))
1908 1923                                  ldterm_outchar(CTRL('g'), wrq, ebsize, tp);
1909 1924                          goto out;
1910 1925                  } else {
1911 1926                          /*
1912 1927                           * MAX_CANON processing. free everything in
1913 1928                           * the current line and start with the
1914 1929                           * current character as the first character.
1915 1930                           */
1916 1931                          DEBUG7(("ldterm_docanon: MAX_CANON processing\n"));
1917 1932                          freemsg(tp->t_message);
1918 1933                          tp->t_message = NULL;
1919 1934                          tp->t_endmsg = NULL;
1920 1935                          tp->t_msglen = 0;
1921 1936                          tp->t_rocount = 0;      /* if it hasn't been type */
1922 1937                          tp->t_rocol = 0;        /* it hasn't been echoed :-) */
1923 1938                          if (tp->t_state & TS_MEUC) {
1924 1939                                  ASSERT(tp->t_eucp_mp);
1925 1940                                  tp->t_eucp = tp->t_eucp_mp->b_rptr;
1926 1941                          }
1927 1942                          tp->t_state &= ~TS_SLNCH;
1928 1943                          bpt = newmsg(tp);
1929 1944                  }
1930 1945          }
1931 1946          /*
1932 1947           * Add the character to the current line.
1933 1948           */
1934 1949          if (bpt->b_wptr >= bpt->b_datap->db_lim) {
1935 1950                  /*
1936 1951                   * No more room in this mblk; save this one away, and
1937 1952                   * allocate a new one.
1938 1953                   */
1939 1954                  bpt->b_datap->db_type = M_DATA;
1940 1955                  if ((bpt = allocb(IBSIZE, BPRI_MED)) == NULL)
1941 1956                          goto out;
1942 1957  
1943 1958                  /*
1944 1959                   * Chain the new one to the end of the old one, and
1945 1960                   * mark it as the last block in the current line.
1946 1961                   */
1947 1962                  tp->t_endmsg->b_cont = bpt;
1948 1963                  tp->t_endmsg = bpt;
1949 1964          }
1950 1965          *bpt->b_wptr++ = c;
1951 1966          tp->t_msglen++;         /* message length in BYTES */
1952 1967  
1953 1968          /*
1954 1969           * In multi-byte mode, we have to keep track of where we are.
1955 1970           * The first bytes of multi-byte chars get the full count for the
1956 1971           * whole character.  We don't do any column calculations
1957 1972           * here, but we need the information for when we do. We could
1958 1973           * come across cases where we are getting garbage on the
1959 1974           * line, but we're in multi-byte mode.  In that case, we may
1960 1975           * see ASCII controls come in the middle of what should have been a
1961 1976           * multi-byte character.  Call ldterm_eucwarn...eventually, a
1962 1977           * warning message will be printed about it.
1963 1978           */
1964 1979          if (tp->t_state & TS_MEUC) {
1965 1980                  if (tp->t_eucleft) {    /* if in a multi-byte char already */
1966 1981                          --tp->t_eucleft;
1967 1982                          *tp->t_eucp++ = 0;      /* is a subsequent byte */
1968 1983                          if (c < (uchar_t)0x20)
1969 1984                                  ldterm_eucwarn(tp);
1970 1985                  } else { /* is the first byte of a multi-byte, or is ASCII */
1971 1986                          if (ISASCII(c)) {
1972 1987                                  *tp->t_eucp++ =
1973 1988                                      tp->t_csmethods.ldterm_dispwidth(c,
1974 1989                                      (void *)tp, tp->t_modes.c_lflag & ECHOCTL);
1975 1990                                  tp->t_codeset = 0;
1976 1991                          } else {
1977 1992                                  *tp->t_eucp++ =
1978 1993                                      tp->t_csmethods.ldterm_dispwidth(c,
1979 1994                                      (void *)tp, tp->t_modes.c_lflag & ECHOCTL);
1980 1995                                  tp->t_eucleft =
1981 1996                                      tp->t_csmethods.ldterm_memwidth(c,
1982 1997                                      (void *)tp) - 1;
1983 1998                                  tp->t_codeset = ldterm_codeset(
1984 1999                                      tp->t_csdata.codeset_type, c);
1985 2000                          }
1986 2001                  }
1987 2002          }
1988 2003          /*
1989 2004           * AT&T is concerned about the following but we aren't since
1990 2005           * we have already shipped code that works.
1991 2006           *
1992 2007           * EOL2/XCASE should be conditioned with IEXTEN to be truly
1993 2008           * POSIX conformant. This is going to cause problems for
1994 2009           * pre-SVR4.0 programs that don't know about IEXTEN. Hence
1995 2010           * EOL2/IEXTEN is not conditioned with IEXTEN.
1996 2011           */
1997 2012          if (!(tp->t_state & TS_SLNCH) &&
1998 2013              (c == '\n' || (c != '\0' && (c == tp->t_modes.c_cc[VEOL] ||
1999 2014              (c == tp->t_modes.c_cc[VEOL2]))))) {
2000 2015                  /*
2001 2016                   * || ((tp->t_modes.c_lflag & IEXTEN) && c ==
2002 2017                   * tp->t_modes.c_cc[VEOL2]))))) {
2003 2018                   */
2004 2019                  /*
2005 2020                   * It's a line-termination character; send the line
2006 2021                   * up the stream.
2007 2022                   */
2008 2023                  bpt->b_datap->db_type = M_DATA;
2009 2024                  ldterm_msg_upstream(q, tp);
2010 2025                  if (tp->t_state & TS_MEUC) {
2011 2026                          ASSERT(tp->t_eucp_mp);
2012 2027                          tp->t_eucp = tp->t_eucp_mp->b_rptr;
2013 2028                  }
2014 2029                  if ((bpt = newmsg(tp)) == NULL)
2015 2030                          goto out;
2016 2031          } else {
2017 2032                  /*
2018 2033                   * Character was escaped with LNEXT.
2019 2034                   */
2020 2035                  if (tp->t_rocount++ == 0)
2021 2036                          tp->t_rocol = tp->t_col;
2022 2037                  tp->t_state &= ~(TS_SLNCH|TS_QUOT);
2023 2038                  /*
2024 2039                   * If the current character is a single byte and single
2025 2040                   * column character and it is the backslash character and
2026 2041                   * IEXTEN, then the state will have TS_QUOT.
2027 2042                   */
2028 2043                  if ((c == '\\') && (tp->t_modes.c_lflag & IEXTEN) &&
2029 2044                      (!(tp->t_state & TS_MEUC) ||
2030 2045                      ((tp->t_state & TS_MEUC) && (!tp->t_eucleft))))
2031 2046                          tp->t_state |= TS_QUOT;
2032 2047          }
2033 2048  
2034 2049          /*
2035 2050           * Echo it.
2036 2051           */
2037 2052          if (tp->t_state & TS_ERASE) {
2038 2053                  tp->t_state &= ~TS_ERASE;
2039 2054                  if (tp->t_modes.c_lflag & ECHO)
2040 2055                          ldterm_outchar('/', wrq, ebsize, tp);
2041 2056          }
2042 2057          if (tp->t_modes.c_lflag & ECHO)
2043 2058                  (void) ldterm_echo(c, wrq, ebsize, tp);
2044 2059          else {
2045 2060                  /*
2046 2061                   * Echo NL when ECHO turned off, if ECHONL flag is
2047 2062                   * set.
2048 2063                   */
2049 2064                  if (c == '\n' && (tp->t_modes.c_lflag & ECHONL))
2050 2065                          ldterm_outchar(c, wrq, ebsize, tp);
2051 2066          }
2052 2067  
2053 2068  out:
2054 2069  
2055 2070          return (bpt);
2056 2071  }
2057 2072  
2058 2073  
2059 2074  static int
2060 2075  ldterm_unget(ldtermstd_state_t *tp)
2061 2076  {
2062 2077          mblk_t *bpt;
2063 2078  
2064 2079          if ((bpt = tp->t_endmsg) == NULL)
2065 2080                  return (-1);    /* no buffers */
2066 2081          if (bpt->b_rptr == bpt->b_wptr)
2067 2082                  return (-1);    /* zero-length record */
2068 2083          tp->t_msglen--;         /* one fewer character */
2069 2084          return (*--bpt->b_wptr);
2070 2085  }
2071 2086  
2072 2087  
2073 2088  static void
2074 2089  ldterm_trim(ldtermstd_state_t *tp)
2075 2090  {
2076 2091          mblk_t *bpt;
2077 2092          mblk_t *bp;
2078 2093  
2079 2094          ASSERT(tp->t_endmsg);
2080 2095          bpt = tp->t_endmsg;
2081 2096  
2082 2097          if (bpt->b_rptr == bpt->b_wptr) {
2083 2098                  /*
2084 2099                   * This mblk is now empty. Find the previous mblk;
2085 2100                   * throw this one away, unless it's the first one.
2086 2101                   */
2087 2102                  bp = tp->t_message;
2088 2103                  if (bp != bpt) {
2089 2104                          while (bp->b_cont != bpt) {
2090 2105                                  ASSERT(bp->b_cont);
2091 2106                                  bp = bp->b_cont;
2092 2107                          }
2093 2108                          bp->b_cont = NULL;
2094 2109                          freeb(bpt);
2095 2110                          tp->t_endmsg = bp;      /* point to that mblk */
2096 2111                  }
2097 2112          }
2098 2113  }
2099 2114  
2100 2115  
2101 2116  /*
2102 2117   * Rubout one character from the current line being built for tp as
2103 2118   * cleanly as possible.  q is the write queue for tp. Most of this
2104 2119   * can't be applied to multi-byte processing.  We do our own thing
2105 2120   * for that... See the "ldterm_eucerase" routine.  We never call
2106 2121   * ldterm_rubout on a multi-byte or multi-column character.
2107 2122   */
2108 2123  static void
2109 2124  ldterm_rubout(uchar_t c, queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
2110 2125  {
2111 2126          int tabcols;
2112 2127          static unsigned char crtrubout[] = "\b \b\b \b";
2113 2128  #define RUBOUT1 &crtrubout[3]   /* rub out one position */
2114 2129  #define RUBOUT2 &crtrubout[0]   /* rub out two positions */
2115 2130  
2116 2131          if (!(tp->t_modes.c_lflag & ECHO))
2117 2132                  return;
2118 2133          if (tp->t_modes.c_lflag & ECHOE) {
2119 2134                  /*
2120 2135                   * "CRT rubout"; try erasing it from the screen.
2121 2136                   */
2122 2137                  if (tp->t_rocount == 0) {
2123 2138                          /*
2124 2139                           * After the character being erased was
2125 2140                           * echoed, some data was written to the
2126 2141                           * terminal; we can't erase it cleanly, so we
2127 2142                           * just reprint the whole line as if the user
2128 2143                           * had typed the reprint character.
2129 2144                           */
2130 2145                          ldterm_reprint(q, ebsize, tp);
2131 2146                          return;
2132 2147                  } else {
2133 2148                          /*
2134 2149                           * XXX what about escaped characters?
2135 2150                           */
2136 2151                          switch (typetab[c]) {
2137 2152  
2138 2153                          case ORDINARY:
2139 2154                                  if ((tp->t_modes.c_lflag & XCASE) &&
2140 2155                                      omaptab[c])
2141 2156                                          ldterm_outstring(RUBOUT1, 3, q, ebsize,
2142 2157                                              tp);
2143 2158                                  ldterm_outstring(RUBOUT1, 3, q, ebsize, tp);
2144 2159                                  break;
2145 2160  
2146 2161                          case VTAB:
2147 2162                          case BACKSPACE:
2148 2163                          case CONTROL:
2149 2164                          case RETURN:
2150 2165                          case NEWLINE:
2151 2166                                  if ((tp->t_modes.c_lflag & ECHOCTL) &&
2152 2167                                      (tp->t_modes.c_lflag & IEXTEN))
2153 2168                                          ldterm_outstring(RUBOUT2, 6, q, ebsize,
2154 2169                                              tp);
2155 2170                                  break;
2156 2171  
2157 2172                          case TAB:
2158 2173                                  if (tp->t_rocount < tp->t_msglen) {
2159 2174                                          /*
2160 2175                                           * While the tab being erased was
2161 2176                                           * expanded, some data was written
2162 2177                                           * to the terminal; we can't erase
2163 2178                                           * it cleanly, so we just reprint
2164 2179                                           * the whole line as if the user
2165 2180                                           * had typed the reprint character.
2166 2181                                           */
2167 2182                                          ldterm_reprint(q, ebsize, tp);
2168 2183                                          return;
2169 2184                                  }
2170 2185                                  tabcols = ldterm_tabcols(tp);
2171 2186                                  while (--tabcols >= 0)
2172 2187                                          ldterm_outchar('\b', q, ebsize, tp);
2173 2188                                  break;
2174 2189                          }
2175 2190                  }
2176 2191          } else if ((tp->t_modes.c_lflag & ECHOPRT) &&
2177 2192              (tp->t_modes.c_lflag & IEXTEN)) {
2178 2193                  /*
2179 2194                   * "Printing rubout"; echo it between \ and /.
2180 2195                   */
2181 2196                  if (!(tp->t_state & TS_ERASE)) {
2182 2197                          ldterm_outchar('\\', q, ebsize, tp);
2183 2198                          tp->t_state |= TS_ERASE;
2184 2199                  }
2185 2200                  (void) ldterm_echo(c, q, ebsize, tp);
2186 2201          } else
2187 2202                  (void) ldterm_echo(tp->t_modes.c_cc[VERASE], q, ebsize, tp);
2188 2203          tp->t_rocount--;        /* we "unechoed" this character */
2189 2204  }
2190 2205  
2191 2206  
2192 2207  /*
2193 2208   * Find the number of characters the tab we just deleted took up by
2194 2209   * zipping through the current line and recomputing the column
2195 2210   * number.
2196 2211   */
2197 2212  static int
2198 2213  ldterm_tabcols(ldtermstd_state_t *tp)
2199 2214  {
2200 2215          int col;
2201 2216          int i;
2202 2217          mblk_t *bp;
2203 2218          unsigned char *readp, *endp;
2204 2219          unsigned char c;
2205 2220          uchar_t *startp;
2206 2221          char errflg;
2207 2222          uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
2208 2223  
2209 2224          col = tp->t_rocol;
2210 2225          /*
2211 2226           * If we're doing multi-byte stuff, zip through the list of
2212 2227           * widths to figure out where we are (we've kept track in most
2213 2228           * cases).
2214 2229           */
2215 2230          if (tp->t_state & TS_MEUC) {
2216 2231                  ASSERT(tp->t_eucp_mp);
2217 2232                  bp = tp->t_message;
2218 2233                  startp = bp->b_datap->db_base;
2219 2234                  readp = tp->t_eucp_mp->b_rptr;
2220 2235                  endp = tp->t_eucp;
2221 2236                  errflg = (char)0;
2222 2237                  while (readp < endp) {
2223 2238                          switch (*readp) {
2224 2239                          case EUC_TWIDTH:        /* it's a tab */
2225 2240                                  col |= 07;      /* bump up */
2226 2241                                  col++;
2227 2242                                  break;
2228 2243                          case EUC_BSWIDTH:       /* backspace */
2229 2244                                  if (col)
2230 2245                                          col--;
2231 2246                                  break;
2232 2247                          case EUC_NLWIDTH:       /* newline */
2233 2248                                  if (tp->t_modes.c_oflag & ONLRET)
2234 2249                                          col = 0;
2235 2250                                  break;
2236 2251                          case EUC_CRWIDTH:       /* return */
2237 2252                                  col = 0;
2238 2253                                  break;
2239 2254                          case UNKNOWN_WIDTH:     /* UTF-8 unknown width */
2240 2255                                  if (tp->t_csdata.codeset_type !=
2241 2256                                      LDTERM_CS_TYPE_UTF8 || errflg) {
2242 2257                                          *readp = 1;
2243 2258                                          col++;
2244 2259                                          break;
2245 2260                                  }
2246 2261                                  /*
2247 2262                                   * Collect the current UTF-8 character bytes
2248 2263                                   * from (possibly multiple) data buffers so
2249 2264                                   * that we can figure out the display width.
2250 2265                                   */
2251 2266                                  u8[0] = *startp;
2252 2267                                  for (i = 1; (i < LDTERM_CS_MAX_BYTE_LENGTH) &&
2253 2268                                      (*(readp + i) == 0); i++) {
2254 2269                                          startp++;
2255 2270                                          if (startp >= bp->b_datap->db_lim) {
2256 2271                                                  if (bp->b_cont) {
2257 2272                                                          bp = bp->b_cont;
2258 2273                                                          startp =
2259 2274                                                              bp->b_datap->
2260 2275                                                              db_base;
2261 2276                                                  } else {
2262 2277                                                          *readp = 1;
2263 2278                                                          col++;
2264 2279                                                          break;
2265 2280                                                  }
2266 2281                                          }
2267 2282                                          u8[i] = *startp;
2268 2283                                  }
2269 2284  
2270 2285                                  /* tp->t_eucp_mp contains wrong info?? */
2271 2286                                  if (*readp == 1)
2272 2287                                          break;
2273 2288  
2274 2289                                  *readp = ldterm_utf8_width(u8, i);
2275 2290  
2276 2291                                  col += *readp;
2277 2292                                  readp += (i - 1);
2278 2293                                  break;
2279 2294                          default:
2280 2295                                  col += *readp;
2281 2296                                  break;
2282 2297                          }
2283 2298                          ++readp;
2284 2299                          ++startp;
2285 2300                          if (startp >= bp->b_datap->db_lim) {
2286 2301                                  if (bp->b_cont) {
2287 2302                                          bp = bp->b_cont;
2288 2303                                          startp = bp->b_datap->db_base;
2289 2304                                  } else {
2290 2305                                          /*
2291 2306                                           * This will happen only if
2292 2307                                           * tp->t_eucp_mp contains wrong
2293 2308                                           * display width info.
2294 2309                                           */
2295 2310                                          errflg = (char)1;
2296 2311                                          startp--;
2297 2312                                  }
2298 2313                          }
2299 2314                  }
2300 2315                  goto eucout;    /* finished! */
2301 2316          }
2302 2317          bp = tp->t_message;
2303 2318          do {
2304 2319                  readp = bp->b_rptr;
2305 2320                  while (readp < bp->b_wptr) {
2306 2321                          c = *readp++;
2307 2322                          if ((tp->t_modes.c_lflag & ECHOCTL) &&
2308 2323                              (tp->t_modes.c_lflag & IEXTEN)) {
2309 2324                                  if (c <= 037 && c != '\t' && c != '\n' ||
2310 2325                                      c == 0177) {
2311 2326                                          col += 2;
2312 2327                                          continue;
2313 2328                                  }
2314 2329                          }
2315 2330                          /*
2316 2331                           * Column position calculated here.
2317 2332                           */
2318 2333                          switch (typetab[c]) {
2319 2334  
2320 2335                                  /*
2321 2336                                   * Ordinary characters; advance by
2322 2337                                   * one.
2323 2338                                   */
2324 2339                          case ORDINARY:
2325 2340                                  col++;
2326 2341                                  break;
2327 2342  
2328 2343                                  /*
2329 2344                                   * Non-printing characters; nothing
2330 2345                                   * happens.
2331 2346                                   */
2332 2347                          case CONTROL:
2333 2348                                  break;
2334 2349  
2335 2350                                  /* Backspace */
2336 2351                          case BACKSPACE:
2337 2352                                  if (col != 0)
2338 2353                                          col--;
2339 2354                                  break;
2340 2355  
2341 2356                                  /* Newline; column depends on flags. */
2342 2357                          case NEWLINE:
2343 2358                                  if (tp->t_modes.c_oflag & ONLRET)
2344 2359                                          col = 0;
2345 2360                                  break;
2346 2361  
2347 2362                                  /* tab */
2348 2363                          case TAB:
2349 2364                                  col |= 07;
2350 2365                                  col++;
2351 2366                                  break;
2352 2367  
2353 2368                                  /* vertical motion */
2354 2369                          case VTAB:
2355 2370                                  break;
2356 2371  
2357 2372                                  /* carriage return */
2358 2373                          case RETURN:
2359 2374                                  col = 0;
2360 2375                                  break;
2361 2376                          }
2362 2377                  }
2363 2378          } while ((bp = bp->b_cont) != NULL);    /* next block, if any */
2364 2379  
2365 2380          /*
2366 2381           * "col" is now the column number before the tab. "tp->t_col"
2367 2382           * is still the column number after the tab, since we haven't
2368 2383           * erased the tab yet. Thus "tp->t_col - col" is the number
2369 2384           * of positions the tab moved.
2370 2385           */
2371 2386  eucout:
2372 2387          col = tp->t_col - col;
2373 2388          if (col > 8)
2374 2389                  col = 8;        /* overflow screw */
2375 2390          return (col);
2376 2391  }
2377 2392  
2378 2393  
2379 2394  /*
2380 2395   * Erase a single character; We ONLY ONLY deal with ASCII or
2381 2396   * single-column single-byte codeset character.  For multi-byte characters,
2382 2397   * see "ldterm_csi_erase".
2383 2398   */
2384 2399  static void
2385 2400  ldterm_erase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
2386 2401  {
2387 2402          int c;
2388 2403  
2389 2404          if ((c = ldterm_unget(tp)) != -1) {
2390 2405                  ldterm_rubout((unsigned char) c, q, ebsize, tp);
2391 2406                  ldterm_trim(tp);
2392 2407                  if (tp->t_state & TS_MEUC)
2393 2408                          --tp->t_eucp;
2394 2409          }
2395 2410  }
2396 2411  
2397 2412  
2398 2413  /*
2399 2414   * Erase an entire word, single-byte EUC only please.
2400 2415   */
2401 2416  static void
2402 2417  ldterm_werase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
2403 2418  {
2404 2419          int c;
2405 2420  
2406 2421          /*
2407 2422           * Erase trailing white space, if any.
2408 2423           */
2409 2424          while ((c = ldterm_unget(tp)) == ' ' || c == '\t') {
2410 2425                  ldterm_rubout((unsigned char) c, q, ebsize, tp);
2411 2426                  ldterm_trim(tp);
2412 2427          }
2413 2428  
2414 2429          /*
2415 2430           * Erase non-white-space characters, if any.
2416 2431           */
2417 2432          while (c != -1 && c != ' ' && c != '\t') {
2418 2433                  ldterm_rubout((unsigned char) c, q, ebsize, tp);
2419 2434                  ldterm_trim(tp);
2420 2435                  c = ldterm_unget(tp);
2421 2436          }
2422 2437          if (c != -1) {
2423 2438                  /*
2424 2439                   * We removed one too many characters; put the last
2425 2440                   * one back.
2426 2441                   */
2427 2442                  tp->t_endmsg->b_wptr++; /* put 'c' back */
2428 2443                  tp->t_msglen++;
2429 2444          }
2430 2445  }
2431 2446  
2432 2447  
2433 2448  /*
2434 2449   * ldterm_csi_werase - This is multi-byte equivalent of "word erase".
2435 2450   * "Word erase" only makes sense in languages which space between words,
2436 2451   * and it's presumptuous for us to attempt "word erase" when we don't
2437 2452   * know anything about what's really going on.  It makes no sense for
2438 2453   * many languages, as the criteria for defining words and tokens may
2439 2454   * be completely different.
2440 2455   *
2441 2456   * In the TS_MEUC case (which is how we got here), we define a token to
2442 2457   * be space- or tab-delimited, and erase one of them.  It helps to
2443 2458   * have this for command lines, but it's otherwise useless for text
2444 2459   * editing applications; you need more sophistication than we can
2445 2460   * provide here.
2446 2461   */
2447 2462  static void
2448 2463  ldterm_csi_werase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
2449 2464  {
2450 2465          int c, i;
2451 2466          int len;
2452 2467          uchar_t *ip;
2453 2468          uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
2454 2469          uchar_t u8_2[LDTERM_CS_MAX_BYTE_LENGTH];
2455 2470  
2456 2471          /*
2457 2472           * ip points to the width of the actual bytes.  t_eucp points
2458 2473           * one byte beyond, where the next thing will be inserted.
2459 2474           */
2460 2475          ip = tp->t_eucp - 1;
2461 2476          /*
2462 2477           * Erase trailing white space, if any.
2463 2478           */
2464 2479          while ((c = ldterm_unget(tp)) == ' ' || c == '\t') {
2465 2480                  tp->t_eucp--;
2466 2481                  ldterm_rubout((unsigned char) c, q, ebsize, tp);
2467 2482                  ldterm_trim(tp);
2468 2483                  --ip;
2469 2484          }
2470 2485  
2471 2486          /*
2472 2487           * Erase non-white-space characters, if any.  The outer loop
2473 2488           * bops through each byte in the buffer. Multi-byte is removed, as
2474 2489           * is ASCII, one byte at a time. The inner loop (for) is only
2475 2490           * executed for first bytes of multi-byte.  The inner loop erases
2476 2491           * the number of columns required for the multi-byte char.  We check
2477 2492           * for ASCII first, and ldterm_rubout knows about ASCII.
2478 2493           */
2479 2494          len = 0;
2480 2495          while (c != -1 && c != ' ' && c != '\t') {
2481 2496                  tp->t_eucp--;
2482 2497                  if (len < LDTERM_CS_MAX_BYTE_LENGTH) {
2483 2498                          u8[len++] = (uchar_t)c;
2484 2499                  }
2485 2500                  /*
2486 2501                   * Unlike EUC, except the leading byte, some bytes of
2487 2502                   * a non-EUC multi-byte characters are in the ASCII code
2488 2503                   * range, esp., 0x41 ~ 0x7a. Thus, we cannot simply check
2489 2504                   * ISASCII().
2490 2505                   * Checking the (*ip == 1 || *ip == 2 || *ip > UNKNOWN_WIDTH)
2491 2506                   * will ensure that it is a single byte character (even though
2492 2507                   * it is on display width not byte length) and can be further
2493 2508                   * checked whether it is an ASCII character or not.
2494 2509                   *
2495 2510                   * When ECHOCTL is on and 'c' is an ASCII control character,
2496 2511                   * *ip == 2 happens.
2497 2512                   */
2498 2513                  if ((*ip == 1 || *ip == 2 || *ip > UNKNOWN_WIDTH) &&
2499 2514                      ISASCII(c)) {
2500 2515                          ldterm_rubout((unsigned char) c, q, ebsize, tp);
2501 2516                          len = 0;
2502 2517                  } else if (*ip) {
2503 2518                          if (*ip == UNKNOWN_WIDTH) {
2504 2519                                  if (tp->t_csdata.codeset_type ==
2505 2520                                      LDTERM_CS_TYPE_UTF8) {
2506 2521                                          for (i = 0; i < len; i++)
2507 2522                                                  u8_2[i] = u8[len - i - 1];
2508 2523                                          *ip = ldterm_utf8_width(u8_2, len);
2509 2524                                  } else {
2510 2525                                          *ip = 1;
2511 2526                                  }
2512 2527                          }
2513 2528                          /*
2514 2529                           * erase for number of columns required for
2515 2530                           * this multi-byte character. Hopefully, matches
2516 2531                           * ldterm_dispwidth!
2517 2532                           */
2518 2533                          for (i = 0; i < (int)*ip; i++)
2519 2534                                  ldterm_rubout(' ', q, ebsize, tp);
2520 2535                          len = 0;
2521 2536                  }
2522 2537                  ldterm_trim(tp);
2523 2538                  --ip;
2524 2539                  c = ldterm_unget(tp);
2525 2540          }
2526 2541          if (c != -1) {
2527 2542                  /*
2528 2543                   * We removed one too many characters; put the last
2529 2544                   * one back.
2530 2545                   */
2531 2546                  tp->t_endmsg->b_wptr++; /* put 'c' back */
2532 2547                  tp->t_msglen++;
2533 2548          }
2534 2549  }
2535 2550  
2536 2551  
2537 2552  /*
2538 2553   * Kill an entire line, erasing each character one-by-one (if ECHOKE
2539 2554   * is set) or just echoing the kill character, followed by a newline
2540 2555   * (if ECHOK is set).  Multi-byte processing is included here.
2541 2556   */
2542 2557  
2543 2558  static void
2544 2559  ldterm_kill(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
2545 2560  {
2546 2561          int c, i;
2547 2562          int len;
2548 2563          uchar_t *ip;
2549 2564          uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
2550 2565          uchar_t u8_2[LDTERM_CS_MAX_BYTE_LENGTH];
2551 2566  
2552 2567          if ((tp->t_modes.c_lflag & ECHOKE) &&
2553 2568              (tp->t_modes.c_lflag & IEXTEN) &&
2554 2569              (tp->t_msglen == tp->t_rocount)) {
2555 2570                  if (tp->t_state & TS_MEUC) {
2556 2571                          ip = tp->t_eucp - 1;
2557 2572                          /*
2558 2573                           * This loop similar to "ldterm_csi_werase" above.
2559 2574                           */
2560 2575                          len = 0;
2561 2576                          while ((c = ldterm_unget(tp)) != (-1)) {
2562 2577                                  tp->t_eucp--;
2563 2578                                  if (len < LDTERM_CS_MAX_BYTE_LENGTH) {
2564 2579                                          u8[len++] = (uchar_t)c;
2565 2580                                  }
2566 2581                                  if ((*ip == 1 || *ip == 2 ||
2567 2582                                      *ip > UNKNOWN_WIDTH) && ISASCII(c)) {
2568 2583                                          ldterm_rubout((unsigned char) c, q,
2569 2584                                              ebsize, tp);
2570 2585                                          len = 0;
2571 2586                                  } else if (*ip) {
2572 2587                                          if (*ip == UNKNOWN_WIDTH) {
2573 2588                                                  if (tp->t_csdata.codeset_type
2574 2589                                                      == LDTERM_CS_TYPE_UTF8) {
2575 2590                                                          for (i = 0; i < len;
2576 2591                                                              i++)
2577 2592                                                                  u8_2[i] =
2578 2593                                                                      u8[len-i-1];
2579 2594                                                          *ip = ldterm_utf8_width(
2580 2595                                                              u8_2, len);
2581 2596                                                  } else {
2582 2597                                                          *ip = 1;
2583 2598                                                  }
2584 2599                                          }
2585 2600                                          for (i = 0; i < (int)*ip; i++)
2586 2601                                                  ldterm_rubout(' ', q, ebsize,
2587 2602                                                      tp);
2588 2603                                          len = 0;
2589 2604                                  }
2590 2605                                  ldterm_trim(tp);
2591 2606                                  --ip;
2592 2607                          }
2593 2608                  } else {
2594 2609                          while ((c = ldterm_unget(tp)) != -1) {
2595 2610                                  ldterm_rubout((unsigned char) c, q, ebsize, tp);
2596 2611                                  ldterm_trim(tp);
2597 2612                          }
2598 2613                  }
2599 2614          } else {
2600 2615                  (void) ldterm_echo(tp->t_modes.c_cc[VKILL], q, ebsize, tp);
2601 2616                  if (tp->t_modes.c_lflag & ECHOK)
2602 2617                          (void) ldterm_echo('\n', q, ebsize, tp);
2603 2618                  while (ldterm_unget(tp) != -1) {
2604 2619                          if (tp->t_state & TS_MEUC)
2605 2620                                  --tp->t_eucp;
2606 2621                          ldterm_trim(tp);
2607 2622                  }
2608 2623                  tp->t_rocount = 0;
2609 2624                  if (tp->t_state & TS_MEUC)
2610 2625                          tp->t_eucp = tp->t_eucp_mp->b_rptr;
2611 2626          }
2612 2627          tp->t_state &= ~(TS_QUOT|TS_ERASE|TS_SLNCH);
2613 2628  }
2614 2629  
2615 2630  
2616 2631  /*
2617 2632   * Reprint the current input line. We assume c_cc has already been
2618 2633   * checked. XXX just the current line, not the whole queue? What
2619 2634   * about DEFECHO mode?
2620 2635   */
2621 2636  static void
2622 2637  ldterm_reprint(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
2623 2638  {
2624 2639          mblk_t *bp;
2625 2640          unsigned char *readp;
2626 2641  
2627 2642          if (tp->t_modes.c_cc[VREPRINT] != (unsigned char) 0)
2628 2643                  (void) ldterm_echo(tp->t_modes.c_cc[VREPRINT], q, ebsize, tp);
2629 2644          ldterm_outchar('\n', q, ebsize, tp);
2630 2645  
2631 2646          bp = tp->t_message;
2632 2647          do {
2633 2648                  readp = bp->b_rptr;
2634 2649                  while (readp < bp->b_wptr)
2635 2650                          (void) ldterm_echo(*readp++, q, ebsize, tp);
2636 2651          } while ((bp = bp->b_cont) != NULL);    /* next block, if any */
2637 2652  
2638 2653          tp->t_state &= ~TS_ERASE;
2639 2654          tp->t_rocount = tp->t_msglen;   /* we reechoed the entire line */
2640 2655          tp->t_rocol = 0;
2641 2656  }
2642 2657  
2643 2658  
2644 2659  /*
2645 2660   * Non canonical processing. Called with q locked from  ldtermrsrv.
2646 2661   *
2647 2662   */
2648 2663  static mblk_t *
2649 2664  ldterm_dononcanon(mblk_t *bp, mblk_t *bpt, size_t ebsize, queue_t *q,
2650 2665      ldtermstd_state_t *tp)
2651 2666  {
2652 2667          queue_t *wrq = WR(q);
2653 2668          unsigned char *rptr;
2654 2669          size_t bytes_in_bp;
2655 2670          size_t roomleft;
2656 2671          size_t bytes_to_move;
2657 2672          int free_flag = 0;
2658 2673  
2659 2674          if (tp->t_modes.c_lflag & (ECHO|ECHONL|IEXTEN)) {
2660 2675                  unsigned char *wptr;
2661 2676                  unsigned char c;
2662 2677  
2663 2678                  /*
2664 2679                   * Either we must echo the characters, or we must
2665 2680                   * echo NL, or we must check for VLNEXT. Process
2666 2681                   * characters one at a time.
2667 2682                   */
2668 2683                  rptr = bp->b_rptr;
2669 2684                  wptr = bp->b_rptr;
2670 2685                  while (rptr < bp->b_wptr) {
2671 2686                          c = *rptr++;
2672 2687                          /*
2673 2688                           * If this character is the literal next
2674 2689                           * character, echo it as '^' and backspace
2675 2690                           * over it if echoing is enabled, indicate
2676 2691                           * that the next character is to be treated
2677 2692                           * literally, and remove the LNEXT from the
2678 2693                           * input stream.
2679 2694                           *
2680 2695                           * If the *previous* character was the literal
2681 2696                           * next character, don't check whether this
2682 2697                           * is a literal next or not.
2683 2698                           */
2684 2699                          if ((tp->t_modes.c_lflag & IEXTEN) &&
2685 2700                              !(tp->t_state & TS_SLNCH) &&
2686 2701                              c != _POSIX_VDISABLE &&
2687 2702                              c == tp->t_modes.c_cc[VLNEXT]) {
2688 2703                                  if (tp->t_modes.c_lflag & ECHO)
2689 2704                                          ldterm_outstring(
2690 2705                                              (unsigned char *)"^\b",
2691 2706                                              2, wrq, ebsize, tp);
2692 2707                                  tp->t_state |= TS_SLNCH;
2693 2708                                  continue;       /* and ignore it */
2694 2709                          }
2695 2710                          /*
2696 2711                           * Not a "literal next" character, so it
2697 2712                           * should show up as input. If it was
2698 2713                           * literal-nexted, turn off the literal-next
2699 2714                           * flag.
2700 2715                           */
2701 2716                          tp->t_state &= ~TS_SLNCH;
2702 2717                          *wptr++ = c;
2703 2718                          if (tp->t_modes.c_lflag & ECHO) {
2704 2719                                  /*
2705 2720                                   * Echo the character.
2706 2721                                   */
2707 2722                                  (void) ldterm_echo(c, wrq, ebsize, tp);
2708 2723                          } else if (tp->t_modes.c_lflag & ECHONL) {
2709 2724                                  /*
2710 2725                                   * Echo NL, even though ECHO is not
2711 2726                                   * set.
2712 2727                                   */
2713 2728                                  if (c == '\n')
2714 2729                                          ldterm_outchar('\n', wrq, 1, tp);
2715 2730                          }
2716 2731                  }
2717 2732                  bp->b_wptr = wptr;
2718 2733          } else {
2719 2734                  /*
2720 2735                   * If there are any characters in this buffer, and
2721 2736                   * the first of them was literal-nexted, turn off the
2722 2737                   * literal-next flag.
2723 2738                   */
2724 2739                  if (bp->b_rptr != bp->b_wptr)
2725 2740                          tp->t_state &= ~TS_SLNCH;
2726 2741          }
2727 2742  
2728 2743          ASSERT(bp->b_wptr >= bp->b_rptr);
2729 2744          bytes_in_bp = bp->b_wptr - bp->b_rptr;
2730 2745          rptr = bp->b_rptr;
2731 2746          while (bytes_in_bp != 0) {
2732 2747                  roomleft = bpt->b_datap->db_lim - bpt->b_wptr;
2733 2748                  if (roomleft == 0) {
2734 2749                          /*
2735 2750                           * No more room in this mblk; save this one
2736 2751                           * away, and allocate a new one.
2737 2752                           */
2738 2753                          if ((bpt = allocb(IBSIZE, BPRI_MED)) == NULL) {
2739 2754                                  freeb(bp);
2740 2755                                  DEBUG4(("ldterm_do_noncanon: allcob failed\n"));
2741 2756                                  return (bpt);
2742 2757                          }
2743 2758                          /*
2744 2759                           * Chain the new one to the end of the old
2745 2760                           * one, and mark it as the last block in the
2746 2761                           * current lump.
2747 2762                           */
2748 2763                          tp->t_endmsg->b_cont = bpt;
2749 2764                          tp->t_endmsg = bpt;
2750 2765                          roomleft = IBSIZE;
2751 2766                  }
2752 2767                  DEBUG5(("roomleft=%d, bytes_in_bp=%d, tp->t_rd_request=%d\n",
2753 2768                      roomleft, bytes_in_bp, tp->t_rd_request));
2754 2769                  /*
2755 2770                   * if there is a read pending before this data got
2756 2771                   * here move bytes according to the minimum of room
2757 2772                   * left in this buffer, bytes in the message and byte
2758 2773                   * count requested in the read. If there is no read
2759 2774                   * pending, move the minimum of the first two
2760 2775                   */
2761 2776                  if (tp->t_rd_request == 0)
2762 2777                          bytes_to_move = MIN(roomleft, bytes_in_bp);
2763 2778                  else
2764 2779                          bytes_to_move =
2765 2780                              MIN(MIN(roomleft, bytes_in_bp), tp->t_rd_request);
2766 2781                  DEBUG5(("Bytes to move = %lu\n", bytes_to_move));
2767 2782                  if (bytes_to_move == 0)
2768 2783                          break;
2769 2784                  bcopy(rptr, bpt->b_wptr, bytes_to_move);
2770 2785                  bpt->b_wptr += bytes_to_move;
2771 2786                  rptr += bytes_to_move;
2772 2787                  tp->t_msglen += bytes_to_move;
2773 2788                  bytes_in_bp -= bytes_to_move;
2774 2789          }
2775 2790          if (bytes_in_bp == 0) {
2776 2791                  DEBUG4(("bytes_in_bp is zero\n"));
2777 2792                  freeb(bp);
2778 2793          } else
2779 2794                  free_flag = 1;  /* for debugging olny */
2780 2795  
2781 2796          DEBUG4(("ldterm_do_noncanon: VMIN = %d, VTIME = %d, msglen = %d, \
2782 2797                  tid = %d\n", V_MIN, V_TIME, tp->t_msglen, tp->t_vtid));
2783 2798          /*
2784 2799           * If there is a pending read request at the stream head we
2785 2800           * need to do VMIN/VTIME processing. The four possible cases
2786 2801           * are:
2787 2802           *      MIN = 0, TIME > 0
2788 2803           *      MIN = >, TIME = 0
2789 2804           *      MIN > 0, TIME > 0
2790 2805           *      MIN = 0, TIME = 0
2791 2806           * If we can satisfy VMIN, send it up, and start a new
2792 2807           * timer if necessary.  These four cases of VMIN/VTIME
2793 2808           * are also dealt with in the write side put routine
2794 2809           * when the M_READ is first seen.
2795 2810           */
2796 2811  
2797 2812          DEBUG4(("Incoming data while M_READ'ing\n"));
2798 2813          /*
2799 2814           * Case 1:  Any data will satisfy the read, so send
2800 2815           * it upstream.
2801 2816           */
2802 2817          if (V_MIN == 0 && V_TIME > 0) {
2803 2818                  if (tp->t_msglen)
2804 2819                          vmin_satisfied(q, tp, 1);
2805 2820                  else {
2806 2821                          /* EMPTY */
2807 2822                          DEBUG4(("ldterm_do_noncanon called, but no data!\n"));
2808 2823                  }
2809 2824                  /*
2810 2825                   * Case 2:  This should never time out, so
2811 2826                   * until there's enough data, do nothing.
2812 2827                   */
2813 2828          } else if (V_MIN > 0 && V_TIME == 0) {
2814 2829                  if (tp->t_msglen >= (int)V_MIN)
2815 2830                          vmin_satisfied(q, tp, 1);
2816 2831  
2817 2832                  /*
2818 2833                   * Case 3:  If MIN is satisfied, send it up.
2819 2834                   * Also, remember to start a new timer *every*
2820 2835                   * time we see something if MIN isn't
2821 2836                   * safisfied
2822 2837                   */
2823 2838          } else if (V_MIN > 0 && V_TIME > 0) {
2824 2839                  if (tp->t_msglen >= (int)V_MIN)
2825 2840                          vmin_satisfied(q, tp, 1);
2826 2841                  else
2827 2842                          vmin_settimer(q);
2828 2843                  /*
2829 2844                   * Case 4:  Not possible.  This request
2830 2845                   * should always be satisfied from the write
2831 2846                   * side, left here for debugging.
2832 2847                   */
2833 2848          } else {        /* V_MIN == 0 && V_TIME == 0 */
2834 2849                          vmin_satisfied(q, tp, 1);
2835 2850          }
2836 2851  
2837 2852          if (free_flag) {
2838 2853                  /* EMPTY */
2839 2854                  DEBUG4(("CAUTION message block not freed\n"));
2840 2855          }
2841 2856          return (newmsg(tp));
2842 2857  }
2843 2858  
2844 2859  
2845 2860  /*
2846 2861   * Echo a typed byte to the terminal.  Returns the number of bytes
2847 2862   * printed. Bytes of EUC characters drop through the ECHOCTL stuff
2848 2863   * and are just output as themselves.
2849 2864   */
2850 2865  static int
2851 2866  ldterm_echo(uchar_t c, queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
2852 2867  {
2853 2868          int i;
2854 2869  
2855 2870          if (!(tp->t_modes.c_lflag & ECHO))
2856 2871                  return (0);
2857 2872          i = 0;
2858 2873  
2859 2874          /*
2860 2875           * Echo control characters (c <= 37) only if the ECHOCTRL
2861 2876           * flag is set as ^X.
2862 2877           */
2863 2878  
2864 2879          if ((tp->t_modes.c_lflag & ECHOCTL) &&
2865 2880              (tp->t_modes.c_lflag & IEXTEN)) {
2866 2881                  if (c <= 037 && c != '\t' && c != '\n') {
2867 2882                          ldterm_outchar('^', q, ebsize, tp);
2868 2883                          i++;
2869 2884                          if (tp->t_modes.c_oflag & OLCUC)
2870 2885                                  c += 'a' - 1;
2871 2886                          else
2872 2887                                  c += 'A' - 1;
2873 2888                  } else if (c == 0177) {
2874 2889                          ldterm_outchar('^', q, ebsize, tp);
2875 2890                          i++;
2876 2891                          c = '?';
2877 2892                  }
2878 2893                  ldterm_outchar(c, q, ebsize, tp);
2879 2894                  return (i + 1);
2880 2895                  /* echo only special control character and the Bell */
2881 2896          } else if ((c > 037 && c != 0177) || c == '\t' || c == '\n' ||
2882 2897              c == '\r' || c == '\b' || c == 007 ||
2883 2898              c == tp->t_modes.c_cc[VKILL]) {
2884 2899                  ldterm_outchar(c, q, ebsize, tp);
2885 2900                  return (i + 1);
2886 2901          }
2887 2902          return (i);
2888 2903  }
2889 2904  
2890 2905  
2891 2906  /*
2892 2907   * Put a character on the output queue.
2893 2908   */
2894 2909  static void
2895 2910  ldterm_outchar(uchar_t c, queue_t *q, size_t bsize, ldtermstd_state_t *tp)
2896 2911  {
2897 2912          mblk_t *curbp;
2898 2913  
2899 2914          /*
2900 2915           * Don't even look at the characters unless we have something
2901 2916           * useful to do with them.
2902 2917           */
2903 2918          if ((tp->t_modes.c_oflag & OPOST) ||
2904 2919              ((tp->t_modes.c_lflag & XCASE) &&
2905 2920              (tp->t_modes.c_lflag & ICANON))) {
2906 2921                  mblk_t *mp;
2907 2922  
2908 2923                  if ((mp = allocb(4, BPRI_HI)) == NULL) {
2909 2924                          cmn_err(CE_WARN,
2910 2925                              "ldterm: (ldterm_outchar) out of blocks");
2911 2926                          return;
2912 2927                  }
2913 2928                  *mp->b_wptr++ = c;
2914 2929                  mp = ldterm_output_msg(q, mp, &tp->t_echomp, tp, bsize, 1);
2915 2930                  if (mp != NULL)
2916 2931                          freemsg(mp);
2917 2932  
2918 2933          } else {
2919 2934                  if ((curbp = tp->t_echomp) != NULL) {
2920 2935                          while (curbp->b_cont != NULL)
2921 2936                                  curbp = curbp->b_cont;
2922 2937                          if (curbp->b_datap->db_lim == curbp->b_wptr) {
2923 2938                                  mblk_t *newbp;
2924 2939  
2925 2940                                  if ((newbp = allocb(bsize, BPRI_HI)) == NULL) {
2926 2941                                          cmn_err(CE_WARN,
2927 2942                                              "ldterm_outchar: out of blocks");
2928 2943                                          return;
2929 2944                                  }
2930 2945                                  curbp->b_cont = newbp;
2931 2946                                  curbp = newbp;
2932 2947                          }
2933 2948                  } else {
2934 2949                          if ((curbp = allocb(bsize, BPRI_HI)) == NULL) {
2935 2950                                  cmn_err(CE_WARN,
2936 2951                                      "ldterm_outchar: out of blocks");
2937 2952                                  return;
2938 2953                          }
2939 2954                          tp->t_echomp = curbp;
2940 2955                  }
2941 2956                  *curbp->b_wptr++ = c;
2942 2957          }
2943 2958  }
2944 2959  
2945 2960  
2946 2961  /*
2947 2962   * Copy a string, of length len, to the output queue.
2948 2963   */
2949 2964  static void
2950 2965  ldterm_outstring(uchar_t *cp, int len, queue_t *q, size_t bsize,
2951 2966      ldtermstd_state_t *tp)
2952 2967  {
2953 2968          while (len > 0) {
2954 2969                  ldterm_outchar(*cp++, q, bsize, tp);
2955 2970                  len--;
2956 2971          }
2957 2972  }
2958 2973  
2959 2974  
2960 2975  static mblk_t *
2961 2976  newmsg(ldtermstd_state_t *tp)
2962 2977  {
2963 2978          mblk_t *bp;
2964 2979  
2965 2980          /*
2966 2981           * If no current message, allocate a block for it.
2967 2982           */
2968 2983          if ((bp = tp->t_endmsg) == NULL) {
2969 2984                  if ((bp = allocb(IBSIZE, BPRI_MED)) == NULL) {
2970 2985                          cmn_err(CE_WARN,
2971 2986                              "ldterm: (ldtermrsrv/newmsg) out of blocks");
2972 2987                          return (bp);
2973 2988                  }
2974 2989                  tp->t_message = bp;
2975 2990                  tp->t_endmsg = bp;
2976 2991          }
2977 2992          return (bp);
2978 2993  }
2979 2994  
2980 2995  
2981 2996  static void
2982 2997  ldterm_msg_upstream(queue_t *q, ldtermstd_state_t *tp)
2983 2998  {
2984 2999          ssize_t s;
2985 3000          mblk_t *bp;
2986 3001  
2987 3002          bp = tp->t_message;
2988 3003          s = msgdsize(bp);
2989 3004          if (bp)
2990 3005                  putnext(q, tp->t_message);
2991 3006  
2992 3007          /*
2993 3008           * update sysinfo canch character.
2994 3009           */
2995 3010          if (CANON_MODE)
2996 3011                  (void) drv_setparm(SYSCANC, s);
2997 3012          tp->t_message = NULL;
2998 3013          tp->t_endmsg = NULL;
2999 3014          tp->t_msglen = 0;
3000 3015          tp->t_rocount = 0;
3001 3016          tp->t_rd_request = 0;
3002 3017          if (tp->t_state & TS_MEUC) {
3003 3018                  ASSERT(tp->t_eucp_mp);
3004 3019                  tp->t_eucp = tp->t_eucp_mp->b_rptr;
3005 3020                  /* can't reset everything, as we may have other input */
3006 3021          }
3007 3022  }
3008 3023  
3009 3024  
3010 3025  /*
3011 3026   * Re-enable the write-side service procedure.  When an allocation
3012 3027   * failure causes write-side processing to stall, we disable the
3013 3028   * write side and arrange to call this function when allocation once
3014 3029   * again becomes possible.
3015 3030   */
3016 3031  static void
3017 3032  ldterm_wenable(void *addr)
3018 3033  {
3019 3034          queue_t *q = addr;
3020 3035          ldtermstd_state_t *tp;
3021 3036  
3022 3037          tp = (ldtermstd_state_t *)q->q_ptr;
3023 3038          /*
3024 3039           * The bufcall is no longer pending.
3025 3040           */
3026 3041          tp->t_wbufcid = 0;
3027 3042          enableok(q);
3028 3043          qenable(q);
3029 3044  }
3030 3045  
3031 3046  
3032 3047  /*
3033 3048   * Line discipline output queue put procedure.  Attempts to process
3034 3049   * the message directly and send it on downstream, queueing it only
3035 3050   * if there's already something pending or if its downstream neighbor
3036 3051   * is clogged.
3037 3052   */
3038 3053  static void
3039 3054  ldtermwput(queue_t *q, mblk_t *mp)
3040 3055  {
3041 3056          ldtermstd_state_t *tp;
3042 3057          unsigned char type = mp->b_datap->db_type;
3043 3058  
3044 3059          tp = (ldtermstd_state_t *)q->q_ptr;
3045 3060  
3046 3061          /*
3047 3062           * Always process priority messages, regardless of whether or
3048 3063           * not our queue is nonempty.
3049 3064           */
3050 3065          if (type >= QPCTL) {
3051 3066                  switch (type) {
3052 3067  
3053 3068                  case M_FLUSH:
3054 3069                          /*
3055 3070                           * Get rid of it, see comment in
3056 3071                           * ldterm_dosig().
3057 3072                           */
3058 3073                          if ((tp->t_state & TS_FLUSHWAIT) &&
3059 3074                              (*mp->b_rptr == FLUSHW)) {
3060 3075                                  tp->t_state &= ~TS_FLUSHWAIT;
3061 3076                                  freemsg(mp);
3062 3077                                  return;
3063 3078                          }
3064 3079                          /*
3065 3080                           * This is coming from above, so we only
3066 3081                           * handle the write queue here.  If FLUSHR is
3067 3082                           * set, it will get turned around at the
3068 3083                           * driver, and the read procedure will see it
3069 3084                           * eventually.
3070 3085                           */
3071 3086                          if (*mp->b_rptr & FLUSHW) {
3072 3087                                  if ((tp->t_state & TS_ISPTSTTY) &&
3073 3088                                      (*mp->b_rptr & FLUSHBAND))
3074 3089                                          flushband(q, *(mp->b_rptr + 1),
3075 3090                                              FLUSHDATA);
3076 3091                                  else
3077 3092                                          flushq(q, FLUSHDATA);
3078 3093                          }
3079 3094  
3080 3095                          putnext(q, mp);
3081 3096                          /*
3082 3097                           * If a timed read is interrupted, there is
3083 3098                           * no way to cancel an existing M_READ
3084 3099                           * request.  We kludge by allowing a flush to
3085 3100                           * do so.
3086 3101                           */
3087 3102                          if (tp->t_state & TS_MREAD)
3088 3103                                  vmin_satisfied(RD(q), tp, 0);
3089 3104                          break;
3090 3105  
3091 3106                  case M_READ:
3092 3107                          DEBUG1(("ldtermwmsg:M_READ RECEIVED\n"));
3093 3108                          /*
3094 3109                           * Stream head needs data to satisfy timed
3095 3110                           * read. Has meaning only if ICANON flag is
3096 3111                           * off indicating raw mode
3097 3112                           */
3098 3113  
3099 3114                          DEBUG4((
3100 3115                              "M_READ: RAW_MODE=%d, CNT=%d, VMIN=%d, VTIME=%d\n",
3101 3116                              RAW_MODE, *(unsigned int *)mp->b_rptr, V_MIN,
3102 3117                              V_TIME));
3103 3118  
3104 3119                          tp->t_rd_request = *(unsigned int *)mp->b_rptr;
3105 3120  
3106 3121                          if (RAW_MODE) {
3107 3122                                  if (newmsg(tp) != NULL) {
3108 3123                                          /*
3109 3124                                           * VMIN/VTIME processing...
3110 3125                                           * The four possible cases are:
3111 3126                                           *      MIN = 0, TIME > 0
3112 3127                                           *      MIN = >, TIME = 0
3113 3128                                           *      MIN > 0, TIME > 0
3114 3129                                           *      MIN = 0, TIME = 0
3115 3130                                           * These four conditions must be dealt
3116 3131                                           * with on the read side as well in
3117 3132                                           * ldterm_do_noncanon(). Set TS_MREAD
3118 3133                                           * so that the read side will know
3119 3134                                           * there is a pending read request
3120 3135                                           * waiting at the stream head.  If we
3121 3136                                           * can satisfy MIN do it here, rather
3122 3137                                           * than on the read side.  If we can't,
3123 3138                                           * start timers if necessary and let
3124 3139                                           * the other side deal with it.
3125 3140                                           *
3126 3141                                           * We got another M_READ before the
3127 3142                                           * pending one completed, cancel any
3128 3143                                           * existing timeout.
3129 3144                                           */
3130 3145                                          if (tp->t_state & TS_MREAD) {
3131 3146                                                  vmin_satisfied(RD(q),
3132 3147                                                      tp, 0);
3133 3148                                          }
3134 3149                                          tp->t_state |= TS_MREAD;
3135 3150                                          /*
3136 3151                                           * Case 1:  Any data will
3137 3152                                           * satisfy read, otherwise
3138 3153                                           * start timer
3139 3154                                           */
3140 3155                                          if (V_MIN == 0 && V_TIME > 0) {
3141 3156                                                  if (tp->t_msglen)
3142 3157                                                          vmin_satisfied(RD(q),
3143 3158                                                              tp, 1);
3144 3159                                                  else
3145 3160                                                          vmin_settimer(RD(q));
3146 3161  
3147 3162                                                  /*
3148 3163                                                   * Case 2:  If we have enough
3149 3164                                                   * data, send up now.
3150 3165                                                   * Otherwise, the read side
3151 3166                                                   * should wait forever until MIN
3152 3167                                                   * is satisified.
3153 3168                                                   */
3154 3169                                          } else if (V_MIN > 0 && V_TIME == 0) {
3155 3170                                                  if (tp->t_msglen >= (int)V_MIN)
3156 3171                                                          vmin_satisfied(RD(q),
3157 3172                                                              tp, 1);
3158 3173  
3159 3174                                                  /*
3160 3175                                                   * Case 3:  If we can satisfy
3161 3176                                                   * the read, send it up. If we
3162 3177                                                   * don't have enough data, but
3163 3178                                                   * there is at least one char,
3164 3179                                                   * start a timer.  Otherwise,
3165 3180                                                   * let the read side start
3166 3181                                                   * the timer.
3167 3182                                                   */
3168 3183                                          } else if (V_MIN > 0 && V_TIME > 0) {
3169 3184                                                  if (tp->t_msglen >= (int)V_MIN)
3170 3185                                                          vmin_satisfied(RD(q),
3171 3186                                                              tp, 1);
3172 3187                                                  else if (tp->t_msglen)
3173 3188                                                          vmin_settimer(RD(q));
3174 3189                                                  /*
3175 3190                                                   * Case 4:  Read returns
3176 3191                                                   * whatever data is available
3177 3192                                                   * or zero if none.
3178 3193                                                   */
3179 3194                                          } else { /* V_MIN == 0 && V_TIME == 0 */
3180 3195                                                  vmin_satisfied(RD(q), tp, 1);
3181 3196                                          }
3182 3197  
3183 3198                                  } else  /* should do bufcall, really! */
3184 3199                                          cmn_err(CE_WARN,
3185 3200                                              "ldtermwmsg: out of blocks");
3186 3201                          }
3187 3202                          /*
3188 3203                           * pass M_READ down
3189 3204                           */
3190 3205                          putnext(q, mp);
3191 3206                          break;
3192 3207  
3193 3208                  default:
3194 3209                          /* Pass it through unmolested. */
3195 3210                          putnext(q, mp);
3196 3211                          break;
3197 3212                  }
3198 3213                  return;
3199 3214          }
3200 3215          /*
3201 3216           * If our queue is nonempty or there's a traffic jam
3202 3217           * downstream, this message must get in line.
3203 3218           */
3204 3219          if (q->q_first != NULL || !bcanputnext(q, mp->b_band)) {
3205 3220                  /*
3206 3221                   * Exception: ioctls, except for those defined to
3207 3222                   * take effect after output has drained, should be
3208 3223                   * processed immediately.
3209 3224                   */
3210 3225                  if (type == M_IOCTL) {
3211 3226                          struct iocblk *iocp;
3212 3227  
3213 3228                          iocp = (struct iocblk *)mp->b_rptr;
3214 3229                          switch (iocp->ioc_cmd) {
3215 3230  
3216 3231                                  /*
3217 3232                                   * Queue these.
3218 3233                                   */
3219 3234                          case TCSETSW:
3220 3235                          case TCSETSF:
3221 3236                          case TCSETAW:
3222 3237                          case TCSETAF:
3223 3238                          case TCSBRK:
3224 3239                                  break;
3225 3240  
3226 3241                                  /*
3227 3242                                   * Handle all others immediately.
3228 3243                                   */
3229 3244                          default:
3230 3245                                  (void) ldtermwmsg(q, mp);
3231 3246                                  return;
3232 3247                          }
3233 3248                  }
3234 3249                  (void) putq(q, mp);
3235 3250                  return;
3236 3251          }
3237 3252          /*
3238 3253           * We can take the fast path through, by simply calling
3239 3254           * ldtermwmsg to dispose of mp.
3240 3255           */
3241 3256          (void) ldtermwmsg(q, mp);
3242 3257  }
3243 3258  
3244 3259  
3245 3260  /*
3246 3261   * Line discipline output queue service procedure.
3247 3262   */
3248 3263  static void
3249 3264  ldtermwsrv(queue_t *q)
3250 3265  {
3251 3266          mblk_t *mp;
3252 3267  
3253 3268          /*
3254 3269           * We expect this loop to iterate at most once, but must be
3255 3270           * prepared for more in case our upstream neighbor isn't
3256 3271           * paying strict attention to what canput tells it.
3257 3272           */
3258 3273          while ((mp = getq(q)) != NULL) {
3259 3274                  /*
3260 3275                   * N.B.: ldtermwput has already handled high-priority
3261 3276                   * messages, so we don't have to worry about them
3262 3277                   * here. Hence, the putbq call is safe.
3263 3278                   */
3264 3279                  if (!bcanputnext(q, mp->b_band)) {
3265 3280                          (void) putbq(q, mp);
3266 3281                          break;
3267 3282                  }
3268 3283                  if (!ldtermwmsg(q, mp)) {
3269 3284                          /*
3270 3285                           * Couldn't handle the whole thing; give up
3271 3286                           * for now and wait to be rescheduled.
3272 3287                           */
3273 3288                          break;
3274 3289                  }
3275 3290          }
3276 3291  }
3277 3292  
3278 3293  
3279 3294  /*
3280 3295   * Process the write-side message denoted by mp.  If mp can't be
3281 3296   * processed completely (due to allocation failures), put the
3282 3297   * residual unprocessed part on the front of the write queue, disable
3283 3298   * the queue, and schedule a qbufcall to arrange to complete its
3284 3299   * processing later.
3285 3300   *
3286 3301   * Return 1 if the message was processed completely and 0 if not.
3287 3302   *
3288 3303   * This routine is called from both ldtermwput and ldtermwsrv to do the
3289 3304   * actual work of dealing with mp.  ldtermwput will have already
3290 3305   * dealt with high priority messages.
3291 3306   */
3292 3307  static int
3293 3308  ldtermwmsg(queue_t *q, mblk_t *mp)
3294 3309  {
3295 3310          ldtermstd_state_t *tp;
3296 3311          mblk_t *residmp = NULL;
3297 3312          size_t size;
3298 3313  
3299 3314          tp = (ldtermstd_state_t *)q->q_ptr;
3300 3315  
3301 3316          switch (mp->b_datap->db_type) {
3302 3317  
3303 3318          case M_IOCTL:
3304 3319                  ldterm_do_ioctl(q, mp);
3305 3320                  break;
3306 3321  
3307 3322          case M_DATA:
3308 3323                  {
3309 3324                          mblk_t *omp = NULL;
3310 3325  
3311 3326                          if ((tp->t_modes.c_lflag & FLUSHO) &&
3312 3327                              (tp->t_modes.c_lflag & IEXTEN)) {
3313 3328                                  freemsg(mp);    /* drop on floor */
3314 3329                                  break;
3315 3330                          }
3316 3331                          tp->t_rocount = 0;
3317 3332                          /*
3318 3333                           * Don't even look at the characters unless
3319 3334                           * we have something useful to do with them.
3320 3335                           */
3321 3336                          if (((tp->t_modes.c_oflag & OPOST) ||
3322 3337                              ((tp->t_modes.c_lflag & XCASE) &&
3323 3338                              (tp->t_modes.c_lflag & ICANON))) &&
3324 3339                              (msgdsize(mp) || !(tp->t_state & TS_ISPTSTTY))) {
3325 3340                                  unsigned char band = mp->b_band;
3326 3341                                  short flag = mp->b_flag;
3327 3342  
3328 3343                                  residmp = ldterm_output_msg(q, mp, &omp,
3329 3344                                      tp, OBSIZE, 0);
3330 3345                                  if ((mp = omp) == NULL)
3331 3346                                          break;
3332 3347                                  mp->b_band |= band;
3333 3348                                  mp->b_flag |= flag;
3334 3349                          }
3335 3350                          /* Update sysinfo outch */
3336 3351                          (void) drv_setparm(SYSOUTC, msgdsize(mp));
3337 3352                          putnext(q, mp);
3338 3353                          break;
3339 3354                  }
3340 3355  
3341 3356          default:
3342 3357                  putnext(q, mp); /* pass it through unmolested */
3343 3358                  break;
3344 3359          }
3345 3360  
3346 3361          if (residmp == NULL)
3347 3362                  return (1);
3348 3363  
3349 3364          /*
3350 3365           * An allocation failure occurred that prevented the message
3351 3366           * from being completely processed.  First, disable our
3352 3367           * queue, since it's pointless to attempt further processing
3353 3368           * until the allocation situation is resolved.  (This must
3354 3369           * precede the putbq call below, which would otherwise mark
3355 3370           * the queue to be serviced.)
3356 3371           */
3357 3372          noenable(q);
3358 3373          /*
3359 3374           * Stuff the remnant on our write queue so that we can
3360 3375           * complete it later when times become less lean.  Note that
3361 3376           * this sets QFULL, so that our upstream neighbor will be
3362 3377           * blocked by flow control.
3363 3378           */
3364 3379          (void) putbq(q, residmp);
3365 3380          /*
3366 3381           * Schedule a qbufcall to re-enable the queue.  The failure
3367 3382           * won't have been for an allocation of more than OBSIZE
3368 3383           * bytes, so don't ask for more than that from bufcall.
3369 3384           */
3370 3385          size = msgdsize(residmp);
3371 3386          if (size > OBSIZE)
3372 3387                  size = OBSIZE;
3373 3388          if (tp->t_wbufcid)
3374 3389                  qunbufcall(q, tp->t_wbufcid);
3375 3390          tp->t_wbufcid = qbufcall(q, size, BPRI_MED, ldterm_wenable, q);
3376 3391  
3377 3392          return (0);
3378 3393  }
3379 3394  
3380 3395  
3381 3396  /*
3382 3397   * Perform output processing on a message, accumulating the output
3383 3398   * characters in a new message.
3384 3399   */
3385 3400  static mblk_t *
3386 3401  ldterm_output_msg(queue_t *q, mblk_t *imp, mblk_t **omp,
3387 3402      ldtermstd_state_t *tp, size_t bsize, int echoing)
3388 3403  {
3389 3404          mblk_t *ibp;            /* block we're examining from input message */
3390 3405          mblk_t *obp;            /* block we're filling in output message */
3391 3406          mblk_t *cbp;            /* continuation block */
3392 3407          mblk_t *oobp;           /* old value of obp; valid if NEW_BLOCK fails */
3393 3408          mblk_t **contpp;        /* where to stuff ptr to newly-allocated blk */
3394 3409          unsigned char c, n;
3395 3410          int count, ctype;
3396 3411          ssize_t bytes_left;
3397 3412  
3398 3413          mblk_t *bp;             /* block to stuff an M_DELAY message in */
3399 3414  
3400 3415  
3401 3416          /*
3402 3417           * Allocate a new block into which to put bytes. If we can't,
3403 3418           * we just drop the rest of the message on the floor. If x is
3404 3419           * non-zero, just fall thru; failure requires cleanup before
3405 3420           * going out
3406 3421           */
3407 3422  
3408 3423  #define NEW_BLOCK(x) \
3409 3424          { \
3410 3425                  oobp = obp; \
3411 3426                  if ((obp = allocb(bsize, BPRI_MED)) == NULL) { \
3412 3427                          if (x == 0) \
3413 3428                                  goto outofbufs; \
3414 3429                  } else { \
3415 3430                          *contpp = obp; \
3416 3431                          contpp = &obp->b_cont; \
3417 3432                          bytes_left = obp->b_datap->db_lim - obp->b_wptr; \
3418 3433                  } \
3419 3434          }
3420 3435  
3421 3436          ibp = imp;
3422 3437  
3423 3438          /*
3424 3439           * When we allocate the first block of a message, we should
3425 3440           * stuff the pointer to it in "*omp".  All subsequent blocks
3426 3441           * should have the pointer to them stuffed into the "b_cont"
3427 3442           * field of the previous block.  "contpp" points to the place
3428 3443           * where we should stuff the pointer.
3429 3444           *
3430 3445           * If we already have a message we're filling in, continue doing
3431 3446           * so.
3432 3447           */
3433 3448          if ((obp = *omp) != NULL) {
3434 3449                  while (obp->b_cont != NULL)
3435 3450                          obp = obp->b_cont;
3436 3451                  contpp = &obp->b_cont;
3437 3452                  bytes_left = obp->b_datap->db_lim - obp->b_wptr;
3438 3453          } else {
3439 3454                  contpp = omp;
3440 3455                  bytes_left = 0;
3441 3456          }
3442 3457  
3443 3458          do {
3444 3459                  while (ibp->b_rptr < ibp->b_wptr) {
3445 3460                          /*
3446 3461                           * Make sure there's room for one more
3447 3462                           * character.  At most, we'll need "t_maxeuc"
3448 3463                           * bytes.
3449 3464                           */
3450 3465                          if ((bytes_left < (int)tp->t_maxeuc)) {
3451 3466                                  /* LINTED */
3452 3467                                  NEW_BLOCK(0);
3453 3468                          }
3454 3469                          /*
3455 3470                           * If doing XCASE processing (not very
3456 3471                           * likely, in this day and age), look at each
3457 3472                           * character individually.
3458 3473                           */
3459 3474                          if ((tp->t_modes.c_lflag & XCASE) &&
3460 3475                              (tp->t_modes.c_lflag & ICANON)) {
3461 3476                                  c = *ibp->b_rptr++;
3462 3477  
3463 3478                                  /*
3464 3479                                   * We need to make sure that this is not
3465 3480                                   * a following byte of a multibyte character
3466 3481                                   * before applying an XCASE processing.
3467 3482                                   *
3468 3483                                   * tp->t_eucign will be 0 if and only
3469 3484                                   * if the current 'c' is an ASCII character
3470 3485                                   * and also a byte. Otherwise, it will have
3471 3486                                   * the byte length of a multibyte character.
3472 3487                                   */
3473 3488                                  if ((tp->t_state & TS_MEUC) &&
3474 3489                                      tp->t_eucign == 0 && NOTASCII(c)) {
3475 3490                                          tp->t_eucign =
3476 3491                                              tp->t_csmethods.ldterm_memwidth(
3477 3492                                              c, (void *)tp);
3478 3493                                          tp->t_scratch_len = tp->t_eucign;
3479 3494  
3480 3495                                          if (tp->t_csdata.codeset_type !=
3481 3496                                              LDTERM_CS_TYPE_UTF8) {
3482 3497                                                  tp->t_col +=
3483 3498                                                      tp->
3484 3499                                                      t_csmethods.
3485 3500                                                      ldterm_dispwidth(c,
3486 3501                                                      (void *)tp,
3487 3502                                                      tp->t_modes.c_lflag &
3488 3503                                                      ECHOCTL);
3489 3504                                          }
3490 3505                                  }
3491 3506  
3492 3507                                  /*
3493 3508                                   * If character is mapped on output,
3494 3509                                   * put out a backslash followed by
3495 3510                                   * what it is mapped to.
3496 3511                                   */
3497 3512                                  if (tp->t_eucign == 0 && omaptab[c] != 0 &&
3498 3513                                      (!echoing || c != '\\')) {
3499 3514                                          /* backslash is an ordinary character */
3500 3515                                          tp->t_col++;
3501 3516                                          *obp->b_wptr++ = '\\';
3502 3517                                          bytes_left--;
3503 3518                                          if (bytes_left == 0) {
3504 3519                                                  /* LINTED */
3505 3520                                                  NEW_BLOCK(1);
3506 3521                                          }
3507 3522                                          /*
3508 3523                                           * Allocation failed, make
3509 3524                                           * state consistent before
3510 3525                                           * returning
3511 3526                                           */
3512 3527                                          if (obp == NULL) {
3513 3528                                                  ibp->b_rptr--;
3514 3529                                                  tp->t_col--;
3515 3530                                                  oobp->b_wptr--;
3516 3531                                                  goto outofbufs;
3517 3532                                          }
3518 3533                                          c = omaptab[c];
3519 3534                                  }
3520 3535                                  /*
3521 3536                                   * If no other output processing is
3522 3537                                   * required, push the character into
3523 3538                                   * the block and get another.
3524 3539                                   */
3525 3540                                  if (!(tp->t_modes.c_oflag & OPOST)) {
3526 3541                                          if (tp->t_eucign > 0) {
3527 3542                                                  --tp->t_eucign;
3528 3543                                          } else {
3529 3544                                                  tp->t_col++;
3530 3545                                          }
3531 3546                                          *obp->b_wptr++ = c;
3532 3547                                          bytes_left--;
3533 3548                                          continue;
3534 3549                                  }
3535 3550                                  /*
3536 3551                                   * OPOST output flag is set. Map
3537 3552                                   * lower case to upper case if OLCUC
3538 3553                                   * flag is set and the 'c' is a lowercase
3539 3554                                   * ASCII character.
3540 3555                                   */
3541 3556                                  if (tp->t_eucign == 0 &&
3542 3557                                      (tp->t_modes.c_oflag & OLCUC) &&
3543 3558                                      c >= 'a' && c <= 'z')
3544 3559                                          c -= 'a' - 'A';
3545 3560                          } else {
3546 3561                                  /*
3547 3562                                   * Copy all the ORDINARY characters,
3548 3563                                   * possibly mapping upper case to
3549 3564                                   * lower case.  We use "movtuc",
3550 3565                                   * STOPPING when we can't move some
3551 3566                                   * character. For multi-byte or
3552 3567                                   * multi-column EUC, we can't depend
3553 3568                                   * on the regular tables. Rather than
3554 3569                                   * just drop through to the "big
3555 3570                                   * switch" for all characters, it
3556 3571                                   * _might_ be faster to let "movtuc"
3557 3572                                   * move a bunch of characters.
3558 3573                                   * Chances are, even in multi-byte
3559 3574                                   * mode we'll have lots of ASCII
3560 3575                                   * going through. We check the flag
3561 3576                                   * once, and call movtuc with the
3562 3577                                   * appropriate table as an argument.
3563 3578                                   *
3564 3579                                   * "movtuc will work for all codeset
3565 3580                                   * types since it stops at the beginning
3566 3581                                   * byte of a multibyte character.
3567 3582                                   */
3568 3583                                  size_t bytes_to_move;
3569 3584                                  size_t bytes_moved;
3570 3585  
3571 3586                                  ASSERT(ibp->b_wptr >= ibp->b_rptr);
3572 3587                                  bytes_to_move = ibp->b_wptr - ibp->b_rptr;
3573 3588                                  if (bytes_to_move > bytes_left)
3574 3589                                          bytes_to_move = bytes_left;
3575 3590                                  if (tp->t_state & TS_MEUC) {
3576 3591                                          bytes_moved = movtuc(bytes_to_move,
3577 3592                                              ibp->b_rptr, obp->b_wptr,
3578 3593                                              (tp->t_modes.c_oflag & OLCUC ?
3579 3594                                              elcuctab : enotrantab));
3580 3595                                  } else {
3581 3596                                          bytes_moved = movtuc(bytes_to_move,
3582 3597                                              ibp->b_rptr, obp->b_wptr,
3583 3598                                              (tp->t_modes.c_oflag & OLCUC ?
3584 3599                                              lcuctab : notrantab));
3585 3600                                  }
3586 3601                                  /*
3587 3602                                   * We're save to just do this column
3588 3603                                   * calculation, because if TS_MEUC is
3589 3604                                   * set, we used the proper EUC
3590 3605                                   * tables, and won't have copied any
3591 3606                                   * EUC bytes.
3592 3607                                   */
3593 3608                                  tp->t_col += bytes_moved;
3594 3609                                  ibp->b_rptr += bytes_moved;
3595 3610                                  obp->b_wptr += bytes_moved;
3596 3611                                  bytes_left -= bytes_moved;
3597 3612                                  if (ibp->b_rptr >= ibp->b_wptr)
3598 3613                                          continue;       /* moved all of block */
3599 3614                                  if (bytes_left == 0) {
3600 3615                                          /* LINTED */
3601 3616                                          NEW_BLOCK(0);
3602 3617                                  }
3603 3618                                  c = *ibp->b_rptr++;     /* stopper */
3604 3619                          }
3605 3620  
3606 3621                          /*
3607 3622                           * Again, we need to make sure that this is not
3608 3623                           * a following byte of a multibyte character at
3609 3624                           * here.
3610 3625                           *
3611 3626                           * 'tp->t_eucign' will be 0 iff the current 'c' is
3612 3627                           * an ASCII character. Otherwise, it will have
3613 3628                           * the byte length of a multibyte character.
3614 3629                           * We also add the display width to 'tp->t_col' if
3615 3630                           * the current codeset is not UTF-8 since this is
3616 3631                           * a leading byte of a multibyte character.
3617 3632                           * For UTF-8 codeset type, we add the display width
3618 3633                           * when we get the last byte of a character.
3619 3634                           */
3620 3635                          if ((tp->t_state & TS_MEUC) && tp->t_eucign == 0 &&
3621 3636                              NOTASCII(c)) {
3622 3637                                  tp->t_eucign = tp->t_csmethods.ldterm_memwidth(
3623 3638                                      c, (void *)tp);
3624 3639                                  tp->t_scratch_len = tp->t_eucign;
3625 3640  
3626 3641                                  if (tp->t_csdata.codeset_type !=
3627 3642                                      LDTERM_CS_TYPE_UTF8) {
3628 3643                                          tp->t_col +=
3629 3644                                              tp->t_csmethods.ldterm_dispwidth(c,
3630 3645                                              (void *)tp,
3631 3646                                              tp->t_modes.c_lflag & ECHOCTL);
3632 3647                                  }
3633 3648                          }
3634 3649  
3635 3650                          /*
3636 3651                           * If the driver has requested, don't process
3637 3652                           * output flags.  However, if we're in
3638 3653                           * multi-byte mode, we HAVE to look at
3639 3654                           * EVERYTHING going out to maintain column
3640 3655                           * position properly. Therefore IF the driver
3641 3656                           * says don't AND we're not doing multi-byte,
3642 3657                           * then don't do it.  Otherwise, do it.
3643 3658                           *
3644 3659                           * NOTE:  Hardware USUALLY doesn't expand tabs
3645 3660                           * properly for multi-byte situations anyway;
3646 3661                           * that's a known problem with the 3B2
3647 3662                           * "PORTS" board firmware, and any other
3648 3663                           * hardware that doesn't ACTUALLY know about
3649 3664                           * the current EUC mapping that WE are using
3650 3665                           * at this very moment.  The problem is that
3651 3666                           * memory width is INDEPENDENT of screen
3652 3667                           * width - no relation - so WE know how wide
3653 3668                           * the characters are, but an off-the-host
3654 3669                           * board probably doesn't.  So, until we're
3655 3670                           * SURE that the hardware below us can
3656 3671                           * correctly expand tabs in a
3657 3672                           * multi-byte/multi-column EUC situation, we
3658 3673                           * do it ourselves.
3659 3674                           */
3660 3675                          /*
3661 3676                           * Map <CR>to<NL> on output if OCRNL flag
3662 3677                           * set. ONLCR processing is not done if OCRNL
3663 3678                           * is set.
3664 3679                           */
3665 3680                          if (c == '\r' && (tp->t_modes.c_oflag & OCRNL)) {
3666 3681                                  c = '\n';
3667 3682                                  ctype = typetab[c];
3668 3683                                  goto jocrnl;
3669 3684                          }
3670 3685  
3671 3686                          if (tp->t_csdata.codeset_type == LDTERM_CS_TYPE_EUC) {
3672 3687                                  ctype = typetab[c];
3673 3688                          } else {
3674 3689                                  /*
3675 3690                                   * In other codeset types, we safely assume
3676 3691                                   * any byte of a multibyte character will have
3677 3692                                   * 'ORDINARY' type. For ASCII characters, we
3678 3693                                   * still use the typetab[].
3679 3694                                   */
3680 3695                                  if (tp->t_eucign == 0)
3681 3696                                          ctype = typetab[c];
3682 3697                                  else
3683 3698                                          ctype = ORDINARY;
3684 3699                          }
3685 3700  
3686 3701                          /*
3687 3702                           * Map <NL> to <CR><NL> on output if ONLCR
3688 3703                           * flag is set.
3689 3704                           */
3690 3705                          if (c == '\n' && (tp->t_modes.c_oflag & ONLCR)) {
3691 3706                                  if (!(tp->t_state & TS_TTCR)) {
3692 3707                                          tp->t_state |= TS_TTCR;
3693 3708                                          c = '\r';
3694 3709                                          ctype = typetab['\r'];
3695 3710                                          --ibp->b_rptr;
3696 3711                                  } else
3697 3712                                          tp->t_state &= ~TS_TTCR;
3698 3713                          }
3699 3714                          /*
3700 3715                           * Delay values and column position
3701 3716                           * calculated here.  For EUC chars in
3702 3717                           * multi-byte mode, we use "t_eucign" to help
3703 3718                           * calculate columns.  When we see the first
3704 3719                           * byte of an EUC, we set t_eucign to the
3705 3720                           * number of bytes that will FOLLOW it, and
3706 3721                           * we add the screen width of the WHOLE EUC
3707 3722                           * character to the column position.  In
3708 3723                           * particular, we can't count SS2 or SS3 as
3709 3724                           * printing characters.  Remember, folks, the
3710 3725                           * screen width and memory width are
3711 3726                           * independent - no relation. We could have
3712 3727                           * dropped through for ASCII, but we want to
3713 3728                           * catch any bad characters (i.e., t_eucign
3714 3729                           * set and an ASCII char received) and
3715 3730                           * possibly report the garbage situation.
3716 3731                           */
3717 3732          jocrnl:
3718 3733  
3719 3734                          count = 0;
3720 3735                          switch (ctype) {
3721 3736  
3722 3737                          case T_SS2:
3723 3738                          case T_SS3:
3724 3739                          case ORDINARY:
3725 3740                                  if (tp->t_state & TS_MEUC) {
3726 3741                                          if (tp->t_eucign) {
3727 3742                                                  *obp->b_wptr++ = c;
3728 3743                                                  bytes_left--;
3729 3744  
3730 3745                                                  tp->t_scratch[tp->t_scratch_len
3731 3746                                                      - tp->t_eucign] = c;
3732 3747  
3733 3748                                                  --tp->t_eucign;
3734 3749  
3735 3750                                                  if (tp->t_csdata.codeset_type
3736 3751                                                      == LDTERM_CS_TYPE_UTF8 &&
3737 3752                                                      tp->t_eucign <= 0) {
3738 3753                                                          tp->t_col +=
3739 3754                                                              ldterm_utf8_width(
3740 3755                                                              tp->t_scratch,
3741 3756                                                              tp->t_scratch_len);
3742 3757                                                  }
3743 3758                                          } else {
3744 3759                                                  if (tp->t_modes.c_oflag & OLCUC)
3745 3760                                                          n = elcuctab[c];
3746 3761                                                  else
3747 3762                                                          n = enotrantab[c];
3748 3763                                                  if (n)
3749 3764                                                          c = n;
3750 3765                                                  tp->t_col++;
3751 3766                                                  *obp->b_wptr++ = c;
3752 3767                                                  bytes_left--;
3753 3768                                          }
3754 3769                                  } else {        /* ho hum, ASCII mode... */
3755 3770                                          if (tp->t_modes.c_oflag & OLCUC)
3756 3771                                                  n = lcuctab[c];
3757 3772                                          else
3758 3773                                                  n = notrantab[c];
3759 3774                                          if (n)
3760 3775                                                  c = n;
3761 3776                                          tp->t_col++;
3762 3777                                          *obp->b_wptr++ = c;
3763 3778                                          bytes_left--;
3764 3779                                  }
3765 3780                                  break;
3766 3781  
3767 3782                                  /*
3768 3783                                   * If we're doing ECHOCTL, we've
3769 3784                                   * already mapped the thing during
3770 3785                                   * the process of canonising.  Don't
3771 3786                                   * bother here, as it's not one that
3772 3787                                   * we did.
3773 3788                                   */
3774 3789                          case CONTROL:
3775 3790                                  *obp->b_wptr++ = c;
3776 3791                                  bytes_left--;
3777 3792                                  break;
3778 3793  
3779 3794                                  /*
3780 3795                                   * This is probably a backspace
3781 3796                                   * received, not one that we're
3782 3797                                   * echoing.  Let it go as a
3783 3798                                   * single-column backspace.
3784 3799                                   */
3785 3800                          case BACKSPACE:
3786 3801                                  if (tp->t_col)
3787 3802                                          tp->t_col--;
3788 3803                                  if (tp->t_modes.c_oflag & BSDLY) {
3789 3804                                          if (tp->t_modes.c_oflag & OFILL)
3790 3805                                                  count = 1;
3791 3806                                  }
3792 3807                                  *obp->b_wptr++ = c;
3793 3808                                  bytes_left--;
3794 3809                                  break;
3795 3810  
3796 3811                          case NEWLINE:
3797 3812                                  if (tp->t_modes.c_oflag & ONLRET)
3798 3813                                          goto cr;
3799 3814                                  if ((tp->t_modes.c_oflag & NLDLY) == NL1)
3800 3815                                          count = 2;
3801 3816                                  *obp->b_wptr++ = c;
3802 3817                                  bytes_left--;
3803 3818                                  break;
3804 3819  
3805 3820                          case TAB:
3806 3821                                  /*
3807 3822                                   * Map '\t' to spaces if XTABS flag
3808 3823                                   * is set.  The calculation of
3809 3824                                   * "t_eucign" has probably insured
3810 3825                                   * that column will be correct, as we
3811 3826                                   * bumped t_col by the DISP width,
3812 3827                                   * not the memory width.
3813 3828                                   */
3814 3829                                  if ((tp->t_modes.c_oflag & TABDLY) == XTABS) {
3815 3830                                          for (;;) {
3816 3831                                                  *obp->b_wptr++ = ' ';
3817 3832                                                  bytes_left--;
3818 3833                                                  tp->t_col++;
3819 3834                                                  if ((tp->t_col & 07) == 0)
3820 3835                                                          break;  /* every 8th */
3821 3836                                                  /*
3822 3837                                                   * If we don't have
3823 3838                                                   * room to fully
3824 3839                                                   * expand this tab in
3825 3840                                                   * this block, back
3826 3841                                                   * up to continue
3827 3842                                                   * expanding it into
3828 3843                                                   * the next block.
3829 3844                                                   */
3830 3845                                                  if (obp->b_wptr >=
3831 3846                                                      obp->b_datap->db_lim) {
3832 3847                                                          ibp->b_rptr--;
3833 3848                                                          break;
3834 3849                                                  }
3835 3850                                          }
3836 3851                                  } else {
3837 3852                                          tp->t_col |= 07;
3838 3853                                          tp->t_col++;
3839 3854                                          if (tp->t_modes.c_oflag & OFILL) {
3840 3855                                                  if (tp->t_modes.c_oflag &
3841 3856                                                      TABDLY)
3842 3857                                                          count = 2;
3843 3858                                          } else {
3844 3859                                                  switch (tp->t_modes.c_oflag &
3845 3860                                                      TABDLY) {
3846 3861                                                  case TAB2:
3847 3862                                                          count = 6;
3848 3863                                                          break;
3849 3864  
3850 3865                                                  case TAB1:
3851 3866                                                          count = 1 + (tp->t_col |
3852 3867                                                              ~07);
3853 3868                                                          if (count < 5)
3854 3869                                                                  count = 0;
3855 3870                                                          break;
3856 3871                                                  }
3857 3872                                          }
3858 3873                                          *obp->b_wptr++ = c;
3859 3874                                          bytes_left--;
3860 3875                                  }
3861 3876                                  break;
3862 3877  
3863 3878                          case VTAB:
3864 3879                                  if ((tp->t_modes.c_oflag & VTDLY) &&
3865 3880                                      !(tp->t_modes.c_oflag & OFILL))
3866 3881                                          count = 127;
3867 3882                                  *obp->b_wptr++ = c;
3868 3883                                  bytes_left--;
3869 3884                                  break;
3870 3885  
3871 3886                          case RETURN:
3872 3887                                  /*
3873 3888                                   * Ignore <CR> in column 0 if ONOCR
3874 3889                                   * flag set.
3875 3890                                   */
3876 3891                                  if (tp->t_col == 0 &&
3877 3892                                      (tp->t_modes.c_oflag & ONOCR))
3878 3893                                          break;
3879 3894  
3880 3895                  cr:
3881 3896                                  switch (tp->t_modes.c_oflag & CRDLY) {
3882 3897  
3883 3898                                  case CR1:
3884 3899                                          if (tp->t_modes.c_oflag & OFILL)
3885 3900                                                  count = 2;
3886 3901                                          else
3887 3902                                                  count = tp->t_col % 2;
3888 3903                                          break;
3889 3904  
3890 3905                                  case CR2:
3891 3906                                          if (tp->t_modes.c_oflag & OFILL)
3892 3907                                                  count = 4;
3893 3908                                          else
3894 3909                                                  count = 6;
3895 3910                                          break;
3896 3911  
3897 3912                                  case CR3:
3898 3913                                          if (tp->t_modes.c_oflag & OFILL)
3899 3914                                                  count = 0;
3900 3915                                          else
3901 3916                                                  count = 9;
3902 3917                                          break;
3903 3918                                  }
3904 3919                                  tp->t_col = 0;
3905 3920                                  *obp->b_wptr++ = c;
3906 3921                                  bytes_left--;
3907 3922                                  break;
3908 3923                          }
3909 3924  
3910 3925                          if (count != 0) {
3911 3926                                  if (tp->t_modes.c_oflag & OFILL) {
3912 3927                                          do {
3913 3928                                                  if (bytes_left == 0) {
3914 3929                                                          /* LINTED */
3915 3930                                                          NEW_BLOCK(0);
3916 3931                                                  }
3917 3932                                                  if (tp->t_modes.c_oflag & OFDEL)
3918 3933                                                          *obp->b_wptr++ = CDEL;
3919 3934                                                  else
3920 3935                                                          *obp->b_wptr++ = CNUL;
3921 3936                                                  bytes_left--;
3922 3937                                          } while (--count != 0);
3923 3938                                  } else {
3924 3939                                          if ((tp->t_modes.c_lflag & FLUSHO) &&
3925 3940                                              (tp->t_modes.c_lflag & IEXTEN)) {
3926 3941                                                  /* drop on floor */
3927 3942                                                  freemsg(*omp);
3928 3943                                          } else {
3929 3944                                                  /*
3930 3945                                                   * Update sysinfo
3931 3946                                                   * outch
3932 3947                                                   */
3933 3948                                                  (void) drv_setparm(SYSOUTC,
3934 3949                                                      msgdsize(*omp));
3935 3950                                                  putnext(q, *omp);
3936 3951                                                  /*
3937 3952                                                   * Send M_DELAY
3938 3953                                                   * downstream
3939 3954                                                   */
3940 3955                                                  if ((bp =
3941 3956                                                      allocb(1, BPRI_MED)) !=
3942 3957                                                      NULL) {
3943 3958                                                          bp->b_datap->db_type =
3944 3959                                                              M_DELAY;
3945 3960                                                          *bp->b_wptr++ =
3946 3961                                                              (uchar_t)count;
3947 3962                                                          putnext(q, bp);
3948 3963                                                  }
3949 3964                                          }
3950 3965                                          bytes_left = 0;
3951 3966                                          /*
3952 3967                                           * We have to start a new
3953 3968                                           * message; the delay
3954 3969                                           * introduces a break between
3955 3970                                           * messages.
3956 3971                                           */
3957 3972                                          *omp = NULL;
3958 3973                                          contpp = omp;
3959 3974                                  }
3960 3975                          }
3961 3976                  }
3962 3977                  cbp = ibp->b_cont;
3963 3978                  freeb(ibp);
3964 3979          } while ((ibp = cbp) != NULL);  /* next block, if any */
3965 3980  
3966 3981  outofbufs:
3967 3982          return (ibp);
3968 3983  #undef NEW_BLOCK
3969 3984  }
3970 3985  
3971 3986  
3972 3987  #if !defined(__sparc)
3973 3988  int
3974 3989  movtuc(size_t size, unsigned char *from, unsigned char *origto,
3975 3990      unsigned char *table)
3976 3991  {
3977 3992          unsigned char *to = origto;
3978 3993          unsigned char c;
3979 3994  
3980 3995          while (size != 0 && (c = table[*from++]) != 0) {
3981 3996                  *to++ = c;
3982 3997                  size--;
3983 3998          }
3984 3999          return (to - origto);
3985 4000  }
3986 4001  #endif
3987 4002  
3988 4003  static void
3989 4004  ldterm_flush_output(uchar_t c, queue_t *q, ldtermstd_state_t *tp)
3990 4005  {
3991 4006          /* Already conditioned with IEXTEN during VDISCARD processing */
3992 4007          if (tp->t_modes.c_lflag & FLUSHO)
3993 4008                  tp->t_modes.c_lflag &= ~FLUSHO;
3994 4009          else {
3995 4010                  flushq(q, FLUSHDATA);   /* flush our write queue */
3996 4011                  /* flush ones below us */
3997 4012                  (void) putnextctl1(q, M_FLUSH, FLUSHW);
3998 4013                  if ((tp->t_echomp = allocb(EBSIZE, BPRI_HI)) != NULL) {
3999 4014                          (void) ldterm_echo(c, q, 1, tp);
4000 4015                          if (tp->t_msglen != 0)
4001 4016                                  ldterm_reprint(q, EBSIZE, tp);
4002 4017                          if (tp->t_echomp != NULL) {
4003 4018                                  putnext(q, tp->t_echomp);
4004 4019                                  tp->t_echomp = NULL;
4005 4020                          }
4006 4021                  }
4007 4022                  tp->t_modes.c_lflag |= FLUSHO;
4008 4023          }
4009 4024  }
4010 4025  
4011 4026  
4012 4027  /*
4013 4028   * Signal generated by the reader: M_PCSIG and M_FLUSH messages sent.
4014 4029   */
4015 4030  static void
4016 4031  ldterm_dosig(queue_t *q, int sig, uchar_t c, int mtype, int mode)
4017 4032  {
4018 4033          ldtermstd_state_t *tp = (ldtermstd_state_t *)q->q_ptr;
4019 4034          int sndsig = 0;
4020 4035  
4021 4036          /*
4022 4037           * c == \0 is brk case; need to flush on BRKINT even if
4023 4038           * noflsh is set.
4024 4039           */
4025 4040          if ((!(tp->t_modes.c_lflag & NOFLSH)) || (c == '\0')) {
4026 4041                  if (mode) {
4027 4042                          if (tp->t_state & TS_TTSTOP) {
4028 4043                                  sndsig = 1;
4029 4044                                  (void) putnextctl1(q, mtype, sig);
4030 4045                          }
4031 4046                          /*
4032 4047                           * Flush read or write side.
4033 4048                           * Restart the input or output.
4034 4049                           */
4035 4050                          if (mode & FLUSHR) {
4036 4051                                  flushq(q, FLUSHDATA);
4037 4052                                  (void) putnextctl1(WR(q), M_FLUSH, mode);
4038 4053                                  if (tp->t_state & (TS_TBLOCK|TS_IFBLOCK)) {
4039 4054                                          (void) putnextctl(WR(q), M_STARTI);
4040 4055                                          tp->t_state &= ~(TS_TBLOCK|TS_IFBLOCK);
4041 4056                                  }
4042 4057                          }
4043 4058                          if (mode & FLUSHW) {
4044 4059                                  flushq(WR(q), FLUSHDATA);
4045 4060                                  /*
4046 4061                                   * XXX This is extremely gross.
4047 4062                                   * Since we can't be sure our M_FLUSH
4048 4063                                   * will have run its course by the
4049 4064                                   * time we do the echo below, we set
4050 4065                                   * state and toss it in the write put
4051 4066                                   * routine to prevent flushing our
4052 4067                                   * own data.  Note that downstream
4053 4068                                   * modules on the write side will be
4054 4069                                   * flushed by the M_FLUSH sent above.
4055 4070                                   */
4056 4071                                  tp->t_state |= TS_FLUSHWAIT;
4057 4072                                  (void) putnextctl1(q, M_FLUSH, FLUSHW);
4058 4073                                  if (tp->t_state & TS_TTSTOP) {
4059 4074                                          (void) putnextctl(WR(q), M_START);
4060 4075                                          tp->t_state &= ~(TS_TTSTOP|TS_OFBLOCK);
  
    | 
      ↓ open down ↓ | 
    3315 lines elided | 
    
      ↑ open up ↑ | 
  
4061 4076                                  }
4062 4077                          }
4063 4078                  }
4064 4079          }
4065 4080          tp->t_state &= ~TS_QUOT;
4066 4081          if (sndsig == 0)
4067 4082                  (void) putnextctl1(q, mtype, sig);
4068 4083  
4069 4084          if (c != '\0') {
4070 4085                  if ((tp->t_echomp = allocb(4, BPRI_HI)) != NULL) {
4071      -                        (void) ldterm_echo(c, WR(q), 4, tp);
4072      -                        putnext(WR(q), tp->t_echomp);
     4086 +                        if (ldterm_echo(c, WR(q), 4, tp) > 0)
     4087 +                                putnext(WR(q), tp->t_echomp);
     4088 +                        else
     4089 +                                freemsg(tp->t_echomp);
4073 4090                          tp->t_echomp = NULL;
4074 4091                  }
4075 4092          }
4076 4093  }
4077 4094  
4078 4095  
4079 4096  /*
4080 4097   * Called when an M_IOCTL message is seen on the write queue; does
4081 4098   * whatever we're supposed to do with it, and either replies
4082 4099   * immediately or passes it to the next module down.
4083 4100   */
4084 4101  static void
4085 4102  ldterm_do_ioctl(queue_t *q, mblk_t *mp)
4086 4103  {
4087 4104          ldtermstd_state_t *tp;
4088 4105          struct iocblk *iocp;
4089 4106          struct eucioc *euciocp; /* needed for EUC ioctls */
4090 4107          ldterm_cs_data_user_t *csdp;
4091 4108          int i;
4092 4109          int locale_name_sz;
4093 4110          uchar_t maxbytelen;
4094 4111          uchar_t maxscreenlen;
4095 4112          int error;
4096 4113  
4097 4114          iocp = (struct iocblk *)mp->b_rptr;
4098 4115          tp = (ldtermstd_state_t *)q->q_ptr;
4099 4116  
4100 4117          switch (iocp->ioc_cmd) {
4101 4118  
4102 4119          case TCSETS:
4103 4120          case TCSETSW:
4104 4121          case TCSETSF:
4105 4122                  {
4106 4123                          /*
4107 4124                           * Set current parameters and special
4108 4125                           * characters.
4109 4126                           */
4110 4127                          struct termios *cb;
4111 4128                          struct termios oldmodes;
4112 4129  
4113 4130                          error = miocpullup(mp, sizeof (struct termios));
4114 4131                          if (error != 0) {
4115 4132                                  miocnak(q, mp, 0, error);
4116 4133                                  return;
4117 4134                          }
4118 4135  
4119 4136                          cb = (struct termios *)mp->b_cont->b_rptr;
4120 4137  
4121 4138                          oldmodes = tp->t_amodes;
4122 4139                          tp->t_amodes = *cb;
4123 4140                          if ((tp->t_amodes.c_lflag & PENDIN) &&
4124 4141                              (tp->t_modes.c_lflag & IEXTEN)) {
4125 4142                                  /*
4126 4143                                   * Yuk.  The C shell file completion
4127 4144                                   * code actually uses this "feature",
4128 4145                                   * so we have to support it.
4129 4146                                   */
4130 4147                                  if (tp->t_message != NULL) {
4131 4148                                          tp->t_state |= TS_RESCAN;
4132 4149                                          qenable(RD(q));
4133 4150                                  }
4134 4151                                  tp->t_amodes.c_lflag &= ~PENDIN;
4135 4152                          }
4136 4153                          bcopy(tp->t_amodes.c_cc, tp->t_modes.c_cc, NCCS);
4137 4154  
4138 4155                          /*
4139 4156                           * ldterm_adjust_modes does not deal with
4140 4157                           * cflags
4141 4158                           */
4142 4159                          tp->t_modes.c_cflag = tp->t_amodes.c_cflag;
4143 4160  
4144 4161                          ldterm_adjust_modes(tp);
4145 4162                          if (chgstropts(&oldmodes, tp, RD(q)) == (-1)) {
4146 4163                                  miocnak(q, mp, 0, EAGAIN);
4147 4164                                  return;
4148 4165                          }
4149 4166                          /*
4150 4167                           * The driver may want to know about the
4151 4168                           * following iflags: IGNBRK, BRKINT, IGNPAR,
4152 4169                           * PARMRK, INPCK, IXON, IXANY.
4153 4170                           */
4154 4171                          break;
4155 4172                  }
4156 4173  
4157 4174          case TCSETA:
4158 4175          case TCSETAW:
4159 4176          case TCSETAF:
4160 4177                  {
4161 4178                          /*
4162 4179                           * Old-style "ioctl" to set current
4163 4180                           * parameters and special characters. Don't
4164 4181                           * clear out the unset portions, leave them
4165 4182                           * as they are.
4166 4183                           */
4167 4184                          struct termio *cb;
4168 4185                          struct termios oldmodes;
4169 4186  
4170 4187                          error = miocpullup(mp, sizeof (struct termio));
4171 4188                          if (error != 0) {
4172 4189                                  miocnak(q, mp, 0, error);
4173 4190                                  return;
4174 4191                          }
4175 4192  
4176 4193                          cb = (struct termio *)mp->b_cont->b_rptr;
4177 4194  
4178 4195                          oldmodes = tp->t_amodes;
4179 4196                          tp->t_amodes.c_iflag =
4180 4197                              (tp->t_amodes.c_iflag & 0xffff0000 | cb->c_iflag);
4181 4198                          tp->t_amodes.c_oflag =
4182 4199                              (tp->t_amodes.c_oflag & 0xffff0000 | cb->c_oflag);
4183 4200                          tp->t_amodes.c_cflag =
4184 4201                              (tp->t_amodes.c_cflag & 0xffff0000 | cb->c_cflag);
4185 4202                          tp->t_amodes.c_lflag =
4186 4203                              (tp->t_amodes.c_lflag & 0xffff0000 | cb->c_lflag);
4187 4204  
4188 4205                          bcopy(cb->c_cc, tp->t_modes.c_cc, NCC);
4189 4206                          /* TCGETS returns amodes, so update that too */
4190 4207                          bcopy(cb->c_cc, tp->t_amodes.c_cc, NCC);
4191 4208  
4192 4209                          /* ldterm_adjust_modes does not deal with cflags */
4193 4210  
4194 4211                          tp->t_modes.c_cflag = tp->t_amodes.c_cflag;
4195 4212  
4196 4213                          ldterm_adjust_modes(tp);
4197 4214                          if (chgstropts(&oldmodes, tp, RD(q)) == (-1)) {
4198 4215                                  miocnak(q, mp, 0, EAGAIN);
4199 4216                                  return;
4200 4217                          }
4201 4218                          /*
4202 4219                           * The driver may want to know about the
4203 4220                           * following iflags: IGNBRK, BRKINT, IGNPAR,
4204 4221                           * PARMRK, INPCK, IXON, IXANY.
4205 4222                           */
4206 4223                          break;
4207 4224                  }
4208 4225  
4209 4226          case TCFLSH:
4210 4227                  /*
4211 4228                   * Do the flush on the write queue immediately, and
4212 4229                   * queue up any flush on the read queue for the
4213 4230                   * service procedure to see.  Then turn it into the
4214 4231                   * appropriate M_FLUSH message, so that the module
4215 4232                   * below us doesn't have to know about TCFLSH.
4216 4233                   */
4217 4234                  error = miocpullup(mp, sizeof (int));
4218 4235                  if (error != 0) {
4219 4236                          miocnak(q, mp, 0, error);
4220 4237                          return;
4221 4238                  }
4222 4239  
4223 4240                  ASSERT(mp->b_datap != NULL);
4224 4241                  if (*(int *)mp->b_cont->b_rptr == 0) {
4225 4242                          ASSERT(mp->b_datap != NULL);
4226 4243                          (void) putnextctl1(q, M_FLUSH, FLUSHR);
4227 4244                          (void) putctl1(RD(q), M_FLUSH, FLUSHR);
4228 4245                  } else if (*(int *)mp->b_cont->b_rptr == 1) {
4229 4246                          flushq(q, FLUSHDATA);
4230 4247                          ASSERT(mp->b_datap != NULL);
4231 4248                          tp->t_state |= TS_FLUSHWAIT;
4232 4249                          (void) putnextctl1(RD(q), M_FLUSH, FLUSHW);
4233 4250                          (void) putnextctl1(q, M_FLUSH, FLUSHW);
4234 4251                  } else if (*(int *)mp->b_cont->b_rptr == 2) {
4235 4252                          flushq(q, FLUSHDATA);
4236 4253                          ASSERT(mp->b_datap != NULL);
4237 4254                          (void) putnextctl1(q, M_FLUSH, FLUSHRW);
4238 4255                          tp->t_state |= TS_FLUSHWAIT;
4239 4256                          (void) putnextctl1(RD(q), M_FLUSH, FLUSHRW);
4240 4257                  } else {
4241 4258                          miocnak(q, mp, 0, EINVAL);
4242 4259                          return;
4243 4260                  }
4244 4261                  ASSERT(mp->b_datap != NULL);
4245 4262                  iocp->ioc_rval = 0;
4246 4263                  miocack(q, mp, 0, 0);
4247 4264                  return;
4248 4265  
4249 4266          case TCXONC:
4250 4267                  error = miocpullup(mp, sizeof (int));
4251 4268                  if (error != 0) {
4252 4269                          miocnak(q, mp, 0, error);
4253 4270                          return;
4254 4271                  }
4255 4272  
4256 4273                  switch (*(int *)mp->b_cont->b_rptr) {
4257 4274                  case 0:
4258 4275                          if (!(tp->t_state & TS_TTSTOP)) {
4259 4276                                  (void) putnextctl(q, M_STOP);
4260 4277                                  tp->t_state |= (TS_TTSTOP|TS_OFBLOCK);
4261 4278                          }
4262 4279                          break;
4263 4280  
4264 4281                  case 1:
4265 4282                          if (tp->t_state & TS_TTSTOP) {
4266 4283                                  (void) putnextctl(q, M_START);
4267 4284                                  tp->t_state &= ~(TS_TTSTOP|TS_OFBLOCK);
4268 4285                          }
4269 4286                          break;
4270 4287  
4271 4288                  case 2:
4272 4289                          (void) putnextctl(q, M_STOPI);
4273 4290                          tp->t_state |= (TS_TBLOCK|TS_IFBLOCK);
4274 4291                          break;
4275 4292  
4276 4293                  case 3:
4277 4294                          (void) putnextctl(q, M_STARTI);
4278 4295                          tp->t_state &= ~(TS_TBLOCK|TS_IFBLOCK);
4279 4296                          break;
4280 4297  
4281 4298                  default:
4282 4299                          miocnak(q, mp, 0, EINVAL);
4283 4300                          return;
4284 4301                  }
4285 4302                  ASSERT(mp->b_datap != NULL);
4286 4303                  iocp->ioc_rval = 0;
4287 4304                  miocack(q, mp, 0, 0);
4288 4305                  return;
4289 4306                  /*
4290 4307                   * TCSBRK is expected to be handled by the driver.
4291 4308                   * The reason its left for the driver is that when
4292 4309                   * the argument to TCSBRK is zero driver has to drain
4293 4310                   * the data and sending a M_IOCACK from LDTERM before
4294 4311                   * the driver drains the data is going to cause
4295 4312                   * problems.
4296 4313                   */
4297 4314  
4298 4315                  /*
4299 4316                   * The following are EUC related ioctls.  For
4300 4317                   * EUC_WSET, we have to pass the information on, even
4301 4318                   * though we ACK the call.  It's vital in the EUC
4302 4319                   * environment that everybody downstream knows about
4303 4320                   * the EUC codeset widths currently in use; we
4304 4321                   * therefore pass down the information in an M_CTL
4305 4322                   * message.  It will bottom out in the driver.
4306 4323                   */
4307 4324          case EUC_WSET:
4308 4325                  {
4309 4326  
4310 4327                          /* only needed for EUC_WSET */
4311 4328                          struct iocblk *riocp;
4312 4329  
4313 4330                          mblk_t *dmp, *dmp_cont;
4314 4331  
4315 4332                          /*
4316 4333                           * If the user didn't supply any information,
4317 4334                           * NAK it.
4318 4335                           */
4319 4336                          error = miocpullup(mp, sizeof (struct eucioc));
4320 4337                          if (error != 0) {
4321 4338                                  miocnak(q, mp, 0, error);
4322 4339                                  return;
4323 4340                          }
4324 4341  
4325 4342                          euciocp = (struct eucioc *)mp->b_cont->b_rptr;
4326 4343                          /*
4327 4344                           * Check here for something reasonable.  If
4328 4345                           * anything will take more than EUC_MAXW
4329 4346                           * columns or more than EUC_MAXW bytes
4330 4347                           * following SS2 or SS3, then just reject it
4331 4348                           * out of hand. It's not impossible for us to
4332 4349                           * do it, it just isn't reasonable.  So far,
4333 4350                           * in the world, we've seen the absolute max
4334 4351                           * columns to be 2 and the max number of
4335 4352                           * bytes to be 3.  This allows room for some
4336 4353                           * expansion of that, but it probably won't
4337 4354                           * even be necessary. At the moment, we
4338 4355                           * return a "range" error.  If you really
4339 4356                           * need to, you can push EUC_MAXW up to over
4340 4357                           * 200; it doesn't make sense, though, with
4341 4358                           * only a CANBSIZ sized input limit (usually
4342 4359                           * 256)!
4343 4360                           */
4344 4361                          for (i = 0; i < 4; i++) {
4345 4362                                  if ((euciocp->eucw[i] > EUC_MAXW) ||
4346 4363                                      (euciocp->scrw[i] > EUC_MAXW)) {
4347 4364                                          miocnak(q, mp, 0, ERANGE);
4348 4365                                          return;
4349 4366                                  }
4350 4367                          }
4351 4368                          /*
4352 4369                           * Otherwise, save the information in tp,
4353 4370                           * force codeset 0 (ASCII) to be one byte,
4354 4371                           * one column.
4355 4372                           */
4356 4373                          cp_eucwioc(euciocp, &tp->eucwioc, EUCIN);
4357 4374                          tp->eucwioc.eucw[0] = tp->eucwioc.scrw[0] = 1;
4358 4375                          /*
4359 4376                           * Now, check out whether we're doing
4360 4377                           * multibyte processing. if we are, we need
4361 4378                           * to allocate a block to hold the parallel
4362 4379                           * array. By convention, we've been passed
4363 4380                           * what amounts to a CSWIDTH definition.  We
4364 4381                           * actually NEED the number of bytes for
4365 4382                           * Codesets 2 & 3.
4366 4383                           */
4367 4384                          tp->t_maxeuc = 0;       /* reset to say we're NOT */
4368 4385  
4369 4386                          tp->t_state &= ~TS_MEUC;
4370 4387                          /*
4371 4388                           * We'll set TS_MEUC if we're doing
4372 4389                           * multi-column OR multi- byte OR both.  It
4373 4390                           * makes things easier...  NOTE:  If we fail
4374 4391                           * to get the buffer we need to hold display
4375 4392                           * widths, then DON'T let the TS_MEUC bit get
4376 4393                           * set!
4377 4394                           */
4378 4395                          for (i = 0; i < 4; i++) {
4379 4396                                  if (tp->eucwioc.eucw[i] > tp->t_maxeuc)
4380 4397                                          tp->t_maxeuc = tp->eucwioc.eucw[i];
4381 4398                                  if (tp->eucwioc.scrw[i] > 1)
4382 4399                                          tp->t_state |= TS_MEUC;
4383 4400                          }
4384 4401                          if ((tp->t_maxeuc > 1) || (tp->t_state & TS_MEUC)) {
4385 4402                                  if (!tp->t_eucp_mp) {
4386 4403                                          if ((tp->t_eucp_mp = allocb(_TTY_BUFSIZ,
4387 4404                                              BPRI_HI)) == NULL) {
4388 4405                                                  tp->t_maxeuc = 1;
4389 4406                                                  tp->t_state &= ~TS_MEUC;
4390 4407                                                  cmn_err(CE_WARN,
4391 4408                                                      "Can't allocate eucp_mp");
4392 4409                                                  miocnak(q, mp, 0, ENOSR);
4393 4410                                                  return;
4394 4411                                          }
4395 4412                                          /*
4396 4413                                           * here, if there's junk in
4397 4414                                           * the canonical buffer, then
4398 4415                                           * move the eucp pointer past
4399 4416                                           * it, so we don't run off
4400 4417                                           * the beginning.  This is a
4401 4418                                           * total botch, but will
4402 4419                                           * hopefully keep stuff from
4403 4420                                           * getting too messed up
4404 4421                                           * until the user flushes
4405 4422                                           * this line!
4406 4423                                           */
4407 4424                                          if (tp->t_msglen) {
4408 4425                                                  tp->t_eucp =
4409 4426                                                      tp->t_eucp_mp->b_rptr;
4410 4427                                                  for (i = tp->t_msglen; i; i--)
4411 4428                                                          *tp->t_eucp++ = 1;
4412 4429                                          } else {
4413 4430                                                  tp->t_eucp =
4414 4431                                                      tp->t_eucp_mp->b_rptr;
4415 4432                                          }
4416 4433                                  }
4417 4434                                  /* doing multi-byte handling */
4418 4435                                  tp->t_state |= TS_MEUC;
4419 4436  
4420 4437                          } else if (tp->t_eucp_mp) {
4421 4438                                  freemsg(tp->t_eucp_mp);
4422 4439                                  tp->t_eucp_mp = NULL;
4423 4440                                  tp->t_eucp = NULL;
4424 4441                          }
4425 4442  
4426 4443                          /*
4427 4444                           * Save the EUC width data we have at
4428 4445                           * the t_csdata, set t_csdata.codeset_type to
4429 4446                           * EUC one, and, switch the codeset methods at
4430 4447                           * t_csmethods.
4431 4448                           */
4432 4449                          bzero(&tp->t_csdata.eucpc_data,
4433 4450                              (sizeof (ldterm_eucpc_data_t) *
4434 4451                              LDTERM_CS_MAX_CODESETS));
4435 4452                          tp->t_csdata.eucpc_data[0].byte_length =
4436 4453                              tp->eucwioc.eucw[1];
4437 4454                          tp->t_csdata.eucpc_data[0].screen_width =
4438 4455                              tp->eucwioc.scrw[1];
4439 4456                          tp->t_csdata.eucpc_data[1].byte_length =
4440 4457                              tp->eucwioc.eucw[2];
4441 4458                          tp->t_csdata.eucpc_data[1].screen_width =
4442 4459                              tp->eucwioc.scrw[2];
4443 4460                          tp->t_csdata.eucpc_data[2].byte_length =
4444 4461                              tp->eucwioc.eucw[3];
4445 4462                          tp->t_csdata.eucpc_data[2].screen_width =
4446 4463                              tp->eucwioc.scrw[3];
4447 4464                          tp->t_csdata.version = LDTERM_DATA_VERSION;
4448 4465                          tp->t_csdata.codeset_type = LDTERM_CS_TYPE_EUC;
4449 4466                          /*
4450 4467                           * We are not using the 'csinfo_num' anyway if the
4451 4468                           * current codeset type is EUC. So, set it to
4452 4469                           * the maximum possible.
4453 4470                           */
4454 4471                          tp->t_csdata.csinfo_num =
4455 4472                              LDTERM_CS_TYPE_EUC_MAX_SUBCS;
4456 4473                          if (tp->t_csdata.locale_name != (char *)NULL) {
4457 4474                                  kmem_free(tp->t_csdata.locale_name,
4458 4475                                      strlen(tp->t_csdata.locale_name) + 1);
4459 4476                                  tp->t_csdata.locale_name = (char *)NULL;
4460 4477                          }
4461 4478                          tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_EUC];
4462 4479  
4463 4480                          /*
4464 4481                           * If we are able to allocate two blocks (the
4465 4482                           * iocblk and the associated data), then pass
4466 4483                           * it downstream, otherwise we'll need to NAK
4467 4484                           * it, and drop whatever we WERE able to
4468 4485                           * allocate.
4469 4486                           */
4470 4487                          if ((dmp = mkiocb(EUC_WSET)) == NULL) {
4471 4488                                  miocnak(q, mp, 0, ENOSR);
4472 4489                                  return;
4473 4490                          }
4474 4491                          if ((dmp_cont = allocb(EUCSIZE, BPRI_HI)) == NULL) {
4475 4492                                  freemsg(dmp);
4476 4493                                  miocnak(q, mp, 0, ENOSR);
4477 4494                                  return;
4478 4495                          }
4479 4496  
4480 4497                          /*
4481 4498                           * We got both buffers.  Copy out the EUC
4482 4499                           * information (as we received it, not what
4483 4500                           * we're using!) & pass it on.
4484 4501                           */
4485 4502                          bcopy(mp->b_cont->b_rptr, dmp_cont->b_rptr, EUCSIZE);
4486 4503                          dmp_cont->b_wptr += EUCSIZE;
4487 4504                          dmp->b_cont = dmp_cont;
4488 4505                          dmp->b_datap->db_type = M_CTL;
4489 4506                          dmp_cont->b_datap->db_type = M_DATA;
4490 4507                          riocp = (struct iocblk *)dmp->b_rptr;
4491 4508                          riocp->ioc_count = EUCSIZE;
4492 4509                          putnext(q, dmp);
4493 4510  
4494 4511                          /*
4495 4512                           * Now ACK the ioctl.
4496 4513                           */
4497 4514                          iocp->ioc_rval = 0;
4498 4515                          miocack(q, mp, 0, 0);
4499 4516                          return;
4500 4517                  }
4501 4518  
4502 4519          case EUC_WGET:
4503 4520                  error = miocpullup(mp, sizeof (struct eucioc));
4504 4521                  if (error != 0) {
4505 4522                          miocnak(q, mp, 0, error);
4506 4523                          return;
4507 4524                  }
4508 4525                  euciocp = (struct eucioc *)mp->b_cont->b_rptr;
4509 4526                  cp_eucwioc(&tp->eucwioc, euciocp, EUCOUT);
4510 4527                  iocp->ioc_rval = 0;
4511 4528                  miocack(q, mp, EUCSIZE, 0);
4512 4529                  return;
4513 4530  
4514 4531          case CSDATA_SET:
4515 4532                  error = miocpullup(mp, sizeof (ldterm_cs_data_user_t));
4516 4533                  if (error != 0) {
4517 4534                          miocnak(q, mp, 0, error);
4518 4535                          return;
4519 4536                  }
4520 4537  
4521 4538                  csdp = (ldterm_cs_data_user_t *)mp->b_cont->b_rptr;
4522 4539  
4523 4540                  /* Validate the codeset data provided. */
4524 4541                  if (csdp->version > LDTERM_DATA_VERSION ||
4525 4542                      csdp->codeset_type < LDTERM_CS_TYPE_MIN ||
4526 4543                      csdp->codeset_type > LDTERM_CS_TYPE_MAX) {
4527 4544                          miocnak(q, mp, 0, ERANGE);
4528 4545                          return;
4529 4546                  }
4530 4547  
4531 4548                  if ((csdp->codeset_type == LDTERM_CS_TYPE_EUC &&
4532 4549                      csdp->csinfo_num > LDTERM_CS_TYPE_EUC_MAX_SUBCS) ||
4533 4550                      (csdp->codeset_type == LDTERM_CS_TYPE_PCCS &&
4534 4551                      (csdp->csinfo_num < LDTERM_CS_TYPE_PCCS_MIN_SUBCS ||
4535 4552                      csdp->csinfo_num > LDTERM_CS_TYPE_PCCS_MAX_SUBCS))) {
4536 4553                          miocnak(q, mp, 0, ERANGE);
4537 4554                          return;
4538 4555                  }
4539 4556  
4540 4557                  maxbytelen = maxscreenlen = 0;
4541 4558                  if (csdp->codeset_type == LDTERM_CS_TYPE_EUC) {
4542 4559                          for (i = 0; i < LDTERM_CS_TYPE_EUC_MAX_SUBCS; i++) {
4543 4560                                  if (csdp->eucpc_data[i].byte_length >
4544 4561                                      EUC_MAXW ||
4545 4562                                      csdp->eucpc_data[i].screen_width >
4546 4563                                      EUC_MAXW) {
4547 4564                                          miocnak(q, mp, 0, ERANGE);
4548 4565                                          return;
4549 4566                                  }
4550 4567  
4551 4568                                  if (csdp->eucpc_data[i].byte_length >
4552 4569                                      maxbytelen)
4553 4570                                          maxbytelen =
4554 4571                                              csdp->eucpc_data[i].byte_length;
4555 4572                                  if (csdp->eucpc_data[i].screen_width >
4556 4573                                      maxscreenlen)
4557 4574                                          maxscreenlen =
4558 4575                                              csdp->eucpc_data[i].screen_width;
4559 4576                          }
4560 4577                          /* POSIX/C locale? */
4561 4578                          if (maxbytelen == 0 && maxscreenlen == 0)
4562 4579                                  maxbytelen = maxscreenlen = 1;
4563 4580                  } else if (csdp->codeset_type == LDTERM_CS_TYPE_PCCS) {
4564 4581                          for (i = 0; i < LDTERM_CS_MAX_CODESETS; i++) {
4565 4582                                  if (csdp->eucpc_data[i].byte_length >
4566 4583                                      LDTERM_CS_MAX_BYTE_LENGTH) {
4567 4584                                          miocnak(q, mp, 0, ERANGE);
4568 4585                                          return;
4569 4586                                  }
4570 4587                                  if (csdp->eucpc_data[i].byte_length >
4571 4588                                      maxbytelen)
4572 4589                                          maxbytelen =
4573 4590                                              csdp->eucpc_data[i].byte_length;
4574 4591                                  if (csdp->eucpc_data[i].screen_width >
4575 4592                                      maxscreenlen)
4576 4593                                          maxscreenlen =
4577 4594                                              csdp->eucpc_data[i].screen_width;
4578 4595                          }
4579 4596                  } else if (csdp->codeset_type == LDTERM_CS_TYPE_UTF8) {
4580 4597                          maxbytelen = 4;
4581 4598                          maxscreenlen = 2;
4582 4599                  }
4583 4600  
4584 4601                  locale_name_sz = 0;
4585 4602                  if (csdp->locale_name) {
4586 4603                          for (i = 0; i < MAXNAMELEN; i++)
4587 4604                                  if (csdp->locale_name[i] == '\0')
4588 4605                                          break;
4589 4606                          /*
4590 4607                           * We cannot have any string that is not NULL byte
4591 4608                           * terminated.
4592 4609                           */
4593 4610                          if (i >= MAXNAMELEN) {
4594 4611                                  miocnak(q, mp, 0, ERANGE);
4595 4612                                  return;
4596 4613                          }
4597 4614  
4598 4615                          locale_name_sz = i + 1;
4599 4616                  }
4600 4617  
4601 4618                  /*
4602 4619                   * As the final check, if there was invalid codeset_type
4603 4620                   * given, or invalid byte_length was specified, it's an error.
4604 4621                   */
4605 4622                  if (maxbytelen <= 0 || maxscreenlen <= 0) {
4606 4623                          miocnak(q, mp, 0, ERANGE);
4607 4624                          return;
4608 4625                  }
4609 4626  
4610 4627                  /* Do the switching. */
4611 4628                  tp->t_maxeuc = maxbytelen;
4612 4629                  tp->t_state &= ~TS_MEUC;
4613 4630                  if (maxbytelen > 1 || maxscreenlen > 1) {
4614 4631                          if (!tp->t_eucp_mp) {
4615 4632                                  if (!(tp->t_eucp_mp = allocb(_TTY_BUFSIZ,
4616 4633                                      BPRI_HI))) {
4617 4634                                          cmn_err(CE_WARN,
4618 4635                                              "Can't allocate eucp_mp");
4619 4636                                          miocnak(q, mp, 0, ENOSR);
4620 4637                                          return;
4621 4638                                  }
4622 4639                                  /*
4623 4640                                   * If there's junk in the canonical buffer,
4624 4641                                   * then move the eucp pointer past it,
4625 4642                                   * so we don't run off the beginning. This is
4626 4643                                   * a total botch, but will hopefully keep
4627 4644                                   * stuff from getting too messed up until
4628 4645                                   * the user flushes this line!
4629 4646                                   */
4630 4647                                  if (tp->t_msglen) {
4631 4648                                          tp->t_eucp = tp->t_eucp_mp->b_rptr;
4632 4649                                          for (i = tp->t_msglen; i; i--)
4633 4650                                                  *tp->t_eucp++ = 1;
4634 4651                                  } else {
4635 4652                                          tp->t_eucp = tp->t_eucp_mp->b_rptr;
4636 4653                                  }
4637 4654                          }
4638 4655  
4639 4656                          /*
4640 4657                           * We only set TS_MEUC for a multibyte/multi-column
4641 4658                           * codeset.
4642 4659                           */
4643 4660                          tp->t_state |= TS_MEUC;
4644 4661  
4645 4662                          tp->t_csdata.version = csdp->version;
4646 4663                          tp->t_csdata.codeset_type = csdp->codeset_type;
4647 4664                          tp->t_csdata.csinfo_num = csdp->csinfo_num;
4648 4665                          bcopy(csdp->eucpc_data, tp->t_csdata.eucpc_data,
4649 4666                              sizeof (ldterm_eucpc_data_t) *
4650 4667                              LDTERM_CS_MAX_CODESETS);
4651 4668                          tp->t_csmethods = cs_methods[csdp->codeset_type];
4652 4669  
4653 4670                          if (csdp->codeset_type == LDTERM_CS_TYPE_EUC) {
4654 4671                                  tp->eucwioc.eucw[0] = 1;
4655 4672                                  tp->eucwioc.scrw[0] = 1;
4656 4673  
4657 4674                                  tp->eucwioc.eucw[1] =
4658 4675                                      csdp->eucpc_data[0].byte_length;
4659 4676                                  tp->eucwioc.scrw[1] =
4660 4677                                      csdp->eucpc_data[0].screen_width;
4661 4678  
4662 4679                                  tp->eucwioc.eucw[2] =
4663 4680                                      csdp->eucpc_data[1].byte_length + 1;
4664 4681                                  tp->eucwioc.scrw[2] =
4665 4682                                      csdp->eucpc_data[1].screen_width;
4666 4683  
4667 4684                                  tp->eucwioc.eucw[3] =
4668 4685                                      csdp->eucpc_data[2].byte_length + 1;
4669 4686                                  tp->eucwioc.scrw[3] =
4670 4687                                      csdp->eucpc_data[2].screen_width;
4671 4688                          } else {
4672 4689                                  /*
4673 4690                                   * We are not going to use this data
4674 4691                                   * structure. So, clear it. Also, stty(1) will
4675 4692                                   * make use of the cleared tp->eucwioc when
4676 4693                                   * it prints out codeset width setting.
4677 4694                                   */
4678 4695                                  bzero(&tp->eucwioc, EUCSIZE);
4679 4696                          }
4680 4697                  } else {
4681 4698                          /*
4682 4699                           * If this codeset is a single byte codeset that
4683 4700                           * requires only single display column for all
4684 4701                           * characters, we switch to default EUC codeset
4685 4702                           * methods and data setting.
4686 4703                           */
4687 4704  
4688 4705                          if (tp->t_eucp_mp) {
4689 4706                                  freemsg(tp->t_eucp_mp);
4690 4707                                  tp->t_eucp_mp = NULL;
4691 4708                                  tp->t_eucp = NULL;
4692 4709                          }
4693 4710  
4694 4711                          bzero(&tp->eucwioc, EUCSIZE);
4695 4712                          tp->eucwioc.eucw[0] = 1;
4696 4713                          tp->eucwioc.scrw[0] = 1;
4697 4714                          if (tp->t_csdata.locale_name != (char *)NULL) {
4698 4715                                  kmem_free(tp->t_csdata.locale_name,
4699 4716                                      strlen(tp->t_csdata.locale_name) + 1);
4700 4717                          }
4701 4718                          tp->t_csdata = default_cs_data;
4702 4719                          tp->t_csmethods = cs_methods[LDTERM_CS_TYPE_EUC];
4703 4720                  }
4704 4721  
4705 4722                  /* Copy over locale_name. */
4706 4723                  if (tp->t_csdata.locale_name != (char *)NULL) {
4707 4724                          kmem_free(tp->t_csdata.locale_name,
4708 4725                              strlen(tp->t_csdata.locale_name) + 1);
4709 4726                  }
4710 4727                  if (locale_name_sz > 1) {
4711 4728                          tp->t_csdata.locale_name = (char *)kmem_alloc(
4712 4729                              locale_name_sz, KM_SLEEP);
4713 4730                          (void) strcpy(tp->t_csdata.locale_name,
4714 4731                              csdp->locale_name);
4715 4732                  } else {
4716 4733                          tp->t_csdata.locale_name = (char *)NULL;
4717 4734                  }
4718 4735  
4719 4736                  /*
4720 4737                   * Now ACK the ioctl.
4721 4738                   */
4722 4739                  iocp->ioc_rval = 0;
4723 4740                  miocack(q, mp, 0, 0);
4724 4741                  return;
4725 4742  
4726 4743          case CSDATA_GET:
4727 4744                  error = miocpullup(mp, sizeof (ldterm_cs_data_user_t));
4728 4745                  if (error != 0) {
4729 4746                          miocnak(q, mp, 0, error);
4730 4747                          return;
4731 4748                  }
4732 4749  
4733 4750                  csdp = (ldterm_cs_data_user_t *)mp->b_cont->b_rptr;
4734 4751  
4735 4752                  csdp->version = tp->t_csdata.version;
4736 4753                  csdp->codeset_type = tp->t_csdata.codeset_type;
4737 4754                  csdp->csinfo_num = tp->t_csdata.csinfo_num;
4738 4755                  csdp->pad = tp->t_csdata.pad;
4739 4756                  if (tp->t_csdata.locale_name) {
4740 4757                          (void) strcpy(csdp->locale_name,
4741 4758                              tp->t_csdata.locale_name);
4742 4759                  } else {
4743 4760                          csdp->locale_name[0] = '\0';
4744 4761                  }
4745 4762                  bcopy(tp->t_csdata.eucpc_data, csdp->eucpc_data,
4746 4763                      sizeof (ldterm_eucpc_data_t) * LDTERM_CS_MAX_CODESETS);
4747 4764                  /*
4748 4765                   * If the codeset is an EUC codeset and if it has 2nd and/or
4749 4766                   * 3rd supplementary codesets, we subtract one from each
4750 4767                   * byte length of the supplementary codesets. This is
4751 4768                   * because single shift characters, SS2 and SS3, are not
4752 4769                   * included in the byte lengths in the user space.
4753 4770                   */
4754 4771                  if (csdp->codeset_type == LDTERM_CS_TYPE_EUC) {
4755 4772                          if (csdp->eucpc_data[1].byte_length)
4756 4773                                  csdp->eucpc_data[1].byte_length -= 1;
4757 4774                          if (csdp->eucpc_data[2].byte_length)
4758 4775                                  csdp->eucpc_data[2].byte_length -= 1;
4759 4776                  }
4760 4777                  iocp->ioc_rval = 0;
4761 4778                  miocack(q, mp, sizeof (ldterm_cs_data_user_t), 0);
4762 4779                  return;
4763 4780  
4764 4781          case PTSSTTY:
4765 4782                  tp->t_state |= TS_ISPTSTTY;
4766 4783                  break;
4767 4784  
4768 4785          }
4769 4786  
4770 4787          putnext(q, mp);
4771 4788  }
4772 4789  
4773 4790  
4774 4791  /*
4775 4792   * Send an M_SETOPTS message upstream if any mode changes are being
4776 4793   * made that affect the stream head options. returns -1 if allocb
4777 4794   * fails, else returns 0.
4778 4795   */
4779 4796  static int
4780 4797  chgstropts(struct termios *oldmodep, ldtermstd_state_t *tp, queue_t *q)
4781 4798  {
4782 4799          struct stroptions optbuf;
4783 4800          mblk_t *bp;
4784 4801  
4785 4802          optbuf.so_flags = 0;
4786 4803          if ((oldmodep->c_lflag ^ tp->t_modes.c_lflag) & ICANON) {
4787 4804                  /*
4788 4805                   * Canonical mode is changing state; switch the
4789 4806                   * stream head to message-nondiscard or byte-stream
4790 4807                   * mode.  Also, rerun the service procedure so it can
4791 4808                   * change its mind about whether to send data
4792 4809                   * upstream or not.
4793 4810                   */
4794 4811                  if (tp->t_modes.c_lflag & ICANON) {
4795 4812                          DEBUG4(("CHANGING TO CANON MODE\n"));
4796 4813                          optbuf.so_flags = SO_READOPT|SO_MREADOFF;
4797 4814                          optbuf.so_readopt = RMSGN;
4798 4815  
4799 4816                          /*
4800 4817                           * if there is a pending raw mode timeout,
4801 4818                           * clear it
4802 4819                           */
4803 4820  
4804 4821                          /*
4805 4822                           * Clear VMIN/VTIME state, cancel timers
4806 4823                           */
4807 4824                          vmin_satisfied(q, tp, 0);
4808 4825                  } else {
4809 4826                          DEBUG4(("CHANGING TO RAW MODE\n"));
4810 4827                          optbuf.so_flags = SO_READOPT|SO_MREADON;
4811 4828                          optbuf.so_readopt = RNORM;
4812 4829                  }
4813 4830          }
4814 4831          if ((oldmodep->c_lflag ^ tp->t_modes.c_lflag) & TOSTOP) {
4815 4832                  /*
4816 4833                   * The "stop on background write" bit is changing.
4817 4834                   */
4818 4835                  if (tp->t_modes.c_lflag & TOSTOP)
4819 4836                          optbuf.so_flags |= SO_TOSTOP;
4820 4837                  else
4821 4838                          optbuf.so_flags |= SO_TONSTOP;
4822 4839          }
4823 4840          if (optbuf.so_flags != 0) {
4824 4841                  if ((bp = allocb(sizeof (struct stroptions), BPRI_HI)) ==
4825 4842                      NULL) {
4826 4843                          return (-1);
4827 4844                  }
4828 4845                  *(struct stroptions *)bp->b_wptr = optbuf;
4829 4846                  bp->b_wptr += sizeof (struct stroptions);
4830 4847                  bp->b_datap->db_type = M_SETOPTS;
4831 4848                  DEBUG4(("M_SETOPTS to stream head\n"));
4832 4849                  putnext(q, bp);
4833 4850          }
4834 4851          return (0);
4835 4852  }
4836 4853  
4837 4854  
4838 4855  /*
4839 4856   * Called when an M_IOCACK message is seen on the read queue;
4840 4857   * modifies the data being returned, if necessary, and passes the
4841 4858   * reply up.
4842 4859   */
4843 4860  static void
4844 4861  ldterm_ioctl_reply(queue_t *q, mblk_t *mp)
4845 4862  {
4846 4863          ldtermstd_state_t *tp;
4847 4864          struct iocblk *iocp;
4848 4865  
4849 4866          iocp = (struct iocblk *)mp->b_rptr;
4850 4867          tp = (ldtermstd_state_t *)q->q_ptr;
4851 4868  
4852 4869          switch (iocp->ioc_cmd) {
4853 4870  
4854 4871          case TCGETS:
4855 4872                  {
4856 4873                          /*
4857 4874                           * Get current parameters and return them to
4858 4875                           * stream head eventually.
4859 4876                           */
4860 4877                          struct termios *cb =
4861 4878                              (struct termios *)mp->b_cont->b_rptr;
4862 4879  
4863 4880                          /*
4864 4881                           * cflag has cflags sent upstream by the
4865 4882                           * driver
4866 4883                           */
4867 4884                          tcflag_t cflag = cb->c_cflag;
4868 4885  
4869 4886                          *cb = tp->t_amodes;
4870 4887                          if (cflag != 0)
4871 4888                                  cb->c_cflag = cflag;    /* set by driver */
4872 4889                          break;
4873 4890                  }
4874 4891  
4875 4892          case TCGETA:
4876 4893                  {
4877 4894                          /*
4878 4895                           * Old-style "ioctl" to get current
4879 4896                           * parameters and return them to stream head
4880 4897                           * eventually.
4881 4898                           */
4882 4899                          struct termio *cb =
4883 4900                              (struct termio *)mp->b_cont->b_rptr;
4884 4901  
4885 4902                          cb->c_iflag = tp->t_amodes.c_iflag; /* all except the */
4886 4903                          cb->c_oflag = tp->t_amodes.c_oflag; /* cb->c_cflag */
4887 4904                          cb->c_lflag = tp->t_amodes.c_lflag;
4888 4905  
4889 4906                          if (cb->c_cflag == 0)   /* not set by driver */
4890 4907                                  cb->c_cflag = tp->t_amodes.c_cflag;
4891 4908  
4892 4909                          cb->c_line = 0;
4893 4910                          bcopy(tp->t_amodes.c_cc, cb->c_cc, NCC);
4894 4911                          break;
4895 4912                  }
4896 4913          }
4897 4914          putnext(q, mp);
4898 4915  }
4899 4916  
4900 4917  
4901 4918  /*
4902 4919   * A VMIN/VTIME request has been satisfied. Cancel outstanding timers
4903 4920   * if they exist, clear TS_MREAD state, and send upstream. If a NULL
4904 4921   * queue ptr is passed, just reset VMIN/VTIME state.
4905 4922   */
4906 4923  static void
4907 4924  vmin_satisfied(queue_t *q, ldtermstd_state_t *tp, int sendup)
4908 4925  {
4909 4926          ASSERT(q);
4910 4927          if (tp->t_vtid != 0)  {
4911 4928                  DEBUG4(("vmin_satisfied: cancelled timer id %d\n", tp->t_vtid));
4912 4929                  (void) quntimeout(q, tp->t_vtid);
4913 4930                  tp->t_vtid = 0;
4914 4931          }
4915 4932          if (sendup) {
4916 4933                  if (tp->t_msglen == 0 && V_MIN) {
4917 4934                          /* EMPTY */
4918 4935                          DEBUG4(("vmin_satisfied: data swiped, msglen = 0\n"));
4919 4936                  } else {
4920 4937                          if ((!q->q_first) ||
4921 4938                              (q->q_first->b_datap->db_type != M_DATA) ||
4922 4939                              (tp->t_msglen >= LDCHUNK)) {
4923 4940                                  ldterm_msg_upstream(q, tp);
4924 4941                                  DEBUG4(("vmin_satisfied: delivering data\n"));
4925 4942                          }
4926 4943                  }
4927 4944          } else {
4928 4945                  /* EMPTY */
4929 4946                  DEBUG4(("vmin_satisfied: VMIN/TIME state reset\n"));
4930 4947          }
4931 4948          tp->t_state &= ~TS_MREAD;
4932 4949  }
4933 4950  
4934 4951  static void
4935 4952  vmin_settimer(queue_t *q)
4936 4953  {
4937 4954          ldtermstd_state_t *tp;
4938 4955  
4939 4956          tp = (ldtermstd_state_t *)q->q_ptr;
4940 4957  
4941 4958          /*
4942 4959           * Don't start any time bombs.
4943 4960           */
4944 4961          if (tp->t_state & TS_CLOSE)
4945 4962                  return;
4946 4963  
4947 4964          /*
4948 4965           * tp->t_vtid should NOT be set here unless VMIN > 0 and
4949 4966           * VTIME > 0.
4950 4967           */
4951 4968          if (tp->t_vtid) {
4952 4969                  if (V_MIN && V_TIME) {
4953 4970                          /* EMPTY */
4954 4971                          DEBUG4(("vmin_settimer: timer restarted, old tid=%d\n",
4955 4972                              tp->t_vtid));
4956 4973                  } else {
4957 4974                          /* EMPTY */
4958 4975                          DEBUG4(("vmin_settimer: tid = %d was still active!\n",
4959 4976                              tp->t_vtid));
4960 4977                  }
4961 4978                  (void) quntimeout(q, tp->t_vtid);
4962 4979                  tp->t_vtid = 0;
4963 4980          }
4964 4981          tp->t_vtid = qtimeout(q, vmin_timed_out, q,
4965 4982              (clock_t)(V_TIME * (hz / 10)));
4966 4983          DEBUG4(("vmin_settimer: timer started, tid = %d\n", tp->t_vtid));
4967 4984  }
4968 4985  
4969 4986  
4970 4987  /*
4971 4988   * BRRrrringgg!! VTIME was satisfied instead of VMIN
4972 4989   */
4973 4990  static void
4974 4991  vmin_timed_out(void *arg)
4975 4992  {
4976 4993          queue_t *q = arg;
4977 4994          ldtermstd_state_t *tp;
4978 4995  
4979 4996          tp = (ldtermstd_state_t *)q->q_ptr;
4980 4997  
4981 4998          DEBUG4(("vmin_timed_out: tid = %d\n", tp->t_vtid));
4982 4999          /* don't call untimeout now that we are in the timeout */
4983 5000          tp->t_vtid = 0;
4984 5001          vmin_satisfied(q, tp, 1);
4985 5002  }
4986 5003  
4987 5004  
4988 5005  /*
4989 5006   * Routine to adjust termios flags to be processed by the line
4990 5007   * discipline. Driver below sends a termios structure, with the flags
4991 5008   * the driver intends to process. XOR'ing the driver sent termios
4992 5009   * structure with current termios structure with the default values
4993 5010   * (or set by ioctls from userland), we come up with a new termios
4994 5011   * structrue, the flags of which will be used by the line discipline
4995 5012   * in processing input and output. On return from this routine, we
4996 5013   * will have the following fields set in tp structure -->
4997 5014   * tp->t_modes: modes the line discipline will process tp->t_amodes:
4998 5015   * modes the user process thinks the line discipline is processing
4999 5016   */
5000 5017  
5001 5018  static void
5002 5019  ldterm_adjust_modes(ldtermstd_state_t *tp)
5003 5020  {
5004 5021  
5005 5022          DEBUG6(("original iflag = %o\n", tp->t_modes.c_iflag));
5006 5023          tp->t_modes.c_iflag = tp->t_amodes.c_iflag & ~(tp->t_dmodes.c_iflag);
5007 5024          tp->t_modes.c_oflag = tp->t_amodes.c_oflag & ~(tp->t_dmodes.c_oflag);
5008 5025          tp->t_modes.c_lflag = tp->t_amodes.c_lflag & ~(tp->t_dmodes.c_lflag);
5009 5026          DEBUG6(("driver iflag = %o\n", tp->t_dmodes.c_iflag));
5010 5027          DEBUG6(("apparent iflag = %o\n", tp->t_amodes.c_iflag));
5011 5028          DEBUG6(("effective iflag = %o\n", tp->t_modes.c_iflag));
5012 5029  
5013 5030          /* No negotiation of clfags  c_cc array special characters */
5014 5031          /*
5015 5032           * Copy from amodes to modes already done by TCSETA/TCSETS
5016 5033           * code
5017 5034           */
5018 5035  }
5019 5036  
5020 5037  
5021 5038  /*
5022 5039   * Erase one multi-byte character.  If TS_MEUC is set AND this
5023 5040   * is a multi-byte character, then this should be called instead of
5024 5041   * ldterm_erase.  "ldterm_erase" will handle ASCII nicely, thank you.
5025 5042   *
5026 5043   * We'd better be pointing to the last byte.  If we aren't, it will get
5027 5044   * screwed up.
5028 5045   */
5029 5046  static void
5030 5047  ldterm_csi_erase(queue_t *q, size_t ebsize, ldtermstd_state_t *tp)
5031 5048  {
5032 5049          int i, ung;
5033 5050          uchar_t *p, *bottom;
5034 5051          uchar_t u8[LDTERM_CS_MAX_BYTE_LENGTH];
5035 5052          int c;
5036 5053          int j;
5037 5054          int len;
5038 5055  
5039 5056          if (tp->t_eucleft) {
5040 5057                  /* XXX Ick.  We're in the middle of an EUC! */
5041 5058                  /* What to do now? */
5042 5059                  ldterm_eucwarn(tp);
5043 5060                  return;         /* ignore it??? */
5044 5061          }
5045 5062          bottom = tp->t_eucp_mp->b_rptr;
5046 5063          p = tp->t_eucp - 1;     /* previous byte */
5047 5064          if (p < bottom)
5048 5065                  return;
5049 5066          ung = 1;                /* number of bytes to un-get from buffer */
5050 5067          /*
5051 5068           * go through the buffer until we find the beginning of the
5052 5069           * multi-byte char.
5053 5070           */
5054 5071          while ((*p == 0) && (p > bottom)) {
5055 5072                  p--;
5056 5073                  ++ung;
5057 5074          }
5058 5075  
5059 5076          /*
5060 5077           * Now, "ung" is the number of bytes to unget from the buffer
5061 5078           * and "*p" is the disp width of it. Fool "ldterm_rubout"
5062 5079           * into thinking we're rubbing out ASCII characters.  Do that
5063 5080           * for the display width of the character.
5064 5081           *
5065 5082           * Also we accumulate bytes of the character so that if the character
5066 5083           * is a UTF-8 character, we will get the display width of the UTF-8
5067 5084           * character.
5068 5085           */
5069 5086          if (ung >= LDTERM_CS_MAX_BYTE_LENGTH) {
5070 5087                  j = len = LDTERM_CS_MAX_BYTE_LENGTH;
5071 5088          } else {
5072 5089                  j = len = ung;
5073 5090          }
5074 5091          for (i = 0; i < ung; i++) {     /* remove from buf */
5075 5092                  if ((c = ldterm_unget(tp)) != (-1)) {
5076 5093                          ldterm_trim(tp);
5077 5094                          if (j > 0)
5078 5095                                  u8[--j] = (uchar_t)c;
5079 5096                  }
5080 5097          }
5081 5098          if (*p == UNKNOWN_WIDTH) {
5082 5099                  if (tp->t_csdata.codeset_type == LDTERM_CS_TYPE_UTF8) {
5083 5100                          *p = ldterm_utf8_width(u8, len);
5084 5101                  } else {
5085 5102                          *p = 1;
5086 5103                  }
5087 5104          }
5088 5105          for (i = 0; i < (int)*p; i++)   /* remove from screen */
5089 5106                  ldterm_rubout(' ', q, ebsize, tp);
5090 5107          /*
5091 5108           * Adjust the parallel array pointer.  Zero out the contents
5092 5109           * of parallel array for this position, just to make sure...
5093 5110           */
5094 5111          tp->t_eucp = p;
5095 5112          *p = 0;
5096 5113  }
5097 5114  
5098 5115  
5099 5116  /*
5100 5117   * This is kind of a safety valve.  Whenever we see a bad sequence
5101 5118   * come up, we call eucwarn.  It just tallies the junk until a
5102 5119   * threshold is reached.  Then it prints ONE message on the console
5103 5120   * and not any more. Hopefully, we can catch garbage; maybe it will
5104 5121   * be useful to somebody.
5105 5122   */
5106 5123  static void
5107 5124  ldterm_eucwarn(ldtermstd_state_t *tp)
5108 5125  {
5109 5126          ++tp->t_eucwarn;
5110 5127  #ifdef DEBUG
5111 5128          if ((tp->t_eucwarn > EUC_WARNCNT) && !(tp->t_state & TS_WARNED)) {
5112 5129                  cmn_err(CE_WARN,
5113 5130                      "ldterm: tty at addr %p in multi-byte mode --",
5114 5131                      (void *)tp);
5115 5132                  cmn_err(CE_WARN,
5116 5133                      "Over %d bad EUC characters this session", EUC_WARNCNT);
5117 5134                  tp->t_state |= TS_WARNED;
5118 5135          }
5119 5136  #endif
5120 5137  }
5121 5138  
5122 5139  
5123 5140  /*
5124 5141   * Copy an "eucioc_t" structure.  We use the structure with
5125 5142   * incremented values for Codesets 2 & 3.  The specification in
5126 5143   * eucioctl is that the sames values as the CSWIDTH definition at
5127 5144   * user level are passed to us. When we copy it "in" to ourselves, we
5128 5145   * do the increment.  That allows us to avoid treating each character
5129 5146   * set separately for "t_eucleft" purposes. When we copy it "out" to
5130 5147   * return it to the user, we decrement the values so the user gets
5131 5148   * what it expects, and it matches CSWIDTH in the environment (if
5132 5149   * things are consistent!).
5133 5150   */
5134 5151  static void
5135 5152  cp_eucwioc(eucioc_t *from, eucioc_t *to, int dir)
5136 5153  {
5137 5154          bcopy(from, to, EUCSIZE);
5138 5155          if (dir == EUCOUT) {    /* copying out to user */
5139 5156                  if (to->eucw[2])
5140 5157                          --to->eucw[2];
5141 5158                  if (to->eucw[3])
5142 5159                          --to->eucw[3];
5143 5160          } else {                /* copying in */
5144 5161                  if (to->eucw[2])
5145 5162                          ++to->eucw[2];
5146 5163                  if (to->eucw[3])
5147 5164                          ++to->eucw[3];
5148 5165          }
5149 5166  }
5150 5167  
5151 5168  
5152 5169  /*
5153 5170   * Take the first byte of a multi-byte, or an ASCII char.  Return its
5154 5171   * codeset. If it's NOT the first byte of an EUC, then the return
5155 5172   * value may be garbage, as it's probably not SS2 or SS3, and
5156 5173   * therefore must be in codeset 1.  Another bizarre catch here is the
5157 5174   * fact that we don't do anything about the "C1" control codes.  In
5158 5175   * real life, we should; but nobody's come up with a good way of
5159 5176   * treating them.
5160 5177   */
5161 5178  
5162 5179  static int
5163 5180  ldterm_codeset(uchar_t codeset_type, uchar_t c)
5164 5181  {
5165 5182  
5166 5183          if (ISASCII(c))
5167 5184                  return (0);
5168 5185  
5169 5186          if (codeset_type != LDTERM_CS_TYPE_EUC)
5170 5187                  return (1);
5171 5188  
5172 5189          switch (c) {
5173 5190          case SS2:
5174 5191                  return (2);
5175 5192          case SS3:
5176 5193                  return (3);
5177 5194          default:
5178 5195                  return (1);
5179 5196          }
5180 5197  }
5181 5198  
5182 5199  /* The following two functions are additional EUC codeset specific methods. */
5183 5200  /*
5184 5201   * ldterm_dispwidth - Take the first byte of an EUC (or ASCII) and
5185 5202   * return the display width.  Since this is intended mostly for
5186 5203   * multi-byte handling, it returns EUC_TWIDTH for tabs so they can be
5187 5204   * differentiated from EUC characters (assumption: EUC require fewer
5188 5205   * than 255 columns).  Also, if it's a backspace and !flag, it
5189 5206   * returns EUC_BSWIDTH.  Newline & CR also depend on flag.  This
5190 5207   * routine SHOULD be cleaner than this, but we have the situation
5191 5208   * where we may or may not be counting control characters as having a
5192 5209   * column width. Therefore, the computation of ASCII is pretty messy.
5193 5210   * The caller will be storing the value, and then switching on it
5194 5211   * when it's used.  We really should define the EUC_TWIDTH and other
5195 5212   * constants in a header so that the routine could be used in other
5196 5213   * modules in the kernel.
5197 5214   */
5198 5215  static int
5199 5216  __ldterm_dispwidth_euc(uchar_t c, void *p, int mode)
5200 5217  {
5201 5218          ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
5202 5219  
5203 5220          if (ISASCII(c)) {
5204 5221                  if (c <= '\037') {
5205 5222                          switch (c) {
5206 5223                          case '\t':
5207 5224                                  return (EUC_TWIDTH);
5208 5225                          case '\b':
5209 5226                                  return (mode ? 2 : EUC_BSWIDTH);
5210 5227                          case '\n':
5211 5228                                  return (EUC_NLWIDTH);
5212 5229                          case '\r':
5213 5230                                  return (mode ? 2 : EUC_CRWIDTH);
5214 5231                          default:
5215 5232                                  return (mode ? 2 : 0);
5216 5233                          }
5217 5234                  }
5218 5235                  return (1);
5219 5236          }
5220 5237          switch (c) {
5221 5238          case SS2:
5222 5239                  return (tp->eucwioc.scrw[2]);
5223 5240          case SS3:
5224 5241                  return (tp->eucwioc.scrw[3]);
5225 5242          default:
5226 5243                  return (tp->eucwioc.scrw[1]);
5227 5244          }
5228 5245  }
5229 5246  
5230 5247  /*
5231 5248   * ldterm_memwidth_euc - Take the first byte of an EUC (or an ASCII char)
5232 5249   * and return its memory width.  The routine could have been
5233 5250   * implemented to use only the codeset number, but that would require
5234 5251   * the caller to have that value available.  Perhaps the user doesn't
5235 5252   * want to make the extra call or keep the value of codeset around.
5236 5253   * Therefore, we use the actual character with which they're
5237 5254   * concerned.  This should never be called with anything but the
5238 5255   * first byte of an EUC, otherwise it will return a garbage value.
5239 5256   */
5240 5257  static int
5241 5258  __ldterm_memwidth_euc(uchar_t c, void *p)
5242 5259  {
5243 5260          ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
5244 5261  
5245 5262          if (ISASCII(c))
5246 5263                  return (1);
5247 5264          switch (c) {
5248 5265          case SS2:
5249 5266                  return (tp->eucwioc.eucw[2]);
5250 5267          case SS3:
5251 5268                  return (tp->eucwioc.eucw[3]);
5252 5269          default:
5253 5270                  return (tp->eucwioc.eucw[1]);
5254 5271          }
5255 5272  }
5256 5273  
5257 5274  
5258 5275  /* The following two functions are PCCS codeset specific methods. */
5259 5276  static int
5260 5277  __ldterm_dispwidth_pccs(uchar_t c, void *p, int mode)
5261 5278  {
5262 5279          ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
5263 5280          int i;
5264 5281  
5265 5282          if (ISASCII(c)) {
5266 5283                  if (c <= '\037') {
5267 5284                          switch (c) {
5268 5285                          case '\t':
5269 5286                                  return (EUC_TWIDTH);
5270 5287                          case '\b':
5271 5288                                  return (mode ? 2 : EUC_BSWIDTH);
5272 5289                          case '\n':
5273 5290                                  return (EUC_NLWIDTH);
5274 5291                          case '\r':
5275 5292                                  return (mode ? 2 : EUC_CRWIDTH);
5276 5293                          default:
5277 5294                                  return (mode ? 2 : 0);
5278 5295                          }
5279 5296                  }
5280 5297                  return (1);
5281 5298          }
5282 5299  
5283 5300          for (i = 0; i < tp->t_csdata.csinfo_num; i++) {
5284 5301                  if (c >= tp->t_csdata.eucpc_data[i].msb_start &&
5285 5302                      c <= tp->t_csdata.eucpc_data[i].msb_end)
5286 5303                          return (tp->t_csdata.eucpc_data[i].screen_width);
5287 5304          }
5288 5305  
5289 5306          /*
5290 5307           * If this leading byte is not in the range list, either provided
5291 5308           * locale data is not sufficient or we encountered an invalid
5292 5309           * character. We return 1 in this case as a fallback value.
5293 5310           */
5294 5311          return (1);
5295 5312  }
5296 5313  
5297 5314  static int
5298 5315  __ldterm_memwidth_pccs(uchar_t c, void *p)
5299 5316  {
5300 5317          ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
5301 5318          int i;
5302 5319  
5303 5320          for (i = 0; i < tp->t_csdata.csinfo_num; i++) {
5304 5321                  if (c >= tp->t_csdata.eucpc_data[i].msb_start &&
5305 5322                      c <= tp->t_csdata.eucpc_data[i].msb_end)
5306 5323                          return (tp->t_csdata.eucpc_data[i].byte_length);
5307 5324          }
5308 5325  
5309 5326          /*
5310 5327           * If this leading byte is not in the range list, either provided
5311 5328           * locale data is not sufficient or we encountered an invalid
5312 5329           * character. We return 1 in this case as a fallback value.
5313 5330           */
5314 5331          return (1);
5315 5332  }
5316 5333  
5317 5334  
5318 5335  /* The following two functions are UTF-8 codeset specific methods. */
5319 5336  static int
5320 5337  __ldterm_dispwidth_utf8(uchar_t c, void *p, int mode)
5321 5338  {
5322 5339          ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
5323 5340  
5324 5341          if (ISASCII(c)) {
5325 5342                  if (c <= '\037') {
5326 5343                          switch (c) {
5327 5344                          case '\t':
5328 5345                                  return (EUC_TWIDTH);
5329 5346                          case '\b':
5330 5347                                  return (mode ? 2 : EUC_BSWIDTH);
5331 5348                          case '\n':
5332 5349                                  return (EUC_NLWIDTH);
5333 5350                          case '\r':
5334 5351                                  return (mode ? 2 : EUC_CRWIDTH);
5335 5352                          default:
5336 5353                                  return (mode ? 2 : 0);
5337 5354                          }
5338 5355                  }
5339 5356                  return (1);
5340 5357          }
5341 5358  
5342 5359          /* This is to silence the lint. */
5343 5360          if (tp->t_csdata.codeset_type != LDTERM_CS_TYPE_UTF8)
5344 5361                  return (1);
5345 5362  
5346 5363          /*
5347 5364           * If it is a valid leading byte of a UTF-8 character, we set
5348 5365           * the width as 'UNKNOWN_WIDTH' for now. We need to have all
5349 5366           * the bytes to figure out the display width.
5350 5367           */
5351 5368          if (c >= (uchar_t)0xc0 && c <= (uchar_t)0xfd)
5352 5369                  return (UNKNOWN_WIDTH);
5353 5370  
5354 5371          /*
5355 5372           * If it is an invalid leading byte, we just do our best by
5356 5373           * giving the display width of 1.
5357 5374           */
5358 5375          return (1);
5359 5376  }
5360 5377  
5361 5378  
5362 5379  static int
5363 5380  __ldterm_memwidth_utf8(uchar_t c, void *p)
5364 5381  {
5365 5382          ldtermstd_state_t *tp = (ldtermstd_state_t *)p;
5366 5383          int len;
5367 5384  
5368 5385          /*
5369 5386           * If the codeset type doesn't match, we treat them as
5370 5387           * an illegal character and return 1.
5371 5388           */
5372 5389          if (tp->t_csdata.codeset_type != LDTERM_CS_TYPE_UTF8)
5373 5390                  return (1);
5374 5391  
5375 5392          len = u8_number_of_bytes[c];
5376 5393  
5377 5394          /*
5378 5395           * If this is a start of an illegal character, we treat
5379 5396           * such as an 1 byte character and screen out.
5380 5397           */
5381 5398          return ((len <= 0) ? 1 : len);
5382 5399  }
5383 5400  
5384 5401  static uchar_t
5385 5402  ldterm_utf8_width(uchar_t *u8, int length)
5386 5403  {
5387 5404          int i;
5388 5405          int j;
5389 5406          uint_t intcode = 0;
5390 5407  
5391 5408          if (length == 0)
5392 5409                  return ('\0');
5393 5410  
5394 5411          j = u8_number_of_bytes[u8[0]] - 1;
5395 5412  
5396 5413          /*
5397 5414           * If the UTF-8 character is out of UTF-16 code range, or,
5398 5415           * if it is either an ASCII character or an invalid leading byte for
5399 5416           * a UTF-8 character, return 1.
5400 5417           */
5401 5418          if (length > 4 || j <= 0)
5402 5419                  return ('\1');
5403 5420  
5404 5421          intcode = u8[0] & u8_masks_tbl[j];
5405 5422          for (i = 1; j > 0; j--, i++) {
5406 5423                  /*
5407 5424                   * The following additional checking is needed to conform to
5408 5425                   * the "UTF-8 Corrigendum" introduced at the Unicode 3.1 and
5409 5426                   * then updated one more time at the Unicode 3.2.
5410 5427                   */
5411 5428                  if (i == 1) {
5412 5429                          if (u8[i] < u8_valid_min_2nd_byte[u8[0]] ||
5413 5430                              u8[i] > u8_valid_max_2nd_byte[u8[0]])
5414 5431                                  return ('\1');
5415 5432                  } else if (u8[i] < (uchar_t)LDTERM_CS_TYPE_UTF8_MIN_BYTE ||
5416 5433                      u8[i] > (uchar_t)LDTERM_CS_TYPE_UTF8_MAX_BYTE)
5417 5434                          return ('\1');
5418 5435  
5419 5436                  /*
5420 5437                   * All subsequent bytes of UTF-8 character has the following
5421 5438                   * binary encoding:
5422 5439                   *
5423 5440                   * 10xx xxxx
5424 5441                   *
5425 5442                   * hence left shift six bits to make space and then get
5426 5443                   * six bits from the new byte.
5427 5444                   */
5428 5445                  intcode = (intcode << LDTERM_CS_TYPE_UTF8_SHIFT_BITS) |
5429 5446                      (u8[i] & LDTERM_CS_TYPE_UTF8_BIT_MASK);
5430 5447          }
5431 5448  
5432 5449          i = 0;
5433 5450          if (intcode <= LDTERM_CS_TYPE_UTF8_MAX_P00) {
5434 5451                  /* Basic Multilingual Plane. */
5435 5452                  i = intcode / 4;
5436 5453                  j = intcode % 4;
5437 5454                  switch (j) {
5438 5455                  case 0:
5439 5456                          i = ldterm_ucode[0][i].u0;
5440 5457                          break;
5441 5458                  case 1:
5442 5459                          i = ldterm_ucode[0][i].u1;
5443 5460                          break;
5444 5461                  case 2:
5445 5462                          i = ldterm_ucode[0][i].u2;
5446 5463                          break;
5447 5464                  case 3:
5448 5465                          i = ldterm_ucode[0][i].u3;
5449 5466                          break;
5450 5467                  }
5451 5468          } else if (intcode <= LDTERM_CS_TYPE_UTF8_MAX_P01) {
5452 5469                  /* Secondary Multilingual Plane. */
5453 5470                  intcode = intcode & (uint_t)0xffff;
5454 5471                  i = intcode / 4;
5455 5472                  j = intcode % 4;
5456 5473                  switch (j) {
5457 5474                  case 0:
5458 5475                          i = ldterm_ucode[1][i].u0;
5459 5476                          break;
5460 5477                  case 1:
5461 5478                          i = ldterm_ucode[1][i].u1;
5462 5479                          break;
5463 5480                  case 2:
5464 5481                          i = ldterm_ucode[1][i].u2;
5465 5482                          break;
5466 5483                  case 3:
5467 5484                          i = ldterm_ucode[1][i].u3;
5468 5485                          break;
5469 5486                  }
5470 5487          } else if ((intcode >= LDTERM_CS_TYPE_UTF8_MIN_CJKEXTB &&
5471 5488              intcode <= LDTERM_CS_TYPE_UTF8_MAX_CJKEXTB) ||
5472 5489              (intcode >= LDTERM_CS_TYPE_UTF8_MIN_CJKCOMP &&
5473 5490              intcode <= LDTERM_CS_TYPE_UTF8_MAX_CJKCOMP) ||
5474 5491              (intcode >= LDTERM_CS_TYPE_UTF8_MIN_P15 &&
5475 5492              intcode <= LDTERM_CS_TYPE_UTF8_MAX_P15) ||
5476 5493              (intcode >= LDTERM_CS_TYPE_UTF8_MIN_P16 &&
5477 5494              intcode <= LDTERM_CS_TYPE_UTF8_MAX_P16)) {
5478 5495                  /*
5479 5496                   * Supplementary Plane for CJK Ideographs and
5480 5497                   * Private Use Planes.
5481 5498                   */
5482 5499                  return ('\2');
5483 5500          } else if ((intcode >= LDTERM_CS_TYPE_UTF8_MIN_P14 &&
5484 5501              intcode <= LDTERM_CS_TYPE_UTF8_MAX_P14) ||
5485 5502              (intcode >= LDTERM_CS_TYPE_UTF8_MIN_VARSEL &&
5486 5503              intcode <= LDTERM_CS_TYPE_UTF8_MAX_VARSEL)) {
5487 5504                  /*
5488 5505                   * Some Special Purpose Plane characters:
5489 5506                   * These are like control characters and not printable.
5490 5507                   */
5491 5508                  return ('\0');
5492 5509          }
5493 5510  
5494 5511          /*
5495 5512           * We return the display width of 1 for all character code points
5496 5513           * that we didn't catch from the above logic and also for combining
5497 5514           * and conjoining characters with width value of zero.
5498 5515           *
5499 5516           * In particular, the reason why we are returning 1 for combining
5500 5517           * and conjoining characters is because the GUI-based terminal
5501 5518           * emulators are not yet capable of properly handling such characters
5502 5519           * and in most of the cases, they just treat such characters as if
5503 5520           * they occupy a display cell. If the terminal emulators are capable of
5504 5521           * handling the characters correctly, then, this logic of returning
5505 5522           * 1 should be revisited and changed. See CR 6660526 for more
5506 5523           * details on this.
5507 5524           */
5508 5525          return ((i == 0) ? '\1' : (uchar_t)i);
5509 5526  }
  
    | 
      ↓ open down ↓ | 
    1427 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX