[BACK]Return to line.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Annotation of src/usr.bin/less/line.c, Revision 1.7

1.1       etheisen    1: /*
1.5       millert     2:  * Copyright (C) 1984-2002  Mark Nudelman
1.1       etheisen    3:  *
1.5       millert     4:  * You may distribute under the terms of either the GNU General Public
                      5:  * License or the Less License, as specified in the README file.
1.1       etheisen    6:  *
1.5       millert     7:  * For more information about less, or for information on how to
                      8:  * contact the author, see the README file.
1.1       etheisen    9:  */
                     10:
                     11:
                     12: /*
                     13:  * Routines to manipulate the "line buffer".
                     14:  * The line buffer holds a line of output as it is being built
                     15:  * in preparation for output to the screen.
                     16:  */
                     17:
                     18: #include "less.h"
                     19:
1.5       millert    20: #define IS_CONT(c)  (((c) & 0xC0) == 0x80)
                     21:
                     22: public char *linebuf = NULL;   /* Buffer which holds the current output line */
                     23: static char *attr = NULL;      /* Extension of linebuf to hold attributes */
                     24: public int size_linebuf = 0;   /* Size of line buffer (and attr buffer) */
                     25:
                     26: public int cshift;             /* Current left-shift of output line buffer */
                     27: public int hshift;             /* Desired left-shift of output line buffer */
                     28: public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
                     29: public int ntabstops = 1;      /* Number of tabstops */
                     30: public int tabdefault = 8;     /* Default repeated tabstops */
1.1       etheisen   31:
                     32: static int curr;               /* Index into linebuf */
                     33: static int column;             /* Printable length, accounting for
                     34:                                   backspaces, etc. */
                     35: static int overstrike;         /* Next char should overstrike previous char */
1.5       millert    36: static int last_overstrike = AT_NORMAL;
1.1       etheisen   37: static int is_null_line;       /* There is no current line */
1.5       millert    38: static int lmargin;            /* Left margin */
                     39: static int hilites;            /* Number of hilites in this line */
1.1       etheisen   40: static char pendc;
                     41: static POSITION pendpos;
1.5       millert    42: static char *end_ansi_chars;
1.1       etheisen   43:
                     44: static int do_append();
                     45:
                     46: extern int bs_mode;
                     47: extern int linenums;
                     48: extern int ctldisp;
                     49: extern int twiddle;
                     50: extern int binattr;
1.5       millert    51: extern int status_col;
1.1       etheisen   52: extern int auto_wrap, ignaw;
                     53: extern int bo_s_width, bo_e_width;
                     54: extern int ul_s_width, ul_e_width;
                     55: extern int bl_s_width, bl_e_width;
                     56: extern int so_s_width, so_e_width;
                     57: extern int sc_width, sc_height;
1.5       millert    58: extern int utf_mode;
                     59: extern POSITION start_attnpos;
                     60: extern POSITION end_attnpos;
                     61:
                     62: /*
                     63:  * Initialize from environment variables.
                     64:  */
                     65:        public void
                     66: init_line()
                     67: {
                     68:        end_ansi_chars = lgetenv("LESSANSIENDCHARS");
                     69:        if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
                     70:                end_ansi_chars = "m";
                     71:        linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
                     72:        attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
                     73:        size_linebuf = LINEBUF_SIZE;
                     74: }
                     75:
                     76: /*
                     77:  * Expand the line buffer.
                     78:  */
                     79:        static int
                     80: expand_linebuf()
                     81: {
1.7     ! millert    82:        int new_size = size_linebuf * 2;
        !            83:        char *new_buf = (char *) realloc(linebuf, new_size);
        !            84:        char *new_attr = (char *) realloc(attr, new_size);
1.5       millert    85:        if (new_buf == NULL || new_attr == NULL)
                     86:        {
                     87:                if (new_attr != NULL)
                     88:                        free(new_attr);
                     89:                if (new_buf != NULL)
                     90:                        free(new_buf);
                     91:                return 1;
                     92:        }
1.7     ! millert    93:        memset(new_buf + size_linebuf, 0, new_size - size_linebuf);
        !            94:        memset(new_attr + size_linebuf, 0, new_size - size_linebuf);
1.5       millert    95:        linebuf = new_buf;
                     96:        attr = new_attr;
                     97:        size_linebuf = new_size;
                     98:        return 0;
                     99: }
1.1       etheisen  100:
                    101: /*
                    102:  * Rewind the line buffer.
                    103:  */
                    104:        public void
                    105: prewind()
                    106: {
                    107:        curr = 0;
                    108:        column = 0;
                    109:        overstrike = 0;
                    110:        is_null_line = 0;
                    111:        pendc = '\0';
1.5       millert   112:        lmargin = 0;
                    113:        if (status_col)
                    114:                lmargin += 1;
                    115: #if HILITE_SEARCH
                    116:        hilites = 0;
                    117: #endif
1.1       etheisen  118: }
                    119:
                    120: /*
                    121:  * Insert the line number (of the given position) into the line buffer.
                    122:  */
                    123:        public void
                    124: plinenum(pos)
                    125:        POSITION pos;
                    126: {
1.5       millert   127:        register LINENUM linenum = 0;
                    128:        register int i;
                    129:
                    130:        if (linenums == OPT_ONPLUS)
                    131:        {
                    132:                /*
                    133:                 * Get the line number and put it in the current line.
                    134:                 * {{ Note: since find_linenum calls forw_raw_line,
                    135:                 *    it may seek in the input file, requiring the caller
                    136:                 *    of plinenum to re-seek if necessary. }}
                    137:                 * {{ Since forw_raw_line modifies linebuf, we must
                    138:                 *    do this first, before storing anything in linebuf. }}
                    139:                 */
                    140:                linenum = find_linenum(pos);
                    141:        }
1.1       etheisen  142:
                    143:        /*
1.5       millert   144:         * Display a status column if the -J option is set.
1.1       etheisen  145:         */
1.5       millert   146:        if (status_col)
                    147:        {
                    148:                linebuf[curr] = ' ';
                    149:                if (start_attnpos != NULL_POSITION &&
                    150:                    pos >= start_attnpos && pos < end_attnpos)
                    151:                        attr[curr] = AT_STANDOUT;
                    152:                else
                    153:                        attr[curr] = 0;
                    154:                curr++;
                    155:                column++;
                    156:        }
1.1       etheisen  157:        /*
1.5       millert   158:         * Display the line number at the start of each line
                    159:         * if the -N option is set.
1.1       etheisen  160:         */
1.5       millert   161:        if (linenums == OPT_ONPLUS)
                    162:        {
                    163:                char buf[INT_STRLEN_BOUND(pos) + 2];
                    164:                int n;
1.1       etheisen  165:
1.5       millert   166:                linenumtoa(linenum, buf, sizeof(buf));
                    167:                n = strlen(buf);
                    168:                if (n < MIN_LINENUM_WIDTH)
                    169:                        n = MIN_LINENUM_WIDTH;
1.6       millert   170:                snprintf(linebuf+curr, size_linebuf-curr, "%*s ", n, buf);
1.5       millert   171:                n++;  /* One space after the line number. */
                    172:                for (i = 0; i < n; i++)
                    173:                        attr[curr+i] = AT_NORMAL;
                    174:                curr += n;
                    175:                column += n;
                    176:                lmargin += n;
                    177:        }
1.1       etheisen  178:
                    179:        /*
1.5       millert   180:         * Append enough spaces to bring us to the lmargin.
1.1       etheisen  181:         */
1.5       millert   182:        while (column < lmargin)
1.1       etheisen  183:        {
                    184:                linebuf[curr] = ' ';
                    185:                attr[curr++] = AT_NORMAL;
                    186:                column++;
1.5       millert   187:        }
                    188: }
                    189:
                    190: /*
                    191:  * Determine how many characters are required to shift N columns.
                    192:  */
                    193:        static int
                    194: shift_chars(s, len)
                    195:        char *s;
                    196:        int len;
                    197: {
                    198:        char *p = s;
                    199:
                    200:        /*
                    201:         * Each char counts for one column, except ANSI color escape
                    202:         * sequences use no columns since they don't move the cursor.
                    203:         */
                    204:        while (*p != '\0' && len > 0)
                    205:        {
                    206:                if (*p++ != ESC)
                    207:                {
                    208:                        len--;
                    209:                } else
                    210:                {
                    211:                        while (*p != '\0')
                    212:                        {
                    213:                                if (is_ansi_end(*p++))
                    214:                                        break;
                    215:                        }
                    216:                }
                    217:        }
                    218:        return (p - s);
                    219: }
                    220:
                    221: /*
                    222:  * Determine how many characters are required to shift N columns (UTF version).
                    223:  * {{ FIXME: what about color escape sequences in UTF mode? }}
                    224:  */
                    225:        static int
                    226: utf_shift_chars(s, len)
                    227:        char *s;
                    228:        int len;
                    229: {
                    230:        int ulen = 0;
                    231:
                    232:        while (*s != '\0' && len > 0)
                    233:        {
                    234:                if (!IS_CONT(*s))
                    235:                        len--;
                    236:                s++;
                    237:                ulen++;
                    238:        }
                    239:        while (IS_CONT(*s))
                    240:        {
                    241:                s++;
                    242:                ulen++;
                    243:        }
                    244:        return (ulen);
                    245: }
                    246:
                    247: /*
                    248:  * Shift the input line left.
                    249:  * This means discarding N printable chars at the start of the buffer.
                    250:  */
                    251:        static void
                    252: pshift(shift)
                    253:        int shift;
                    254: {
                    255:        int i;
                    256:        int nchars;
                    257:
                    258:        if (shift > column - lmargin)
                    259:                shift = column - lmargin;
                    260:        if (shift > curr - lmargin)
                    261:                shift = curr - lmargin;
                    262:
                    263:        if (utf_mode)
                    264:                nchars = utf_shift_chars(linebuf + lmargin, shift);
                    265:        else
                    266:                nchars = shift_chars(linebuf + lmargin, shift);
                    267:        if (nchars > curr)
                    268:                nchars = curr;
                    269:        for (i = 0;  i < curr - nchars;  i++)
                    270:        {
                    271:                linebuf[lmargin + i] = linebuf[lmargin + i + nchars];
                    272:                attr[lmargin + i] = attr[lmargin + i + nchars];
                    273:        }
                    274:        curr -= nchars;
                    275:        column -= shift;
                    276:        cshift += shift;
1.1       etheisen  277: }
                    278:
                    279: /*
                    280:  * Return the printing width of the start (enter) sequence
                    281:  * for a given character attribute.
                    282:  */
1.5       millert   283:        static int
1.1       etheisen  284: attr_swidth(a)
                    285:        int a;
                    286: {
                    287:        switch (a)
                    288:        {
                    289:        case AT_BOLD:           return (bo_s_width);
                    290:        case AT_UNDERLINE:      return (ul_s_width);
                    291:        case AT_BLINK:          return (bl_s_width);
                    292:        case AT_STANDOUT:       return (so_s_width);
                    293:        }
                    294:        return (0);
                    295: }
                    296:
                    297: /*
                    298:  * Return the printing width of the end (exit) sequence
                    299:  * for a given character attribute.
                    300:  */
1.5       millert   301:        static int
1.1       etheisen  302: attr_ewidth(a)
                    303:        int a;
                    304: {
                    305:        switch (a)
                    306:        {
                    307:        case AT_BOLD:           return (bo_e_width);
                    308:        case AT_UNDERLINE:      return (ul_e_width);
                    309:        case AT_BLINK:          return (bl_e_width);
                    310:        case AT_STANDOUT:       return (so_e_width);
                    311:        }
                    312:        return (0);
                    313: }
                    314:
                    315: /*
                    316:  * Return the printing width of a given character and attribute,
                    317:  * if the character were added to the current position in the line buffer.
                    318:  * Adding a character with a given attribute may cause an enter or exit
                    319:  * attribute sequence to be inserted, so this must be taken into account.
                    320:  */
                    321:        static int
                    322: pwidth(c, a)
                    323:        int c;
                    324:        int a;
                    325: {
1.5       millert   326:        register int w;
                    327:
                    328:        if (utf_mode && IS_CONT(c))
                    329:                return (0);
1.1       etheisen  330:
                    331:        if (c == '\b')
                    332:                /*
                    333:                 * Backspace moves backwards one position.
                    334:                 */
                    335:                return (-1);
                    336:
                    337:        if (control_char(c))
                    338:                /*
                    339:                 * Control characters do unpredicatable things,
                    340:                 * so we don't even try to guess; say it doesn't move.
                    341:                 * This can only happen if the -r flag is in effect.
                    342:                 */
                    343:                return (0);
                    344:
                    345:        /*
                    346:         * Other characters take one space,
                    347:         * plus the width of any attribute enter/exit sequence.
                    348:         */
                    349:        w = 1;
                    350:        if (curr > 0 && attr[curr-1] != a)
                    351:                w += attr_ewidth(attr[curr-1]);
                    352:        if (a && (curr == 0 || attr[curr-1] != a))
                    353:                w += attr_swidth(a);
                    354:        return (w);
                    355: }
                    356:
                    357: /*
                    358:  * Delete the previous character in the line buffer.
                    359:  */
                    360:        static void
                    361: backc()
                    362: {
                    363:        curr--;
                    364:        column -= pwidth(linebuf[curr], attr[curr]);
                    365: }
                    366:
                    367: /*
1.5       millert   368:  * Are we currently within a recognized ANSI escape sequence?
                    369:  */
                    370:        static int
                    371: in_ansi_esc_seq()
                    372: {
                    373:        int i;
                    374:
                    375:        /*
                    376:         * Search backwards for either an ESC (which means we ARE in a seq);
                    377:         * or an end char (which means we're NOT in a seq).
                    378:         */
                    379:        for (i = curr-1;  i >= 0;  i--)
                    380:        {
                    381:                if (linebuf[i] == ESC)
                    382:                        return (1);
                    383:                if (is_ansi_end(linebuf[i]))
                    384:                        return (0);
                    385:        }
                    386:        return (0);
                    387: }
                    388:
                    389: /*
                    390:  * Is a character the end of an ANSI escape sequence?
                    391:  */
                    392:        public int
                    393: is_ansi_end(c)
                    394:        char c;
                    395: {
                    396:        return (strchr(end_ansi_chars, c) != NULL);
                    397: }
                    398:
                    399: /*
1.1       etheisen  400:  * Append a character and attribute to the line buffer.
                    401:  */
1.5       millert   402: #define        STORE_CHAR(c,a,pos) \
                    403:        do { if (store_char((c),(a),(pos))) return (1); else curr++; } while (0)
                    404:
1.1       etheisen  405:        static int
1.5       millert   406: store_char(c, a, pos)
1.1       etheisen  407:        int c;
                    408:        int a;
                    409:        POSITION pos;
                    410: {
1.5       millert   411:        register int w;
1.1       etheisen  412:
1.5       millert   413:        if (a != AT_NORMAL)
                    414:                last_overstrike = a;
1.1       etheisen  415: #if HILITE_SEARCH
                    416:        if (is_hilited(pos, pos+1, 0))
1.5       millert   417:        {
1.1       etheisen  418:                /*
                    419:                 * This character should be highlighted.
                    420:                 * Override the attribute passed in.
                    421:                 */
                    422:                a = AT_STANDOUT;
1.5       millert   423:                hilites++;
                    424:        }
1.1       etheisen  425: #endif
1.5       millert   426:        if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
                    427:                w = 0;
                    428:        else
                    429:                w = pwidth(c, a);
                    430:        if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
1.1       etheisen  431:                /*
                    432:                 * Won't fit on screen.
                    433:                 */
                    434:                return (1);
                    435:
1.5       millert   436:        if (curr >= size_linebuf-2)
                    437:        {
1.1       etheisen  438:                /*
                    439:                 * Won't fit in line buffer.
1.5       millert   440:                 * Try to expand it.
1.1       etheisen  441:                 */
1.5       millert   442:                if (expand_linebuf())
                    443:                        return (1);
                    444:        }
1.1       etheisen  445:
                    446:        /*
                    447:         * Special handling for "magic cookie" terminals.
                    448:         * If an attribute enter/exit sequence has a printing width > 0,
                    449:         * and the sequence is adjacent to a space, delete the space.
                    450:         * We just mark the space as invisible, to avoid having too
                    451:         * many spaces deleted.
                    452:         * {{ Note that even if the attribute width is > 1, we
                    453:         *    delete only one space.  It's not worth trying to do more.
                    454:         *    It's hardly worth doing this much. }}
                    455:         */
                    456:        if (curr > 0 && a != AT_NORMAL &&
                    457:                linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&
                    458:                attr_swidth(a) > 0)
                    459:        {
                    460:                /*
                    461:                 * We are about to append an enter-attribute sequence
                    462:                 * just after a space.  Delete the space.
                    463:                 */
                    464:                attr[curr-1] = AT_INVIS;
                    465:                column--;
                    466:        } else if (curr > 0 && attr[curr-1] != AT_NORMAL &&
                    467:                attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&
                    468:                attr_ewidth(attr[curr-1]) > 0)
                    469:        {
                    470:                /*
                    471:                 * We are about to append a space just after an
                    472:                 * exit-attribute sequence.  Delete the space.
                    473:                 */
                    474:                a = AT_INVIS;
                    475:                column--;
                    476:        }
                    477:        /* End of magic cookie handling. */
                    478:
                    479:        linebuf[curr] = c;
                    480:        attr[curr] = a;
                    481:        column += w;
                    482:        return (0);
                    483: }
                    484:
                    485: /*
1.5       millert   486:  * Append a tab to the line buffer.
                    487:  * Store spaces to represent the tab.
                    488:  */
                    489: #define        STORE_TAB(a,pos) \
                    490:        do { if (store_tab((a),(pos))) return (1); } while (0)
                    491:
                    492:        static int
                    493: store_tab(attr, pos)
                    494:        int attr;
                    495:        POSITION pos;
                    496: {
                    497:        int to_tab = column + cshift - lmargin;
                    498:        int i;
                    499:
                    500:        if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
                    501:                to_tab = tabdefault -
                    502:                     ((to_tab - tabstops[ntabstops-1]) % tabdefault);
                    503:        else
                    504:        {
                    505:                for (i = ntabstops - 2;  i >= 0;  i--)
                    506:                        if (to_tab >= tabstops[i])
                    507:                                break;
                    508:                to_tab = tabstops[i+1] - to_tab;
                    509:        }
                    510:
                    511:        do {
                    512:                STORE_CHAR(' ', attr, pos);
                    513:        } while (--to_tab > 0);
                    514:        return 0;
                    515: }
                    516:
                    517: /*
1.1       etheisen  518:  * Append a character to the line buffer.
                    519:  * Expand tabs into spaces, handle underlining, boldfacing, etc.
                    520:  * Returns 0 if ok, 1 if couldn't fit in buffer.
                    521:  */
                    522:        public int
                    523: pappend(c, pos)
1.5       millert   524:        register int c;
1.1       etheisen  525:        POSITION pos;
                    526: {
1.5       millert   527:        int r;
                    528:
1.1       etheisen  529:        if (pendc)
                    530:        {
                    531:                if (do_append(pendc, pendpos))
                    532:                        /*
                    533:                         * Oops.  We've probably lost the char which
                    534:                         * was in pendc, since caller won't back up.
                    535:                         */
                    536:                        return (1);
                    537:                pendc = '\0';
                    538:        }
                    539:
                    540:        if (c == '\r' && bs_mode == BS_SPECIAL)
                    541:        {
                    542:                /*
                    543:                 * Don't put the CR into the buffer until we see
                    544:                 * the next char.  If the next char is a newline,
                    545:                 * discard the CR.
                    546:                 */
                    547:                pendc = c;
                    548:                pendpos = pos;
                    549:                return (0);
                    550:        }
                    551:
1.5       millert   552:        r = do_append(c, pos);
                    553:        /*
                    554:         * If we need to shift the line, do it.
                    555:         * But wait until we get to at least the middle of the screen,
                    556:         * so shifting it doesn't affect the chars we're currently
                    557:         * pappending.  (Bold & underline can get messed up otherwise.)
                    558:         */
                    559:        if (cshift < hshift && column > sc_width / 2)
                    560:        {
                    561:                linebuf[curr] = '\0';
                    562:                pshift(hshift - cshift);
                    563:        }
                    564:        return (r);
1.1       etheisen  565: }
                    566:
1.5       millert   567: #define IS_UTF8_4BYTE(c) ( ((c) & 0xf8) == 0xf0 )
                    568: #define IS_UTF8_3BYTE(c) ( ((c) & 0xf0) == 0xe0 )
                    569: #define IS_UTF8_2BYTE(c) ( ((c) & 0xe0) == 0xc0 )
                    570: #define IS_UTF8_TRAIL(c) ( ((c) & 0xc0) == 0x80 )
                    571:
1.1       etheisen  572:        static int
                    573: do_append(c, pos)
                    574:        int c;
                    575:        POSITION pos;
                    576: {
1.5       millert   577:        register char *s;
                    578:        register int a;
1.1       etheisen  579:
1.5       millert   580: #define STOREC(c,a) \
                    581:        if ((c) == '\t') STORE_TAB((a),pos); else STORE_CHAR((c),(a),pos)
1.1       etheisen  582:
1.5       millert   583:        if (c == '\b')
                    584:        {
                    585:                switch (bs_mode)
                    586:                {
                    587:                case BS_NORMAL:
                    588:                        STORE_CHAR(c, AT_NORMAL, pos);
                    589:                        break;
                    590:                case BS_CONTROL:
                    591:                        goto do_control_char;
                    592:                case BS_SPECIAL:
                    593:                        if (curr == 0)
                    594:                                break;
                    595:                        backc();
                    596:                        overstrike = 1;
                    597:                        break;
                    598:                }
                    599:        } else if (overstrike)
1.1       etheisen  600:        {
                    601:                /*
                    602:                 * Overstrike the character at the current position
                    603:                 * in the line buffer.  This will cause either
                    604:                 * underline (if a "_" is overstruck),
                    605:                 * bold (if an identical character is overstruck),
                    606:                 * or just deletion of the character in the buffer.
                    607:                 */
1.5       millert   608:                overstrike--;
                    609:                if (utf_mode && IS_UTF8_4BYTE(c) && curr > 2 && (char)c == linebuf[curr-3])
                    610:                {
                    611:                        backc();
                    612:                        backc();
                    613:                        backc();
                    614:                        STORE_CHAR(linebuf[curr], AT_BOLD, pos);
                    615:                        overstrike = 3;
                    616:                } else if (utf_mode && (IS_UTF8_3BYTE(c) || (overstrike==2 && IS_UTF8_TRAIL(c))) && curr > 1 && (char)c == linebuf[curr-2])
                    617:                {
                    618:                        backc();
                    619:                        backc();
                    620:                        STORE_CHAR(linebuf[curr], AT_BOLD, pos);
                    621:                        overstrike = 2;
                    622:                } else if (utf_mode && curr > 0 && (IS_UTF8_2BYTE(c) || (overstrike==1 && IS_UTF8_TRAIL(c))) && (char)c == linebuf[curr-1])
                    623:                {
                    624:                        backc();
                    625:                        STORE_CHAR(linebuf[curr], AT_BOLD, pos);
                    626:                        overstrike = 1;
                    627:                } else if (utf_mode && curr > 0 && IS_UTF8_TRAIL(c) && attr[curr-1] == AT_UNDERLINE)
                    628:                {
                    629:                        STOREC(c, AT_UNDERLINE);
                    630:                } else if ((char)c == linebuf[curr])
                    631:                {
                    632:                        /*
                    633:                         * Overstriking a char with itself means make it bold.
                    634:                         * But overstriking an underscore with itself is
                    635:                         * ambiguous.  It could mean make it bold, or
                    636:                         * it could mean make it underlined.
                    637:                         * Use the previous overstrike to resolve it.
                    638:                         */
                    639:                        if (c == '_' && last_overstrike != AT_NORMAL)
                    640:                                STOREC(c, last_overstrike);
                    641:                        else
                    642:                                STOREC(c, AT_BOLD);
                    643:                } else if (c == '_')
                    644:                {
                    645:                        if (utf_mode)
                    646:                        {
                    647:                                int i;
                    648:                                for (i = 0;  i < 5;  i++)
                    649:                                {
                    650:                                        if (curr <= i || !IS_CONT(linebuf[curr-i]))
                    651:                                                break;
                    652:                                        attr[curr-i-1] = AT_UNDERLINE;
                    653:                                }
                    654:                        }
1.1       etheisen  655:                        STOREC(linebuf[curr], AT_UNDERLINE);
1.5       millert   656:                } else if (linebuf[curr] == '_')
                    657:                {
                    658:                        if (utf_mode)
                    659:                        {
                    660:                                if (IS_UTF8_2BYTE(c))
                    661:                                        overstrike = 1;
                    662:                                else if (IS_UTF8_3BYTE(c))
                    663:                                        overstrike = 2;
                    664:                                else if (IS_UTF8_4BYTE(c))
                    665:                                        overstrike = 3;
                    666:                        }
1.1       etheisen  667:                        STOREC(c, AT_UNDERLINE);
1.5       millert   668:                } else if (control_char(c))
1.1       etheisen  669:                        goto do_control_char;
                    670:                else
                    671:                        STOREC(c, AT_NORMAL);
1.5       millert   672:        } else if (c == '\t')
1.1       etheisen  673:        {
1.5       millert   674:                /*
                    675:                 * Expand a tab into spaces.
                    676:                 */
1.1       etheisen  677:                switch (bs_mode)
                    678:                {
                    679:                case BS_CONTROL:
                    680:                        goto do_control_char;
1.5       millert   681:                case BS_NORMAL:
1.1       etheisen  682:                case BS_SPECIAL:
1.5       millert   683:                        STORE_TAB(AT_NORMAL, pos);
1.1       etheisen  684:                        break;
                    685:                }
                    686:        } else if (control_char(c))
                    687:        {
                    688:        do_control_char:
1.5       millert   689:                if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && c == ESC))
1.1       etheisen  690:                {
                    691:                        /*
                    692:                         * Output as a normal character.
                    693:                         */
1.5       millert   694:                        STORE_CHAR(c, AT_NORMAL, pos);
1.1       etheisen  695:                } else
                    696:                {
                    697:                        /*
                    698:                         * Convert to printable representation.
                    699:                         */
                    700:                        s = prchar(c);
                    701:                        a = binattr;
                    702:
                    703:                        /*
                    704:                         * Make sure we can get the entire representation
                    705:                         * of the character on this line.
                    706:                         */
                    707:                        if (column + (int) strlen(s) +
                    708:                            attr_swidth(a) + attr_ewidth(a) > sc_width)
                    709:                                return (1);
                    710:
                    711:                        for ( ;  *s != 0;  s++)
1.5       millert   712:                                STORE_CHAR(*s, a, pos);
1.1       etheisen  713:                }
                    714:        } else
                    715:        {
                    716:                STOREC(c, AT_NORMAL);
                    717:        }
                    718:
                    719:        return (0);
                    720: }
                    721:
                    722: /*
                    723:  * Terminate the line in the line buffer.
                    724:  */
                    725:        public void
                    726: pdone(endline)
                    727:        int endline;
                    728: {
                    729:        if (pendc && (pendc != '\r' || !endline))
                    730:                /*
                    731:                 * If we had a pending character, put it in the buffer.
                    732:                 * But discard a pending CR if we are at end of line
                    733:                 * (that is, discard the CR in a CR/LF sequence).
                    734:                 */
                    735:                (void) do_append(pendc, pendpos);
                    736:
                    737:        /*
1.5       millert   738:         * Make sure we've shifted the line, if we need to.
                    739:         */
                    740:        if (cshift < hshift)
                    741:                pshift(hshift - cshift);
                    742:
                    743:        /*
1.1       etheisen  744:         * Add a newline if necessary,
                    745:         * and append a '\0' to the end of the line.
                    746:         */
1.5       millert   747:        if (column < sc_width || !auto_wrap || ignaw || ctldisp == OPT_ON)
1.1       etheisen  748:        {
                    749:                linebuf[curr] = '\n';
                    750:                attr[curr] = AT_NORMAL;
                    751:                curr++;
                    752:        }
                    753:        linebuf[curr] = '\0';
                    754:        attr[curr] = AT_NORMAL;
1.5       millert   755:
                    756: #if HILITE_SEARCH
                    757:        if (status_col && hilites > 0)
                    758:        {
                    759:                linebuf[0] = '*';
                    760:                attr[0] = AT_STANDOUT;
                    761:        }
                    762: #endif
                    763:        /*
                    764:         * If we are done with this line, reset the current shift.
                    765:         */
                    766:        if (endline)
                    767:                cshift = 0;
1.1       etheisen  768: }
                    769:
                    770: /*
                    771:  * Get a character from the current line.
                    772:  * Return the character as the function return value,
                    773:  * and the character attribute in *ap.
                    774:  */
                    775:        public int
                    776: gline(i, ap)
1.5       millert   777:        register int i;
                    778:        register int *ap;
1.1       etheisen  779: {
                    780:        char *s;
                    781:
                    782:        if (is_null_line)
                    783:        {
                    784:                /*
                    785:                 * If there is no current line, we pretend the line is
                    786:                 * either "~" or "", depending on the "twiddle" flag.
                    787:                 */
1.5       millert   788:                *ap = AT_BOLD;
1.1       etheisen  789:                s = (twiddle) ? "~\n" : "\n";
                    790:                return (s[i]);
                    791:        }
                    792:
                    793:        *ap = attr[i];
                    794:        return (linebuf[i] & 0377);
                    795: }
                    796:
                    797: /*
                    798:  * Indicate that there is no current line.
                    799:  */
                    800:        public void
                    801: null_line()
                    802: {
                    803:        is_null_line = 1;
1.5       millert   804:        cshift = 0;
1.1       etheisen  805: }
                    806:
                    807: /*
                    808:  * Analogous to forw_line(), but deals with "raw lines":
                    809:  * lines which are not split for screen width.
                    810:  * {{ This is supposed to be more efficient than forw_line(). }}
                    811:  */
                    812:        public POSITION
                    813: forw_raw_line(curr_pos, linep)
                    814:        POSITION curr_pos;
                    815:        char **linep;
                    816: {
1.5       millert   817:        register int n;
                    818:        register int c;
1.1       etheisen  819:        POSITION new_pos;
                    820:
                    821:        if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
                    822:                (c = ch_forw_get()) == EOI)
                    823:                return (NULL_POSITION);
                    824:
1.5       millert   825:        n = 0;
1.1       etheisen  826:        for (;;)
                    827:        {
                    828:                if (c == '\n' || c == EOI)
                    829:                {
                    830:                        new_pos = ch_tell();
                    831:                        break;
                    832:                }
1.5       millert   833:                if (n >= size_linebuf-1)
1.1       etheisen  834:                {
1.5       millert   835:                        if (expand_linebuf())
                    836:                        {
                    837:                                /*
                    838:                                 * Overflowed the input buffer.
                    839:                                 * Pretend the line ended here.
                    840:                                 */
                    841:                                new_pos = ch_tell() - 1;
                    842:                                break;
                    843:                        }
1.1       etheisen  844:                }
1.5       millert   845:                linebuf[n++] = c;
1.1       etheisen  846:                c = ch_forw_get();
                    847:        }
1.5       millert   848:        linebuf[n] = '\0';
1.1       etheisen  849:        if (linep != NULL)
                    850:                *linep = linebuf;
                    851:        return (new_pos);
                    852: }
                    853:
                    854: /*
                    855:  * Analogous to back_line(), but deals with "raw lines".
                    856:  * {{ This is supposed to be more efficient than back_line(). }}
                    857:  */
                    858:        public POSITION
                    859: back_raw_line(curr_pos, linep)
                    860:        POSITION curr_pos;
                    861:        char **linep;
                    862: {
1.5       millert   863:        register int n;
                    864:        register int c;
1.1       etheisen  865:        POSITION new_pos;
                    866:
                    867:        if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
                    868:                ch_seek(curr_pos-1))
                    869:                return (NULL_POSITION);
                    870:
1.5       millert   871:        n = size_linebuf;
                    872:        linebuf[--n] = '\0';
1.1       etheisen  873:        for (;;)
                    874:        {
                    875:                c = ch_back_get();
                    876:                if (c == '\n')
                    877:                {
                    878:                        /*
                    879:                         * This is the newline ending the previous line.
                    880:                         * We have hit the beginning of the line.
                    881:                         */
                    882:                        new_pos = ch_tell() + 1;
                    883:                        break;
                    884:                }
                    885:                if (c == EOI)
                    886:                {
                    887:                        /*
                    888:                         * We have hit the beginning of the file.
                    889:                         * This must be the first line in the file.
                    890:                         * This must, of course, be the beginning of the line.
                    891:                         */
                    892:                        new_pos = ch_zero();
                    893:                        break;
                    894:                }
1.5       millert   895:                if (n <= 0)
1.1       etheisen  896:                {
1.5       millert   897:                        int old_size_linebuf = size_linebuf;
                    898:                        char *fm;
                    899:                        char *to;
                    900:                        if (expand_linebuf())
                    901:                        {
                    902:                                /*
                    903:                                 * Overflowed the input buffer.
                    904:                                 * Pretend the line ended here.
                    905:                                 */
                    906:                                new_pos = ch_tell() + 1;
                    907:                                break;
                    908:                        }
1.1       etheisen  909:                        /*
1.5       millert   910:                         * Shift the data to the end of the new linebuf.
1.1       etheisen  911:                         */
1.5       millert   912:                        for (fm = linebuf + old_size_linebuf,
                    913:                              to = linebuf + size_linebuf;
                    914:                             fm >= linebuf;  fm--, to--)
                    915:                                *to = *fm;
                    916:                        n = size_linebuf - old_size_linebuf;
1.1       etheisen  917:                }
1.5       millert   918:                linebuf[--n] = c;
1.1       etheisen  919:        }
                    920:        if (linep != NULL)
1.5       millert   921:                *linep = &linebuf[n];
1.1       etheisen  922:        return (new_pos);
                    923: }