]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/sam/mesg.c
aux/realemu: run cpuproc in same fd group as fileserver
[plan9front.git] / sys / src / cmd / sam / mesg.c
1 #include "sam.h"
2
3 Header  h;
4 uchar   indata[DATASIZE];
5 uchar   outdata[2*DATASIZE+3];  /* room for overflow message */
6 uchar   *inp;
7 uchar   *outp;
8 uchar   *outmsg = outdata;
9 Posn    cmdpt;
10 Posn    cmdptadv;
11 Buffer  snarfbuf;
12 int     waitack;
13 int     outbuffered;
14 int     tversion;
15
16 int     inshort(void);
17 long    inlong(void);
18 vlong   invlong(void);
19 int     inmesg(Tmesg);
20
21 void    outshort(int);
22 void    outlong(long);
23 void    outvlong(vlong);
24 void    outcopy(int, void*);
25 void    outsend(void);
26 void    outstart(Hmesg);
27
28 void    setgenstr(File*, Posn, Posn);
29
30 #ifdef DEBUG
31 char *hname[] = {
32         [Hversion]      "Hversion",
33         [Hbindname]     "Hbindname",
34         [Hcurrent]      "Hcurrent",
35         [Hnewname]      "Hnewname",
36         [Hmovname]      "Hmovname",
37         [Hgrow]         "Hgrow",
38         [Hcheck0]       "Hcheck0",
39         [Hcheck]        "Hcheck",
40         [Hunlock]       "Hunlock",
41         [Hdata]         "Hdata",
42         [Horigin]       "Horigin",
43         [Hunlockfile]   "Hunlockfile",
44         [Hsetdot]       "Hsetdot",
45         [Hgrowdata]     "Hgrowdata",
46         [Hmoveto]       "Hmoveto",
47         [Hclean]        "Hclean",
48         [Hdirty]        "Hdirty",
49         [Hcut]          "Hcut",
50         [Hsetpat]       "Hsetpat",
51         [Hdelname]      "Hdelname",
52         [Hclose]        "Hclose",
53         [Hsetsnarf]     "Hsetsnarf",
54         [Hsnarflen]     "Hsnarflen",
55         [Hack]          "Hack",
56         [Hexit]         "Hexit",
57         [Hplumb]                "Hplumb",
58 };
59
60 char *tname[] = {
61         [Tversion]      "Tversion",
62         [Tstartcmdfile] "Tstartcmdfile",
63         [Tcheck]        "Tcheck",
64         [Trequest]      "Trequest",
65         [Torigin]       "Torigin",
66         [Tstartfile]    "Tstartfile",
67         [Tworkfile]     "Tworkfile",
68         [Ttype]         "Ttype",
69         [Tcut]          "Tcut",
70         [Tpaste]        "Tpaste",
71         [Tsnarf]        "Tsnarf",
72         [Tstartnewfile] "Tstartnewfile",
73         [Twrite]        "Twrite",
74         [Tclose]        "Tclose",
75         [Tlook]         "Tlook",
76         [Tsearch]       "Tsearch",
77         [Tsend]         "Tsend",
78         [Tdclick]       "Tdclick",
79         [Tstartsnarf]   "Tstartsnarf",
80         [Tsetsnarf]     "Tsetsnarf",
81         [Tack]          "Tack",
82         [Texit]         "Texit",
83         [Tplumb]                "Tplumb",
84 };
85
86 void
87 journal(int out, char *s)
88 {
89         static int fd = -1;
90
91         if(fd < 0)
92                 fd = create("/tmp/sam.out", 1, 0666L);
93         if(fd >= 0)
94                 fprint(fd, "%s%s\n", out? "out: " : "in:  ", s);
95 }
96
97 void
98 journaln(int out, long n)
99 {
100         char buf[32];
101
102         snprint(buf, sizeof(buf), "%ld", n);
103         journal(out, buf);
104 }
105
106 void
107 journalv(int out, vlong v)
108 {
109         char buf[32];
110
111         sprint(buf, sizeof(buf), "%lld", v);
112         journal(out, buf);
113 }
114 #else
115 #define journal(a, b)
116 #define journaln(a, b)
117 #define journalv(a, b)
118 #endif
119
120 int
121 rcvchar(void){
122         static uchar buf[64];
123         static i, nleft = 0;
124
125         if(nleft <= 0){
126                 nleft = read(0, (char *)buf, sizeof buf);
127                 if(nleft <= 0)
128                         return -1;
129                 i = 0;
130         }
131         --nleft;
132         return buf[i++];
133 }
134
135 int
136 rcv(void){
137         int c;
138         static state = 0;
139         static count = 0;
140         static i = 0;
141
142         while((c=rcvchar()) != -1)
143                 switch(state){
144                 case 0:
145                         h.type = c;
146                         state++;
147                         break;
148
149                 case 1:
150                         h.count0 = c;
151                         state++;
152                         break;
153
154                 case 2:
155                         h.count1 = c;
156                         count = h.count0|(h.count1<<8);
157                         i = 0;
158                         if(count > DATASIZE)
159                                 panic("count>DATASIZE");
160                         if(count == 0)
161                                 goto zerocount;
162                         state++;
163                         break;
164
165                 case 3:
166                         indata[i++] = c;
167                         if(i == count){
168                 zerocount:
169                                 indata[i] = 0;
170                                 state = count = 0;
171                                 return inmesg(h.type);
172                         }
173                         break;
174                 }
175         return 0;
176 }
177
178 File *
179 whichfile(int tag)
180 {
181         int i;
182
183         for(i = 0; i<file.nused; i++)
184                 if(file.filepptr[i]->tag==tag)
185                         return file.filepptr[i];
186         hiccough((char *)0);
187         return 0;
188 }
189
190 int
191 inmesg(Tmesg type)
192 {
193         Rune buf[1025];
194         char cbuf[64];
195         int i, m;
196         short s;
197         long l, l1;
198         vlong v;
199         File *f;
200         Posn p0, p1, p;
201         Range r;
202         String *str;
203         char *c, *wdir;
204         Rune *rp;
205         Plumbmsg *pm;
206
207         if(type > TMAX)
208                 panic("inmesg");
209
210         journal(0, tname[type]);
211
212         inp = indata;
213         switch(type){
214         case -1:
215                 panic("rcv error");
216
217         default:
218                 fprint(2, "unknown type %d\n", type);
219                 panic("rcv unknown");
220
221         case Tversion:
222                 tversion = inshort();
223                 journaln(0, tversion);
224                 break;
225
226         case Tstartcmdfile:
227                 v = invlong();          /* for 64-bit pointers */
228                 journalv(0, v);
229                 Strdupl(&genstr, samname);
230                 cmd = newfile();
231                 cmd->unread = 0;
232                 outTsv(Hbindname, cmd->tag, v);
233                 outTs(Hcurrent, cmd->tag);
234                 logsetname(cmd, &genstr);
235                 cmd->rasp = listalloc('P');
236                 cmd->mod = 0;
237                 if(cmdstr.n){
238                         loginsert(cmd, 0L, cmdstr.s, cmdstr.n);
239                         Strdelete(&cmdstr, 0L, (Posn)cmdstr.n);
240                 }
241                 fileupdate(cmd, FALSE, TRUE);
242                 outT0(Hunlock);
243                 break;
244
245         case Tcheck:
246                 /* go through whichfile to check the tag */
247                 outTs(Hcheck, whichfile(inshort())->tag);
248                 break;
249
250         case Trequest:
251                 f = whichfile(inshort());
252                 p0 = inlong();
253                 p1 = p0+inshort();
254                 journaln(0, p0);
255                 journaln(0, p1-p0);
256                 if(f->unread)
257                         panic("Trequest: unread");
258                 if(p1>f->nc)
259                         p1 = f->nc;
260                 if(p0>f->nc) /* can happen e.g. scrolling during command */
261                         p0 = f->nc;
262                 if(p0 == p1){
263                         i = 0;
264                         r.p1 = r.p2 = p0;
265                 }else{
266                         r = rdata(f->rasp, p0, p1-p0);
267                         i = r.p2-r.p1;
268                         bufread(f, r.p1, buf, i);
269                 }
270                 buf[i]=0;
271                 outTslS(Hdata, f->tag, r.p1, tmprstr(buf, i+1));
272                 break;
273
274         case Torigin:
275                 s = inshort();
276                 l = inlong();
277                 l1 = inlong();
278                 journaln(0, l1);
279                 lookorigin(whichfile(s), l, l1);
280                 break;
281
282         case Tstartfile:
283                 termlocked++;
284                 f = whichfile(inshort());
285                 if(!f->rasp)    /* this might be a duplicate message */
286                         f->rasp = listalloc('P');
287                 current(f);
288                 outTsv(Hbindname, f->tag, invlong());   /* for 64-bit pointers */
289                 outTs(Hcurrent, f->tag);
290                 journaln(0, f->tag);
291                 if(f->unread)
292                         load(f);
293                 else{
294                         if(f->nc>0){
295                                 rgrow(f->rasp, 0L, f->nc);
296                                 outTsll(Hgrow, f->tag, 0L, f->nc);
297                         }
298                         outTs(Hcheck0, f->tag);
299                         moveto(f, f->dot.r);
300                 }
301                 break;
302
303         case Tworkfile:
304                 i = inshort();
305                 f = whichfile(i);
306                 current(f);
307                 f->dot.r.p1 = inlong();
308                 f->dot.r.p2 = inlong();
309                 f->tdot = f->dot.r;
310                 journaln(0, i);
311                 journaln(0, f->dot.r.p1);
312                 journaln(0, f->dot.r.p2);
313                 break;
314
315         case Ttype:
316                 f = whichfile(inshort());
317                 p0 = inlong();
318                 journaln(0, p0);
319                 journal(0, (char*)inp);
320                 str = tmpcstr((char*)inp);
321                 i = str->n;
322                 loginsert(f, p0, str->s, str->n);
323                 if(fileupdate(f, FALSE, FALSE))
324                         seq++;
325                 if(f==cmd && p0==f->nc-i && i>0 && str->s[i-1]=='\n'){
326                         freetmpstr(str);
327                         termlocked++;
328                         termcommand();
329                 }else
330                         freetmpstr(str);
331                 f->dot.r.p1 = f->dot.r.p2 = p0+i; /* terminal knows this already */
332                 f->tdot = f->dot.r;
333                 break;
334
335         case Tcut:
336                 f = whichfile(inshort());
337                 p0 = inlong();
338                 p1 = inlong();
339                 journaln(0, p0);
340                 journaln(0, p1);
341                 logdelete(f, p0, p1);
342                 if(fileupdate(f, FALSE, FALSE))
343                         seq++;
344                 f->dot.r.p1 = f->dot.r.p2 = p0;
345                 f->tdot = f->dot.r;   /* terminal knows the value of dot already */
346                 break;
347
348         case Tpaste:
349                 f = whichfile(inshort());
350                 p0 = inlong();
351                 journaln(0, p0);
352                 for(l=0; l<snarfbuf.nc; l+=m){
353                         m = snarfbuf.nc-l;
354                         if(m>BLOCKSIZE)
355                                 m = BLOCKSIZE;
356                         bufread(&snarfbuf, l, genbuf, m);
357                         loginsert(f, p0, tmprstr(genbuf, m)->s, m);
358                 }
359                 if(fileupdate(f, FALSE, TRUE))
360                         seq++;
361                 f->dot.r.p1 = p0;
362                 f->dot.r.p2 = p0+snarfbuf.nc;
363                 f->tdot.p1 = -1; /* force telldot to tell (arguably a BUG) */
364                 telldot(f);
365                 outTs(Hunlockfile, f->tag);
366                 break;
367
368         case Tsnarf:
369                 i = inshort();
370                 p0 = inlong();
371                 p1 = inlong();
372                 snarf(whichfile(i), p0, p1, &snarfbuf, 0);
373                 break;
374
375         case Tstartnewfile:
376                 v = invlong();
377                 Strdupl(&genstr, empty);
378                 f = newfile();
379                 f->rasp = listalloc('P');
380                 outTsv(Hbindname, f->tag, v);
381                 logsetname(f, &genstr);
382                 outTs(Hcurrent, f->tag);
383                 current(f);
384                 load(f);
385                 break;
386
387         case Twrite:
388                 termlocked++;
389                 i = inshort();
390                 journaln(0, i);
391                 f = whichfile(i);
392                 addr.r.p1 = 0;
393                 addr.r.p2 = f->nc;
394                 if(f->name.s[0] == 0)
395                         error(Enoname);
396                 Strduplstr(&genstr, &f->name);
397                 writef(f);
398                 break;
399
400         case Tclose:
401                 termlocked++;
402                 i = inshort();
403                 journaln(0, i);
404                 f = whichfile(i);
405                 current(f);
406                 trytoclose(f);
407                 /* if trytoclose fails, will error out */
408                 delete(f);
409                 break;
410
411         case Tlook:
412                 f = whichfile(inshort());
413                 termlocked++;
414                 p0 = inlong();
415                 p1 = inlong();
416                 journaln(0, p0);
417                 journaln(0, p1);
418                 setgenstr(f, p0, p1);
419                 for(l = 0; l<genstr.n; l++){
420                         i = genstr.s[l];
421                         if(utfrune(".*+?(|)\\[]^$", i)){
422                                 str = tmpcstr("\\");
423                                 Strinsert(&genstr, str, l++);
424                                 freetmpstr(str);
425                         }
426                 }
427                 Straddc(&genstr, '\0');
428                 nextmatch(f, &genstr, p1, 1);
429                 moveto(f, sel.p[0]);
430                 break;
431
432         case Tsearch:
433                 termlocked++;
434                 if(curfile == 0)
435                         error(Enofile);
436                 if(lastpat.s[0] == 0)
437                         panic("Tsearch");
438                 nextmatch(curfile, &lastpat, curfile->dot.r.p2, 1);
439                 moveto(curfile, sel.p[0]);
440                 break;
441
442         case Tsend:
443                 termlocked++;
444                 inshort();      /* ignored */
445                 p0 = inlong();
446                 p1 = inlong();
447                 setgenstr(cmd, p0, p1);
448                 bufreset(&snarfbuf);
449                 bufinsert(&snarfbuf, (Posn)0, genstr.s, genstr.n);
450                 outTl(Hsnarflen, genstr.n);
451                 if(genstr.s[genstr.n-1] != '\n')
452                         Straddc(&genstr, '\n');
453                 loginsert(cmd, cmd->nc, genstr.s, genstr.n);
454                 fileupdate(cmd, FALSE, TRUE);
455                 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nc;
456                 telldot(cmd);
457                 termcommand();
458                 break;
459
460         case Tdclick:
461                 f = whichfile(inshort());
462                 p1 = inlong();
463                 doubleclick(f, p1);
464                 f->tdot.p1 = f->tdot.p2 = p1;
465                 telldot(f);
466                 outTs(Hunlockfile, f->tag);
467                 break;
468
469         case Tstartsnarf:
470                 if (snarfbuf.nc <= 0) { /* nothing to export */
471                         outTs(Hsetsnarf, 0);
472                         break;
473                 }
474                 c = 0;
475                 i = 0;
476                 m = snarfbuf.nc;
477                 if(m > SNARFSIZE) {
478                         m = SNARFSIZE;
479                         dprint("?warning: snarf buffer truncated\n");
480                 }
481                 rp = malloc(m*sizeof(Rune));
482                 if(rp){
483                         bufread(&snarfbuf, 0, rp, m);
484                         c = Strtoc(tmprstr(rp, m));
485                         free(rp);
486                         i = strlen(c);
487                 }
488                 outTs(Hsetsnarf, i);
489                 if(c){
490                         Write(1, c, i);
491                         free(c);
492                 } else
493                         dprint("snarf buffer too long\n");
494                 break;
495
496         case Tsetsnarf:
497                 m = inshort();
498                 if(m > SNARFSIZE)
499                         error(Etoolong);
500                 c = malloc(m+1);
501                 if(c){
502                         for(i=0; i<m; i++)
503                                 c[i] = rcvchar();
504                         c[m] = 0;
505                         str = tmpcstr(c);
506                         free(c);
507                         bufreset(&snarfbuf);
508                         bufinsert(&snarfbuf, (Posn)0, str->s, str->n);
509                         freetmpstr(str);
510                         outT0(Hunlock);
511                 }
512                 break;
513
514         case Tack:
515                 waitack = 0;
516                 break;
517
518         case Tplumb:
519                 f = whichfile(inshort());
520                 p0 = inlong();
521                 p1 = inlong();
522                 pm = emalloc(sizeof(Plumbmsg));
523                 pm->src = strdup("sam");
524                 pm->dst = 0;
525                 /* construct current directory */
526                 c = Strtoc(&f->name);
527                 if(c[0] == '/')
528                         pm->wdir = c;
529                 else{
530                         wdir = emalloc(1024);
531                         getwd(wdir, 1024);
532                         pm->wdir = emalloc(1024);
533                         snprint(pm->wdir, 1024, "%s/%s", wdir, c);
534                         cleanname(pm->wdir);
535                         free(wdir);
536                         free(c);
537                 }
538                 c = strrchr(pm->wdir, '/');
539                 if(c)
540                         *c = '\0';
541                 pm->type = strdup("text");
542                 if(p1 > p0)
543                         pm->attr = nil;
544                 else{
545                         p = p0;
546                         while(p0>0 && (i=filereadc(f, p0 - 1))!=' ' && i!='\t' && i!='\n')
547                                 p0--;
548                         while(p1<f->nc && (i=filereadc(f, p1))!=' ' && i!='\t' && i!='\n')
549                                 p1++;
550                         sprint(cbuf, "click=%ld", p-p0);
551                         pm->attr = plumbunpackattr(cbuf);
552                 }
553                 if(p0==p1 || p1-p0>=BLOCKSIZE){
554                         plumbfree(pm);
555                         break;
556                 }
557                 setgenstr(f, p0, p1);
558                 pm->data = Strtoc(&genstr);
559                 pm->ndata = strlen(pm->data);
560                 c = plumbpack(pm, &i);
561                 if(c != 0){
562                         outTs(Hplumb, i);
563                         Write(1, c, i);
564                         free(c);
565                 }
566                 plumbfree(pm);
567                 break;
568
569         case Texit:
570                 exits(0);
571         }
572         return TRUE;
573 }
574
575 void
576 snarf(File *f, Posn p1, Posn p2, Buffer *buf, int emptyok)
577 {
578         Posn l;
579         int i;
580
581         if(!emptyok && p1==p2)
582                 return;
583         bufreset(buf);
584         /* Stage through genbuf to avoid compaction problems (vestigial) */
585         if(p2 > f->nc){
586                 fprint(2, "bad snarf addr p1=%ld p2=%ld f->nc=%d\n", p1, p2, f->nc); /*ZZZ should never happen, can remove */
587                 p2 = f->nc;
588         }
589         for(l=p1; l<p2; l+=i){
590                 i = p2-l>BLOCKSIZE? BLOCKSIZE : p2-l;
591                 bufread(f, l, genbuf, i);
592                 bufinsert(buf, buf->nc, tmprstr(genbuf, i)->s, i);
593         }
594 }
595
596 int
597 inshort(void)
598 {
599         ushort n;
600
601         n = inp[0] | (inp[1]<<8);
602         inp += 2;
603         return n;
604 }
605
606 long
607 inlong(void)
608 {
609         ulong n;
610
611         n = inp[0] | (inp[1]<<8) | (inp[2]<<16) | (inp[3]<<24);
612         inp += 4;
613         return n;
614 }
615
616 vlong
617 invlong(void)
618 {
619         vlong v;
620         
621         v = (inp[7]<<24) | (inp[6]<<16) | (inp[5]<<8) | inp[4];
622         v = (v<<16) | (inp[3]<<8) | inp[2];
623         v = (v<<16) | (inp[1]<<8) | inp[0];
624         inp += 8;
625         return v;
626 }
627
628 void
629 setgenstr(File *f, Posn p0, Posn p1)
630 {
631         if(p0 != p1){
632                 if(p1-p0 >= TBLOCKSIZE)
633                         error(Etoolong);
634                 Strinsure(&genstr, p1-p0);
635                 bufread(f, p0, genbuf, p1-p0);
636                 memmove(genstr.s, genbuf, RUNESIZE*(p1-p0));
637                 genstr.n = p1-p0;
638         }else{
639                 if(snarfbuf.nc == 0)
640                         error(Eempty);
641                 if(snarfbuf.nc > TBLOCKSIZE)
642                         error(Etoolong);
643                 bufread(&snarfbuf, (Posn)0, genbuf, snarfbuf.nc);
644                 Strinsure(&genstr, snarfbuf.nc);
645                 memmove(genstr.s, genbuf, RUNESIZE*snarfbuf.nc);
646                 genstr.n = snarfbuf.nc;
647         }
648 }
649
650 void
651 outT0(Hmesg type)
652 {
653         outstart(type);
654         outsend();
655 }
656
657 void
658 outTl(Hmesg type, long l)
659 {
660         outstart(type);
661         outlong(l);
662         outsend();
663 }
664
665 void
666 outTs(Hmesg type, int s)
667 {
668         outstart(type);
669         journaln(1, s);
670         outshort(s);
671         outsend();
672 }
673
674 void
675 outS(String *s)
676 {
677         char *c;
678         int i;
679
680         c = Strtoc(s);
681         i = strlen(c);
682         outcopy(i, c);
683         if(i > 99)
684                 c[99] = 0;
685         journaln(1, i);
686         journal(1, c);
687         free(c);
688 }
689
690 void
691 outTsS(Hmesg type, int s1, String *s)
692 {
693         outstart(type);
694         outshort(s1);
695         outS(s);
696         outsend();
697 }
698
699 void
700 outTslS(Hmesg type, int s1, Posn l1, String *s)
701 {
702         outstart(type);
703         outshort(s1);
704         journaln(1, s1);
705         outlong(l1);
706         journaln(1, l1);
707         outS(s);
708         outsend();
709 }
710
711 void
712 outTS(Hmesg type, String *s)
713 {
714         outstart(type);
715         outS(s);
716         outsend();
717 }
718
719 void
720 outTsllS(Hmesg type, int s1, Posn l1, Posn l2, String *s)
721 {
722         outstart(type);
723         outshort(s1);
724         outlong(l1);
725         outlong(l2);
726         journaln(1, l1);
727         journaln(1, l2);
728         outS(s);
729         outsend();
730 }
731
732 void
733 outTsll(Hmesg type, int s, Posn l1, Posn l2)
734 {
735         outstart(type);
736         outshort(s);
737         outlong(l1);
738         outlong(l2);
739         journaln(1, l1);
740         journaln(1, l2);
741         outsend();
742 }
743
744 void
745 outTsl(Hmesg type, int s, Posn l)
746 {
747         outstart(type);
748         outshort(s);
749         outlong(l);
750         journaln(1, l);
751         outsend();
752 }
753
754 void
755 outTsv(Hmesg type, int s, vlong v)
756 {
757         outstart(type);
758         outshort(s);
759         outvlong(v);
760         journalv(1, v);
761         outsend();
762 }
763
764 void
765 outstart(Hmesg type)
766 {
767         journal(1, hname[type]);
768         outmsg[0] = type;
769         outp = outmsg+3;
770 }
771
772 void
773 outcopy(int count, void *data)
774 {
775         memmove(outp, data, count);
776         outp += count;
777 }
778
779 void
780 outshort(int s)
781 {
782         *outp++ = s;
783         *outp++ = s>>8; 
784 }
785
786 void
787 outlong(long l)
788 {
789         *outp++ = l;
790         *outp++ = l>>8;
791         *outp++ = l>>16;
792         *outp++ = l>>24;
793 }
794
795 void
796 outvlong(vlong v)
797 {
798         int i;
799
800         for(i = 0; i < 8; i++){
801                 *outp++ = v;
802                 v >>= 8;
803         }
804 }
805
806 void
807 outsend(void)
808 {
809         int outcount;
810
811         if(outp >= outdata+nelem(outdata))
812                 panic("outsend");
813         outcount = outp-outmsg;
814         outcount -= 3;
815         outmsg[1] = outcount;
816         outmsg[2] = outcount>>8;
817         outmsg = outp;
818         if(!outbuffered){
819                 outcount = outmsg-outdata;
820                 if (write(1, (char*) outdata, outcount) != outcount)
821                         rescue();
822                 outmsg = outdata;
823                 return;
824         }
825 }
826
827 int
828 needoutflush(void)
829 {
830         return outmsg >= outdata+DATASIZE;
831 }
832
833 void
834 outflush(void)
835 {
836         if(outmsg == outdata)
837                 return;
838         outbuffered = 0;
839         /* flow control */
840         outT0(Hack);
841         waitack = 1;
842         do
843                 if(rcv() == 0){
844                         rescue();
845                         exits("eof");
846                 }
847         while(waitack);
848         outmsg = outdata;
849         outbuffered = 1;
850 }