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

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;
        !           172:                snprintf(linebuf+curr, sizeof(linebuf)-curr, "%*s ", n, buf);
        !           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: }