home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume19 / ash / part08 < prev    next >
Text File  |  1989-05-29  |  44KB  |  1,943 lines

  1. Subject:  v19i008:  A reimplementation of the System V shell, Part08/08
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: ka@june.cs.washington.edu (Kenneth Almquist)
  7. Posting-number: Volume 19, Issue 8
  8. Archive-name: ash/part08
  9.  
  10. # This is part 8 of ash.  To unpack, feed it into the shell (not csh).
  11. # The ash distribution consists of eight pieces.  Be sure you get them all.
  12. # After you unpack everything, read the file README.
  13.  
  14. if test ! -d bltin
  15. then    mkdir bltin
  16. fi
  17. echo extracting bltin/binary_op
  18. cat > bltin/binary_op <<\EOF
  19. # List of binary operators used by test/expr.
  20. #
  21. # Copyright 1989 by Kenneth Almquist.  All rights reserved.
  22. # This file is part of ash, which is distributed under the terms specified
  23. # by the Ash General Public License.  See the file named LICENSE.
  24.  
  25. OR1     -o    1
  26. OR2     |    1
  27. AND1     -a    2
  28. AND2     &    2
  29. STREQ     =    4    OP_STRING
  30. STRNE     !=    4    OP_STRING
  31. EQ     -eq    4    OP_INT
  32. NE     -ne    4    OP_INT
  33. GT     -gt    4    OP_INT
  34. LT     -lt    4    OP_INT
  35. LE     -le    4    OP_INT
  36. GE     -ge    4    OP_INT
  37. PLUS     +    5    OP_INT
  38. MINUS     -    5    OP_INT
  39. TIMES     *    6    OP_INT
  40. DIVIDE     /    6    OP_INT
  41. REM     %    6    OP_INT
  42. MATCHPAT :    7    OP_STRING
  43. EOF
  44. if test `wc -c < bltin/binary_op` -ne 588
  45. then    echo 'bltin/binary_op is the wrong size'
  46. fi
  47. echo extracting bltin/bltin.h
  48. cat > bltin/bltin.h <<\EOF
  49. /*
  50.  * This file is included by programs which are optionally built into the
  51.  * shell.  If SHELL is defined, we try to map the standard UNIX library
  52.  * routines to ash routines using defines.
  53.  *
  54.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  55.  * This file is part of ash, which is distributed under the terms specified
  56.  * by the Ash General Public License.  See the file named LICENSE.
  57.  */
  58.  
  59. #include "../shell.h"
  60. #include "../mystring.h"
  61. #ifdef SHELL
  62. #include "../output.h"
  63. #define stdout out1
  64. #define stderr out2
  65. #define printf out1fmt
  66. #define putc(c, file)    outc(c, file)
  67. #define putchar(c)    out1c(c)
  68. #define fprintf outfmt
  69. #define fputs outstr
  70. #define fflush flushout
  71. #define INITARGS(argv)
  72. #else
  73. #undef NULL
  74. #include <stdio.h>
  75. #undef main
  76. #define INITARGS(argv)    if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
  77. #endif
  78.  
  79. #ifdef __STDC__
  80. pointer stalloc(int);
  81. void error(char *, ...);
  82. #else
  83. pointer stalloc();
  84. void error();
  85. #endif
  86.  
  87.  
  88. extern char *commandname;
  89. EOF
  90. if test `wc -c < bltin/bltin.h` -ne 1011
  91. then    echo 'bltin/bltin.h is the wrong size'
  92. fi
  93. echo extracting bltin/catf.1
  94. cat > bltin/catf.1 <<\EOF
  95. .TH CATF 1
  96. .SH NAME \"    Copyright (C) 1989 by Kenneth Almquist.
  97. catf \- concatenate files
  98. .SH SYNOPSIS
  99. .B catf
  100. .I file...
  101. .SH COPYRIGHT
  102. .if n Copyright (C) 1989 by Kenneth Almquist.
  103. .if t Copyright \(co 1989 by Kenneth Almquist.  
  104. .SH DESCRIPTION
  105. Catf copies the files specified as arguments to the standard output.
  106. As a special case, the file name ``-'' causes
  107. .I catf
  108. to read from the standard input.
  109. .SH "HISTORICAL NOTE"
  110. Early versions of UNIX had a program which was very similar to
  111. .IR catf .
  112. It was named
  113. .IR cat .
  114. EOF
  115. if test `wc -c < bltin/catf.1` -ne 521
  116. then    echo 'bltin/catf.1 is the wrong size'
  117. fi
  118. echo extracting bltin/catf.c
  119. cat > bltin/catf.c <<\EOF
  120. /*
  121.  * Copy the files given as arguments to the standard output.  The file
  122.  * name "-" refers to the standard input.
  123.  *
  124.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  125.  * This file is part of ash, which is distributed under the terms specified
  126.  * by the Ash General Public License.  See the file named LICENSE.
  127.  */
  128.  
  129. #define main catfcmd
  130.  
  131. #include "bltin.h"
  132. #include "../error.h"
  133. #include <sys/param.h>
  134. #include <fcntl.h>
  135.  
  136.  
  137. #ifdef SBUFSIZE
  138. #define BUFSIZE() SBUFSIZE
  139. #else
  140. #ifdef MAXBSIZE
  141. #define BUFSIZE() MAXBSIZE
  142. #else
  143. #define BUFSIZE() BSIZE
  144. #endif
  145. #endif
  146.  
  147.  
  148. main(argc, argv)  char **argv; {
  149.       char *filename;
  150.       char *buf = stalloc(BUFSIZE());
  151.       int fd;
  152.       int i;
  153. #ifdef SHELL
  154.       volatile int input;
  155.       struct jmploc jmploc;
  156.       struct jmploc *volatile savehandler;
  157. #endif
  158.  
  159.       INITARGS(argv);
  160. #ifdef SHELL
  161.       input = -1;
  162.       if (setjmp(jmploc.loc)) {
  163.         close(input);
  164.         handler = savehandler;
  165.         longjmp(handler, 1);
  166.       }
  167.       savehandler = handler;
  168.       handler = &jmploc;
  169. #endif
  170.       while ((filename = *++argv) != NULL) {
  171.         if (filename[0] == '-' && filename[1] == '\0') {
  172.           fd = 0;
  173.         } else {
  174. #ifdef SHELL
  175.           INTOFF;
  176.           if ((fd = open(filename, O_RDONLY)) < 0)
  177.             error("Can't open %s", filename);
  178.           input = fd;
  179.           INTON;
  180. #else
  181.           if ((fd = open(filename, O_RDONLY)) < 0) {
  182.             fprintf(stderr, "catf: Can't open %s\n", filename);
  183.             exit(2);
  184.           }
  185. #endif
  186.         }
  187.         while ((i = read(fd, buf, BUFSIZE())) > 0) {
  188. #ifdef SHELL
  189.           if (out1 == &memout) {
  190.             register char *p;
  191.             for (p = buf ; --i >= 0 ; p++) {
  192.                   outc(*p, &memout);
  193.             }
  194.           } else {
  195.             write(1, buf, i);
  196.           }
  197. #else
  198.           write(1, buf, i);
  199. #endif
  200.         }
  201.         if (fd != 0)
  202.           close(fd);
  203.       }
  204. #ifdef SHELL
  205.       handler = savehandler;
  206. #endif
  207. }
  208. EOF
  209. if test `wc -c < bltin/catf.c` -ne 1795
  210. then    echo 'bltin/catf.c is the wrong size'
  211. fi
  212. echo extracting bltin/echo.1
  213. cat > bltin/echo.1 <<\EOF
  214. .TH ECHO 1
  215. .SH NAME \"    Copyright (C) 1989 by Kenneth Almquist.
  216. echo \- produce message in a shell script
  217. .SH SYNOPSIS
  218. .B echo
  219. [
  220. .B -n
  221. |
  222. .B -e
  223. ]
  224. .I args...
  225. .SH COPYRIGHT
  226. .if n Copyright (C) 1989 by Kenneth Almquist.
  227. .if t Copyright \(co 1989 by Kenneth Almquist.  
  228. .SH DESCRIPTION
  229. .I Echo
  230. prints its arguments on the standard output, separated by spaces.
  231. Unless the
  232. .B -n
  233. option is present, a newline is output following the arguments.
  234. The
  235. .B -e
  236. option causes
  237. .I echo
  238. to treat the escape sequences specially, as described in the following
  239. paragraph.  The
  240. .B -e
  241. option is the default, and is provided solely for compatibility with
  242. other systems.
  243. Only one of the options
  244. .B -n
  245. and
  246. .B -e
  247. may be given.
  248. .PP
  249. If any of the following sequences of characters is encountered during
  250. output, the sequence is not output.  Instead, the specified action is
  251. performed:
  252. .nr i 0.6i
  253. .de i
  254. .sp
  255. .ti -\\niu
  256. \\$1    \c
  257. .if \w'\\$1'-\\ni .br
  258. ..
  259. .in 1.1i
  260. .ta 0.6i
  261. .i \eb
  262. A backspace character is output.
  263. .i \ec
  264. Subsequent output is suppressed.  This is normally used at the end of the
  265. last argument to suppress the trailing newline that
  266. .I echo
  267. would otherwise output.
  268. .i \ef
  269. Output a form feed.
  270. .i \en
  271. Output a newline character.
  272. .i \er
  273. Output a carriage return.
  274. .i \et
  275. Output a (horizontal) tab character.
  276. .i \ev
  277. Output a vertical tab.
  278. .i \e0\fIdigits\fR
  279. Output the character whose value is given by zero to three digits.
  280. If there are zero digits, a nul character is output.
  281. .i \e\e
  282. Output a backslash.
  283. .in -1.1i
  284. .SH HINTS
  285. Remember that backslash is special to the shell and needs to be escaped.
  286. To output a message to standard error, say
  287. .sp
  288. .ti +1i
  289. echo message >&2
  290. .SH BUGS
  291. The octal character escape mechanism (\e0\fIdigits\fR) differs from the
  292. C language mechanism.
  293. .PP
  294. There is no way to force
  295. .I echo
  296. to treat its arguments literally, rather than interpreting them as
  297. options and escape sequences.
  298. EOF
  299. if test `wc -c < bltin/echo.1` -ne 1879
  300. then    echo 'bltin/echo.1 is the wrong size'
  301. fi
  302. echo extracting bltin/echo.c
  303. cat > bltin/echo.c <<\EOF
  304. /*
  305.  * Echo command.
  306.  *
  307.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  308.  * This file is part of ash, which is distributed under the terms specified
  309.  * by the Ash General Public License.  See the file named LICENSE.
  310.  */
  311.  
  312. #define main echocmd
  313.  
  314. #include "bltin.h"
  315.  
  316. #define eflag 1
  317.  
  318.  
  319. main(argc, argv)  char **argv; {
  320.       register char **ap;
  321.       register char *p;
  322.       register char c;
  323.       int count;
  324.       int nflag = 0;
  325. #ifndef eflag
  326.       int eflag = 0;
  327. #endif
  328.  
  329.       ap = argv;
  330.       if (argc)
  331.         ap++;
  332.       if ((p = *ap) != NULL) {
  333.         if (equal(p, "-n")) {
  334.           nflag++;
  335.           ap++;
  336.         } else if (equal(p, "-e")) {
  337. #ifndef eflag
  338.           eflag++;
  339. #endif
  340.           ap++;
  341.         }
  342.       }
  343.       while ((p = *ap++) != NULL) {
  344.         while ((c = *p++) != '\0') {
  345.           if (c == '\\' && eflag) {
  346.             switch (*p++) {
  347.             case 'b':  c = '\b';  break;
  348.             case 'c':  return 0;        /* exit */
  349.             case 'f':  c = '\f';  break;
  350.             case 'n':  c = '\n';  break;
  351.             case 'r':  c = '\r';  break;
  352.             case 't':  c = '\t';  break;
  353.             case 'v':  c = '\v';  break;
  354.             case '\\':  break;        /* c = '\\' */
  355.             case '0':
  356.                   c = 0;
  357.                   count = 3;
  358.                   while (--count >= 0 && (unsigned)(*p - '0') < 8)
  359.                     c = (c << 3) + (*p++ - '0');
  360.                   break;
  361.             default:
  362.                   p--;
  363.                   break;
  364.             }
  365.           }
  366.           putchar(c);
  367.         }
  368.         if (*ap)
  369.           putchar(' ');
  370.       }
  371.       if (! nflag)
  372.         putchar('\n');
  373.       return 0;
  374. }
  375. EOF
  376. if test `wc -c < bltin/echo.c` -ne 1419
  377. then    echo 'bltin/echo.c is the wrong size'
  378. fi
  379. echo extracting bltin/error.c
  380. cat > bltin/error.c <<\EOF
  381. /*
  382.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  383.  * This file is part of ash, which is distributed under the terms specified
  384.  * by the Ash General Public License.  See the file named LICENSE.
  385.  */
  386.  
  387. #include <stdio.h>
  388.  
  389. char *commandname;
  390.  
  391.  
  392. void
  393. #ifdef __STDC__
  394. error(char *msg, ...) {
  395. #else
  396. error(msg)
  397.       char *msg;
  398.       {
  399. #endif
  400.  
  401.       fprintf(stderr, "%s: %s\n", commandname, msg);
  402.       exit(2);
  403. }
  404. EOF
  405. if test `wc -c < bltin/error.c` -ne 422
  406. then    echo 'bltin/error.c is the wrong size'
  407. fi
  408. echo extracting bltin/expr.1
  409. cat > bltin/expr.1 <<\EOF
  410. .TH EXPR 1
  411. .SH NAME \"    Copyright (C) 1989 by Kenneth Almquist.
  412. expr, text \- evaluate expressions
  413. .SH SYNOPSIS
  414. .B expr
  415. .I expression
  416. .br
  417. .B test
  418. .I expression
  419. .br
  420. .B [
  421. .I expression
  422. .B ]
  423. .SH COPYRIGHT
  424. .if n Copyright (C) 1989 by Kenneth Almquist.
  425. .if t Copyright \(co 1989 by Kenneth Almquist.  
  426. .SH DESCRIPTION
  427. .I Expr
  428. evaluates the expression and prints the result.
  429. .I Test
  430. evaluates the expression without printing the result.
  431. The ``[''
  432. command is a synonym for
  433. .IR test ;
  434. when invoked under this name
  435. the last argument to
  436. .I expr
  437. must be a ``]'', which is deleted and not considered part of the expression.
  438. .PP
  439. Three data types may occur in the
  440. .IR expression :
  441. string, integer, and boolean.
  442. The rules for conversion are as follows:
  443. .sp
  444. .nr i 2
  445. .ta \nii
  446. .in +\nii
  447. .ti -\nii
  448. \fIstring\fR->\fIinteger\fR    Done via
  449. .IR atoi (3).
  450. .ti -\nii
  451. \fIinteger\fR->\fIstring\fR    Convert to decimal reprentation.
  452. .ti -\nii
  453. \fIstring\fR->\fIboolean\fR    "" -> false, everything else to true.
  454. .ti -\nii
  455. \fIboolean\fR->\fIstring\fR    false -> "", true -> "true".
  456. .ti -\nii
  457. \fIinteger\fR->\fIboolean\fR    0 -> false, everything else to true.
  458. .ti -\nii
  459. \fIboolean\fR->\fIinteger\fR    false -> 0, true -> 1.
  460. .in -\nii
  461. .PP
  462. Any argument to
  463. .I expr
  464. which is not a legal operator is treated as a string operand of type
  465. .IR string .
  466. If the operand is enclosed in single quotes, these are deleted.
  467. (Note that single quotes are stripped by the shell, so the single quotes
  468. must be quoted, typically by enclosing them in double quotes.)
  469. .PP
  470. As a special case, if
  471. .I expression
  472. is omitted, the result is false.
  473. .PP
  474. We now list the operators.  The syntax
  475. .sp
  476. .ti +8
  477. \fIinteger\fB op \fIinteger\fR -> \fIboolean\fB (3)\fR
  478. .sp
  479. means that \fBop\fR is a binary operator which takes operands of type
  480. \fIinteger\fR and produces a result of type \fIboolean\fR.
  481. The ``(3)'' means that the priority of \fBop\fR is 3.
  482. Operands are automaticly converted to the appropriate type.  The type
  483. \fIany\fR is used for operator that take operands of any type.
  484. .nr p 1
  485. .de b
  486. .HP 0.5i
  487. \fI\\$1\fB \\$2 \fI\\$3\fR -> \\fI\\$4\\fR  (\\np)
  488. .br
  489. ..
  490. .de u
  491. .HP 0.5i
  492. \\$1 \fI\\$2\fR -> \\fI\\$3\\fR  (\\np)
  493. .br
  494. ..
  495. .b any -o any any
  496. Returns the value of the left hand operand if the left hand operand
  497. would yield
  498. .I true
  499. if converted to type
  500. .IR boolean ,
  501. and the value of the right hand operand otherwise.
  502. The right hand operand is evaluated only if necessary.
  503. ``|'' is a synonym for ``-o''.
  504. .nr p \np+1
  505. .b any -a any any
  506. Returns the value of the left hand operand if the left hand operand
  507. would yield
  508. .I false
  509. if converted to type
  510. .IR boolean ,
  511. and the value of the right hand operand otherwise.
  512. The right hand operand is evaluated only if necessary.
  513. ``&'' is a synonym for ``-a''.
  514. .nr p \np+1
  515. .u ! boolean boolean
  516. Returns true if the operand is false, and false if the operand is true.
  517. .nr p \np+1
  518. .b string = string boolean
  519. True if the two strings are equal.
  520. .b string != string boolean
  521. True if the two strings are not equal.
  522. .b integer -eq integer boolean
  523. True if the two operands are equal.
  524. .b integer -ne integer boolean
  525. True if the two operands are not equal.
  526. .b integer -gt integer boolean
  527. True if the first operand is greater than the second one.
  528. .b integer -lt integer boolean
  529. True if the first operand is less than the second one.
  530. .b integer -ge integer boolean
  531. True if the first operand is greater than or equal to the second one.
  532. .b integer -le integer boolean
  533. True if the first operand is less than or equal to the second one.
  534. .nr p \np+1
  535. .b integer + integer integer
  536. Add two integers.
  537. .b integer - integer integer
  538. Subtract two integers.
  539. .nr p \np+1
  540. .b integer * integer integer
  541. Multiply two integers.  ``*'' is special to the shell, so you generally
  542. have to write this operator as ``\e*''.
  543. .b integer / integer integer
  544. Divide two integers.
  545. .b integer % integer integer
  546. Returns the remainder when the first operand is divided by the second one.
  547. .nr p \np+1
  548. .b string : string "integer or string"
  549. The second operand is interpreted as a regular expression (as in the
  550. System V
  551. .I ed
  552. program).
  553. This operator attempts to match part (or all) of the first operand
  554. with the regular expression.  The match must start at the beginning of
  555. the first operand.
  556. If the regular expression contains and \e( \e) pairs, then the result
  557. of this operator is the string which is matched by the regular expression
  558. between these pairs, or the null string if no match occurred.  Otherwise,
  559. the result is the number of characters matched by the regular expression,
  560. or zero if no no match occurred.
  561. .nr p \np+1
  562. .u -n string integer
  563. Returns the number of characters in the string.
  564. .u -z string boolean
  565. Returns true if the string contains zero characters.
  566. .u -t integer boolean
  567. Returns true if the specified file descriptor is associated with a tty.
  568. .PP
  569. The remaining operators all deal with files.  Except as noted, they return
  570. false if the
  571. specified file does not exist.  The ones dealing with permission use
  572. the effective user and group ids of the shell.
  573. .u -r string boolean
  574. True if you have read permission on the file.
  575. .u -w string boolean
  576. True if you have write permission on the file.
  577. .u -x string boolean
  578. True if you have execute permission on the file.
  579. .u -f string boolean
  580. True if the file is a regular file.
  581. .u -d string boolean
  582. True if the file is a directory.
  583. .u -c string boolean
  584. True if the file is a character special file.
  585. .u -b string boolean
  586. True if the file is a block special file.
  587. .u -p string boolean
  588. True if the file is a named pipe (i.e. a fifo).
  589. .u -u string boolean
  590. True if the file is setuid.
  591. .u -g string boolean
  592. True if the file is setgid.
  593. .u -k string boolean
  594. True if the file has the sticky bit set.
  595. .u -s string "integer or boolean"
  596. Returns the size of the file, or 0 if the file does not exist.
  597. .SH "EXIT CODE"
  598. 0 if the result of 
  599. .I expression
  600. would be
  601. .I true
  602. if the result were converted to
  603. .IR boolean .
  604. .br
  605. 1 if the result of 
  606. .I expression
  607. would be
  608. .I false
  609. if the result were converted to
  610. .IR boolean .
  611. .br
  612. 2 if
  613. .I expression
  614. is syntactically incorrect.
  615. .SH EXAMPLES
  616. .HP 0.5i
  617. filesize=`expr -s file`
  618. .br
  619. Sets the shell variable
  620. .I filesize
  621. to the size of
  622. .IR file .
  623. .HP 0.5i
  624. if [ -s file ]; then command; fi
  625. .br
  626. Execute
  627. .I command
  628. if
  629. .I file
  630. exists and is not empty.
  631. .HP 0.5i
  632. x=`expr "'$x'" : '.\{4\}\(.\{0,3\}\)'`
  633. .br
  634. Sets
  635. .I x
  636. to the substring of
  637. .I x
  638. beginning after the fourth character of
  639. .I x
  640. and continuing for three characters or until the end of the string,
  641. whichever comes first.
  642. .HP 0.5i
  643. x=`expr X"$x" : X'.\{4\}\(.\{0,3\}\)'`
  644. .br
  645. This example is the same as the previous one, but it uses a leading
  646. ``X'' rather than single quotes to make things work when the value of
  647. .I x
  648. looks like an operator.  An alternative would be to write
  649. .HP 0.5i
  650. x=`expr "'$x'" : '.\{4\}\(.\{0,3\}\)'`
  651. .br
  652. This encloses the value of
  653. .I x
  654. in single quotes to keep it from being confused with an operator,
  655. which is simpler than the preceding example, but also less portable.
  656. .SH BUGS
  657. The relational operators of the System V
  658. .I expr
  659. command are not implemented.
  660. .PP
  661. Certain features of this version of
  662. .I expr
  663. are not present in System V, so care should be used when writing
  664. portable code.
  665. EOF
  666. if test `wc -c < bltin/expr.1` -ne 7136
  667. then    echo 'bltin/expr.1 is the wrong size'
  668. fi
  669. echo extracting bltin/expr.c
  670. cat > bltin/expr.c <<\EOF
  671. /*
  672.  * The expr and test commands.
  673.  *
  674.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  675.  * This file is part of ash, which is distributed under the terms specified
  676.  * by the Ash General Public License.  See the file named LICENSE.
  677.  */
  678.  
  679.  
  680. #define main exprcmd
  681.  
  682. #include "bltin.h"
  683. #include "operators.h"
  684. #include <sys/types.h>
  685. #include <sys/stat.h>
  686.  
  687.  
  688. #define STACKSIZE 12
  689. #define NESTINCR 16
  690.  
  691. /* data types */
  692. #define STRING 0
  693. #define INTEGER 1
  694. #define BOOLEAN 2
  695.  
  696.  
  697. /*
  698.  * This structure hold a value.  The type keyword specifies the type of
  699.  * the value, and the union u holds the value.  The value of a boolean
  700.  * is stored in u.num (1 = TRUE, 0 = FALSE).
  701.  */
  702.  
  703. struct value {
  704.       int type;
  705.       union {
  706.         char *string;
  707.         long num;
  708.       } u;
  709. };
  710.  
  711.  
  712. struct operator {
  713.       short op;            /* which operator */
  714.       short pri;        /* priority of operator */
  715. };
  716.  
  717.  
  718. struct filestat {
  719.       char *name;        /* name of file */
  720.       int rcode;        /* return code from stat */
  721.       struct stat stat;        /* status info on file */
  722. };
  723.  
  724.  
  725. extern char *match_begin[10];    /* matched string */
  726. extern short match_length[10];    /* defined in regexp.c */
  727. extern short number_parens;    /* number of \( \) pairs */
  728.  
  729.  
  730. #ifdef __STDC__
  731. int expr_is_false(struct value *);
  732. void expr_operator(int, struct value *, struct filestat *);
  733. int lookup_op(char *, char *const*);
  734. char *re_compile(char *);    /* defined in regexp.c */
  735. int re_match(char *, char *);    /* defined in regexp.c */
  736. long atol(const char *);
  737. #else
  738. int expr_is_false();
  739. void expr_operator();
  740. int lookup_op();
  741. char *re_compile();    /* defined in regexp.c */
  742. int re_match();    /* defined in regexp.c */
  743. long atol();
  744. #endif
  745.  
  746.  
  747.  
  748. main(argc, argv)  char **argv; {
  749.       char **ap;
  750.       char *opname;
  751.       char c;
  752.       char *p;
  753.       int print;
  754.       int nest;        /* parenthises nesting */
  755.       int op;
  756.       int pri;
  757.       int skipping;
  758.       int binary;
  759.       struct operator opstack[STACKSIZE];
  760.       struct operator *opsp;
  761.       struct value valstack[STACKSIZE + 1];
  762.       struct value *valsp;
  763.       struct filestat fs;
  764.  
  765.       INITARGS(argv);
  766.       c = **argv;
  767.       print = 1;
  768.       if (c == 't')
  769.         print = 0;
  770.       else if (c == '[') {
  771.         if (! equal(argv[argc - 1], "]"))
  772.           error("missing ]");
  773.         argv[argc - 1] = NULL;
  774.         print = 0;
  775.       }
  776.       ap = argv + 1;
  777.       fs.name = NULL;
  778.  
  779.       /*
  780.        * We use operator precedence parsing, evaluating the expression
  781.        * as we parse it.  Parentheses are handled by bumping up the
  782.        * priority of operators using the variable "nest."  We use the
  783.        * variable "skipping" to turn off evaluation temporarily for the
  784.        * short circuit boolean operators.  (It is important do the short
  785.        * circuit evaluation because under NFS a stat operation can take
  786.        * infinitely long.)
  787.        */
  788.  
  789.       nest = 0;
  790.       skipping = 0;
  791.       opsp = opstack + STACKSIZE;
  792.       valsp = valstack;
  793.       if (*ap == NULL) {
  794.         valstack[0].type = BOOLEAN;
  795.         valstack[0].u.num = 0;
  796.         goto done;
  797.       }
  798.       for (;;) {
  799.         opname = *ap++;
  800.         if (opname == NULL)
  801. syntax:          error("syntax error");
  802.         if (opname[0] == '(' && opname[1] == '\0') {
  803.           nest += NESTINCR;
  804.           continue;
  805.         } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
  806.           if (opsp == &opstack[0])
  807. overflow:        error("Expression too complex");
  808.           --opsp;
  809.           opsp->op = op;
  810.           opsp->pri = op_priority[op] + nest;
  811.           continue;
  812.  
  813.         } else {
  814.           if (opname[0] == '\'') {
  815.             for (p = opname ; *++p != '\0' ; );
  816.             if (--p > opname && *p == '\'') {
  817.                   *p = '\0';
  818.                   opname++;
  819.             }
  820.           }
  821.           valsp->type = STRING;
  822.           valsp->u.string = opname;
  823.           valsp++;
  824.         }
  825.         for (;;) {
  826.           opname = *ap++;
  827.           if (opname == NULL) {
  828.             if (nest != 0)
  829.                   goto syntax;
  830.             pri = 0;
  831.             break;
  832.           }
  833.           if (opname[0] != ')' || opname[1] != '\0') {
  834.             if ((op = lookup_op(opname, binary_op)) < 0)
  835.                   goto syntax;
  836.             op += FIRST_BINARY_OP;
  837.             pri = op_priority[op] + nest;
  838.             break;
  839.           }
  840.           if ((nest -= NESTINCR) < 0)
  841.             goto syntax;
  842.         }
  843.         while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
  844.           binary = opsp->op;
  845.           for (;;) {
  846.             valsp--;
  847.             c = op_argflag[opsp->op];
  848.             if (c == OP_INT) {
  849.                   if (valsp->type == STRING)
  850.                     valsp->u.num = atol(valsp->u.string);
  851.                   valsp->type = INTEGER;
  852.             } else if (c >= OP_STRING) { /* OP_STRING or OP_FILE */
  853.                   if (valsp->type == INTEGER) {
  854.                     p = stalloc(32);
  855. #ifdef SHELL
  856.                     fmtstr(p, 32, "%d", valsp->u.num);
  857. #else
  858.                     sprintf(p, "%d", valsp->u.num);
  859. #endif
  860.                     valsp->u.string = p;
  861.                   } else if (valsp->type == BOOLEAN) {
  862.                     if (valsp->u.num)
  863.                       valsp->u.string = "true";
  864.                     else
  865.                       valsp->u.string = "";
  866.                   }
  867.                   valsp->type = STRING;
  868.                   if (c == OP_FILE
  869.                    && (fs.name == NULL
  870.                        || ! equal(fs.name, valsp->u.string))) {
  871.                     fs.name = valsp->u.string;
  872.                     fs.rcode = stat(valsp->u.string, &fs.stat);
  873.                   }
  874.             }
  875.             if (binary < FIRST_BINARY_OP)
  876.                   break;
  877.             binary = 0;
  878.           }
  879.           if (! skipping)
  880.             expr_operator(opsp->op, valsp, &fs);
  881.           else if (opsp->op == AND1 || opsp->op == OR1)
  882.             skipping--;
  883.           valsp++;        /* push value */
  884.           opsp++;        /* pop operator */
  885.         }
  886.         if (opname == NULL)
  887.           break;
  888.         if (opsp == &opstack[0])
  889.           goto overflow;
  890.         if (op == AND1 || op == AND2) {
  891.           op = AND1;
  892.           if (skipping || expr_is_false(valsp - 1))
  893.             skipping++;
  894.         }
  895.         if (op == OR1 || op == OR2) {
  896.           op = OR1;
  897.           if (skipping || ! expr_is_false(valsp - 1))
  898.             skipping++;
  899.         }
  900.         opsp--;
  901.         opsp->op = op;
  902.         opsp->pri = pri;
  903.       }
  904. done:
  905.       if (print) {
  906.         if (valstack[0].type == STRING)
  907.           printf("%s\n", valstack[0].u.string);
  908.         else if (valstack[0].type == INTEGER)
  909.           printf("%ld\n", valstack[0].u.num);
  910.         else if (valstack[0].u.num != 0)
  911.           printf("true\n");
  912.       }
  913.       return expr_is_false(&valstack[0]);
  914. }
  915.  
  916.  
  917. int
  918. expr_is_false(val)
  919.       struct value *val;
  920.       {
  921.       if (val->type == STRING) {
  922.         if (val->u.string[0] == '\0')
  923.           return 1;
  924.       } else {    /* INTEGER or BOOLEAN */
  925.         if (val->u.num == 0)
  926.           return 1;
  927.       }
  928.       return 0;
  929. }
  930.  
  931.  
  932. /*
  933.  * Execute an operator.  Op is the operator.  Sp is the stack pointer;
  934.  * sp[0] refers to the first operand, sp[1] refers to the second operand
  935.  * (if any), and the result is placed in sp[0].  The operands are converted
  936.  * to the type expected by the operator before expr_operator is called.
  937.  * Fs is a pointer to a structure which holds the value of the last call
  938.  * to stat, to avoid repeated stat calls on the same file.
  939.  */
  940.  
  941. void
  942. expr_operator(op, sp, fs)
  943.       int op;
  944.       struct value *sp;
  945.       struct filestat *fs;
  946.       {
  947.       int i;
  948.  
  949.       switch (op) {
  950.       case NOT:
  951.         sp->u.num = expr_is_false(sp);
  952.         sp->type = BOOLEAN;
  953.         break;
  954.       case ISREAD:
  955.         i = 04;
  956.         goto permission;
  957.       case ISWRITE:
  958.         i = 02;
  959.         goto permission;
  960.       case ISEXEC:
  961.         i = 01;
  962. permission:
  963.         if (fs->stat.st_uid == geteuid())
  964.           i <<= 6;
  965.         else if (fs->stat.st_gid == getegid())
  966.           i <<= 3;
  967.         goto filebit;    /* true if (stat.st_mode & i) != 0 */
  968.       case ISFILE:
  969.         i = S_IFREG;
  970.         goto filetype;
  971.       case ISDIR:
  972.         i = S_IFDIR;
  973.         goto filetype;
  974.       case ISCHAR:
  975.         i = S_IFCHR;
  976.         goto filetype;
  977.       case ISBLOCK:
  978.         i = S_IFBLK;
  979.         goto filetype;
  980.       case ISFIFO:
  981. #ifdef S_IFIFO
  982.         i = S_IFIFO;
  983.         goto filetype;
  984. #else
  985.         goto false;
  986. #endif
  987. filetype:
  988.         if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) {
  989. true:
  990.           sp->u.num = 1;
  991.         } else {
  992. false:
  993.           sp->u.num = 0;
  994.         }
  995.         sp->type = BOOLEAN;
  996.         break;
  997.       case ISSETUID:
  998.         i = S_ISUID;
  999.         goto filebit;
  1000.       case ISSETGID:
  1001.         i = S_ISGID;
  1002.         goto filebit;
  1003.       case ISSTICKY:
  1004.         i = S_ISVTX;
  1005. filebit:
  1006.         if (fs->stat.st_mode & i && fs->rcode >= 0)
  1007.           goto true;
  1008.         goto false;
  1009.       case ISSIZE:
  1010.         sp->u.num = fs->rcode >= 0? fs->stat.st_size : 0L;
  1011.         sp->type = INTEGER;
  1012.         break;
  1013.       case ISTTY:
  1014.         sp->u.num = isatty(sp->u.num);
  1015.         sp->type = BOOLEAN;
  1016.         break;
  1017.       case NULSTR:
  1018.         if (sp->u.string[0] == '\0')
  1019.           goto true;
  1020.         goto false;
  1021.       case STRLEN:
  1022.         sp->u.num = strlen(sp->u.string);
  1023.         sp->type = INTEGER;
  1024.         break;
  1025.       case OR1:
  1026.       case AND1:
  1027.         /*
  1028.          * These operators are mostly handled by the parser.  If we
  1029.          * get here it means that both operands were evaluated, so
  1030.          * the value is the value of the second operand.
  1031.          */
  1032.         *sp = *(sp + 1);
  1033.         break;
  1034.       case STREQ:
  1035.       case STRNE:
  1036.         i = 0;
  1037.         if (equal(sp->u.string, (sp + 1)->u.string))
  1038.           i++;
  1039.         if (op == STRNE)
  1040.           i = 1 - i;
  1041.         sp->u.num = i;
  1042.         sp->type = BOOLEAN;
  1043.         break;
  1044.       case EQ:
  1045.         if (sp->u.num == (sp + 1)->u.num)
  1046.           goto true;
  1047.         goto false;
  1048.       case NE:
  1049.         if (sp->u.num != (sp + 1)->u.num)
  1050.           goto true;
  1051.         goto false;
  1052.       case GT:
  1053.         if (sp->u.num > (sp + 1)->u.num)
  1054.           goto true;
  1055.         goto false;
  1056.       case LT:
  1057.         if (sp->u.num < (sp + 1)->u.num)
  1058.           goto true;
  1059.         goto false;
  1060.       case LE:
  1061.         if (sp->u.num <= (sp + 1)->u.num)
  1062.           goto true;
  1063.         goto false;
  1064.       case GE:
  1065.         if (sp->u.num >= (sp + 1)->u.num)
  1066.           goto true;
  1067.         goto false;
  1068.       case PLUS:
  1069.         sp->u.num += (sp + 1)->u.num;
  1070.         break;
  1071.       case MINUS:
  1072.         sp->u.num -= (sp + 1)->u.num;
  1073.         break;
  1074.       case TIMES:
  1075.         sp->u.num *= (sp + 1)->u.num;
  1076.         break;
  1077.       case DIVIDE:
  1078.         if ((sp + 1)->u.num == 0)
  1079.           error("Division by zero");
  1080.         sp->u.num /= (sp + 1)->u.num;
  1081.         break;
  1082.       case REM:
  1083.         if ((sp + 1)->u.num == 0)
  1084.           error("Division by zero");
  1085.         sp->u.num %= (sp + 1)->u.num;
  1086.         break;
  1087.       case MATCHPAT:
  1088.         {
  1089.           char *pat;
  1090.  
  1091.           pat = re_compile((sp + 1)->u.string);
  1092.           if (re_match(pat, sp->u.string)) {
  1093.             if (number_parens > 0) {
  1094.                   sp->u.string = match_begin[1];
  1095.                   sp->u.string[match_length[1]] = '\0';
  1096.             } else {
  1097.                   sp->u.num = match_length[0];
  1098.                   sp->type = INTEGER;
  1099.             }
  1100.           } else {
  1101.             if (number_parens > 0) {
  1102.                   sp->u.string[0] = '\0';
  1103.             } else {
  1104.                   sp->u.num = 0;
  1105.                   sp->type = INTEGER;
  1106.             }
  1107.           }
  1108.         }
  1109.         break;
  1110.       }
  1111. }
  1112.  
  1113.  
  1114. int
  1115. lookup_op(name, table)
  1116.       char *name;
  1117.       char *const*table;
  1118.       {
  1119.       register char *const*tp;
  1120.       register char const *p;
  1121.       char c = name[1];
  1122.  
  1123.       for (tp = table ; (p = *tp) != NULL ; tp++) {
  1124.         if (p[1] == c && equal(p, name))
  1125.           return tp - table;
  1126.       }
  1127.       return -1;
  1128. }
  1129. EOF
  1130. if test `wc -c < bltin/expr.c` -ne 10535
  1131. then    echo 'bltin/expr.c is the wrong size'
  1132. fi
  1133. echo extracting bltin/line.1
  1134. cat > bltin/line.1 <<\EOF
  1135. .TH LINE 1
  1136. .SH NAME \"    Copyright (C) 1989 by Kenneth Almquist.
  1137. line \- read a line
  1138. .SH SYNOPSIS
  1139. .B line
  1140. .SH COPYRIGHT
  1141. .if n Copyright (C) 1989 by Kenneth Almquist.
  1142. .if t Copyright \(co 1989 by Kenneth Almquist.  
  1143. .SH DESCRIPTION
  1144. .I Line
  1145. copies one line from its standard input to its standard output.
  1146. If it encounters an end of file before reading a newline, it
  1147. outputs a newline character and returns an exit status of 1.
  1148. EOF
  1149. if test `wc -c < bltin/line.1` -ne 423
  1150. then    echo 'bltin/line.1 is the wrong size'
  1151. fi
  1152. echo extracting bltin/line.c
  1153. cat > bltin/line.c <<\EOF
  1154. /*
  1155.  * The line command.  Reads one line from the standard input and writes it
  1156.  * to the standard output.
  1157.  *
  1158.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1159.  * This file is part of ash, which is distributed under the terms specified
  1160.  * by the Ash General Public License.  See the file named LICENSE.
  1161.  */
  1162.  
  1163. #define main linecmd
  1164.  
  1165. #include "bltin.h"
  1166.  
  1167.  
  1168. main(argc, argv)  char **argv; {
  1169.       char c;
  1170.  
  1171.       for (;;) {
  1172.         if (read(0, &c, 1) != 1) {
  1173.           putchar('\n');
  1174.           return 1;
  1175.         }
  1176.         putchar(c);
  1177.         if (c == '\n')
  1178.           return 0;
  1179.       }
  1180. }
  1181. EOF
  1182. if test `wc -c < bltin/line.c` -ne 562
  1183. then    echo 'bltin/line.c is the wrong size'
  1184. fi
  1185. echo extracting bltin/makefile
  1186. cat > bltin/makefile <<\EOF
  1187. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1188. # This file is part of ash, which is distributed under the terms specified
  1189. # by the Ash General Public License.  See the file named LICENSE.
  1190.  
  1191. LIBFILES=catfcmd.o echocmd.o exprcmd.o linecmd.o nlechocmd.o\
  1192.     operators.o regexp.o
  1193. DEBUG=-g
  1194. CFLAGS=$(DEBUG)
  1195. #CC=gcc
  1196.  
  1197. all:$P bltinlib.a catf echo expr line nlecho true umask
  1198.  
  1199. bltinlib.a:$P $(LIBFILES)
  1200.     ar rc $@ $(LIBFILES)
  1201.  
  1202. catf: catf.c bltin.h ../shell.h ../error.h error.o stalloc.o
  1203.     $(CC) $(CFLAGS) -o $@ catf.c error.o stalloc.o
  1204.  
  1205. catfcmd.o: catf.c bltin.h ../shell.h ../error.h
  1206.     $(CC) -DSHELL $(CFLAGS) -c catf.c
  1207.     mv catf.o $@
  1208.  
  1209. expr: expr.c bltin.h ../shell.h operators.h operators.o regexp.o error.o stalloc.o
  1210.     $(CC) $(CFLAGS) -o $@ expr.c operators.o regexp.o error.o stalloc.o
  1211.     -rm -f test '['
  1212.     ln expr test
  1213.     ln expr '['
  1214.  
  1215. exprcmd.o: expr.c bltin.h ../shell.h operators.h
  1216.     $(CC) -DSHELL $(CFLAGS) -c expr.c
  1217.     mv expr.o $@
  1218.  
  1219. operators.c operators.h: unary_op binary_op mkexpr
  1220.     ./mkexpr
  1221.  
  1222. operators.o: ../shell.h operators.h
  1223.  
  1224. regexp.o: bltin.h ../shell.h
  1225.  
  1226. echo: echo.c bltin.h ../shell.h
  1227.     $(CC) $(CFLAGS) -o $@ echo.c
  1228.  
  1229. echocmd.o: echo.c bltin.h ../shell.h
  1230.     $(CC) -DSHELL $(CFLAGS) -c echo.c
  1231.     mv echo.o $@
  1232.  
  1233. line: line.c bltin.h ../shell.h
  1234.     $(CC) $(CFLAGS) -o $@ line.c
  1235.  
  1236. linecmd.o: line.c bltin.h ../shell.h
  1237.     $(CC) -DSHELL $(CFLAGS) -c line.c
  1238.     mv line.o $@
  1239.  
  1240. nlecho: nlecho.c bltin.h ../shell.h
  1241.     $(CC) $(CFLAGS) -o $@ nlecho.c
  1242.  
  1243. nlechocmd.o: nlecho.c bltin.h ../shell.h
  1244.     $(CC) -DSHELL $(CFLAGS) -c nlecho.c
  1245.     mv nlecho.o $@
  1246.  
  1247. umask: umask.c bltin.h
  1248.     $(CC) $(CFLAGS) -o $@ umask.c
  1249.  
  1250. true:
  1251.     > :
  1252.     chmod 755 :
  1253.     rm -f true
  1254.     ln : true
  1255.  
  1256. stalloc.o: ../shell.h
  1257.  
  1258. EOF
  1259. if test `wc -c < bltin/makefile` -ne 1653
  1260. then    echo 'bltin/makefile is the wrong size'
  1261. fi
  1262. echo extracting bltin/mkexpr
  1263. cat > bltin/mkexpr <<\EOF
  1264. # Copyright 1989 by Kenneth Almquist.  All rights reserved.
  1265. #
  1266. # This file is part of ash.  Ash is distributed under the terms specified
  1267. # by the Ash General Public License.  See the file named LICENSE.
  1268.  
  1269. exec > operators.h
  1270. awk '/^[^#]/    {printf "#define %s %d\n", $1, n++}' unary_op binary_op
  1271. awk '/^[^#]/    {n++}
  1272. END    {printf "\n#define FIRST_BINARY_OP %d\n", n}
  1273. ' unary_op
  1274. echo '
  1275. #define OP_INT 1        /* arguments to operator are integer */
  1276. #define OP_STRING 2        /* arguments to operator are string */
  1277. #define OP_FILE 3        /* argument is a file name */
  1278.  
  1279. extern char *const unary_op[];
  1280. extern char *const binary_op[];
  1281. extern const char op_priority[];
  1282. extern const char op_argflag[];'
  1283.  
  1284. exec > operators.c
  1285. echo '/*
  1286.  * Operators used in the expr/test command.
  1287.  */
  1288.  
  1289. #include "../shell.h"
  1290. #include "operators.h"
  1291.  
  1292. char *const unary_op[] = {'
  1293. awk '/^[^#]/    {printf "      \"%s\",\n", $2}' unary_op
  1294. echo '      NULL
  1295. };
  1296.  
  1297. char *const binary_op[] = {'
  1298. awk '/^[^#]/    {printf "      \"%s\",\n", $2}' binary_op
  1299. echo '      NULL
  1300. };
  1301.  
  1302. const char op_priority[] = {'
  1303. awk '/^[^#]/    {printf "      %s,\n", $3}' unary_op binary_op
  1304. echo '};
  1305.  
  1306. const char op_argflag[] = {'
  1307. awk '/^[^#]/    {if (length($4) > 0)    printf "      %s,\n", $4
  1308.          else            printf "      0,\n"}
  1309. ' unary_op binary_op
  1310. echo '};'
  1311. EOF
  1312. if test `wc -c < bltin/mkexpr` -ne 1256
  1313. then    echo 'bltin/mkexpr is the wrong size'
  1314. fi
  1315. chmod 755 bltin/mkexpr
  1316. echo extracting bltin/nlecho.1
  1317. cat > bltin/nlecho.1 <<\EOF
  1318. .TH NLECHO 1
  1319. .SH NAME \"    Copyright (C) 1989 by Kenneth Almquist.
  1320. nlecho \- echo arguments, one per line
  1321. .SH SYNOPSIS
  1322. .B nlecho
  1323. [
  1324. .I arg
  1325. ] ...
  1326. .SH COPYRIGHT
  1327. .if n Copyright (C) 1989 by Kenneth Almquist.
  1328. .if t Copyright \(co 1989 by Kenneth Almquist.  
  1329. .SH DESCRIPTION
  1330. .I Nlecho
  1331. prints its arguments on the standard output, one argument per line.
  1332. EOF
  1333. if test `wc -c < bltin/nlecho.1` -ne 345
  1334. then    echo 'bltin/nlecho.1 is the wrong size'
  1335. fi
  1336. echo extracting bltin/nlecho.c
  1337. cat > bltin/nlecho.c <<\EOF
  1338. /*
  1339.  * Echo the command argument to the standard output, one line at a time.
  1340.  * This command is useful for debugging th shell and whenever you what
  1341.  * to output strings literally.
  1342.  *
  1343.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1344.  * This file is part of ash, which is distributed under the terms specified
  1345.  * by the Ash General Public License.  See the file named LICENSE.
  1346.  */
  1347.  
  1348.  
  1349. #define main nlechocmd
  1350.  
  1351. #include "bltin.h"
  1352.  
  1353.  
  1354. main(argc, argv)  char **argv; {
  1355.       register char **ap;
  1356.  
  1357.       for (ap = argv + 1 ; *ap ; ap++) {
  1358.         fputs(*ap, stdout);
  1359.         putchar('\n');
  1360.       }
  1361.       return 0;
  1362. }
  1363. EOF
  1364. if test `wc -c < bltin/nlecho.c` -ne 613
  1365. then    echo 'bltin/nlecho.c is the wrong size'
  1366. fi
  1367. echo extracting bltin/regexp.c
  1368. cat > bltin/regexp.c <<\EOF
  1369. /*
  1370.  * Regular expression matching for expr(1).  Bugs:  The upper bound of
  1371.  * a range specified by the \{ feature cannot be zero.
  1372.  *
  1373.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1374.  * This file is part of ash, which is distributed under the terms specified
  1375.  * by the Ash General Public License.  See the file named LICENSE.
  1376.  */
  1377.  
  1378. #include "bltin.h"
  1379.  
  1380.  
  1381. #define RE_END 0        /* end of regular expression */
  1382. #define RE_LITERAL 1        /* normal character follows */
  1383. #define RE_DOT 2        /* "." */
  1384. #define RE_CCL 3        /* "[...]" */
  1385. #define RE_NCCL 4        /* "[^...]" */
  1386. #define RE_LP 5            /* "\(" */
  1387. #define RE_RP 6            /* "\)" */
  1388. #define RE_MATCHED 7        /* "\digit" */
  1389. #define RE_EOS 8        /* "$" matches end of string */
  1390. #define RE_STAR 9        /* "*" */
  1391. #define RE_RANGE 10        /* "\{num,num\}" */
  1392.  
  1393.  
  1394.  
  1395. char *match_begin[10];
  1396. short match_length[10];
  1397. short number_parens;
  1398.  
  1399.  
  1400.  
  1401. char *
  1402. re_compile(pattern)
  1403.     char *pattern;
  1404.     {
  1405.     register char *p;
  1406.     register char c;
  1407.     char *comp;
  1408.     register char *q;
  1409.     char *begin;
  1410.     char *endp;
  1411.     register int len;
  1412.     int first;
  1413.     int type;
  1414.     char *stackp;
  1415.     char stack[10];
  1416.     int paren_num;
  1417.     int i;
  1418.     char *malloc();
  1419.  
  1420.     p = pattern;
  1421.     if (*p == '^')
  1422.         p++;
  1423.     comp = q = malloc(2 * strlen(p) + 1);
  1424.     begin = q;
  1425.     stackp = stack;
  1426.     paren_num = 0;
  1427.     for (;;) {
  1428.         switch (c = *p++) {
  1429.         case '\0':
  1430.             *q = '\0';
  1431.             goto out;
  1432.         case '.':
  1433.             *q++ = RE_DOT;
  1434.             len = 1;
  1435.             break;
  1436.         case '[':
  1437.             begin = q;
  1438.             *q = RE_CCL;
  1439.             if (*p == '^') {
  1440.                 *q = RE_NCCL;
  1441.                 p++;
  1442.             }
  1443.             q++;
  1444.             first = 1;
  1445.             while (*p != ']' || first == 1) {
  1446.                 if (p[1] == '-' && p[2] != ']') {
  1447.                     *q++ = '-';
  1448.                     *q++ = p[0];
  1449.                     *q++ = p[2];
  1450.                     p += 3;
  1451.                 } else if (*p == '-') {
  1452.                     *q++ = '-';
  1453.                     *q++ = '-';
  1454.                     *q++ = '-';
  1455.                     p++;
  1456.                 } else {
  1457.                     *q++ = *p++;
  1458.                 }
  1459.                 first = 0;
  1460.             }
  1461.             p++;
  1462.             *q++ = '\0';
  1463.             len = q - begin;
  1464.             break;
  1465.         case '$':
  1466.             if (*p != '\0')
  1467.                 goto dft;
  1468.             *q++ = RE_EOS;
  1469.             break;
  1470.         case '*':
  1471.             if (len == 0)
  1472.                 goto dft;
  1473.             type = RE_STAR;
  1474. range:
  1475.             i = (type == RE_RANGE)? 3 : 1;
  1476.             endp = q + i;
  1477.             begin = q - len;
  1478.             do {
  1479.                 --q;
  1480.                 *(q + i) = *q;
  1481.             } while (--len > 0);
  1482.             q = begin;
  1483.             *q++ = type;
  1484.             if (type == RE_RANGE) {
  1485.                 i = 0;
  1486.                 while ((unsigned)(*p - '0') <= 9)
  1487.                     i = 10 * i + (*p++ - '0');
  1488.                 *q++ = i;
  1489.                 if (*p != ',') {
  1490.                     *q++ = i;
  1491.                 } else {
  1492.                     p++;
  1493.                     i = 0;
  1494.                     while ((unsigned)(*p - '0') <= 9)
  1495.                         i = 10 * i + (*p++ - '0');
  1496.                     *q++ = i;
  1497.                 }
  1498.                 if (*p != '\\' || *++p != '}')
  1499.                     error("RE error");
  1500.                 p++;
  1501.             }
  1502.             q = endp;
  1503.             break;
  1504.         case '\\':
  1505.             if ((c = *p++) == '(') {
  1506.                 if (++paren_num > 9)
  1507.                     error("RE error");
  1508.                 *q++ = RE_LP;
  1509.                 *q++ = paren_num;
  1510.                 *stackp++ = paren_num;
  1511.                 len = 0;
  1512.             } else if (c == ')') {
  1513.                 if (stackp == stack)
  1514.                     error("RE error");
  1515.                 *q++ = RE_RP;
  1516.                 *q++ = *--stackp;
  1517.                 len = 0;
  1518.             } else if (c == '{') {
  1519.                 type = RE_RANGE;
  1520.                 goto range;
  1521.             } else if ((unsigned)(c - '1') < 9) {
  1522.                 /* should check validity here */
  1523.                 *q++ = RE_MATCHED;
  1524.                 *q++ = c - '0';
  1525.                 len = 2;
  1526.             } else {
  1527.                 goto dft;
  1528.             }
  1529.             break;
  1530.         default:
  1531. dft:            *q++ = RE_LITERAL;
  1532.             *q++ = c;
  1533.             len = 2;
  1534.             break;
  1535.         }
  1536.     }
  1537. out:
  1538.     if (stackp != stack)
  1539.         error("RE error");
  1540.     number_parens = paren_num;
  1541.     return comp;
  1542. }
  1543.  
  1544.  
  1545.  
  1546. re_match(pattern, string)
  1547.     char *pattern;
  1548.     char *string;
  1549.     {
  1550.     char **pp;
  1551.     static int match();
  1552.  
  1553.     match_begin[0] = string;
  1554.     for (pp = &match_begin[1] ; pp <= &match_begin[9] ; pp++)
  1555.         *pp = 0;
  1556.     return match(pattern, string);
  1557. }
  1558.  
  1559.  
  1560.  
  1561. static
  1562. match(pattern, string)
  1563.     char *pattern;
  1564.     char *string;
  1565.     {
  1566.     register char *p, *q;
  1567.     int counting;
  1568.     int low, high, count;
  1569.     char *curpat;
  1570.     char *start_count;
  1571.     int negate;
  1572.     int found;
  1573.     char *r;
  1574.     int len;
  1575.     char c;
  1576.  
  1577.     p = pattern;
  1578.     q = string;
  1579.     counting = 0;
  1580.     for (;;) {
  1581.         if (counting) {
  1582.             if (++count > high)
  1583.                 goto bad;
  1584.             p = curpat;
  1585.         }
  1586.         switch (*p++) {
  1587.         case RE_END:
  1588.             match_length[0] = q - match_begin[0];
  1589.             return 1;
  1590.         case RE_LITERAL:
  1591.             if (*q++ != *p++)
  1592.                 goto bad;
  1593.             break;
  1594.         case RE_DOT:
  1595.             if (*q++ == '\0')
  1596.                 goto bad;
  1597.             break;
  1598.         case RE_CCL:
  1599.             negate = 0;
  1600.             goto ccl;
  1601.         case RE_NCCL:
  1602.             negate = 1;
  1603. ccl:
  1604.             found = 0;
  1605.             c = *q++;
  1606.             while (*p) {
  1607.                 if (*p == '-') {
  1608.                     if (c >= *++p && c <= *++p)
  1609.                         found = 1;
  1610.                 } else {
  1611.                     if (c == *p)
  1612.                         found = 1;
  1613.                 }
  1614.                 p++;
  1615.             }
  1616.             p++;
  1617.             if (found == negate)
  1618.                 goto bad;
  1619.             break;
  1620.         case RE_LP:
  1621.             match_begin[*p++] = q;
  1622.             break;
  1623.         case RE_RP:
  1624.             match_length[*p] = q - match_begin[*p];
  1625.             p++;
  1626.             break;
  1627.         case RE_MATCHED:
  1628.             r = match_begin[*p];
  1629.             len = match_length[*p++];
  1630.             while (--len >= 0) {
  1631.                 if (*q++ != *r++)
  1632.                     goto bad;
  1633.             }
  1634.             break;
  1635.         case RE_EOS:
  1636.             if (*q != '\0')
  1637.                 goto bad;
  1638.             break;
  1639.         case RE_STAR:
  1640.             low = 0;
  1641.             high = 32767;
  1642.             goto range;
  1643.         case RE_RANGE:
  1644.             low = *p++;
  1645.             high = *p++;
  1646.             if (high == 0)
  1647.                 high = 32767;
  1648. range:
  1649.             curpat = p;
  1650.             start_count = q;
  1651.             count = 0;
  1652.             counting++;
  1653.             break;
  1654.         }
  1655.     }
  1656. bad:
  1657.     if (! counting)
  1658.         return 0;
  1659.     len = 1;
  1660.     if (*curpat == RE_MATCHED)
  1661.         len = match_length[curpat[1]];
  1662.     while (--count >= low) {
  1663.         if (match(p, start_count + count * len))
  1664.             return 1;
  1665.     }
  1666.     return 0;
  1667. }
  1668. EOF
  1669. if test `wc -c < bltin/regexp.c` -ne 5073
  1670. then    echo 'bltin/regexp.c is the wrong size'
  1671. fi
  1672. echo extracting bltin/stalloc.c
  1673. cat > bltin/stalloc.c <<\EOF
  1674. /*
  1675.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1676.  * This file is part of ash, which is distributed under the terms specified
  1677.  * by the Ash General Public License.  See the file named LICENSE.
  1678.  */
  1679.  
  1680. #include "../shell.h"
  1681.  
  1682.  
  1683. void error();
  1684. pointer malloc();
  1685.  
  1686.  
  1687. pointer
  1688. stalloc(nbytes) {
  1689.       register pointer p;
  1690.  
  1691.       if ((p = malloc(nbytes)) == NULL)
  1692.         error("Out of space");
  1693.       return p;
  1694. }
  1695. EOF
  1696. if test `wc -c < bltin/stalloc.c` -ne 413
  1697. then    echo 'bltin/stalloc.c is the wrong size'
  1698. fi
  1699. echo extracting bltin/umask.c
  1700. cat > bltin/umask.c <<\EOF
  1701. /*
  1702.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1703.  * This file is part of ash, which is distributed under the terms specified
  1704.  * by the Ash General Public License.  See the file named LICENSE.
  1705.  */
  1706.  
  1707. #include <stdio.h>
  1708.  
  1709.  
  1710. main(argc, argv)  char **argv; {
  1711.       int mask;
  1712.  
  1713.       if (argc > 1) {
  1714.         fprintf(stderr, "umask: only builtin version of umask can set value\n");
  1715.         exit(2);
  1716.       }
  1717.       printf("%.4o\n", umask(0));
  1718.       return 0;
  1719. }
  1720. EOF
  1721. if test `wc -c < bltin/umask.c` -ne 461
  1722. then    echo 'bltin/umask.c is the wrong size'
  1723. fi
  1724. echo extracting bltin/unary_op
  1725. cat > bltin/unary_op <<\EOF
  1726. # List of unary operators used by test/expr.
  1727. #
  1728. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1729. # This file is part of ash, which is distributed under the terms specified
  1730. # by the Ash General Public License.  See the file named LICENSE.
  1731.  
  1732. NOT     !    3
  1733. ISREAD     -r    12   OP_FILE
  1734. ISWRITE  -w    12   OP_FILE
  1735. ISEXEC     -x    12   OP_FILE
  1736. ISFILE     -f    12   OP_FILE
  1737. ISDIR     -d    12   OP_FILE
  1738. ISCHAR     -c    12   OP_FILE
  1739. ISBLOCK     -b    12   OP_FILE
  1740. ISFIFO     -p    12   OP_FILE
  1741. ISSETUID -u    12   OP_FILE
  1742. ISSETGID -g    12   OP_FILE
  1743. ISSTICKY -k    12   OP_FILE
  1744. ISSIZE     -s    12   OP_FILE
  1745. ISTTY     -t    12   OP_INT
  1746. NULSTR     -z    12   OP_STRING
  1747. STRLEN     -n    12   OP_STRING
  1748. EOF
  1749. if test `wc -c < bltin/unary_op` -ne 628
  1750. then    echo 'bltin/unary_op is the wrong size'
  1751. fi
  1752. if test ! -d funcs
  1753. then    mkdir funcs
  1754. fi
  1755. echo extracting funcs/cmv
  1756. cat > funcs/cmv <<\EOF
  1757. # Conditional move--don't replace an existing file.
  1758. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1759. # This file is part of ash, which is distributed under the terms specified
  1760. # by the Ash General Public License.
  1761.  
  1762. cmv() {
  1763.     if test $# != 2
  1764.     then    echo "cmv: arg count"
  1765.         return 2
  1766.     fi
  1767.     if test -f "$2" -o -w "$2"
  1768.     then    echo "$2 exists"
  1769.         return 2
  1770.     fi
  1771.     /bin/mv "$1" "$2"
  1772. }
  1773. EOF
  1774. if test `wc -c < funcs/cmv` -ne 384
  1775. then    echo 'funcs/cmv is the wrong size'
  1776. fi
  1777. echo extracting funcs/dirs
  1778. cat > funcs/dirs <<\EOF
  1779. # pushd, popd, and dirs --- written by Chris Bertin
  1780. # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
  1781. # as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
  1782.  
  1783. pushd () {
  1784.     SAVE=`pwd`
  1785.     if [ "$1" = "" ] 
  1786.     then    if [ "$DSTACK" = "" ]
  1787.         then    echo "pushd: directory stack empty."
  1788.             return 1
  1789.         fi
  1790.         set $DSTACK
  1791.         cd $1 || return
  1792.         shift 1
  1793.         DSTACK="$*"
  1794.     else    cd $1 > /dev/null || return
  1795.     fi
  1796.     DSTACK="$SAVE $DSTACK"
  1797.     dirs
  1798. }
  1799.  
  1800. popd () {
  1801.     if [ "$DSTACK" = "" ] 
  1802.     then    echo "popd: directory stack empty."
  1803.         return 1
  1804.     fi
  1805.     set $DSTACK
  1806.     cd $1
  1807.     shift
  1808.     DSTACK=$*
  1809.     dirs
  1810. }
  1811.  
  1812. dirs () {
  1813.     echo "`pwd` $DSTACK"
  1814.     return 0
  1815. }
  1816. EOF
  1817. if test `wc -c < funcs/dirs` -ne 609
  1818. then    echo 'funcs/dirs is the wrong size'
  1819. fi
  1820. echo extracting funcs/kill
  1821. cat > funcs/kill <<\EOF
  1822. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1823. # This file is part of ash, which is distributed under the terms specified
  1824. # by the Ash General Public License.
  1825. #
  1826. # Convert job names to process ids and then run /bin/kill.
  1827.  
  1828. kill() {
  1829.     local args x
  1830.     args=
  1831.     for x in "$@"
  1832.     do    case $x in
  1833.         %*)    x=`jobid "$x"` ;;
  1834.         esac
  1835.         args="$args $x"
  1836.     done
  1837.     /bin/kill $args
  1838. }
  1839. EOF
  1840. if test `wc -c < funcs/kill` -ne 372
  1841. then    echo 'funcs/kill is the wrong size'
  1842. fi
  1843. echo extracting funcs/login
  1844. cat > funcs/login <<\EOF
  1845. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1846. # This file is part of ash, which is distributed under the terms specified
  1847. # by the Ash General Public License.
  1848. #
  1849. # replaces the login builtin in the BSD shell
  1850.  
  1851. login () exec login "$@"
  1852. EOF
  1853. if test `wc -c < funcs/login` -ne 250
  1854. then    echo 'funcs/login is the wrong size'
  1855. fi
  1856. echo extracting funcs/newgrp
  1857. cat > funcs/newgrp <<\EOF
  1858. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1859. # This file is part of ash, which is distributed under the terms specified
  1860. # by the Ash General Public License.
  1861.  
  1862. newgrp() exec newgrp "$@"
  1863. EOF
  1864. if test `wc -c < funcs/newgrp` -ne 203
  1865. then    echo 'funcs/newgrp is the wrong size'
  1866. fi
  1867. rm -f funcs/popd
  1868. ln funcs/dirs funcs/popd
  1869. rm -f funcs/pushd
  1870. ln funcs/dirs funcs/pushd
  1871. echo extracting funcs/read
  1872. cat > funcs/read <<\EOF
  1873. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1874. # This file is part of ash, which is distributed under the terms specified
  1875. # by the Ash General Public License.
  1876.  
  1877. read () {
  1878.     if test "$#" = 0
  1879.     then    echo "Usage: read variable..."
  1880.         return 2
  1881.     fi
  1882.     read_line="`line`" read_status=$?
  1883.     read_flag="$-"
  1884.     set -f
  1885.     for read_word in $read_line
  1886.     do    set +f
  1887.         if test $# -eq 0
  1888.         then    eval "$read_var=\"\$$read_var \"'$read_word'"
  1889.         else    eval "$1='$read_word'"
  1890.             read_var="$1"
  1891.             shift
  1892.         fi
  1893.     done
  1894.     set +f
  1895.     set "-$read_flag"
  1896.     while test $# -gt 0
  1897.     do    eval "$1="
  1898.         shift
  1899.     done
  1900.     return "$read_status"
  1901. }
  1902. EOF
  1903. if test `wc -c < funcs/read` -ne 597
  1904. then    echo 'funcs/read is the wrong size'
  1905. fi
  1906. echo extracting funcs/suspend
  1907. cat > funcs/suspend <<\EOF
  1908. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1909. # This file is part of ash, which is distributed under the terms specified
  1910. # by the Ash General Public License.
  1911.  
  1912. suspend() {
  1913.     local -
  1914.     set +j
  1915.     kill -TSTP 0
  1916. }
  1917. EOF
  1918. if test `wc -c < funcs/suspend` -ne 222
  1919. then    echo 'funcs/suspend is the wrong size'
  1920. fi
  1921. echo extracting funcs/tset
  1922. cat > funcs/tset <<\EOF
  1923. # Run the tset command, setting the shell variables appropriately.  With
  1924. # the -s flag, set TERMCAP as well as TERM.  The -s flag must come first.
  1925. #
  1926. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1927. # This file is part of ash, which is distributed under the terms specified
  1928. # by the Ash General Public License.
  1929.  
  1930. tset() {
  1931.     case $1 in
  1932.     -s)    eval "$(/usr/ucb/tset "$@")"
  1933.     *)    TERM=$(/usr/ucb/tset - "$@")
  1934.     esac
  1935. }
  1936. EOF
  1937. if test `wc -c < funcs/tset` -ne 422
  1938. then    echo 'funcs/tset is the wrong size'
  1939. fi
  1940. echo Archive 8 unpacked
  1941. exit
  1942.  
  1943.