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

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