[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     ! 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