[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.6

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