parser.c revision 1.63 1 /* $NetBSD: parser.c,v 1.63 2006/10/16 00:36:19 christos Exp $ */
2
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95";
39 #else
40 __RCSID("$NetBSD: parser.c,v 1.63 2006/10/16 00:36:19 christos Exp $");
41 #endif
42 #endif /* not lint */
43
44 #include <stdlib.h>
45
46 #include "shell.h"
47 #include "parser.h"
48 #include "nodes.h"
49 #include "expand.h" /* defines rmescapes() */
50 #include "eval.h" /* defines commandname */
51 #include "redir.h" /* defines copyfd() */
52 #include "syntax.h"
53 #include "options.h"
54 #include "input.h"
55 #include "output.h"
56 #include "var.h"
57 #include "error.h"
58 #include "memalloc.h"
59 #include "mystring.h"
60 #include "alias.h"
61 #include "show.h"
62 #ifndef SMALL
63 #include "myhistedit.h"
64 #endif
65
66 /*
67 * Shell command parser.
68 */
69
70 #define EOFMARKLEN 79
71
72 /* values returned by readtoken */
73 #include "token.h"
74
75 #define OPENBRACE '{'
76 #define CLOSEBRACE '}'
77
78
79 struct heredoc {
80 struct heredoc *next; /* next here document in list */
81 union node *here; /* redirection node */
82 char *eofmark; /* string indicating end of input */
83 int striptabs; /* if set, strip leading tabs */
84 };
85
86
87
88 static int noalias = 0; /* when set, don't handle aliases */
89 struct heredoc *heredoclist; /* list of here documents to read */
90 int parsebackquote; /* nonzero if we are inside backquotes */
91 int doprompt; /* if set, prompt the user */
92 int needprompt; /* true if interactive and at start of line */
93 int lasttoken; /* last token read */
94 MKINIT int tokpushback; /* last token pushed back */
95 char *wordtext; /* text of last word returned by readtoken */
96 MKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */
97 struct nodelist *backquotelist;
98 union node *redirnode;
99 struct heredoc *heredoc;
100 int quoteflag; /* set if (part of) last token was quoted */
101 int startlinno; /* line # where last token started */
102
103
104 STATIC union node *list(int);
105 STATIC union node *andor(void);
106 STATIC union node *pipeline(void);
107 STATIC union node *command(void);
108 STATIC union node *simplecmd(union node **, union node *);
109 STATIC union node *makename(void);
110 STATIC void parsefname(void);
111 STATIC void parseheredoc(void);
112 STATIC int peektoken(void);
113 STATIC int readtoken(void);
114 STATIC int xxreadtoken(void);
115 STATIC int readtoken1(int, char const *, char *, int);
116 STATIC int noexpand(char *);
117 STATIC void synexpect(int) __attribute__((__noreturn__));
118 STATIC void synerror(const char *) __attribute__((__noreturn__));
119 STATIC void setprompt(int);
120
121
122 /*
123 * Read and parse a command. Returns NEOF on end of file. (NULL is a
124 * valid parse tree indicating a blank line.)
125 */
126
127 union node *
128 parsecmd(int interact)
129 {
130 int t;
131
132 tokpushback = 0;
133 doprompt = interact;
134 if (doprompt)
135 setprompt(1);
136 else
137 setprompt(0);
138 needprompt = 0;
139 t = readtoken();
140 if (t == TEOF)
141 return NEOF;
142 if (t == TNL)
143 return NULL;
144 tokpushback++;
145 return list(1);
146 }
147
148
149 STATIC union node *
150 list(int nlflag)
151 {
152 union node *n1, *n2, *n3;
153 int tok;
154
155 checkkwd = 2;
156 if (nlflag == 0 && tokendlist[peektoken()])
157 return NULL;
158 n1 = NULL;
159 for (;;) {
160 n2 = andor();
161 tok = readtoken();
162 if (tok == TBACKGND) {
163 if (n2->type == NCMD || n2->type == NPIPE) {
164 n2->ncmd.backgnd = 1;
165 } else if (n2->type == NREDIR) {
166 n2->type = NBACKGND;
167 } else {
168 n3 = (union node *)stalloc(sizeof (struct nredir));
169 n3->type = NBACKGND;
170 n3->nredir.n = n2;
171 n3->nredir.redirect = NULL;
172 n2 = n3;
173 }
174 }
175 if (n1 == NULL) {
176 n1 = n2;
177 }
178 else {
179 n3 = (union node *)stalloc(sizeof (struct nbinary));
180 n3->type = NSEMI;
181 n3->nbinary.ch1 = n1;
182 n3->nbinary.ch2 = n2;
183 n1 = n3;
184 }
185 switch (tok) {
186 case TBACKGND:
187 case TSEMI:
188 tok = readtoken();
189 /* fall through */
190 case TNL:
191 if (tok == TNL) {
192 parseheredoc();
193 if (nlflag)
194 return n1;
195 } else {
196 tokpushback++;
197 }
198 checkkwd = 2;
199 if (tokendlist[peektoken()])
200 return n1;
201 break;
202 case TEOF:
203 if (heredoclist)
204 parseheredoc();
205 else
206 pungetc(); /* push back EOF on input */
207 return n1;
208 default:
209 if (nlflag)
210 synexpect(-1);
211 tokpushback++;
212 return n1;
213 }
214 }
215 }
216
217
218
219 STATIC union node *
220 andor(void)
221 {
222 union node *n1, *n2, *n3;
223 int t;
224
225 n1 = pipeline();
226 for (;;) {
227 if ((t = readtoken()) == TAND) {
228 t = NAND;
229 } else if (t == TOR) {
230 t = NOR;
231 } else {
232 tokpushback++;
233 return n1;
234 }
235 n2 = pipeline();
236 n3 = (union node *)stalloc(sizeof (struct nbinary));
237 n3->type = t;
238 n3->nbinary.ch1 = n1;
239 n3->nbinary.ch2 = n2;
240 n1 = n3;
241 }
242 }
243
244
245
246 STATIC union node *
247 pipeline(void)
248 {
249 union node *n1, *n2, *pipenode;
250 struct nodelist *lp, *prev;
251 int negate;
252
253 negate = 0;
254 TRACE(("pipeline: entered\n"));
255 while (readtoken() == TNOT)
256 negate = !negate;
257 tokpushback++;
258 n1 = command();
259 if (readtoken() == TPIPE) {
260 pipenode = (union node *)stalloc(sizeof (struct npipe));
261 pipenode->type = NPIPE;
262 pipenode->npipe.backgnd = 0;
263 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
264 pipenode->npipe.cmdlist = lp;
265 lp->n = n1;
266 do {
267 prev = lp;
268 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
269 lp->n = command();
270 prev->next = lp;
271 } while (readtoken() == TPIPE);
272 lp->next = NULL;
273 n1 = pipenode;
274 }
275 tokpushback++;
276 if (negate) {
277 n2 = (union node *)stalloc(sizeof (struct nnot));
278 n2->type = NNOT;
279 n2->nnot.com = n1;
280 return n2;
281 } else
282 return n1;
283 }
284
285
286
287 STATIC union node *
288 command(void)
289 {
290 union node *n1, *n2;
291 union node *ap, **app;
292 union node *cp, **cpp;
293 union node *redir, **rpp;
294 int t, negate = 0;
295
296 checkkwd = 2;
297 redir = NULL;
298 n1 = NULL;
299 rpp = &redir;
300
301 /* Check for redirection which may precede command */
302 while (readtoken() == TREDIR) {
303 *rpp = n2 = redirnode;
304 rpp = &n2->nfile.next;
305 parsefname();
306 }
307 tokpushback++;
308
309 while (readtoken() == TNOT) {
310 TRACE(("command: TNOT recognized\n"));
311 negate = !negate;
312 }
313 tokpushback++;
314
315 switch (readtoken()) {
316 case TIF:
317 n1 = (union node *)stalloc(sizeof (struct nif));
318 n1->type = NIF;
319 n1->nif.test = list(0);
320 if (readtoken() != TTHEN)
321 synexpect(TTHEN);
322 n1->nif.ifpart = list(0);
323 n2 = n1;
324 while (readtoken() == TELIF) {
325 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
326 n2 = n2->nif.elsepart;
327 n2->type = NIF;
328 n2->nif.test = list(0);
329 if (readtoken() != TTHEN)
330 synexpect(TTHEN);
331 n2->nif.ifpart = list(0);
332 }
333 if (lasttoken == TELSE)
334 n2->nif.elsepart = list(0);
335 else {
336 n2->nif.elsepart = NULL;
337 tokpushback++;
338 }
339 if (readtoken() != TFI)
340 synexpect(TFI);
341 checkkwd = 1;
342 break;
343 case TWHILE:
344 case TUNTIL: {
345 int got;
346 n1 = (union node *)stalloc(sizeof (struct nbinary));
347 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
348 n1->nbinary.ch1 = list(0);
349 if ((got=readtoken()) != TDO) {
350 TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
351 synexpect(TDO);
352 }
353 n1->nbinary.ch2 = list(0);
354 if (readtoken() != TDONE)
355 synexpect(TDONE);
356 checkkwd = 1;
357 break;
358 }
359 case TFOR:
360 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
361 synerror("Bad for loop variable");
362 n1 = (union node *)stalloc(sizeof (struct nfor));
363 n1->type = NFOR;
364 n1->nfor.var = wordtext;
365 if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
366 app = ≈
367 while (readtoken() == TWORD) {
368 n2 = (union node *)stalloc(sizeof (struct narg));
369 n2->type = NARG;
370 n2->narg.text = wordtext;
371 n2->narg.backquote = backquotelist;
372 *app = n2;
373 app = &n2->narg.next;
374 }
375 *app = NULL;
376 n1->nfor.args = ap;
377 if (lasttoken != TNL && lasttoken != TSEMI)
378 synexpect(-1);
379 } else {
380 static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
381 '@', '=', '\0'};
382 n2 = (union node *)stalloc(sizeof (struct narg));
383 n2->type = NARG;
384 n2->narg.text = argvars;
385 n2->narg.backquote = NULL;
386 n2->narg.next = NULL;
387 n1->nfor.args = n2;
388 /*
389 * Newline or semicolon here is optional (but note
390 * that the original Bourne shell only allowed NL).
391 */
392 if (lasttoken != TNL && lasttoken != TSEMI)
393 tokpushback++;
394 }
395 checkkwd = 2;
396 if ((t = readtoken()) == TDO)
397 t = TDONE;
398 else if (t == TBEGIN)
399 t = TEND;
400 else
401 synexpect(-1);
402 n1->nfor.body = list(0);
403 if (readtoken() != t)
404 synexpect(t);
405 checkkwd = 1;
406 break;
407 case TCASE:
408 n1 = (union node *)stalloc(sizeof (struct ncase));
409 n1->type = NCASE;
410 if (readtoken() != TWORD)
411 synexpect(TWORD);
412 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
413 n2->type = NARG;
414 n2->narg.text = wordtext;
415 n2->narg.backquote = backquotelist;
416 n2->narg.next = NULL;
417 while (readtoken() == TNL);
418 if (lasttoken != TWORD || ! equal(wordtext, "in"))
419 synerror("expecting \"in\"");
420 cpp = &n1->ncase.cases;
421 noalias = 1;
422 checkkwd = 2, readtoken();
423 do {
424 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
425 if (lasttoken == TLP)
426 readtoken();
427 cp->type = NCLIST;
428 app = &cp->nclist.pattern;
429 for (;;) {
430 *app = ap = (union node *)stalloc(sizeof (struct narg));
431 ap->type = NARG;
432 ap->narg.text = wordtext;
433 ap->narg.backquote = backquotelist;
434 if (checkkwd = 2, readtoken() != TPIPE)
435 break;
436 app = &ap->narg.next;
437 readtoken();
438 }
439 ap->narg.next = NULL;
440 noalias = 0;
441 if (lasttoken != TRP) {
442 synexpect(TRP);
443 }
444 cp->nclist.body = list(0);
445
446 checkkwd = 2;
447 if ((t = readtoken()) != TESAC) {
448 if (t != TENDCASE) {
449 noalias = 0;
450 synexpect(TENDCASE);
451 } else {
452 noalias = 1;
453 checkkwd = 2;
454 readtoken();
455 }
456 }
457 cpp = &cp->nclist.next;
458 } while(lasttoken != TESAC);
459 noalias = 0;
460 *cpp = NULL;
461 checkkwd = 1;
462 break;
463 case TLP:
464 n1 = (union node *)stalloc(sizeof (struct nredir));
465 n1->type = NSUBSHELL;
466 n1->nredir.n = list(0);
467 n1->nredir.redirect = NULL;
468 if (readtoken() != TRP)
469 synexpect(TRP);
470 checkkwd = 1;
471 break;
472 case TBEGIN:
473 n1 = list(0);
474 if (readtoken() != TEND)
475 synexpect(TEND);
476 checkkwd = 1;
477 break;
478 /* Handle an empty command like other simple commands. */
479 case TSEMI:
480 /*
481 * An empty command before a ; doesn't make much sense, and
482 * should certainly be disallowed in the case of `if ;'.
483 */
484 if (!redir)
485 synexpect(-1);
486 case TAND:
487 case TOR:
488 case TNL:
489 case TEOF:
490 case TWORD:
491 case TRP:
492 tokpushback++;
493 n1 = simplecmd(rpp, redir);
494 goto checkneg;
495 default:
496 synexpect(-1);
497 /* NOTREACHED */
498 }
499
500 /* Now check for redirection which may follow command */
501 while (readtoken() == TREDIR) {
502 *rpp = n2 = redirnode;
503 rpp = &n2->nfile.next;
504 parsefname();
505 }
506 tokpushback++;
507 *rpp = NULL;
508 if (redir) {
509 if (n1->type != NSUBSHELL) {
510 n2 = (union node *)stalloc(sizeof (struct nredir));
511 n2->type = NREDIR;
512 n2->nredir.n = n1;
513 n1 = n2;
514 }
515 n1->nredir.redirect = redir;
516 }
517
518 checkneg:
519 if (negate) {
520 n2 = (union node *)stalloc(sizeof (struct nnot));
521 n2->type = NNOT;
522 n2->nnot.com = n1;
523 return n2;
524 }
525 else
526 return n1;
527 }
528
529
530 STATIC union node *
531 simplecmd(union node **rpp, union node *redir)
532 {
533 union node *args, **app;
534 union node **orig_rpp = rpp;
535 union node *n = NULL, *n2;
536 int negate = 0;
537
538 /* If we don't have any redirections already, then we must reset */
539 /* rpp to be the address of the local redir variable. */
540 if (redir == 0)
541 rpp = &redir;
542
543 args = NULL;
544 app = &args;
545 /*
546 * We save the incoming value, because we need this for shell
547 * functions. There can not be a redirect or an argument between
548 * the function name and the open parenthesis.
549 */
550 orig_rpp = rpp;
551
552 while (readtoken() == TNOT) {
553 TRACE(("command: TNOT recognized\n"));
554 negate = !negate;
555 }
556 tokpushback++;
557
558 for (;;) {
559 if (readtoken() == TWORD) {
560 n = (union node *)stalloc(sizeof (struct narg));
561 n->type = NARG;
562 n->narg.text = wordtext;
563 n->narg.backquote = backquotelist;
564 *app = n;
565 app = &n->narg.next;
566 } else if (lasttoken == TREDIR) {
567 *rpp = n = redirnode;
568 rpp = &n->nfile.next;
569 parsefname(); /* read name of redirection file */
570 } else if (lasttoken == TLP && app == &args->narg.next
571 && rpp == orig_rpp) {
572 /* We have a function */
573 if (readtoken() != TRP)
574 synexpect(TRP);
575 #ifdef notdef
576 if (! goodname(n->narg.text))
577 synerror("Bad function name");
578 #endif
579 n->type = NDEFUN;
580 n->narg.next = command();
581 goto checkneg;
582 } else {
583 tokpushback++;
584 break;
585 }
586 }
587 *app = NULL;
588 *rpp = NULL;
589 n = (union node *)stalloc(sizeof (struct ncmd));
590 n->type = NCMD;
591 n->ncmd.backgnd = 0;
592 n->ncmd.args = args;
593 n->ncmd.redirect = redir;
594
595 checkneg:
596 if (negate) {
597 n2 = (union node *)stalloc(sizeof (struct nnot));
598 n2->type = NNOT;
599 n2->nnot.com = n;
600 return n2;
601 }
602 else
603 return n;
604 }
605
606 STATIC union node *
607 makename(void)
608 {
609 union node *n;
610
611 n = (union node *)stalloc(sizeof (struct narg));
612 n->type = NARG;
613 n->narg.next = NULL;
614 n->narg.text = wordtext;
615 n->narg.backquote = backquotelist;
616 return n;
617 }
618
619 void fixredir(union node *n, const char *text, int err)
620 {
621 TRACE(("Fix redir %s %d\n", text, err));
622 if (!err)
623 n->ndup.vname = NULL;
624
625 if (is_digit(text[0]) && text[1] == '\0')
626 n->ndup.dupfd = digit_val(text[0]);
627 else if (text[0] == '-' && text[1] == '\0')
628 n->ndup.dupfd = -1;
629 else {
630
631 if (err)
632 synerror("Bad fd number");
633 else
634 n->ndup.vname = makename();
635 }
636 }
637
638
639 STATIC void
640 parsefname(void)
641 {
642 union node *n = redirnode;
643
644 if (readtoken() != TWORD)
645 synexpect(-1);
646 if (n->type == NHERE) {
647 struct heredoc *here = heredoc;
648 struct heredoc *p;
649 int i;
650
651 if (quoteflag == 0)
652 n->type = NXHERE;
653 TRACE(("Here document %d\n", n->type));
654 if (here->striptabs) {
655 while (*wordtext == '\t')
656 wordtext++;
657 }
658 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
659 synerror("Illegal eof marker for << redirection");
660 rmescapes(wordtext);
661 here->eofmark = wordtext;
662 here->next = NULL;
663 if (heredoclist == NULL)
664 heredoclist = here;
665 else {
666 for (p = heredoclist ; p->next ; p = p->next);
667 p->next = here;
668 }
669 } else if (n->type == NTOFD || n->type == NFROMFD) {
670 fixredir(n, wordtext, 0);
671 } else {
672 n->nfile.fname = makename();
673 }
674 }
675
676
677 /*
678 * Input any here documents.
679 */
680
681 STATIC void
682 parseheredoc(void)
683 {
684 struct heredoc *here;
685 union node *n;
686
687 while (heredoclist) {
688 here = heredoclist;
689 heredoclist = here->next;
690 if (needprompt) {
691 setprompt(2);
692 needprompt = 0;
693 }
694 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
695 here->eofmark, here->striptabs);
696 n = (union node *)stalloc(sizeof (struct narg));
697 n->narg.type = NARG;
698 n->narg.next = NULL;
699 n->narg.text = wordtext;
700 n->narg.backquote = backquotelist;
701 here->here->nhere.doc = n;
702 }
703 }
704
705 STATIC int
706 peektoken(void)
707 {
708 int t;
709
710 t = readtoken();
711 tokpushback++;
712 return (t);
713 }
714
715 STATIC int
716 readtoken(void)
717 {
718 int t;
719 int savecheckkwd = checkkwd;
720 #ifdef DEBUG
721 int alreadyseen = tokpushback;
722 #endif
723 struct alias *ap;
724
725 top:
726 t = xxreadtoken();
727
728 if (checkkwd) {
729 /*
730 * eat newlines
731 */
732 if (checkkwd == 2) {
733 checkkwd = 0;
734 while (t == TNL) {
735 parseheredoc();
736 t = xxreadtoken();
737 }
738 } else
739 checkkwd = 0;
740 /*
741 * check for keywords and aliases
742 */
743 if (t == TWORD && !quoteflag)
744 {
745 const char *const *pp;
746
747 for (pp = parsekwd; *pp; pp++) {
748 if (**pp == *wordtext && equal(*pp, wordtext))
749 {
750 lasttoken = t = pp -
751 parsekwd + KWDOFFSET;
752 TRACE(("keyword %s recognized\n", tokname[t]));
753 goto out;
754 }
755 }
756 if(!noalias &&
757 (ap = lookupalias(wordtext, 1)) != NULL) {
758 pushstring(ap->val, strlen(ap->val), ap);
759 checkkwd = savecheckkwd;
760 goto top;
761 }
762 }
763 out:
764 checkkwd = (t == TNOT) ? savecheckkwd : 0;
765 }
766 #ifdef DEBUG
767 if (!alreadyseen)
768 TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
769 else
770 TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
771 #endif
772 return (t);
773 }
774
775
776 /*
777 * Read the next input token.
778 * If the token is a word, we set backquotelist to the list of cmds in
779 * backquotes. We set quoteflag to true if any part of the word was
780 * quoted.
781 * If the token is TREDIR, then we set redirnode to a structure containing
782 * the redirection.
783 * In all cases, the variable startlinno is set to the number of the line
784 * on which the token starts.
785 *
786 * [Change comment: here documents and internal procedures]
787 * [Readtoken shouldn't have any arguments. Perhaps we should make the
788 * word parsing code into a separate routine. In this case, readtoken
789 * doesn't need to have any internal procedures, but parseword does.
790 * We could also make parseoperator in essence the main routine, and
791 * have parseword (readtoken1?) handle both words and redirection.]
792 */
793
794 #define RETURN(token) return lasttoken = token
795
796 STATIC int
797 xxreadtoken(void)
798 {
799 int c;
800
801 if (tokpushback) {
802 tokpushback = 0;
803 return lasttoken;
804 }
805 if (needprompt) {
806 setprompt(2);
807 needprompt = 0;
808 }
809 startlinno = plinno;
810 for (;;) { /* until token or start of word found */
811 c = pgetc_macro();
812 if (c == ' ' || c == '\t')
813 continue; /* quick check for white space first */
814 switch (c) {
815 case ' ': case '\t':
816 continue;
817 case '#':
818 while ((c = pgetc()) != '\n' && c != PEOF);
819 pungetc();
820 continue;
821 case '\\':
822 if (pgetc() == '\n') {
823 startlinno = ++plinno;
824 if (doprompt)
825 setprompt(2);
826 else
827 setprompt(0);
828 continue;
829 }
830 pungetc();
831 goto breakloop;
832 case '\n':
833 plinno++;
834 needprompt = doprompt;
835 RETURN(TNL);
836 case PEOF:
837 RETURN(TEOF);
838 case '&':
839 if (pgetc() == '&')
840 RETURN(TAND);
841 pungetc();
842 RETURN(TBACKGND);
843 case '|':
844 if (pgetc() == '|')
845 RETURN(TOR);
846 pungetc();
847 RETURN(TPIPE);
848 case ';':
849 if (pgetc() == ';')
850 RETURN(TENDCASE);
851 pungetc();
852 RETURN(TSEMI);
853 case '(':
854 RETURN(TLP);
855 case ')':
856 RETURN(TRP);
857 default:
858 goto breakloop;
859 }
860 }
861 breakloop:
862 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
863 #undef RETURN
864 }
865
866
867
868 /*
869 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
870 * is not NULL, read a here document. In the latter case, eofmark is the
871 * word which marks the end of the document and striptabs is true if
872 * leading tabs should be stripped from the document. The argument firstc
873 * is the first character of the input token or document.
874 *
875 * Because C does not have internal subroutines, I have simulated them
876 * using goto's to implement the subroutine linkage. The following macros
877 * will run code that appears at the end of readtoken1.
878 */
879
880 #define CHECKEND() {goto checkend; checkend_return:;}
881 #define PARSEREDIR() {goto parseredir; parseredir_return:;}
882 #define PARSESUB() {goto parsesub; parsesub_return:;}
883 #define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
884 #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
885 #define PARSEARITH() {goto parsearith; parsearith_return:;}
886
887 /*
888 * Keep track of nested doublequotes in dblquote and doublequotep.
889 * We use dblquote for the first 32 levels, and we expand to a malloc'ed
890 * region for levels above that. Usually we never need to malloc.
891 * This code assumes that an int is 32 bits. We don't use uint32_t,
892 * because the rest of the code does not.
893 */
894 #define ISDBLQUOTE() ((varnest < 32) ? (dblquote & (1 << varnest)) : \
895 (dblquotep[(varnest / 32) - 1] & (1 << (varnest % 32))))
896
897 #define SETDBLQUOTE() \
898 if (varnest < 32) \
899 dblquote |= (1 << varnest); \
900 else \
901 dblquotep[(varnest / 32) - 1] |= (1 << (varnest % 32))
902
903 #define CLRDBLQUOTE() \
904 if (varnest < 32) \
905 dblquote &= ~(1 << varnest); \
906 else \
907 dblquotep[(varnest / 32) - 1] &= ~(1 << (varnest % 32))
908
909 STATIC int
910 readtoken1(int firstc, char const *syn, char *eofmark, int striptabs)
911 {
912 char const * volatile syntax = syn;
913 int c = firstc;
914 char * volatile out;
915 int len;
916 char line[EOFMARKLEN + 1];
917 struct nodelist *bqlist;
918 volatile int quotef;
919 int * volatile dblquotep = NULL;
920 volatile size_t maxnest = 32;
921 volatile int dblquote;
922 volatile int varnest; /* levels of variables expansion */
923 volatile int arinest; /* levels of arithmetic expansion */
924 volatile int parenlevel; /* levels of parens in arithmetic */
925 volatile int oldstyle;
926 char const * volatile prevsyntax; /* syntax before arithmetic */
927 #ifdef __GNUC__
928 prevsyntax = NULL; /* XXX gcc4 */
929 #endif
930
931 startlinno = plinno;
932 dblquote = 0;
933 varnest = 0;
934 if (syntax == DQSYNTAX) {
935 SETDBLQUOTE();
936 }
937 quotef = 0;
938 bqlist = NULL;
939 arinest = 0;
940 parenlevel = 0;
941
942 STARTSTACKSTR(out);
943 loop: { /* for each line, until end of word */
944 #if ATTY
945 if (c == '\034' && doprompt
946 && attyset() && ! equal(termval(), "emacs")) {
947 attyline();
948 if (syntax == BASESYNTAX)
949 return readtoken();
950 c = pgetc();
951 goto loop;
952 }
953 #endif
954 CHECKEND(); /* set c to PEOF if at end of here document */
955 for (;;) { /* until end of line or end of word */
956 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
957 switch(syntax[c]) {
958 case CNL: /* '\n' */
959 if (syntax == BASESYNTAX)
960 goto endword; /* exit outer loop */
961 USTPUTC(c, out);
962 plinno++;
963 if (doprompt)
964 setprompt(2);
965 else
966 setprompt(0);
967 c = pgetc();
968 goto loop; /* continue outer loop */
969 case CWORD:
970 USTPUTC(c, out);
971 break;
972 case CCTL:
973 if (eofmark == NULL || ISDBLQUOTE())
974 USTPUTC(CTLESC, out);
975 USTPUTC(c, out);
976 break;
977 case CBACK: /* backslash */
978 c = pgetc();
979 if (c == PEOF) {
980 USTPUTC('\\', out);
981 pungetc();
982 break;
983 }
984 if (c == '\n') {
985 if (doprompt)
986 setprompt(2);
987 else
988 setprompt(0);
989 break;
990 }
991 quotef = 1;
992 if (ISDBLQUOTE() && c != '\\' &&
993 c != '`' && c != '$' &&
994 (c != '"' || eofmark != NULL))
995 USTPUTC('\\', out);
996 if (SQSYNTAX[c] == CCTL)
997 USTPUTC(CTLESC, out);
998 else if (eofmark == NULL) {
999 USTPUTC(CTLQUOTEMARK, out);
1000 USTPUTC(c, out);
1001 if (varnest != 0)
1002 USTPUTC(CTLQUOTEEND, out);
1003 break;
1004 }
1005 USTPUTC(c, out);
1006 break;
1007 case CSQUOTE:
1008 if (syntax != SQSYNTAX) {
1009 if (eofmark == NULL)
1010 USTPUTC(CTLQUOTEMARK, out);
1011 quotef = 1;
1012 syntax = SQSYNTAX;
1013 break;
1014 }
1015 if (eofmark != NULL && arinest == 0 &&
1016 varnest == 0) {
1017 /* Ignore inside quoted here document */
1018 USTPUTC(c, out);
1019 break;
1020 }
1021 /* End of single quotes... */
1022 if (arinest)
1023 syntax = ARISYNTAX;
1024 else {
1025 syntax = BASESYNTAX;
1026 if (varnest != 0)
1027 USTPUTC(CTLQUOTEEND, out);
1028 }
1029 break;
1030 case CDQUOTE:
1031 if (eofmark != NULL && arinest == 0 &&
1032 varnest == 0) {
1033 /* Ignore inside here document */
1034 USTPUTC(c, out);
1035 break;
1036 }
1037 quotef = 1;
1038 if (arinest) {
1039 if (ISDBLQUOTE()) {
1040 syntax = ARISYNTAX;
1041 CLRDBLQUOTE();
1042 } else {
1043 syntax = DQSYNTAX;
1044 SETDBLQUOTE();
1045 USTPUTC(CTLQUOTEMARK, out);
1046 }
1047 break;
1048 }
1049 if (eofmark != NULL)
1050 break;
1051 if (ISDBLQUOTE()) {
1052 if (varnest != 0)
1053 USTPUTC(CTLQUOTEEND, out);
1054 syntax = BASESYNTAX;
1055 CLRDBLQUOTE();
1056 } else {
1057 syntax = DQSYNTAX;
1058 SETDBLQUOTE();
1059 USTPUTC(CTLQUOTEMARK, out);
1060 }
1061 break;
1062 case CVAR: /* '$' */
1063 PARSESUB(); /* parse substitution */
1064 break;
1065 case CENDVAR: /* CLOSEBRACE */
1066 if (varnest > 0 && !ISDBLQUOTE()) {
1067 varnest--;
1068 USTPUTC(CTLENDVAR, out);
1069 } else {
1070 USTPUTC(c, out);
1071 }
1072 break;
1073 case CLP: /* '(' in arithmetic */
1074 parenlevel++;
1075 USTPUTC(c, out);
1076 break;
1077 case CRP: /* ')' in arithmetic */
1078 if (parenlevel > 0) {
1079 USTPUTC(c, out);
1080 --parenlevel;
1081 } else {
1082 if (pgetc() == ')') {
1083 if (--arinest == 0) {
1084 USTPUTC(CTLENDARI, out);
1085 syntax = prevsyntax;
1086 if (syntax == DQSYNTAX)
1087 SETDBLQUOTE();
1088 else
1089 CLRDBLQUOTE();
1090 } else
1091 USTPUTC(')', out);
1092 } else {
1093 /*
1094 * unbalanced parens
1095 * (don't 2nd guess - no error)
1096 */
1097 pungetc();
1098 USTPUTC(')', out);
1099 }
1100 }
1101 break;
1102 case CBQUOTE: /* '`' */
1103 PARSEBACKQOLD();
1104 break;
1105 case CEOF:
1106 goto endword; /* exit outer loop */
1107 default:
1108 if (varnest == 0)
1109 goto endword; /* exit outer loop */
1110 USTPUTC(c, out);
1111 }
1112 c = pgetc_macro();
1113 }
1114 }
1115 endword:
1116 if (syntax == ARISYNTAX)
1117 synerror("Missing '))'");
1118 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
1119 synerror("Unterminated quoted string");
1120 if (varnest != 0) {
1121 startlinno = plinno;
1122 /* { */
1123 synerror("Missing '}'");
1124 }
1125 USTPUTC('\0', out);
1126 len = out - stackblock();
1127 out = stackblock();
1128 if (eofmark == NULL) {
1129 if ((c == '>' || c == '<')
1130 && quotef == 0
1131 && len <= 2
1132 && (*out == '\0' || is_digit(*out))) {
1133 PARSEREDIR();
1134 return lasttoken = TREDIR;
1135 } else {
1136 pungetc();
1137 }
1138 }
1139 quoteflag = quotef;
1140 backquotelist = bqlist;
1141 grabstackblock(len);
1142 wordtext = out;
1143 if (dblquotep != NULL)
1144 ckfree(dblquotep);
1145 return lasttoken = TWORD;
1146 /* end of readtoken routine */
1147
1148
1149
1150 /*
1151 * Check to see whether we are at the end of the here document. When this
1152 * is called, c is set to the first character of the next input line. If
1153 * we are at the end of the here document, this routine sets the c to PEOF.
1154 */
1155
1156 checkend: {
1157 if (eofmark) {
1158 if (striptabs) {
1159 while (c == '\t')
1160 c = pgetc();
1161 }
1162 if (c == *eofmark) {
1163 if (pfgets(line, sizeof line) != NULL) {
1164 char *p, *q;
1165
1166 p = line;
1167 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
1168 if (*p == '\n' && *q == '\0') {
1169 c = PEOF;
1170 plinno++;
1171 needprompt = doprompt;
1172 } else {
1173 pushstring(line, strlen(line), NULL);
1174 }
1175 }
1176 }
1177 }
1178 goto checkend_return;
1179 }
1180
1181
1182 /*
1183 * Parse a redirection operator. The variable "out" points to a string
1184 * specifying the fd to be redirected. The variable "c" contains the
1185 * first character of the redirection operator.
1186 */
1187
1188 parseredir: {
1189 char fd = *out;
1190 union node *np;
1191
1192 np = (union node *)stalloc(sizeof (struct nfile));
1193 if (c == '>') {
1194 np->nfile.fd = 1;
1195 c = pgetc();
1196 if (c == '>')
1197 np->type = NAPPEND;
1198 else if (c == '|')
1199 np->type = NCLOBBER;
1200 else if (c == '&')
1201 np->type = NTOFD;
1202 else {
1203 np->type = NTO;
1204 pungetc();
1205 }
1206 } else { /* c == '<' */
1207 np->nfile.fd = 0;
1208 switch (c = pgetc()) {
1209 case '<':
1210 if (sizeof (struct nfile) != sizeof (struct nhere)) {
1211 np = (union node *)stalloc(sizeof (struct nhere));
1212 np->nfile.fd = 0;
1213 }
1214 np->type = NHERE;
1215 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
1216 heredoc->here = np;
1217 if ((c = pgetc()) == '-') {
1218 heredoc->striptabs = 1;
1219 } else {
1220 heredoc->striptabs = 0;
1221 pungetc();
1222 }
1223 break;
1224
1225 case '&':
1226 np->type = NFROMFD;
1227 break;
1228
1229 case '>':
1230 np->type = NFROMTO;
1231 break;
1232
1233 default:
1234 np->type = NFROM;
1235 pungetc();
1236 break;
1237 }
1238 }
1239 if (fd != '\0')
1240 np->nfile.fd = digit_val(fd);
1241 redirnode = np;
1242 goto parseredir_return;
1243 }
1244
1245
1246 /*
1247 * Parse a substitution. At this point, we have read the dollar sign
1248 * and nothing else.
1249 */
1250
1251 parsesub: {
1252 int subtype;
1253 int typeloc;
1254 int flags;
1255 char *p;
1256 static const char types[] = "}-+?=";
1257
1258 c = pgetc();
1259 if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) {
1260 USTPUTC('$', out);
1261 pungetc();
1262 } else if (c == '(') { /* $(command) or $((arith)) */
1263 if (pgetc() == '(') {
1264 PARSEARITH();
1265 } else {
1266 pungetc();
1267 PARSEBACKQNEW();
1268 }
1269 } else {
1270 USTPUTC(CTLVAR, out);
1271 typeloc = out - stackblock();
1272 USTPUTC(VSNORMAL, out);
1273 subtype = VSNORMAL;
1274 if (c == OPENBRACE) {
1275 c = pgetc();
1276 if (c == '#') {
1277 if ((c = pgetc()) == CLOSEBRACE)
1278 c = '#';
1279 else
1280 subtype = VSLENGTH;
1281 }
1282 else
1283 subtype = 0;
1284 }
1285 if (is_name(c)) {
1286 do {
1287 STPUTC(c, out);
1288 c = pgetc();
1289 } while (is_in_name(c));
1290 } else if (is_digit(c)) {
1291 do {
1292 USTPUTC(c, out);
1293 c = pgetc();
1294 } while (is_digit(c));
1295 }
1296 else if (is_special(c)) {
1297 USTPUTC(c, out);
1298 c = pgetc();
1299 }
1300 else
1301 badsub: synerror("Bad substitution");
1302
1303 STPUTC('=', out);
1304 flags = 0;
1305 if (subtype == 0) {
1306 switch (c) {
1307 case ':':
1308 flags = VSNUL;
1309 c = pgetc();
1310 /*FALLTHROUGH*/
1311 default:
1312 p = strchr(types, c);
1313 if (p == NULL)
1314 goto badsub;
1315 subtype = p - types + VSNORMAL;
1316 break;
1317 case '%':
1318 case '#':
1319 {
1320 int cc = c;
1321 subtype = c == '#' ? VSTRIMLEFT :
1322 VSTRIMRIGHT;
1323 c = pgetc();
1324 if (c == cc)
1325 subtype++;
1326 else
1327 pungetc();
1328 break;
1329 }
1330 }
1331 } else {
1332 pungetc();
1333 }
1334 if (ISDBLQUOTE() || arinest)
1335 flags |= VSQUOTE;
1336 *(stackblock() + typeloc) = subtype | flags;
1337 if (subtype != VSNORMAL) {
1338 varnest++;
1339 if (varnest >= maxnest) {
1340 dblquotep = ckrealloc(dblquotep, maxnest / 8);
1341 dblquotep[(maxnest / 32) - 1] = 0;
1342 maxnest += 32;
1343 }
1344 }
1345 }
1346 goto parsesub_return;
1347 }
1348
1349
1350 /*
1351 * Called to parse command substitutions. Newstyle is set if the command
1352 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
1353 * list of commands (passed by reference), and savelen is the number of
1354 * characters on the top of the stack which must be preserved.
1355 */
1356
1357 parsebackq: {
1358 struct nodelist **nlpp;
1359 int savepbq;
1360 union node *n;
1361 char *volatile str;
1362 struct jmploc jmploc;
1363 struct jmploc *volatile savehandler;
1364 int savelen;
1365 int saveprompt;
1366
1367 savepbq = parsebackquote;
1368 if (setjmp(jmploc.loc)) {
1369 if (str)
1370 ckfree(str);
1371 parsebackquote = 0;
1372 handler = savehandler;
1373 longjmp(handler->loc, 1);
1374 }
1375 INTOFF;
1376 str = NULL;
1377 savelen = out - stackblock();
1378 if (savelen > 0) {
1379 str = ckmalloc(savelen);
1380 memcpy(str, stackblock(), savelen);
1381 }
1382 savehandler = handler;
1383 handler = &jmploc;
1384 INTON;
1385 if (oldstyle) {
1386 /* We must read until the closing backquote, giving special
1387 treatment to some slashes, and then push the string and
1388 reread it as input, interpreting it normally. */
1389 char *pout;
1390 int pc;
1391 int psavelen;
1392 char *pstr;
1393
1394
1395 STARTSTACKSTR(pout);
1396 for (;;) {
1397 if (needprompt) {
1398 setprompt(2);
1399 needprompt = 0;
1400 }
1401 switch (pc = pgetc()) {
1402 case '`':
1403 goto done;
1404
1405 case '\\':
1406 if ((pc = pgetc()) == '\n') {
1407 plinno++;
1408 if (doprompt)
1409 setprompt(2);
1410 else
1411 setprompt(0);
1412 /*
1413 * If eating a newline, avoid putting
1414 * the newline into the new character
1415 * stream (via the STPUTC after the
1416 * switch).
1417 */
1418 continue;
1419 }
1420 if (pc != '\\' && pc != '`' && pc != '$'
1421 && (!ISDBLQUOTE() || pc != '"'))
1422 STPUTC('\\', pout);
1423 break;
1424
1425 case '\n':
1426 plinno++;
1427 needprompt = doprompt;
1428 break;
1429
1430 case PEOF:
1431 startlinno = plinno;
1432 synerror("EOF in backquote substitution");
1433 break;
1434
1435 default:
1436 break;
1437 }
1438 STPUTC(pc, pout);
1439 }
1440 done:
1441 STPUTC('\0', pout);
1442 psavelen = pout - stackblock();
1443 if (psavelen > 0) {
1444 pstr = grabstackstr(pout);
1445 setinputstring(pstr, 1);
1446 }
1447 }
1448 nlpp = &bqlist;
1449 while (*nlpp)
1450 nlpp = &(*nlpp)->next;
1451 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
1452 (*nlpp)->next = NULL;
1453 parsebackquote = oldstyle;
1454
1455 if (oldstyle) {
1456 saveprompt = doprompt;
1457 doprompt = 0;
1458 } else
1459 saveprompt = 0;
1460
1461 n = list(0);
1462
1463 if (oldstyle)
1464 doprompt = saveprompt;
1465 else {
1466 if (readtoken() != TRP)
1467 synexpect(TRP);
1468 }
1469
1470 (*nlpp)->n = n;
1471 if (oldstyle) {
1472 /*
1473 * Start reading from old file again, ignoring any pushed back
1474 * tokens left from the backquote parsing
1475 */
1476 popfile();
1477 tokpushback = 0;
1478 }
1479 while (stackblocksize() <= savelen)
1480 growstackblock();
1481 STARTSTACKSTR(out);
1482 if (str) {
1483 memcpy(out, str, savelen);
1484 STADJUST(savelen, out);
1485 INTOFF;
1486 ckfree(str);
1487 str = NULL;
1488 INTON;
1489 }
1490 parsebackquote = savepbq;
1491 handler = savehandler;
1492 if (arinest || ISDBLQUOTE())
1493 USTPUTC(CTLBACKQ | CTLQUOTE, out);
1494 else
1495 USTPUTC(CTLBACKQ, out);
1496 if (oldstyle)
1497 goto parsebackq_oldreturn;
1498 else
1499 goto parsebackq_newreturn;
1500 }
1501
1502 /*
1503 * Parse an arithmetic expansion (indicate start of one and set state)
1504 */
1505 parsearith: {
1506
1507 if (++arinest == 1) {
1508 prevsyntax = syntax;
1509 syntax = ARISYNTAX;
1510 USTPUTC(CTLARI, out);
1511 if (ISDBLQUOTE())
1512 USTPUTC('"',out);
1513 else
1514 USTPUTC(' ',out);
1515 } else {
1516 /*
1517 * we collapse embedded arithmetic expansion to
1518 * parenthesis, which should be equivalent
1519 */
1520 USTPUTC('(', out);
1521 }
1522 goto parsearith_return;
1523 }
1524
1525 } /* end of readtoken */
1526
1527
1528
1529 #ifdef mkinit
1530 RESET {
1531 tokpushback = 0;
1532 checkkwd = 0;
1533 }
1534 #endif
1535
1536 /*
1537 * Returns true if the text contains nothing to expand (no dollar signs
1538 * or backquotes).
1539 */
1540
1541 STATIC int
1542 noexpand(char *text)
1543 {
1544 char *p;
1545 char c;
1546
1547 p = text;
1548 while ((c = *p++) != '\0') {
1549 if (c == CTLQUOTEMARK)
1550 continue;
1551 if (c == CTLESC)
1552 p++;
1553 else if (BASESYNTAX[(int)c] == CCTL)
1554 return 0;
1555 }
1556 return 1;
1557 }
1558
1559
1560 /*
1561 * Return true if the argument is a legal variable name (a letter or
1562 * underscore followed by zero or more letters, underscores, and digits).
1563 */
1564
1565 int
1566 goodname(char *name)
1567 {
1568 char *p;
1569
1570 p = name;
1571 if (! is_name(*p))
1572 return 0;
1573 while (*++p) {
1574 if (! is_in_name(*p))
1575 return 0;
1576 }
1577 return 1;
1578 }
1579
1580
1581 /*
1582 * Called when an unexpected token is read during the parse. The argument
1583 * is the token that is expected, or -1 if more than one type of token can
1584 * occur at this point.
1585 */
1586
1587 STATIC void
1588 synexpect(int token)
1589 {
1590 char msg[64];
1591
1592 if (token >= 0) {
1593 fmtstr(msg, 64, "%s unexpected (expecting %s)",
1594 tokname[lasttoken], tokname[token]);
1595 } else {
1596 fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
1597 }
1598 synerror(msg);
1599 /* NOTREACHED */
1600 }
1601
1602
1603 STATIC void
1604 synerror(const char *msg)
1605 {
1606 if (commandname)
1607 outfmt(&errout, "%s: %d: ", commandname, startlinno);
1608 else
1609 outfmt(&errout, "%s: ", getprogname());
1610 outfmt(&errout, "Syntax error: %s\n", msg);
1611 error((char *)NULL);
1612 /* NOTREACHED */
1613 }
1614
1615 STATIC void
1616 setprompt(int which)
1617 {
1618 whichprompt = which;
1619
1620 #ifndef SMALL
1621 if (!el)
1622 #endif
1623 out2str(getprompt(NULL));
1624 }
1625
1626 /*
1627 * called by editline -- any expansions to the prompt
1628 * should be added here.
1629 */
1630 const char *
1631 getprompt(void *unused)
1632 {
1633 switch (whichprompt) {
1634 case 0:
1635 return "";
1636 case 1:
1637 return ps1val();
1638 case 2:
1639 return ps2val();
1640 default:
1641 return "<internal prompt error>";
1642 }
1643 }
1644