Annotation of 43BSD/contrib/mh/miscellany/less/line.c, revision 1.1.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.