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

1.2     ! niklas      1: /*     $OpenBSD$       */
        !             2:
1.1       etheisen    3: /*
                      4:  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice in the documentation and/or other materials provided with
                     14:  *    the distribution.
                     15:  *
                     16:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
                     17:  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     18:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     19:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
                     20:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     21:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
                     22:  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
                     23:  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     24:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
                     25:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
                     26:  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     27:  */
                     28:
                     29:
                     30: /*
                     31:  * Routines to manipulate the "line buffer".
                     32:  * The line buffer holds a line of output as it is being built
                     33:  * in preparation for output to the screen.
                     34:  */
                     35:
                     36: #include "less.h"
                     37:
                     38: public char linebuf[1024];     /* Buffer which holds the current output line */
                     39: public int size_linebuf = sizeof(linebuf);
                     40:
                     41: static char attr[1024];                /* Extension of linebuf to hold attributes */
                     42: static int curr;               /* Index into linebuf */
                     43: static int column;             /* Printable length, accounting for
                     44:                                   backspaces, etc. */
                     45: static int lno_indent;         /* Number of chars used for line number */
                     46: static int overstrike;         /* Next char should overstrike previous char */
                     47: static int is_null_line;       /* There is no current line */
                     48: static char pendc;
                     49: static POSITION pendpos;
                     50:
                     51: static int do_append();
                     52:
                     53: extern int bs_mode;
                     54: extern int tabstop;
                     55: extern int linenums;
                     56: extern int ctldisp;
                     57: extern int twiddle;
                     58: extern int binattr;
                     59: extern int auto_wrap, ignaw;
                     60: extern int bo_s_width, bo_e_width;
                     61: extern int ul_s_width, ul_e_width;
                     62: extern int bl_s_width, bl_e_width;
                     63: extern int so_s_width, so_e_width;
                     64: extern int sc_width, sc_height;
                     65:
                     66: /*
                     67:  * Rewind the line buffer.
                     68:  */
                     69:        public void
                     70: prewind()
                     71: {
                     72:        curr = 0;
                     73:        column = 0;
                     74:        overstrike = 0;
                     75:        is_null_line = 0;
                     76:        lno_indent = 0;
                     77:        pendc = '\0';
                     78: }
                     79:
                     80: /*
                     81:  * Insert the line number (of the given position) into the line buffer.
                     82:  */
                     83:        public void
                     84: plinenum(pos)
                     85:        POSITION pos;
                     86: {
                     87:        register int lno;
                     88:        register int i;
                     89:        register int n;
                     90:
                     91:        /*
                     92:         * We display the line number at the start of each line
                     93:         * only if the -N option is set.
                     94:         */
                     95:        if (linenums != OPT_ONPLUS)
                     96:                return;
                     97:
                     98:        /*
                     99:         * Get the line number and put it in the current line.
                    100:         * {{ Note: since find_linenum calls forw_raw_line,
                    101:         *    it may seek in the input file, requiring the caller
                    102:         *    of plinenum to re-seek if necessary. }}
                    103:         */
                    104:        lno = find_linenum(pos);
                    105:
                    106:        sprintf(&linebuf[curr], "%6d", lno);
                    107:        n = strlen(&linebuf[curr]);
                    108:        column += n;
                    109:        for (i = 0;  i < n;  i++)
                    110:                attr[curr++] = 0;
                    111:
                    112:        /*
                    113:         * Append enough spaces to bring us to the next tab stop.
                    114:         * {{ We could avoid this at the cost of adding some
                    115:         *    complication to the tab stop logic in pappend(). }}
                    116:         */
                    117:        if (tabstop == 0)
                    118:                tabstop = 1;
                    119:        do
                    120:        {
                    121:                linebuf[curr] = ' ';
                    122:                attr[curr++] = AT_NORMAL;
                    123:                column++;
                    124:        } while ((column % tabstop) != 0);
                    125:        lno_indent = column;
                    126: }
                    127:
                    128: /*
                    129:  * Return the printing width of the start (enter) sequence
                    130:  * for a given character attribute.
                    131:  */
                    132:        int
                    133: attr_swidth(a)
                    134:        int a;
                    135: {
                    136:        switch (a)
                    137:        {
                    138:        case AT_BOLD:           return (bo_s_width);
                    139:        case AT_UNDERLINE:      return (ul_s_width);
                    140:        case AT_BLINK:          return (bl_s_width);
                    141:        case AT_STANDOUT:       return (so_s_width);
                    142:        }
                    143:        return (0);
                    144: }
                    145:
                    146: /*
                    147:  * Return the printing width of the end (exit) sequence
                    148:  * for a given character attribute.
                    149:  */
                    150:        int
                    151: attr_ewidth(a)
                    152:        int a;
                    153: {
                    154:        switch (a)
                    155:        {
                    156:        case AT_BOLD:           return (bo_e_width);
                    157:        case AT_UNDERLINE:      return (ul_e_width);
                    158:        case AT_BLINK:          return (bl_e_width);
                    159:        case AT_STANDOUT:       return (so_e_width);
                    160:        }
                    161:        return (0);
                    162: }
                    163:
                    164: /*
                    165:  * Return the printing width of a given character and attribute,
                    166:  * if the character were added to the current position in the line buffer.
                    167:  * Adding a character with a given attribute may cause an enter or exit
                    168:  * attribute sequence to be inserted, so this must be taken into account.
                    169:  */
                    170:        static int
                    171: pwidth(c, a)
                    172:        int c;
                    173:        int a;
                    174: {
                    175:        register int w;
                    176:
                    177:        if (c == '\b')
                    178:                /*
                    179:                 * Backspace moves backwards one position.
                    180:                 */
                    181:                return (-1);
                    182:
                    183:        if (control_char(c))
                    184:                /*
                    185:                 * Control characters do unpredicatable things,
                    186:                 * so we don't even try to guess; say it doesn't move.
                    187:                 * This can only happen if the -r flag is in effect.
                    188:                 */
                    189:                return (0);
                    190:
                    191:        /*
                    192:         * Other characters take one space,
                    193:         * plus the width of any attribute enter/exit sequence.
                    194:         */
                    195:        w = 1;
                    196:        if (curr > 0 && attr[curr-1] != a)
                    197:                w += attr_ewidth(attr[curr-1]);
                    198:        if (a && (curr == 0 || attr[curr-1] != a))
                    199:                w += attr_swidth(a);
                    200:        return (w);
                    201: }
                    202:
                    203: /*
                    204:  * Delete the previous character in the line buffer.
                    205:  */
                    206:        static void
                    207: backc()
                    208: {
                    209:        curr--;
                    210:        column -= pwidth(linebuf[curr], attr[curr]);
                    211: }
                    212:
                    213: /*
                    214:  * Append a character and attribute to the line buffer.
                    215:  */
                    216:        static int
                    217: storec(c, a, pos)
                    218:        int c;
                    219:        int a;
                    220:        POSITION pos;
                    221: {
                    222:        register int w;
                    223:
                    224: #if HILITE_SEARCH
                    225:        if (is_hilited(pos, pos+1, 0))
                    226:                /*
                    227:                 * This character should be highlighted.
                    228:                 * Override the attribute passed in.
                    229:                 */
                    230:                a = AT_STANDOUT;
                    231: #endif
                    232:        w = pwidth(c, a);
                    233:        if (ctldisp > 0 && column + w + attr_ewidth(a) > sc_width)
                    234:                /*
                    235:                 * Won't fit on screen.
                    236:                 */
                    237:                return (1);
                    238:
                    239:        if (curr >= sizeof(linebuf)-2)
                    240:                /*
                    241:                 * Won't fit in line buffer.
                    242:                 */
                    243:                return (1);
                    244:
                    245:        /*
                    246:         * Special handling for "magic cookie" terminals.
                    247:         * If an attribute enter/exit sequence has a printing width > 0,
                    248:         * and the sequence is adjacent to a space, delete the space.
                    249:         * We just mark the space as invisible, to avoid having too
                    250:         * many spaces deleted.
                    251:         * {{ Note that even if the attribute width is > 1, we
                    252:         *    delete only one space.  It's not worth trying to do more.
                    253:         *    It's hardly worth doing this much. }}
                    254:         */
                    255:        if (curr > 0 && a != AT_NORMAL &&
                    256:                linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&
                    257:                attr_swidth(a) > 0)
                    258:        {
                    259:                /*
                    260:                 * We are about to append an enter-attribute sequence
                    261:                 * just after a space.  Delete the space.
                    262:                 */
                    263:                attr[curr-1] = AT_INVIS;
                    264:                column--;
                    265:        } else if (curr > 0 && attr[curr-1] != AT_NORMAL &&
                    266:                attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&
                    267:                attr_ewidth(attr[curr-1]) > 0)
                    268:        {
                    269:                /*
                    270:                 * We are about to append a space just after an
                    271:                 * exit-attribute sequence.  Delete the space.
                    272:                 */
                    273:                a = AT_INVIS;
                    274:                column--;
                    275:        }
                    276:        /* End of magic cookie handling. */
                    277:
                    278:        linebuf[curr] = c;
                    279:        attr[curr] = a;
                    280:        column += w;
                    281:        return (0);
                    282: }
                    283:
                    284: /*
                    285:  * Append a character to the line buffer.
                    286:  * Expand tabs into spaces, handle underlining, boldfacing, etc.
                    287:  * Returns 0 if ok, 1 if couldn't fit in buffer.
                    288:  */
                    289:        public int
                    290: pappend(c, pos)
                    291:        register int c;
                    292:        POSITION pos;
                    293: {
                    294:        if (pendc)
                    295:        {
                    296:                if (do_append(pendc, pendpos))
                    297:                        /*
                    298:                         * Oops.  We've probably lost the char which
                    299:                         * was in pendc, since caller won't back up.
                    300:                         */
                    301:                        return (1);
                    302:                pendc = '\0';
                    303:        }
                    304:
                    305:        if (c == '\r' && bs_mode == BS_SPECIAL)
                    306:        {
                    307:                /*
                    308:                 * Don't put the CR into the buffer until we see
                    309:                 * the next char.  If the next char is a newline,
                    310:                 * discard the CR.
                    311:                 */
                    312:                pendc = c;
                    313:                pendpos = pos;
                    314:                return (0);
                    315:        }
                    316:
                    317:        return (do_append(c, pos));
                    318: }
                    319:
                    320:        static int
                    321: do_append(c, pos)
                    322:        int c;
                    323:        POSITION pos;
                    324: {
                    325:        register char *s;
                    326:        register int a;
                    327:
                    328: #define        STOREC(c,a) \
                    329:        if (storec((c),(a),pos)) return (1); else curr++
                    330:
                    331:        if (overstrike)
                    332:        {
                    333:                /*
                    334:                 * Overstrike the character at the current position
                    335:                 * in the line buffer.  This will cause either
                    336:                 * underline (if a "_" is overstruck),
                    337:                 * bold (if an identical character is overstruck),
                    338:                 * or just deletion of the character in the buffer.
                    339:                 */
                    340:                overstrike = 0;
                    341:                if ((char)c == linebuf[curr])
                    342:                        STOREC(linebuf[curr], AT_BOLD);
                    343:                else if (c == '_')
                    344:                        STOREC(linebuf[curr], AT_UNDERLINE);
                    345:                else if (linebuf[curr] == '_')
                    346:                        STOREC(c, AT_UNDERLINE);
                    347:                else if (control_char(c))
                    348:                        goto do_control_char;
                    349:                else
                    350:                        STOREC(c, AT_NORMAL);
                    351:        } else if (c == '\b')
                    352:        {
                    353:                switch (bs_mode)
                    354:                {
                    355:                case BS_NORMAL:
                    356:                        STOREC(c, AT_NORMAL);
                    357:                        break;
                    358:                case BS_CONTROL:
                    359:                        goto do_control_char;
                    360:                case BS_SPECIAL:
                    361:                        if (curr == 0)
                    362:                                break;
                    363:                        backc();
                    364:                        overstrike = 1;
                    365:                        break;
                    366:                }
                    367:        } else if (c == '\t')
                    368:        {
                    369:                /*
                    370:                 * Expand a tab into spaces.
                    371:                 */
                    372:                if (tabstop == 0)
                    373:                        tabstop = 1;
                    374:                do
                    375:                {
                    376:                        STOREC(' ', AT_NORMAL);
                    377:                } while ((column % tabstop) != 0);
                    378:        } else if (control_char(c))
                    379:        {
                    380:        do_control_char:
                    381:                if (ctldisp == 0)
                    382:                {
                    383:                        /*
                    384:                         * Output as a normal character.
                    385:                         */
                    386:                        STOREC(c, AT_NORMAL);
                    387:                } else
                    388:                {
                    389:                        /*
                    390:                         * Convert to printable representation.
                    391:                         */
                    392:                        s = prchar(c);
                    393:                        a = binattr;
                    394:
                    395:                        /*
                    396:                         * Make sure we can get the entire representation
                    397:                         * of the character on this line.
                    398:                         */
                    399:                        if (column + (int) strlen(s) +
                    400:                            attr_swidth(a) + attr_ewidth(a) > sc_width)
                    401:                                return (1);
                    402:
                    403:                        for ( ;  *s != 0;  s++)
                    404:                                STOREC(*s, a);
                    405:                }
                    406:        } else
                    407:        {
                    408:                STOREC(c, AT_NORMAL);
                    409:        }
                    410:
                    411:        return (0);
                    412: }
                    413:
                    414: /*
                    415:  * Terminate the line in the line buffer.
                    416:  */
                    417:        public void
                    418: pdone(endline)
                    419:        int endline;
                    420: {
                    421:        if (pendc && (pendc != '\r' || !endline))
                    422:                /*
                    423:                 * If we had a pending character, put it in the buffer.
                    424:                 * But discard a pending CR if we are at end of line
                    425:                 * (that is, discard the CR in a CR/LF sequence).
                    426:                 */
                    427:                (void) do_append(pendc, pendpos);
                    428:
                    429:        /*
                    430:         * Add a newline if necessary,
                    431:         * and append a '\0' to the end of the line.
                    432:         */
                    433:        if (column < sc_width || !auto_wrap || ignaw || ctldisp == 0)
                    434:        {
                    435:                linebuf[curr] = '\n';
                    436:                attr[curr] = AT_NORMAL;
                    437:                curr++;
                    438:        }
                    439:        linebuf[curr] = '\0';
                    440:        attr[curr] = AT_NORMAL;
                    441: }
                    442:
                    443: /*
                    444:  * Get a character from the current line.
                    445:  * Return the character as the function return value,
                    446:  * and the character attribute in *ap.
                    447:  */
                    448:        public int
                    449: gline(i, ap)
                    450:        register int i;
                    451:        register int *ap;
                    452: {
                    453:        char *s;
                    454:
                    455:        if (is_null_line)
                    456:        {
                    457:                /*
                    458:                 * If there is no current line, we pretend the line is
                    459:                 * either "~" or "", depending on the "twiddle" flag.
                    460:                 */
                    461:                *ap = AT_NORMAL;
                    462:                s = (twiddle) ? "~\n" : "\n";
                    463:                return (s[i]);
                    464:        }
                    465:
                    466:        *ap = attr[i];
                    467:        return (linebuf[i] & 0377);
                    468: }
                    469:
                    470: /*
                    471:  * Indicate that there is no current line.
                    472:  */
                    473:        public void
                    474: null_line()
                    475: {
                    476:        is_null_line = 1;
                    477: }
                    478:
                    479: #if 1
                    480: /*
                    481:  * Analogous to forw_line(), but deals with "raw lines":
                    482:  * lines which are not split for screen width.
                    483:  * {{ This is supposed to be more efficient than forw_line(). }}
                    484:  */
                    485:        public POSITION
                    486: forw_raw_line(curr_pos, linep)
                    487:        POSITION curr_pos;
                    488:        char **linep;
                    489: {
                    490:        register char *p;
                    491:        register int c;
                    492:        POSITION new_pos;
                    493:
                    494:        if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
                    495:                (c = ch_forw_get()) == EOI)
                    496:                return (NULL_POSITION);
                    497:
                    498:        p = linebuf;
                    499:
                    500:        for (;;)
                    501:        {
                    502:                if (c == '\n' || c == EOI)
                    503:                {
                    504:                        new_pos = ch_tell();
                    505:                        break;
                    506:                }
                    507:                if (p >= &linebuf[sizeof(linebuf)-1])
                    508:                {
                    509:                        /*
                    510:                         * Overflowed the input buffer.
                    511:                         * Pretend the line ended here.
                    512:                         * {{ The line buffer is supposed to be big
                    513:                         *    enough that this never happens. }}
                    514:                         */
                    515:                        new_pos = ch_tell() - 1;
                    516:                        break;
                    517:                }
                    518:                *p++ = c;
                    519:                c = ch_forw_get();
                    520:        }
                    521:        *p = '\0';
                    522:        if (linep != NULL)
                    523:                *linep = linebuf;
                    524:        return (new_pos);
                    525: }
                    526:
                    527: /*
                    528:  * Analogous to back_line(), but deals with "raw lines".
                    529:  * {{ This is supposed to be more efficient than back_line(). }}
                    530:  */
                    531:        public POSITION
                    532: back_raw_line(curr_pos, linep)
                    533:        POSITION curr_pos;
                    534:        char **linep;
                    535: {
                    536:        register char *p;
                    537:        register int c;
                    538:        POSITION new_pos;
                    539:
                    540:        if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
                    541:                ch_seek(curr_pos-1))
                    542:                return (NULL_POSITION);
                    543:
                    544:        p = &linebuf[sizeof(linebuf)];
                    545:        *--p = '\0';
                    546:
                    547:        for (;;)
                    548:        {
                    549:                c = ch_back_get();
                    550:                if (c == '\n')
                    551:                {
                    552:                        /*
                    553:                         * This is the newline ending the previous line.
                    554:                         * We have hit the beginning of the line.
                    555:                         */
                    556:                        new_pos = ch_tell() + 1;
                    557:                        break;
                    558:                }
                    559:                if (c == EOI)
                    560:                {
                    561:                        /*
                    562:                         * We have hit the beginning of the file.
                    563:                         * This must be the first line in the file.
                    564:                         * This must, of course, be the beginning of the line.
                    565:                         */
                    566:                        new_pos = ch_zero();
                    567:                        break;
                    568:                }
                    569:                if (p <= linebuf)
                    570:                {
                    571:                        /*
                    572:                         * Overflowed the input buffer.
                    573:                         * Pretend the line ended here.
                    574:                         */
                    575:                        new_pos = ch_tell() + 1;
                    576:                        break;
                    577:                }
                    578:                *--p = c;
                    579:        }
                    580:        if (linep != NULL)
                    581:                *linep = p;
                    582:        return (new_pos);
                    583: }
                    584: #endif