Annotation of 43BSD/contrib/mh/miscellany/less/line.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Routines to manipulate the "line buffer".
        !             3:  * The line buffer holds a line of output as it is being built
        !             4:  * in preparation for output to the screen.
        !             5:  * We keep track of the PRINTABLE length of the line as it is being built.
        !             6:  */
        !             7: 
        !             8: #include "less.h"
        !             9: 
        !            10: static char linebuf[1024];     /* Buffer which holds the current output line */
        !            11: static char *curr;             /* Pointer into linebuf */
        !            12: static int column;             /* Printable length, accounting for
        !            13:                                   backspaces, etc. */
        !            14: /*
        !            15:  * A ridiculously complex state machine takes care of backspaces 
        !            16:  * when in BS_UNDERLINE mode.  The complexity arises from the attempt
        !            17:  * to deal with all cases, especially involving long lines with underlining.
        !            18:  * There are still some cases which will break it.
        !            19:  *
        !            20:  * There are four states:
        !            21:  *     UL_NORMAL is the normal state (not in underline mode).
        !            22:  *     UL_YES means we are in underline mode.  We expect to get
        !            23:  *             either a sequence like "_\bX" or "X\b_" to continue
        !            24:  *             underline mode, or just some ordinary characters
        !            25:  *             (no backspaces) to end underline mode.
        !            26:  *     UL_X means we are one character after UL_YES
        !            27:  *             (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
        !            28:  *     UL_XB means we are one character after UL_X 
        !            29:  *             (we have gotten the backspace in "_\bX" or "X\b_";
        !            30:  *             we expect one more ordinary character, 
        !            31:  *             which will put us back in state UL_YES).
        !            32:  */
        !            33: static int ul_state;           /* Currently in underline mode? */
        !            34: #define        UL_NORMAL       0       /* Not in underline mode */
        !            35: #define        UL_YES          1       /* In underline, need next char */
        !            36: #define        UL_X            2       /* In underline, got char, need \b */
        !            37: #define        UL_XB           3       /* In underline, got char & \b, need one more */
        !            38: 
        !            39: public char *line;             /* Pointer to the current line.
        !            40:                                   Usually points to linebuf. */
        !            41: 
        !            42: extern int bs_mode;
        !            43: extern int tabstop;
        !            44: extern int ul_width, ue_width;
        !            45: extern int sc_width, sc_height;
        !            46: 
        !            47: /*
        !            48:  * Rewind the line buffer.
        !            49:  */
        !            50:        public void
        !            51: prewind()
        !            52: {
        !            53:        line = curr = linebuf;
        !            54:        ul_state = UL_NORMAL;
        !            55:        column = 0;
        !            56: }
        !            57: 
        !            58: /*
        !            59:  * Append a character to the line buffer.
        !            60:  * Expand tabs into spaces, handle underlining.
        !            61:  * Returns 0 if ok, 1 if couldn't fit in buffer.
        !            62:  */
        !            63: 
        !            64: #define        NEW_COLUMN(newcol)      if ((newcol) + ((ul_state)?ue_width:0) > sc_width) \
        !            65:                                        return (1); else column = (newcol)
        !            66: 
        !            67:        public int
        !            68: pappend(c)
        !            69:        int c;
        !            70: {
        !            71:        if (c == '\0')
        !            72:        {
        !            73:                /*
        !            74:                 * Terminate underline mode, if necessary.
        !            75:                 * Append a '\0' to the end of the line.
        !            76:                 */
        !            77:                switch (ul_state)
        !            78:                {
        !            79:                case UL_X:
        !            80:                        curr[0] = curr[-1];
        !            81:                        curr[-1] = UE_CHAR;
        !            82:                        curr++;
        !            83:                        break;
        !            84:                case UL_XB:
        !            85:                case UL_YES:
        !            86:                        *curr++ = UE_CHAR;
        !            87:                        break;
        !            88:                }
        !            89:                ul_state = UL_NORMAL;
        !            90:                *curr = '\0';
        !            91:                return (0);
        !            92:        }
        !            93: 
        !            94:        if (curr > linebuf + sizeof(linebuf) - 12)
        !            95:                /*
        !            96:                 * Almost out of room in the line buffer.
        !            97:                 * Don't take any chances.
        !            98:                 * {{ Linebuf is supposed to be big enough that this
        !            99:                 *    will never happen, but may need to be made 
        !           100:                 *    bigger for wide screens or lots of backspaces. }}
        !           101:                 */
        !           102:                return (1);
        !           103: 
        !           104:        if (bs_mode == BS_UNDERLINE)
        !           105:        {
        !           106:                /*
        !           107:                 * Advance the state machine.
        !           108:                 */
        !           109:                switch (ul_state)
        !           110:                {
        !           111:                case UL_NORMAL:
        !           112:                        if (curr <= linebuf + 1 || curr[-1] != '\b')
        !           113:                                break;
        !           114:                        if (c != '_' && curr[-2] != '_')
        !           115:                        {
        !           116:                                curr -= 2;
        !           117:                                break;
        !           118:                        }
        !           119: 
        !           120:                        /*
        !           121:                         * We have either "_\bX" or "X\b_" (including
        !           122:                         * the current char).  Switch into underline mode.
        !           123:                         */
        !           124:                        if (column + ul_width + ue_width + 1 >= sc_width)
        !           125:                                /*
        !           126:                                 * Not enough room left on the screen to 
        !           127:                                 * enter and exit underline mode.
        !           128:                                 */
        !           129:                                return (1);
        !           130: 
        !           131:                        if (ul_width > 0 && 
        !           132:                            curr > linebuf + 2 && curr[-3] == ' ')
        !           133:                        {
        !           134:                                /*
        !           135:                                 * Special case for magic cookie terminals:
        !           136:                                 * if the previous char was a space, replace 
        !           137:                                 * it with the "enter underline" sequence.
        !           138:                                 */
        !           139:                                curr[-3] = UL_CHAR;
        !           140:                                column += ul_width-1;
        !           141:                        } else
        !           142:                        {
        !           143:                                curr[-1] = curr[-2];
        !           144:                                curr[-2] = UL_CHAR;
        !           145:                                column += ul_width;
        !           146:                                curr++;
        !           147:                        }
        !           148:                        /* Fall thru */
        !           149:                case UL_XB:
        !           150:                        /*
        !           151:                         * Termination of a sequnce "_\bX" or "X\b_".
        !           152:                         */
        !           153:                        if (c == '_')
        !           154:                                c = curr[-2];
        !           155:                        curr -= 2;
        !           156:                        ul_state = UL_YES;
        !           157:                        break;
        !           158:                case UL_YES:
        !           159:                        if (column + ue_width + 1 >= sc_width)
        !           160:                                /*
        !           161:                                 * We have just barely enough room to 
        !           162:                                 * exit underline mode.  
        !           163:                                 */
        !           164:                                return (1);
        !           165:                        ul_state = UL_X;
        !           166:                        break;
        !           167:                case UL_X:
        !           168:                        if (c == '\b')
        !           169:                                ul_state = UL_XB;
        !           170:                        else
        !           171:                        {
        !           172:                                /*
        !           173:                                 * Exit underline mode.
        !           174:                                 * We have to shuffle the chars a bit
        !           175:                                 * to make this work.
        !           176:                                 */
        !           177:                                curr[0] = curr[-1];
        !           178:                                curr[-1] = UE_CHAR;
        !           179:                                column += ue_width;
        !           180:                                if (ul_width > 0 && curr[0] == ' ')
        !           181:                                        /*
        !           182:                                         * Another special case for magic
        !           183:                                         * cookie terminals: if the next
        !           184:                                         * char is a space, replace it
        !           185:                                         * with the "exit underline" sequence.
        !           186:                                         */
        !           187:                                        column--;
        !           188:                                else
        !           189:                                        curr++;
        !           190:                                ul_state = UL_NORMAL;
        !           191:                        } 
        !           192:                        break;
        !           193:                }
        !           194:        }
        !           195:        
        !           196:        if (c == '\t') 
        !           197:        {
        !           198:                /*
        !           199:                 * Expand a tab into spaces.
        !           200:                 */
        !           201:                do
        !           202:                {
        !           203:                        NEW_COLUMN(column+1);
        !           204:                } while ((column % tabstop) != 0);
        !           205:                *curr++ = '\t';
        !           206:                return (0);
        !           207:        }
        !           208: 
        !           209:        if (c == '\b')
        !           210:        {
        !           211:                if (bs_mode == BS_CONTROL)
        !           212:                {
        !           213:                        /*
        !           214:                         * Treat backspace as a control char: output "^H".
        !           215:                         */
        !           216:                        NEW_COLUMN(column+2);
        !           217:                        *curr++ = ('H' | 0200);
        !           218:                } else
        !           219:                {
        !           220:                        /*
        !           221:                         * Output a real backspace.
        !           222:                         */
        !           223:                        column--;
        !           224:                        *curr++ = '\b';
        !           225:                }
        !           226:                return (0);
        !           227:        } 
        !           228: 
        !           229:        if (control_char(c))
        !           230:        {
        !           231:                /*
        !           232:                 * Put a "^X" into the buffer.
        !           233:                 * The 0200 bit is used to tell put_line() to prefix
        !           234:                 * the char with a ^.  We don't actually put the ^
        !           235:                 * in the buffer because we sometimes need to move
        !           236:                 * chars around, and such movement might separate 
        !           237:                 * the ^ from its following character.
        !           238:                 */
        !           239:                NEW_COLUMN(column+2);
        !           240:                *curr++ = (carat_char(c) | 0200);
        !           241:                return (0);
        !           242:        }
        !           243: 
        !           244:        /*
        !           245:         * Ordinary character.  Just put it in the buffer.
        !           246:         */
        !           247:        NEW_COLUMN(column+1);
        !           248:        *curr++ = c;
        !           249:        return (0);
        !           250: }
        !           251: 
        !           252: /*
        !           253:  * Analogous to forw_line(), but deals with "raw lines":
        !           254:  * lines which are not split for screen width.
        !           255:  * {{ This is supposed to be more efficient than forw_line(). }}
        !           256:  */
        !           257:        public POSITION
        !           258: forw_raw_line(curr_pos)
        !           259:        POSITION curr_pos;
        !           260: {
        !           261:        register char *p;
        !           262:        register int c;
        !           263:        POSITION new_pos;
        !           264: 
        !           265:        if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
        !           266:                (c = ch_forw_get()) == EOF)
        !           267:                return (NULL_POSITION);
        !           268: 
        !           269:        p = linebuf;
        !           270: 
        !           271:        for (;;)
        !           272:        {
        !           273:                if (c == '\n' || c == EOF)
        !           274:                {
        !           275:                        new_pos = ch_tell();
        !           276:                        break;
        !           277:                }
        !           278:                if (p >= &linebuf[sizeof(linebuf)-1])
        !           279:                {
        !           280:                        /*
        !           281:                         * Overflowed the input buffer.
        !           282:                         * Pretend the line ended here.
        !           283:                         * {{ The line buffer is supposed to be big
        !           284:                         *    enough that this never happens. }}
        !           285:                         */
        !           286:                        new_pos = ch_tell() - 1;
        !           287:                        break;
        !           288:                }
        !           289:                *p++ = c;
        !           290:                c = ch_forw_get();
        !           291:        }
        !           292:        *p = '\0';
        !           293:        line = linebuf;
        !           294:        return (new_pos);
        !           295: }
        !           296: 
        !           297: /*
        !           298:  * Analogous to back_line(), but deals with "raw lines".
        !           299:  * {{ This is supposed to be more efficient than back_line(). }}
        !           300:  */
        !           301:        public POSITION
        !           302: back_raw_line(curr_pos)
        !           303:        POSITION curr_pos;
        !           304: {
        !           305:        register char *p;
        !           306:        register int c;
        !           307:        POSITION new_pos;
        !           308: 
        !           309:        if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
        !           310:                ch_seek(curr_pos-1))
        !           311:                return (NULL_POSITION);
        !           312: 
        !           313:        p = &linebuf[sizeof(linebuf)];
        !           314:        *--p = '\0';
        !           315: 
        !           316:        for (;;)
        !           317:        {
        !           318:                c = ch_back_get();
        !           319:                if (c == '\n')
        !           320:                {
        !           321:                        /*
        !           322:                         * This is the newline ending the previous line.
        !           323:                         * We have hit the beginning of the line.
        !           324:                         */
        !           325:                        new_pos = ch_tell() + 1;
        !           326:                        break;
        !           327:                }
        !           328:                if (c == EOF)
        !           329:                {
        !           330:                        /*
        !           331:                         * We have hit the beginning of the file.
        !           332:                         * This must be the first line in the file.
        !           333:                         * This must, of course, be the beginning of the line.
        !           334:                         */
        !           335:                        new_pos = (POSITION)0;
        !           336:                        break;
        !           337:                }
        !           338:                if (p <= linebuf)
        !           339:                {
        !           340:                        /*
        !           341:                         * Overflowed the input buffer.
        !           342:                         * Pretend the line ended here.
        !           343:                         */
        !           344:                        new_pos = ch_tell() + 1;
        !           345:                        break;
        !           346:                }
        !           347:                *--p = c;
        !           348:        }
        !           349:        line = p;
        !           350:        return (new_pos);
        !           351: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.