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