From mboxrd@z Thu Jan 1 00:00:00 1970 From: Micah Stetson To: 9fans@cse.psu.edu Subject: Re: [9fans] The VT saga Message-ID: <20001107063227.A21140@cnm-vra.com> References: <200009110945.LAA19698@chuckle.iba.co.za> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="mYCpIKhGyMATD0i+" Content-Transfer-Encoding: 8bit User-Agent: Mutt/1.0.1i In-Reply-To: <200009110945.LAA19698@chuckle.iba.co.za>; from lucio@proxima.alt.za on Mon, Sep 11, 2000 at 11:41:21AM +0000 Date: Tue, 7 Nov 2000 06:32:27 -0800 Topicbox-Message-UUID: 22c36c86-eac9-11e9-9e20-41e7f4b1d025 --mYCpIKhGyMATD0i+ Content-Type: text/plain; charset=us-ascii I took a look at vt tonight and while vttest still just turns up its nose at me, it looks like I've eliminated most of the real problems I had with Mutt and vi. There are mainly three changes. First, I finally think I've got scroll() right. Lucio's previous patch said > /mnt/wrap/sys/src/cmd/vt/main.c:577 c /sys/src/cmd/vt/main.c:584 > < draw(screen, Rpt(pt(0, dy), pt(xmax+1, ly-sy)), screen, nil, pt(0, sy)); > --- > > draw(screen, Rpt(pt(0, dy), pt(xmax+1, ly+1)), screen, nil, pt(0, sy)); but it should really have dy+ly-sy in there. Second, I made the delete to end of line and delete to end of screen stuff work like the vt100 user's guide says it should. Last, I changed yscrmin and yscrmax so that they are always the beginning and end of the scrolling region even when that region is the whole screen. That got rid of a bunch of lines that looked like M = yscrmax ? yscrmax : ymax; Maybe there was a good reason for doing it the other way, but I don't see it. Anyway, attached are my current in-progress versions of vt.c and main.c from /sys/src/cmd/vt. They do not include the patch Lucio posted to this list a few weeks back. I hope this is helpful to somebody. Micah --mYCpIKhGyMATD0i+ Content-Type: text/x-csrc Content-Disposition: attachment; filename="main.c" #include #include #include #include #include #include #include "cons.h" enum{ Ehost = 4, }; char *menutext2[] = { "backup", "forward", "reset", "clear", "send", "page", 0 }; char *menutext3[] = { "24x80", "crnl", "nl", "raw", 0 }; /* variables associated with the screen */ int x, y; /* character positions */ char *backp; int backc; int atend; int nbacklines; int xmax, ymax; int blocked; int resize_flag; int pagemode; int olines; int peekc; Menu menu2; Menu menu3; char *histp; char hist[HISTSIZ]; int yscrmin, yscrmax; Image *text; Image *background; Image *bordercol; Image *cursback; Image *red; /* terminal control */ struct ttystate ttystate[2] = { {0, 1}, {0,0} }; int NS; int CW; Consstate *cs; Mouse mouse; int outfd = -1; Biobuf *snarffp = 0; char *host_buf; char *hostp; /* input from host */ int host_bsize = 2*BSIZE; int hostlength; /* amount of input from host */ char echo_input[BSIZE]; char *echop = echo_input; /* characters to echo, after canon */ char sendbuf[BSIZE]; /* hope you can't type ahead more than BSIZE chars */ char *sendp = sendbuf; /* functions */ void initialize(int, char **); void ebegin(int); int waitchar(void); int rcvchar(void); void set_input(char *); void set_host(Event *); void bigscroll(void); void readmenu(void); void eresized(int); void resize(void); void send_interrupt(void); int alnum(int); void escapedump(int,uchar *,int); char *term; struct funckey *fk; int debug; void main(int argc, char **argv) { initialize(argc, argv); emulate(); } void initialize(int argc, char **argv) { int dayglo = 1; rfork(RFENVG|RFNAMEG|RFNOTEG); term = "vt100"; fk = vt100fk; ARGBEGIN{ case '2': term = "vt220"; fk = vt220fk; break; case 's': /* for sape */ dayglo = 0; break; }ARGEND; host_buf = malloc(host_bsize); hostp = host_buf; hostlength = 0; if(initdraw(0,0,term) < 0){ fprint(2, "%s: initdraw failed: %r\n", term); exits("initdraw"); } ebegin(Ehost); histp = hist; menu2.item = menutext2; menu3.item = menutext3; pagemode = 0; blocked = 0; NS = font->height ; CW = stringwidth(font, "m"); text = allocimage(display, Rect(0,0,1,1), screen->chan, 1, dayglo?0x00F0F0F0:0); background = allocimage(display, Rect(0,0,1,1), screen->chan, 1, dayglo?0x33333333:-1); bordercol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCCC); cursback = allocimage(display, Rect(0, 0, CW+1, NS+1), screen->chan, 0, DNofill); red = allocimage(display, Rect(0, 0, CW+1, NS+1), screen->chan, 1, DRed); eresized(0); if(argc > 0) { sendnchars(strlen(argv[0]),argv[0]); sendnchars(1,"\n"); } } void clear(Rectangle r) { draw(screen, r, background, nil, ZP); } void newline(void) { nbacklines--; if(y >= yscrmax) { y = yscrmax; if(pagemode && olines >= yscrmax) { blocked = 1; return; } scroll(yscrmin+1, yscrmax+1, yscrmin, yscrmax); } else y++; olines++; } void curson(int bl) { Image *col; draw(cursback, cursback->r, screen, nil, subpt(pt(x, y),Pt(1,1))); if(bl) col = red; else col = bordercol; border(screen, Rpt(subpt(pt(x, y), Pt(1, 1)), addpt(pt(x, y), Pt(CW,NS))), 2, col, ZP); } void cursoff(void) { draw(screen, Rpt(subpt(pt(x, y), Pt(1, 1)), addpt(pt(x, y), Pt(CW,NS))), cursback, nil, cursback->r.min); } int get_next_char(void) { int c = peekc; peekc = 0; if(c > 0) return(c); while(c <= 0) { if(backp) { c = *backp; if(c && nbacklines >= 0) { backp++; if(backp >= &hist[HISTSIZ]) backp = hist; return(c); } backp = 0; } c = waitchar(); } *histp++ = c; if(histp >= &hist[HISTSIZ]) histp = hist; *histp = '\0'; return(c); } int canon(char *ep, int c) { if(c&0200) return(SCROLL); switch(c) { case '\b': if(sendp > sendbuf) sendp--; *ep++ = '\b'; *ep++ = ' '; *ep++ = '\b'; break; case 0x15: /* ^U line kill */ sendp = sendbuf; *ep++ = '^'; *ep++ = 'U'; *ep++ = '\n'; break; case 0x17: /* ^W word kill */ while(sendp > sendbuf && !alnum(*sendp)) { *ep++ = '\b'; *ep++ = ' '; *ep++ = '\b'; sendp--; } while(sendp > sendbuf && alnum(*sendp)) { *ep++ = '\b'; *ep++ = ' '; *ep++ = '\b'; sendp--; } break; case '\177': /* interrupt */ sendp = sendbuf; send_interrupt(); return(NEWLINE); case '\021': /* quit */ case '\r': case '\n': if(sendp < &sendbuf[512]) *sendp++ = '\n'; sendnchars((int)(sendp-sendbuf), sendbuf); sendp = sendbuf; if(c == '\n' || c == '\r') { *ep++ = '\n'; } *ep = 0; return(NEWLINE); case '\004': /* EOT */ if(sendp == sendbuf) { sendnchars(0,sendbuf); *ep = 0; return(NEWLINE); } /* fall through */ default: if(sendp < &sendbuf[512]) *sendp++ = c; *ep++ = c; break; } *ep = 0; return(OTHER); } void sendfk(char *name) { int i; static int fd; for(i=0; fk[i].name; i++) if(strcmp(name, fk[i].name)==0){ sendnchars2(strlen(fk[i].sequence), fk[i].sequence); return; } } int waitchar(void) { Event e; int c; char c2; int newmouse; int wasblocked; int kbdchar = -1; char echobuf[3*BSIZE]; static int lastc = -1; for(;;) { if(resize_flag) resize(); wasblocked = blocked; if(backp) return(0); if(ecanmouse() && (button2() || button3())) readmenu(); if(snarffp) { if((c = Bgetc(snarffp)) < 0) { if(lastc != '\n') write(outfd,"\n",1); Bterm(snarffp); snarffp = 0; if(lastc != '\n') { lastc = -1; return('\n'); } lastc = -1; continue; } lastc = c; c2 = c; write(outfd, &c2, 1); return(c); } if(!blocked && host_avail()) return(rcvchar()); if(kbdchar > 0) { if(blocked) resize(); if(cs->raw) { switch(kbdchar){ case Kup: sendfk("up key"); break; case Kdown: sendfk("down key"); break; case Kleft: sendfk("left key"); break; case Kright: sendfk("right key"); break; default: echobuf[0] = kbdchar; sendnchars(1, echobuf); break; } } else if(canon(echobuf,kbdchar) == SCROLL) { if(!blocked) bigscroll(); } else strcat(echo_input,echobuf); blocked = 0; kbdchar = -1; continue; } curson(wasblocked); /* turn on cursor while we're waiting */ do { newmouse = 0; switch(eread(blocked ? Emouse|Ekeyboard : Emouse|Ekeyboard|Ehost, &e)) { case Emouse: mouse = e.mouse; if(button2() || button3()) readmenu(); else if(resize_flag == 0) { /* eresized() is triggered by special mouse event */ newmouse = 1; } break; case Ekeyboard: kbdchar = e.kbdc; break; case Ehost: set_host(&e); break; default: perror("protocol violation"); exits("protocol violation"); } } while(newmouse == 1); cursoff(); /* turn cursor back off */ } return 1; /* to shut up compiler */ } void eresized(int new) { resize_flag = 1+new; } void exportsize(void) { int fd; char buf[10]; if((fd = create("/env/LINES", OWRITE, 0644)) > 0) { sprint(buf,"%d",ymax+1); write(fd,buf,strlen(buf)); close(fd); } if((fd = create("/env/COLS", OWRITE, 0644)) > 0) { sprint(buf,"%d",xmax+1); write(fd,buf,strlen(buf)); close(fd); } if((fd = create("/env/TERM", OWRITE, 0644)) > 0) { fprint(fd, "%s", term); close(fd); } } void resize(void) { if(resize_flag > 1 && getwindow(display, Refnone) < 0){ fprint(2, "can't reattach to window: %r\n"); exits("can't reattach to window"); } xmax = (Dx(screen->r)-2*XMARGIN)/CW-1; ymax = (Dy(screen->r)-2*YMARGIN)/NS-1; if(xmax == 0 || ymax == 0) exits("window gone"); x = 0; y = 0; yscrmin = 0; yscrmax = ymax; olines = 0; exportsize(); clear(screen->r); resize_flag = 0; } void readmenu(void) { Rectangle r; int fd; if(button3()) { menu3.item[1] = ttystate[cs->raw].crnl ? "cr" : "crnl"; menu3.item[2] = ttystate[cs->raw].nlcr ? "nl" : "nlcr"; menu3.item[3] = cs->raw ? "cooked" : "raw"; switch(emenuhit(3, &mouse, &menu3)) { case 0: /* 24x80 */ r.min = screen->r.min; r.max = addpt(screen->r.min, Pt(80*CW+2*XMARGIN+2*INSET, 24*NS+2*YMARGIN+2*INSET)); fd = open("/dev/wctl", OWRITE); if(fd < 0 || fprint(fd, "resize -dx %d -dy %d\n", Dx(r)+2*Borderwidth, Dy(r)+2*Borderwidth) < 0){ border(screen, r, INSET, bordercol, ZP); xmax = 79; ymax = 23; exportsize(); } if(fd >= 0) close(fd); return; case 1: /* newline after cr? */ ttystate[cs->raw].crnl = !ttystate[cs->raw].crnl; return; case 2: /* cr after newline? */ ttystate[cs->raw].nlcr = !ttystate[cs->raw].nlcr; return; case 3: /* switch raw mode */ cs->raw = !cs->raw; return; } return; } menu2.item[5] = pagemode? "scroll": "page"; switch(emenuhit(2, &mouse, &menu2)) { case 0: /* back up */ if(atend == 0) { backc++; backup(backc); } return; case 1: /* move forward */ backc--; if(backc >= 0) backup(backc); else backc = 0; return; case 2: /* reset */ backc = 0; backup(0); return; case 3: /* clear screen */ eresized(0); return; case 4: /* send the snarf buffer */ snarffp = Bopen("/dev/snarf",OREAD); return; case 5: /* pause and clear at end of screen */ pagemode = 1-pagemode; if(blocked && !pagemode) { eresized(0); blocked = 0; } return; } } void backup(int count) { register n; register char *cp; eresized(0); n = 3*(count+1)*ymax/4; cp = histp; atend = 0; while (n >= 0) { cp--; if(cp < hist) cp = &hist[HISTSIZ-1]; if(*cp == '\0') { atend = 1; break; } if(*cp == '\n') n--; } cp++; if(cp >= &hist[HISTSIZ]) cp = hist; backp = cp; nbacklines = ymax-2; } Point pt(int x, int y) { return addpt(screen->r.min, Pt(x*CW+XMARGIN,y*NS+YMARGIN)); } void scroll(int sy, int ly, int dy, int cy) /* source, limit, dest, which line to clear */ { draw(screen, Rpt(pt(0, dy), pt(xmax+1, dy+ly-sy)), screen, nil, pt(0, sy)); clear(Rpt(pt(0, cy), pt(xmax+1, cy+1))); } void bigscroll(void) /* scroll up half a page */ { int half = ymax/3; if(x == 0 && y == 0) return; if(y < half) { clear(Rpt(pt(0,0),pt(xmax+1,ymax+1))); x = y = 0; return; } draw(screen, Rpt(pt(0, 0), pt(xmax+1, ymax+1)), screen, nil, pt(0, half)); clear(Rpt(pt(0,y-half+1),pt(xmax+1,ymax+1))); y -= half; if(olines) olines -= half; } int number(char *p) { int c, n = 0; while ((c = get_next_char()) >= '0' && c <= '9') n = n*10 + c - '0'; *p = c; return(n); } /* stubs */ void sendnchars(int n,char *p) { sendnchars2(n, p); p[n+1] = 0; } void sendnchars2(int n,char *p) { if(write(outfd,p,n) < 0) { close(outfd); close(0); close(1); close(2); exits("write"); } } int host_avail(void) { return(*echop || ((hostp - host_buf) < hostlength)); } int rcvchar(void) { int c; if(*echop) { c = *echop++; if(!*echop) { echop = echo_input; *echop = 0; } return c; } return *hostp++; } void set_host(Event *e) { hostlength = e->n; if(hostlength > host_bsize) { host_bsize *= 2; host_buf = realloc(host_buf,host_bsize); } hostp = host_buf; memmove(host_buf,e->data,hostlength); host_buf[hostlength]=0; } void ringbell(void){ } int alnum(int c) { if(c >= 'a' && c <= 'z') return 1; if(c >= 'A' && c <= 'Z') return 1; if(c >= '0' && c <= '9') return 1; return 0; } void escapedump(int fd,uchar *str,int len) { int i; for(i = 0; i < len; i++) { if((str[i] < ' ' || str[i] > '\177') && str[i] != '\n' && str[i] != '\t') fprint(fd,"^%c",str[i]+64); else if(str[i] == '\177') fprint(fd,"^$"); else if(str[i] == '\n') fprint(fd,"^J\n"); else fprint(fd,"%c",str[i]); } } void funckey(int key) { if(key >= NKEYS) return; if(fk[key].name == 0) return; sendnchars2(strlen(fk[key].sequence), fk[key].sequence); } void drawstring(Point p, char *str, int standout) { Image *txt, *bg; txt = text; bg = background; if(standout){ txt = background; bg = text; } draw(screen, Rpt(p, addpt(p, stringsize(font, str))), bg, nil, p); string(screen, p, txt, ZP, font, str); } --mYCpIKhGyMATD0i+ Content-Type: text/x-csrc Content-Disposition: attachment; filename="vt.c" Content-Transfer-Encoding: 8bit #include #include #include #include #include #include "cons.h" struct funckey vt100fk[NKEYS] = { { "up key", "\033OA", }, { "down key", "\033OB", }, { "left key", "\033OD", }, { "right key", "\033OC", }, }; struct funckey vt220fk[NKEYS] = { { "up key", "\033[A", }, { "down key", "\033[B", }, { "left key", "\033[D", }, { "right key", "\033[C", }, }; char gmap[256] = { ['_'] ' ', /* blank */ ['\\'] '*', /* diamond */ ['a'] 'X', /* checkerboard */ ['b'] '\t', /* HT */ ['c'] '\x0C', /* FF */ ['d'] '\r', /* CR */ ['e'] '\n', /* LF */ ['f'] 'o', /* degree */ ['g'] '+', /* plus/minus */ ['h'] '\n', /* NL, but close enough */ ['i'] '\v', /* VT */ ['j'] '+', /* lower right corner */ ['k'] '+', /* upper right corner */ ['l'] '+', /* upper left corner */ ['m'] '+', /* lower left corner */ ['n'] '+', /* crossing lines */ ['o'] '-', /* horiz line - scan 1 */ ['p'] '-', /* horiz line - scan 3 */ ['q'] '-', /* horiz line - scan 5 */ ['r'] '-', /* horiz line - scan 7 */ ['s'] '-', /* horiz line - scan 9 */ ['t'] '+', /* |- */ ['u'] '+', /* -| */ ['v'] '+', /* upside down T */ ['w'] '+', /* rightside up T */ ['x'] '|', /* vertical bar */ ['y'] '<', /* less/equal */ ['z'] '>', /* gtr/equal */ ['{'] 'p', /* pi */ ['|'] '!', /* not equal */ ['}'] 'L', /* pound symbol */ ['~'] '.', /* centered dot: ยท */ }; void emulate(void) { char buf[BUFS+1]; int n; int c; int standout; int operand, prevOperand; int savex, savey; int isgraphics; int g0set, g1set; standout = 0; isgraphics = 0; g0set = 'B'; /* US ASCII */ g1set = 'B'; /* US ASCII */ savex = savey = 0; yscrmin = 0; yscrmax = ymax; for (;;) { if (y > ymax) { x = 0; newline(); } buf[0] = get_next_char(); buf[1] = '\0'; switch(buf[0]) { case '\000': case '\001': case '\002': case '\003': case '\004': case '\005': case '\006': break; case '\007': /* bell */ ringbell(); break; case '\010': /* backspace */ if (x > 0) --x; break; case '\011': /* tab modulo 8 */ x = (x|7)+1; break; case '\012': /* linefeed */ case '\013': case '\014': newline(); standout = 0; if (ttystate[cs->raw].nlcr) x = 0; break; case '\015': /* carriage return */ x = 0; standout = 0; if (ttystate[cs->raw].crnl) newline(); break; case '\016': /* SO: invoke G1 char set */ isgraphics = (isdigit(g1set)); break; case '\017': /* SI: invoke G0 char set */ isgraphics = (isdigit(g0set)); break; case '\020': /* DLE */ case '\021': /* DC1 */ case '\022': /* XON */ case '\023': /* DC3 */ case '\024': /* XOFF */ case '\025': /* NAK */ case '\026': /* SYN */ case '\027': /* ETB */ case '\030': /* CAN: cancel escape sequence */ case '\031': /* EM: cancel escape sequence, display checkerboard */ case '\032': /* SUB: same as CAN */ break; /* ESC, \033, is handled below */ case '\034': /* FS */ case '\035': /* GS */ case '\036': /* RS */ case '\037': /* US */ break; case '\177': /* delete: ignored */ break; case '\033': switch(get_next_char()){ /* * 7 - save cursor position. */ case '7': savex = x; savey = y; break; /* * 8 - restore cursor position. */ case '8': x = savex; y = savey; break; /* * Received c. Reset terminal. */ case 'c': break; /* * Received D. Active position down a line, scroll if at bottom margin. */ case 'D': if(++y > yscrmax) { y = yscrmax; scroll(yscrmin+1, yscrmax+1, yscrmin, yscrmax); } break; /* * Received E. Active position to start of next line, scroll if at bottom margin. */ case 'E': x = 0; if(++y > yscrmax) { y = yscrmax; scroll(yscrmin+1, yscrmax+1, yscrmin, yscrmax); } break; /* * Received M. Active position up a line, scroll if at top margin.. */ case 'M': if(--y < yscrmin) { y = yscrmin; scroll(yscrmin, yscrmax, yscrmin+1, yscrmin); } break; /* * Z - Identification. The terminal * emulator will return the response * code for a generic VT100. */ case 'Z': Ident: sendnchars2(5, "\033[?6c"); break; /* * H - go home. (No, I think this sets a horizontal tab stop.) */ case 'H': // x = y = 0; break; /* * > - set numeric keypad mode on */ case '>': break; /* * = - set numeric keypad mode off */ case '=': break; /* * # - Takes a one-digit argument that * we need to snarf away. */ case '#': get_next_char(); break; /* * ( - switch G0 character set */ case '(': g0set = get_next_char(); break; /* * - switch G1 character set */ case ')': g1set = get_next_char(); break; /* * Received left bracket. */ case '[': /* * A semi-colon or ? delimits arguments. Only keep one * previous argument (plus the current one) around. */ operand = number(buf); prevOperand = 0; while(buf[0] == ';' || buf[0] == '?'){ prevOperand = operand; operand = number(buf); } /* * do escape2 stuff */ switch(buf[0]){ /* * c - same as ESC Z: who are you? */ case 'c': goto Ident; /* * l - clear various options. */ case 'l': break; /* * h - set various options. */ case 'h': break; /* * A - cursor up. */ case 'A': if(operand == 0) operand = 1; y -= operand; if(y < 0) y = 0; olines -= operand; if(olines < 0) olines = 0; break; /* * B - cursor down */ case 'B': if(operand == 0) operand = 1; y += operand; if(y > ymax) y=ymax; break; /* * C - cursor right. */ case 'C': if(operand == 0) operand = 1; x += operand; /* * VT-100-UG says not to go past the * right margin. */ if(x > xmax) x=xmax; break; /* * D - cursor left */ case 'D': if(operand == 0) operand = 1; x -= operand; if(x < 0) x = 0; break; /* * H and f - cursor motion. operand is the column * and prevOperand is the row, origin 1. */ case 'H': case 'f': x = operand - 1; if(x < 0) x = 0; if(x > xmax) x = xmax; y = prevOperand - 1; if(y < 0) y = 0; if(y > ymax) y = ymax; break; /* * J - clear some or all of the display. */ case 'J': switch (operand) { /* * operand 2: whole screen. */ case 2: clear(Rpt(pt(0, 0), pt(xmax+1, ymax+1))); break; /* * operand 1: start of screen to active position, inclusive. */ case 1: clear(Rpt(pt(0, 0), pt(x, ymax+1))); clear(Rpt(pt(0, y), pt(x+1, y+1))); break; /* * Default: active position to end of screen, inclusive. */ default: clear(Rpt(pt(x, y), pt(xmax+1, y+1))); clear(Rpt(pt(0, y+1), pt(xmax+1, ymax+1))); break; } break; /* * K - clear some or all of the line. */ case 'K': switch (operand) { /* * operand 2: whole line. */ case 2: clear(Rpt(pt(x, y), pt(xmax+1, y+1))); break; /* * operand 1: start of line to active position, inclusive. */ case 1: clear(Rpt(pt(0, y), pt(x+1, y+1))); break; /* * Default: active position to end of line, inclusive. */ default: clear(Rpt(pt(x, y), pt(xmax+1, y+1))); break; } break; /* * L - insert a line at cursor position */ case 'L': scroll(y, yscrmax, y+1, y); break; /* * M - delete a line at the cursor position */ case 'M': scroll(y+1, yscrmax+1, y, yscrmax); break; /* * m - change character attributes. * Attributes are: * 0 Attributes off * 1 Bold or increased intensity * 4 Underscore * 5 Blink * 7 Negative (reverse) image */ case 'm': standout = operand; break; /* * r - change scrolling region. prevOperand is * min scrolling region and operand is max * scrolling region. */ case 'r': yscrmin = prevOperand-1; yscrmax = operand-1; if (yscrmax == 0) yscrmax = ymax; break; /* * Anything else we ignore for now... */ default: break; } break; /* * Ignore other commands. */ default: break; } break; default: /* ordinary char */ if(isgraphics && gmap[(uchar) buf[0]]) buf[0] = gmap[(uchar) buf[0]]; /* line wrap */ if (x > xmax){ x = 0; newline(); } n = 1; c = 0; while (!cs->raw && host_avail() && x+n<=xmax && n=' ' && c<'\177') { buf[n++] = c; c = 0; } buf[n] = 0; // clear(Rpt(pt(x,y), pt(x+n, y+1))); drawstring(pt(x, y), buf, standout); x += n; peekc = c; break; } } } --mYCpIKhGyMATD0i+--