home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #26 / NN_1992_26.iso / spool / comp / sources / misc / 4066 < prev    next >
Encoding:
Text File  |  1992-11-09  |  37.9 KB  |  1,258 lines

  1. Newsgroups: comp.sources.misc
  2. Path: sparky!kent
  3. From: dfs@doe.carleton.ca (David F. Skoll)
  4. Subject:  v33i061:  remind - A replacement for calendar, Part04/12
  5. Message-ID: <1992Nov10.041850.917@sparky.imd.sterling.com>
  6. Followup-To: comp.sources.d
  7. X-Md4-Signature: 45f236c2b4a0d891540eed81669bdf14
  8. Sender: kent@sparky.imd.sterling.com (Kent Landfield)
  9. Organization: Dept. of Electronics, Carleton University
  10. References: <csm-v33i058=remind.221714@sparky.IMD.Sterling.COM>
  11. Date: Tue, 10 Nov 1992 04:18:50 GMT
  12. Approved: kent@sparky.imd.sterling.com
  13. Lines: 1243
  14.  
  15. Submitted-by: dfs@doe.carleton.ca (David F. Skoll)
  16. Posting-number: Volume 33, Issue 61
  17. Archive-name: remind/part04
  18. Environment: UNIX, MS-DOS
  19. Supersedes: remind: Volume 17, Issue 3-6
  20.  
  21. #!/bin/sh
  22. # This is part 04 of Remind 03.00.00
  23. if touch 2>&1 | fgrep 'amc' > /dev/null
  24.  then TOUCH=touch
  25.  else TOUCH=true
  26. fi
  27. # ============= expr.c ==============
  28. if test X"$1" != X"-c" -a -f 'expr.c'; then
  29.     echo "File already exists: skipping 'expr.c'"
  30. else
  31. echo "x - extracting expr.c (Text)"
  32. sed 's/^X//' << 'SHAR_EOF' > expr.c &&
  33. X/***************************************************************/
  34. X/*                                                             */
  35. X/*  EXPR.C                                                     */
  36. X/*                                                             */
  37. X/*  This file contains routines to parse and evaluate          */
  38. X/*  expressions.                                               */
  39. X/*                                                             */
  40. X/*  Copyright 1991 by David F. Skoll.                          */
  41. X/*                                                             */
  42. X/***************************************************************/
  43. X
  44. X/* If we're using Turbo C, turn off annoying warning messages! */
  45. X#ifdef __TURBOC__
  46. X#pragma warn -pia
  47. X#endif
  48. X
  49. X#include <stdio.h>
  50. X#include <ctype.h>
  51. X#include <string.h>
  52. X#include "config.h"
  53. X#ifdef HAVE_STDLIB_H
  54. X#include <stdlib.h>
  55. X#endif
  56. X#ifdef HAVE_MALLOC_H
  57. X#include <malloc.h>
  58. X#endif
  59. X#include "err.h"
  60. X#include "types.h"
  61. X#include "expr.h"
  62. X#include "protos.h"
  63. X#include "globals.h"
  64. X
  65. X#define ISID(c) (isalnum(c) || (c) == '_')
  66. X#define EQ 0
  67. X#define GT 1
  68. X#define LT 2
  69. X#define GE 3
  70. X#define LE 4
  71. X#define NE 5
  72. X
  73. Xstatic char ExprBuf[TOKSIZE+1];
  74. Xstatic char CoerceBuf[TOKSIZE+1];
  75. Xextern int NumFuncs;
  76. X
  77. X#ifdef HAVE_PROTOS
  78. XPRIVATE int Multiply(void), Divide(void), Mod(void), Add(void),
  79. X       Subtract(void), GreaterThan(void), LessThan(void),
  80. X       EqualTo(void), NotEqual(void), LessOrEqual(void),
  81. X       GreaterOrEqual(void), LogAND(void), LogOR(void),
  82. X       UnMinus(void), LogNot(void),
  83. X       Compare(int);
  84. XPRIVATE Operator *FindFunc(char *name, Operator where[], int num);
  85. X#else
  86. XPRIVATE int Multiply(), Divide(), Mod(), Add(),
  87. X       Subtract(), GreaterThan(), LessThan(),
  88. X       EqualTo(), NotEqual(), LessOrEqual(),
  89. X       GreaterOrEqual(), LogAND(), LogOR(),
  90. X           UnMinus(), LogNot(), Compare();
  91. XPRIVATE Operator *FindFunc();
  92. X#endif
  93. X
  94. XPRIVATE int MakeValue ARGS ((char *s, Value *v, Var *locals));
  95. XPRIVATE int PushOpStack ARGS ((Operator *op));
  96. XPRIVATE int PopOpStack ARGS ((Operator *op));
  97. X
  98. X/* Binary operators - all left-associative */
  99. X
  100. X/* Make SURE they are sorted lexically... this may die on an EBCDIC
  101. X   system... */
  102. X
  103. XOperator BinOp[] = {
  104. X   { "!=", 15, BIN_OP, NotEqual },
  105. X   { "%", 20, BIN_OP, Mod },
  106. X   { "&&", 14, BIN_OP, LogAND },
  107. X   { "*", 20, BIN_OP, Multiply },
  108. X   { "+", 18, BIN_OP, Add },
  109. X   { "-", 18, BIN_OP, Subtract },
  110. X   { "/", 20, BIN_OP, Divide },
  111. X   { "<", 16, BIN_OP, LessThan },
  112. X   { "<=", 16, BIN_OP, LessOrEqual },
  113. X   { "==", 15, BIN_OP, EqualTo },
  114. X   { ">", 16, BIN_OP, GreaterThan },
  115. X   { ">=", 16, BIN_OP, GreaterOrEqual },
  116. X   { "||", 12, BIN_OP, LogOR },
  117. X};
  118. X#define NUM_BIN_OPS (sizeof(BinOp) / sizeof(Operator))
  119. X
  120. X/* These ones must be sorted too. */
  121. XOperator UnOp[] = {
  122. X   { "!", 22, UN_OP, LogNot },
  123. X   { "-", 22, UN_OP, UnMinus },
  124. X};
  125. X#define NUM_UN_OPS (sizeof(UnOp) / sizeof(Operator))
  126. X
  127. X/* Functions have the same definitions as operators, except the prec field
  128. X   is used to indicate how many arguments are needed. */
  129. Xextern Operator Func[];
  130. X
  131. XOperator OpStack[OP_STACK_SIZE];
  132. XValue    ValStack[VAL_STACK_SIZE];
  133. Xint OpStackPtr, ValStackPtr;
  134. X
  135. X/***************************************************************/
  136. X/*                                                             */
  137. X/*  DebugPerform                                               */
  138. X/*                                                             */
  139. X/*  Execute an operator or function with debugging.            */
  140. X/*                                                             */
  141. X/***************************************************************/
  142. X#ifdef HAVE_PROTOS
  143. XPRIVATE int DebugPerform(Operator *op)
  144. X#else
  145. Xstatic int DebugPerform(op)
  146. XOperator *op;
  147. X#endif
  148. X{
  149. X   int r;
  150. X
  151. X   if (op->type == UN_OP) {
  152. X      fprintf(ErrFp, "%s ", op->name);
  153. X      PrintValue(&ValStack[ValStackPtr-1], ErrFp);
  154. X   } else { /* Must be binary operator */
  155. X      PrintValue(&ValStack[ValStackPtr-2], ErrFp);
  156. X      fprintf(ErrFp, " %s ", op->name);
  157. X      PrintValue(&ValStack[ValStackPtr-1], ErrFp);
  158. X   }
  159. X
  160. X   r = (op->func)();
  161. X   fprintf(ErrFp, " => ");
  162. X   if (!r) {
  163. X      PrintValue(&ValStack[ValStackPtr-1], ErrFp);
  164. X      putc('\n', ErrFp);
  165. X   } else {
  166. X      fprintf(ErrFp, "%s\n", ErrMsg[r]);
  167. X   }
  168. X   return r;
  169. X}
  170. X
  171. X/***************************************************************/
  172. X/*                                                             */
  173. X/*  CleanStack                                                 */
  174. X/*                                                             */
  175. X/*  Clean the stack after an error occurs.                     */
  176. X/*                                                             */
  177. X/***************************************************************/
  178. X#ifdef HAVE_PROTOS
  179. XPRIVATE void CleanStack(void)
  180. X#else
  181. Xstatic void CleanStack()
  182. X#endif
  183. X{
  184. X   int i;
  185. X
  186. X   for (i=0; i<ValStackPtr; i++) DestroyValue(&ValStack[i]);
  187. X   ValStackPtr = 0;
  188. X}
  189. X
  190. X/***************************************************************/
  191. X/*                                                             */
  192. X/*  PeekChar - peek ahead to next char.                        */
  193. X/*                                                             */
  194. X/***************************************************************/
  195. X#ifdef HAVE_PROTOS
  196. XPRIVATE char PeekChar(char **s)
  197. X#else
  198. Xstatic char PeekChar(s)
  199. Xchar **s;
  200. X#endif
  201. X{
  202. X   char *t = *s;
  203. X   while (*t && isspace(*t)) t++;
  204. X   return *t;
  205. X}
  206. X
  207. X/***************************************************************/
  208. X/*                                                             */
  209. X/*  ParseExprToken                                             */
  210. X/*                                                             */
  211. X/*  Read a token.                                              */
  212. X/*                                                             */
  213. X/***************************************************************/
  214. X#ifdef HAVE_PROTOS
  215. XPRIVATE int ParseExprToken(char *out, char **in)
  216. X#else
  217. Xstatic int ParseExprToken(out, in)
  218. Xchar *out;
  219. Xchar **in;
  220. X#endif
  221. X{
  222. X
  223. X   char c;
  224. X   
  225. X   *out = 0;
  226. X/* Skip white space */
  227. X   while (**in && isspace(**in)) (*in)++;
  228. X   
  229. X   if (!**in) return OK;
  230. X
  231. X   *out++ = c = *(*in)++;
  232. X   *out = 0;
  233. X
  234. X   switch(c) {
  235. X      case COMMA:
  236. X      case END_OF_EXPR:
  237. X      case '+':
  238. X      case '-':
  239. X      case '*':
  240. X      case '/':
  241. X      case '(':
  242. X      case ')':
  243. X      case '%': return OK;
  244. X
  245. X      case '&':
  246. X      case '|':
  247. X      case '=': if (**in == c) {
  248. X                *out++ = c;
  249. X                *out = 0;
  250. X                (*in)++;
  251. X           }
  252. X             return OK;
  253. X             
  254. X      case '!':
  255. X      case '>':
  256. X      case '<': if (**in == '=') {
  257. X                *out++ = '=';
  258. X                *out = 0;
  259. X                (*in)++;
  260. X           }
  261. X           return OK;
  262. X   }           
  263. X
  264. X   /* Handle the parsing of quoted strings */
  265. X   if (c == '\"') {
  266. X      while (**in) if ((c = *out++ = *(*in)++) == '\"') break;
  267. X      *out = 0;
  268. X      if (c == '\"') return OK ; else return E_MISS_QUOTE;
  269. X   }
  270. X
  271. X   if (!ISID(c)) return E_ILLEGAL_CHAR;
  272. X
  273. X   /* Parse a constant, variable name or function */
  274. X   while (ISID(**in) || **in == ':') *out++ = *(*in)++;
  275. X
  276. X   /* Chew up any remaining white space */
  277. X   while (**in && isspace(**in)) (*in)++;
  278. X
  279. X   /* Peek ahead - is it '('?  Then we have a function call */
  280. X   if (**in == '(') *out++ = *(*in)++;
  281. X
  282. X   *out = 0;
  283. X   return OK;
  284. X}
  285. X
  286. X/***************************************************************/
  287. X/*                                                             */
  288. X/*  EvalExpr                                                   */
  289. X/*  Evaluate an expression.  Return 0 if OK, non-zero if error */
  290. X/*  Put the result into value pointed to by v.                 */
  291. X/*                                                             */
  292. X/***************************************************************/
  293. X#ifdef HaveProtos
  294. XPUBLIC int EvalExpr(char **e, Value *v)
  295. X#else
  296. Xint EvalExpr(e, v)
  297. Xchar **e;
  298. XValue *v;
  299. X#endif
  300. X{
  301. X   int r;
  302. X
  303. X   OpStackPtr = 0;
  304. X   ValStackPtr = 0;
  305. X   r = Evaluate(e, NULL);
  306. X
  307. X   /* Put last character parsed back onto input stream */
  308. X   if (*ExprBuf) (*e)--;
  309. X
  310. X   if (r) {
  311. X      CleanStack();
  312. X      return r;
  313. X   }
  314. X   r = CopyValue(v, ValStack);
  315. X   DestroyValue(ValStack);
  316. X   return r;
  317. X}
  318. X
  319. X/* Evaluate - do the actual work of evaluation. */
  320. X#ifdef HAVE_PROTOS
  321. XPUBLIC int Evaluate(char **s, Var *locals)
  322. X#else
  323. Xint Evaluate(s, locals)
  324. Xchar **s;
  325. XVar *locals;
  326. X#endif
  327. X{
  328. X   int OpBase, ValBase;
  329. X   int r;
  330. X   Operator *f;
  331. X   int args; /* Number of function arguments */
  332. X   Operator op, op2;
  333. X   Value va;
  334. X   char *ufname;
  335. X   
  336. X   OpBase = OpStackPtr;
  337. X   ValBase = ValStackPtr;
  338. X   
  339. X   while(1) {
  340. X/* Looking for a value.  Accept: value, unary op, func. call or left paren */
  341. X      r = ParseExprToken(ExprBuf, s);
  342. X      if (r) return r;
  343. X      if (!*ExprBuf) return E_EOLN;
  344. X
  345. X      if (*ExprBuf == '(') { /* Parenthesized expression */
  346. X     r = Evaluate(s, locals);  /* Leaves the last parsed token in ExprBuf */
  347. X         if (r) return r;
  348. X     if (*ExprBuf != ')') return E_MISS_RIGHT_PAREN;
  349. X      } else if (*ExprBuf == '+') continue; /* Ignore unary + */
  350. X      else if (*(ExprBuf + strlen(ExprBuf) -1) == '(') { /* Function Call */
  351. X     *(ExprBuf + strlen(ExprBuf) - 1) = 0;
  352. X     f = FindFunc(ExprBuf, Func, NumFuncs);
  353. X     if (!f) {
  354. X        ufname = StrDup(ExprBuf);
  355. X        if (!ufname) return E_NO_MEM;
  356. X     }
  357. X     args = 0;
  358. X     if (PeekChar(s) == ')') { /* Function has no arguments */
  359. X        if (f) r = CallFunc(f, 0);
  360. X        else {
  361. X           r = CallUserFunc(ufname, 0);
  362. X           free(ufname);
  363. X        }
  364. X        if (r) return r;
  365. X        (void) ParseExprToken(ExprBuf, s); /* Guaranteed to be right paren. */
  366. X     } else { /* Function has some arguments */
  367. X        while(1) {
  368. X           args++;
  369. X           r = Evaluate(s, locals);
  370. X           if (r) return r;
  371. X           if (*ExprBuf == ')') break;
  372. X           else if (*ExprBuf != ',') return E_ILLEGAL_CHAR;
  373. X            }
  374. X        if (f) r = CallFunc(f, args);
  375. X        else {
  376. X           r = CallUserFunc(ufname, args);
  377. X           free(ufname);
  378. X        }
  379. X        if (r) return r;
  380. X         }
  381. X      } else { /* Unary operator */
  382. X     f = FindFunc(ExprBuf, UnOp, NUM_UN_OPS);
  383. X         if (f) {
  384. X            r = PushOpStack(f);
  385. X            if (r) return r;
  386. X        continue;  /* Still looking for an atomic vlue */
  387. X     } else if (!ISID(*ExprBuf) && *ExprBuf != '"') {
  388. X        return E_ILLEGAL_CHAR;
  389. X     } else { /* Must be a literal value */
  390. X        r = MakeValue(ExprBuf, &va, locals);
  391. X        if (r) return r;
  392. X        r = PushValStack(&va);
  393. X        if (r) return r;
  394. X     }
  395. X      }
  396. X/* OK, we've got a literal value; now, we're looking for the end of the
  397. X      expression, or a binary operator. */
  398. X      r = ParseExprToken(ExprBuf, s);
  399. X      if (r) return r;
  400. X      if (*ExprBuf == 0 || *ExprBuf == ',' || *ExprBuf == ']' || *ExprBuf == ')') {
  401. X   /* We've hit the end of the expression.  Pop off and evaluate until
  402. X         OpStackPtr = OpBase and ValStackPtr = ValBase+1 */
  403. X         while (OpStackPtr > OpBase) {
  404. X            r = PopOpStack(&op);
  405. X            if (r) return r;
  406. X        if (DebugFlag & DB_PRTEXPR)
  407. X           r=DebugPerform(&op);
  408. X        else
  409. X           r=(op.func)();
  410. X          if (r) {
  411. X           Eprint("Operator '%s' %s", op.name, ErrMsg[r]);
  412. X           return r;
  413. X            }
  414. X     }
  415. X         if (ValStackPtr != ValBase+1) return E_STACK_ERR; else return OK;
  416. X      }
  417. X      /* Must be a binary operator */
  418. X      f = FindFunc(ExprBuf, BinOp, NUM_BIN_OPS);
  419. X      if (!f) return E_EXPECTING_BINOP;
  420. X
  421. X      /* While operators of higher or equal precedence are on the stack,
  422. X         pop them off and evaluate */
  423. X      while (OpStackPtr > OpBase && OpStack[OpStackPtr-1].prec >= f->prec) {
  424. X         r = PopOpStack(&op2);
  425. X         if (r) return r;
  426. X     if (DebugFlag & DB_PRTEXPR)
  427. X        r=DebugPerform(&op2);
  428. X     else
  429. X        r=(op2.func)();
  430. X     if (r) {
  431. X        Eprint("Operator '%s' %s", op2.name, ErrMsg[r]);
  432. X        return r;
  433. X         }
  434. X      }
  435. X      r = PushOpStack(f);
  436. X      if (r) return r;
  437. X   }
  438. X}
  439. X   
  440. X/***************************************************************/
  441. X/*                                                             */
  442. X/*  MakeValue                                                  */
  443. X/*  Generate a literal value.  It's either a string, a number  */
  444. X/*  or the value of a symbol.                                  */
  445. X/*                                                             */
  446. X/***************************************************************/
  447. X#ifdef HAVE_PROTOS
  448. XPRIVATE int MakeValue(char *s, Value *v, Var *locals)
  449. X#else
  450. Xstatic int MakeValue(s, v, locals)
  451. Xchar *s;
  452. XValue *v;
  453. XVar *locals;
  454. X#endif
  455. X{
  456. X   int len;
  457. X   int h, m, r;
  458. X
  459. X   if (*s == '\"') { /* It's a literal string */
  460. X      len = strlen(s)-1;
  461. X      v->type = STR_TYPE;
  462. X      v->v.str = (char *) malloc(len);
  463. X      if (! v->v.str) {
  464. X         v->type = ERR_TYPE;
  465. X         return E_NO_MEM;
  466. X      }
  467. X      strncpy(v->v.str, s+1, len-1);
  468. X      *(v->v.str+len-1) = 0;
  469. X      return OK;
  470. X   } else if (isdigit(*s)) { /* It's a number - use len to hold it.*/
  471. X      len = 0;
  472. X      while (*s && isdigit(*s)) {
  473. X         len *= 10;
  474. X         len += (*s++ - '0');
  475. X      }
  476. X      if (*s == ':') { /* Must be a literal time */
  477. X     s++;
  478. X     if (!isdigit(*s)) return E_BAD_TIME;
  479. X     h = len;
  480. X     m = 0;
  481. X     while (isdigit(*s)) {
  482. X        m *= 10;
  483. X        m += *s - '0';
  484. X        s++;
  485. X     }
  486. X     if (*s || h>23 || m>59) return E_BAD_TIME;
  487. X     v->type = TIM_TYPE;
  488. X     v->v.val = h*60 + m;
  489. X     return OK;
  490. X      }
  491. X      /* Not a time - must be a number */
  492. X      if (*s) return E_BAD_NUMBER;
  493. X      v->type = INT_TYPE;
  494. X      v->v.val = len;
  495. X      return OK;
  496. X   } else /* Must be a symbol */
  497. X     if (DebugFlag & DB_PRTEXPR)
  498. X        fprintf(ErrFp, "%s => ", s);
  499. X     r = GetVarValue(s, v, locals);
  500. X     if (! (DebugFlag & DB_PRTEXPR)) return r;
  501. X     if (r == OK) {
  502. X        PrintValue(v, ErrFp);
  503. X    putc('\n', ErrFp);
  504. X     }
  505. X     return r;
  506. X}
  507. X
  508. X/***************************************************************/
  509. X/*                                                             */
  510. X/*  PushOpStack                                                */
  511. X/*                                                             */
  512. X/*  Push an operator onto the operator stack.                  */
  513. X/*                                                             */
  514. X/***************************************************************/
  515. X#ifdef HAVE_PROTOS
  516. XPRIVATE int PushOpStack(Operator *op)
  517. X#else
  518. Xstatic int PushOpStack(op)
  519. XOperator *op;
  520. X#endif
  521. X{
  522. X   if (OpStackPtr >= OP_STACK_SIZE)
  523. X      return E_OP_STK_OVER;
  524. X   else {
  525. X      OpStack[OpStackPtr++] = *op;
  526. X      return OK;
  527. X   }
  528. X}
  529. X
  530. X/***************************************************************/
  531. X/*                                                             */
  532. X/*  PushValStack                                               */
  533. X/*                                                             */
  534. X/*  Push a value onto the value stack.                         */
  535. X/*                                                             */
  536. X/***************************************************************/
  537. X#ifdef HAVE_PROTOS
  538. XPUBLIC int PushValStack(Value *val)
  539. X#else
  540. Xint PushValStack(val)
  541. XValue *val;
  542. X#endif
  543. X{
  544. X   if (ValStackPtr >= VAL_STACK_SIZE)
  545. X      return E_VA_STK_OVER;
  546. X   else {
  547. X      ValStack[ValStackPtr++] = *val;
  548. X      return OK;
  549. X   }
  550. X}
  551. X
  552. X/***************************************************************/
  553. X/*                                                             */
  554. X/*  PopOpStack                                                 */
  555. X/*                                                             */
  556. X/*  Pop an operator from the operator stack.                   */
  557. X/*                                                             */
  558. X/***************************************************************/
  559. X#ifdef HAVE_PROTOS
  560. XPRIVATE int PopOpStack(Operator *op)
  561. X#else
  562. Xstatic int PopOpStack(op)
  563. XOperator *op;
  564. X#endif
  565. X{
  566. X   if (OpStackPtr <= 0)
  567. X      return E_OP_STK_UNDER;
  568. X   else {
  569. X      *op = OpStack[--OpStackPtr];
  570. X      return OK;
  571. X   }
  572. X}
  573. X
  574. X/***************************************************************/
  575. X/*                                                             */
  576. X/*  PopValStack                                               */
  577. X/*                                                             */
  578. X/*  Pop a value onto the value stack.                         */
  579. X/*                                                             */
  580. X/***************************************************************/
  581. X#ifdef HAVE_PROTOS
  582. XPUBLIC int PopValStack(Value *val)
  583. X#else
  584. Xint PopValStack(val)
  585. XValue *val;
  586. X#endif
  587. X{
  588. X   if (ValStackPtr <= 0)
  589. X      return E_VA_STK_UNDER;
  590. X   else {
  591. X      *val = ValStack[--ValStackPtr];
  592. X      return OK;
  593. X   }
  594. X}
  595. X
  596. X/***************************************************************/
  597. X/*                                                             */
  598. X/*  DoCoerce - actually coerce a value to the specified type.  */
  599. X/*                                                             */
  600. X/***************************************************************/
  601. X#ifdef HAVE_PROTOS
  602. XPUBLIC int DoCoerce(char type, Value *v)
  603. X#else
  604. Xint DoCoerce(type, v)
  605. Xchar type;
  606. XValue *v;
  607. X#endif
  608. X{
  609. X   int h, d, m, y, i;
  610. X   char *s;
  611. X   
  612. X   /* Do nothing if value is already the right type */
  613. X   if (type == v->type) return OK;
  614. X   
  615. X   switch(type) {
  616. X      case STR_TYPE:
  617. X         switch(v->type) {
  618. X            case INT_TYPE: sprintf(CoerceBuf, "%d", v->v.val); break;
  619. X            case TIM_TYPE: sprintf(CoerceBuf, "%02d:%02d", v->v.val / 60, v->v.val % 60);
  620. X               break;
  621. X        case DATE_TYPE: FromJulian(v->v.val, &y, &m, &d);
  622. X                sprintf(CoerceBuf, "%04d/%02d/%02d", y, m+1, d);
  623. X                break;
  624. X            default: return E_CANT_COERCE;
  625. X         }
  626. X         v->type = STR_TYPE;
  627. X     v->v.str = StrDup(CoerceBuf);
  628. X     if (!v->v.str) {
  629. X        v->type = ERR_TYPE;
  630. X        return E_NO_MEM;
  631. X     }
  632. X     return OK;
  633. X
  634. X      case INT_TYPE:
  635. X     i = 0;
  636. X     m = 1;
  637. X     switch(v->type) {
  638. X        case STR_TYPE:
  639. X           s = v->v.str;
  640. X           if (*s == '-') {
  641. X          m = -1;
  642. X          s++;
  643. X           }
  644. X           while(*s && isdigit(*s)) {
  645. X                  i *= 10;
  646. X                  i += (*s++) - '0';
  647. X               }
  648. X               if (*s) {
  649. X          free (v->v.str);
  650. X                  v->type = ERR_TYPE;
  651. X                  return E_CANT_COERCE;
  652. X               }
  653. X               free(v->v.str);
  654. X               v->type = INT_TYPE;
  655. X           v->v.val = i * m;
  656. X           return OK;
  657. X
  658. X        case DATE_TYPE:
  659. X        case TIM_TYPE:
  660. X           v->type = INT_TYPE;
  661. X           return OK;
  662. X
  663. X            default: return E_CANT_COERCE;
  664. X     }
  665. X
  666. X      case DATE_TYPE:
  667. X     switch(v->type) {
  668. X        case INT_TYPE:
  669. X           if(v->v.val >= 0) {
  670. X          v->type = DATE_TYPE;
  671. X          return OK;
  672. X           } else return E_2LOW;
  673. X
  674. X        case STR_TYPE:
  675. X           y=0; m=0; d=0;
  676. X           s = v->v.str;
  677. X           if (!isdigit(*s)) return E_CANT_COERCE;
  678. X           while (isdigit(*s)) {
  679. X          y *= 10;
  680. X          y += *s++ - '0';
  681. X           }
  682. X           if (*s++ != '/') return E_CANT_COERCE;
  683. X           if (!isdigit(*s)) return E_CANT_COERCE;
  684. X           while (isdigit(*s)) {
  685. X          m *= 10;
  686. X          m += *s++ - '0';
  687. X           }
  688. X           m--;
  689. X           if (*s++ != '/') return E_CANT_COERCE;
  690. X           if (!isdigit(*s)) return E_CANT_COERCE;
  691. X           while (isdigit(*s)) {
  692. X          d *= 10;
  693. X          d += *s++ - '0';
  694. X           }
  695. X           if (*s || y < BASE || y > BASE+YR_RANGE ||
  696. X            m>11 || d<1 || d>DaysInMonth(m, y)) return E_CANT_COERCE;
  697. X           v->type = DATE_TYPE;
  698. X           free(v->v.str);
  699. X           v->v.val = Julian(y, m, d);
  700. X           return OK;
  701. X
  702. X        default: return E_CANT_COERCE;
  703. X     }
  704. X
  705. X      case TIM_TYPE:
  706. X     switch(v->type) {
  707. X        case INT_TYPE:
  708. X           v->type = TIM_TYPE;
  709. X           v->v.val %= 1440;
  710. X           if (v->v.val < 0) v->v.val += 1440;
  711. X           return OK;
  712. X
  713. X        case STR_TYPE:
  714. X           h = 0;
  715. X           m = 0;
  716. X           s = v->v.str;
  717. X           if (!isdigit(*s)) return E_CANT_COERCE;
  718. X           while (isdigit(*s)) {
  719. X          h *= 10;
  720. X          h += *s++ - '0';
  721. X           }
  722. X           if (*s++ != ':') return E_CANT_COERCE;
  723. X           if (!isdigit(*s)) return E_CANT_COERCE;
  724. X           while (isdigit(*s)) {
  725. X          m *= 10;
  726. X          m += *s++ - '0';
  727. X           }
  728. X           if (*s || h>23 || m>59) return E_CANT_COERCE;
  729. X           v->type = TIM_TYPE;
  730. X           free(v->v.str);
  731. X           v->v.val = h*60+m;
  732. X           return OK;
  733. X
  734. X        default: return E_CANT_COERCE;
  735. X     }
  736. X      default: return E_CANT_COERCE;
  737. X   }
  738. X}
  739. X
  740. X/***************************************************************/
  741. X/*                                                             */
  742. X/*  DestroyValue                                               */
  743. X/*                                                             */
  744. X/*  If value is of type string, deallocate string memory.      */
  745. X/*                                                             */
  746. X/***************************************************************/
  747. X#ifdef HAVE_PROTOS
  748. XPUBLIC void DestroyValue(Value *v)
  749. X#else
  750. Xvoid DestroyValue(v)
  751. XValue *v;
  752. X#endif
  753. X{
  754. X   if (v->type == STR_TYPE && v->v.str) free(v->v.str);
  755. X   v->type = ERR_TYPE;
  756. X}
  757. X
  758. X/***************************************************************/
  759. X/*                                                             */
  760. X/*  Add                                                        */
  761. X/*                                                             */
  762. X/*  Perform addition.                                          */
  763. X/*                                                             */
  764. X/***************************************************************/
  765. X#ifdef HAVE_PROTOS
  766. XPRIVATE int Add(void)
  767. X#else
  768. Xstatic int Add()
  769. X#endif
  770. X{
  771. X   Value v1, v2, v3;
  772. X   int r;
  773. X   
  774. X   if (r = PopValStack(&v2)) return r;
  775. X   if (r = PopValStack(&v1)) {
  776. X      DestroyValue(&v2);
  777. X      return r;
  778. X   }
  779. X   
  780. X/* If both are ints, just add 'em */
  781. X   if (v2.type == INT_TYPE && v1.type == INT_TYPE) {
  782. X      v2.v.val += v1.v.val;
  783. X      return (PushValStack(&v2));
  784. X   }
  785. X
  786. X/* If it's a date plus an int, add 'em */
  787. X   if ((v1.type == DATE_TYPE && v2.type == INT_TYPE) ||
  788. X       (v1.type == INT_TYPE && v2.type == DATE_TYPE)) {
  789. X      v1.v.val += v2.v.val;
  790. X      v1.type = DATE_TYPE;
  791. X      return PushValStack(&v1);
  792. X   }
  793. X   
  794. X/* If it's a time plus an int, add 'em mod 1440 */
  795. X   if ((v1.type == TIM_TYPE && v2.type == INT_TYPE) ||
  796. X       (v1.type == INT_TYPE && v2.type == TIM_TYPE)) {
  797. X      v1.v.val = (v1.v.val + v2.v.val) % 1440;
  798. X      v1.type = TIM_TYPE;
  799. X      return PushValStack(&v1);
  800. X   }       
  801. X
  802. X/* If either is a string, coerce them both to strings and concatenate */
  803. X   if (v1.type == STR_TYPE || v2.type == STR_TYPE) {
  804. X      if (r = DoCoerce(STR_TYPE, &v1)) {
  805. X           DestroyValue(&v1); DestroyValue(&v2);
  806. X         return r;
  807. X      }
  808. X      if (r = DoCoerce(STR_TYPE, &v2)) {
  809. X           DestroyValue(&v1); DestroyValue(&v2);
  810. X           return r;
  811. X      }
  812. X      v3.type = STR_TYPE;
  813. X      v3.v.str = (char *) malloc(strlen(v1.v.str) + strlen(v2.v.str) + 1);
  814. X      if (!v3.v.str) {
  815. X           DestroyValue(&v1); DestroyValue(&v2);
  816. X     return E_NO_MEM;
  817. X      }
  818. X      strcpy(v3.v.str, v1.v.str);
  819. X      strcat(v3.v.str, v2.v.str);
  820. X      DestroyValue(&v1); DestroyValue(&v2);
  821. X      return (PushValStack(&v3));
  822. X   }
  823. X
  824. X   /* Don't handle other types yet */
  825. X   return E_BAD_TYPE;
  826. X}
  827. X      
  828. X/***************************************************************/
  829. X/*                                                             */
  830. X/*  Subtract                                                   */
  831. X/*                                                             */
  832. X/*  Perform subtraction.                                       */
  833. X/*                                                             */
  834. X/***************************************************************/
  835. X#ifdef HAVE_PROTOS
  836. XPRIVATE int Subtract(void)
  837. X#else
  838. Xstatic int Subtract()
  839. X#endif
  840. X{
  841. X   Value v1, v2;
  842. X   int r;
  843. X   
  844. X   if (r = PopValStack(&v2)) return r;
  845. X   if (r = PopValStack(&v1)) {
  846. X      DestroyValue(&v2);
  847. X      return r;
  848. X   }
  849. X
  850. X   /* If they're both INTs, do subtraction */
  851. X   if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
  852. X      v1.v.val -= v2.v.val;
  853. X      return PushValStack(&v1);
  854. X   }
  855. X
  856. X   /* If it's a date minus an int, do subtraction, checking for underflow */
  857. X   if (v1.type == DATE_TYPE && v2.type == INT_TYPE) {
  858. X      v1.v.val -= v2.v.val;
  859. X      if (v1.v.val < 0) return E_DATE_OVER;
  860. X      return PushValStack(&v1);
  861. X   }
  862. X
  863. X   /* If it's a time minus an int, do subtraction mod 1440 */
  864. X   if (v1.type == TIM_TYPE && v2.type == INT_TYPE) {
  865. X      v1.v.val = (v1.v.val - v2.v.val) % 1440;
  866. X      return PushValStack(&v1);
  867. X   }
  868. X
  869. X   /* If it's a time minus a time or a date minus a date, do it */
  870. X   if ((v1.type == TIM_TYPE && v2.type == TIM_TYPE) ||
  871. X       (v1.type == DATE_TYPE && v2.type == DATE_TYPE)) {
  872. X      v1.v.val -= v2.v.val;
  873. X      v1.type = INT_TYPE;
  874. X      return PushValStack(&v1);
  875. X   }
  876. X
  877. X   /* Must be types illegal for subtraction */
  878. X   DestroyValue(&v1); DestroyValue(&v2);
  879. X   return E_BAD_TYPE;
  880. X}
  881. X
  882. X/***************************************************************/
  883. X/*                                                             */
  884. X/*  Multiply                                                   */
  885. X/*                                                             */
  886. X/*  Perform multiplication.                                    */
  887. X/*                                                             */
  888. X/***************************************************************/
  889. X#ifdef HAVE_PROTOS
  890. XPRIVATE int Multiply(void)
  891. X#else
  892. Xstatic int Multiply()
  893. X#endif
  894. X{
  895. X   Value v1, v2;
  896. X   int r;
  897. X
  898. X   if (r = PopValStack(&v2)) return r;
  899. X   if (r = PopValStack(&v1)) {
  900. X      DestroyValue(&v2);
  901. X      return r;
  902. X   }
  903. X
  904. X   if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
  905. X      v1.v.val *= v2.v.val;
  906. X      return PushValStack(&v1);
  907. X   }
  908. X   DestroyValue(&v1); DestroyValue(&v2);
  909. X   return E_BAD_TYPE;
  910. X}
  911. X
  912. X/***************************************************************/
  913. X/*                                                             */
  914. X/*  Divide                                                     */
  915. X/*                                                             */
  916. X/*  Perform division.                                          */
  917. X/*                                                             */
  918. X/***************************************************************/
  919. X#ifdef HAVE_PROTOS
  920. XPRIVATE int Divide(void)
  921. X#else
  922. Xstatic int Divide()
  923. X#endif
  924. X{
  925. X   Value v1, v2;
  926. X   int r;
  927. X
  928. X   if (r = PopValStack(&v2)) return r;
  929. X   if (r = PopValStack(&v1)) {
  930. X      DestroyValue(&v2);
  931. X      return r;
  932. X   }
  933. X
  934. X   if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
  935. X      if (v2.v.val == 0) return E_DIV_ZERO;
  936. X      v1.v.val /= v2.v.val;
  937. X      return PushValStack(&v1);
  938. X   }
  939. X   DestroyValue(&v1); DestroyValue(&v2);
  940. X   return E_BAD_TYPE;
  941. X}
  942. X
  943. X/***************************************************************/
  944. X/*                                                             */
  945. X/*  Mod                                                        */
  946. X/*                                                             */
  947. X/*  Perform modulus function.                                  */
  948. X/*                                                             */
  949. X/***************************************************************/
  950. X#ifdef HAVE_PROTOS
  951. XPRIVATE int Mod(void)
  952. X#else
  953. Xstatic int Mod()
  954. X#endif
  955. X{
  956. X   Value v1, v2;
  957. X   int r;
  958. X
  959. X   if (r = PopValStack(&v2)) return r;
  960. X   if (r = PopValStack(&v1)) {
  961. X      DestroyValue(&v2);
  962. X      return r;
  963. X   }
  964. X
  965. X   if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
  966. X      if (v2.v.val == 0) return E_DIV_ZERO;
  967. X      v1.v.val %= v2.v.val;
  968. X      return PushValStack(&v1);
  969. X   }
  970. X   DestroyValue(&v1); DestroyValue(&v2);
  971. X   return E_BAD_TYPE;
  972. X}
  973. X
  974. X
  975. X/***************************************************************/
  976. X/*                                                             */
  977. X/*  GreaterThan, LessThan, EqualTo, NotEqual, LessOrEqual,     */
  978. X/*  GreaterOrEqual                                             */
  979. X/*                                                             */
  980. X/*  All the comparison functions.                              */
  981. X/*                                                             */
  982. X/***************************************************************/
  983. X#ifdef HAVE_PROTOS
  984. XPRIVATE int GreaterThan(void) {return Compare(GT);}
  985. XPRIVATE int LessThan(void) {return Compare(LT);}
  986. XPRIVATE int EqualTo(void) {return Compare(EQ);}
  987. XPRIVATE int NotEqual(void) {return Compare(NE);}
  988. XPRIVATE int LessOrEqual(void) {return Compare(LE);}
  989. XPRIVATE int GreaterOrEqual(void) {return Compare(GE);}
  990. X#else
  991. Xstatic int GreaterThan() {return Compare(GT);}
  992. Xstatic int LessThan() {return Compare(LT);}
  993. Xstatic int EqualTo() {return Compare(EQ);}
  994. Xstatic int NotEqual() {return Compare(NE);}
  995. Xstatic int LessOrEqual() {return Compare(LE);}
  996. Xstatic int GreaterOrEqual() {return Compare(GE);}
  997. X#endif
  998. X
  999. X/***************************************************************/
  1000. X/*                                                             */
  1001. X/*  Compare                                                    */
  1002. X/*  Do the actual work of comparison.                          */
  1003. X/*                                                             */
  1004. X/***************************************************************/
  1005. X#ifdef HAVE_PROTOS
  1006. XPRIVATE int Compare(int how)
  1007. X#else
  1008. Xstatic int Compare(how)
  1009. Xint how;
  1010. X#endif
  1011. X{
  1012. X   Value v1, v2, v3;
  1013. X   int r;
  1014. X
  1015. X   if (r = PopValStack(&v2)) return r;
  1016. X   if (r = PopValStack(&v1)) {
  1017. X      DestroyValue(&v2);
  1018. X      return r;
  1019. X   }
  1020. X
  1021. X/* Special case for EQ and NE */
  1022. X
  1023. X   v3.type = INT_TYPE;
  1024. X   if (v1.type != v2.type) {
  1025. X      DestroyValue(&v1); DestroyValue(&v2);
  1026. X      if (how == EQ) {
  1027. X         v3.v.val = 0;
  1028. X     return PushValStack(&v3);
  1029. X      } else if (how == NE) {
  1030. X         v3.v.val = 1;
  1031. X     return PushValStack(&v3);
  1032. X      } else return E_BAD_TYPE;
  1033. X   }
  1034. X
  1035. X   if (v1.type == STR_TYPE) {
  1036. X      switch(how) {
  1037. X         case EQ: v3.v.val = (strcmp(v1.v.str, v2.v.str) == 0); break;
  1038. X         case NE: v3.v.val = (strcmp(v1.v.str, v2.v.str) != 0); break;
  1039. X         case LT: v3.v.val = (strcmp(v1.v.str, v2.v.str) < 0); break;
  1040. X         case GT: v3.v.val = (strcmp(v1.v.str, v2.v.str) > 0); break;
  1041. X         case LE: v3.v.val = (strcmp(v1.v.str, v2.v.str) <= 0); break;
  1042. X         case GE: v3.v.val = (strcmp(v1.v.str, v2.v.str) >= 0); break;
  1043. X      }
  1044. X   } else {
  1045. X      switch(how) {
  1046. X         case EQ: v3.v.val = (v1.v.val == v2.v.val); break;
  1047. X         case NE: v3.v.val = (v1.v.val != v2.v.val); break;
  1048. X         case LT: v3.v.val = (v1.v.val < v2.v.val); break;
  1049. X         case GT: v3.v.val = (v1.v.val > v2.v.val); break;
  1050. X         case LE: v3.v.val = (v1.v.val <= v2.v.val); break;
  1051. X         case GE: v3.v.val = (v1.v.val >= v2.v.val); break;
  1052. X      }
  1053. X   }
  1054. X   DestroyValue(&v1); DestroyValue(&v2);
  1055. X   return PushValStack(&v3);
  1056. X}
  1057. X
  1058. X/***************************************************************/
  1059. X/*                                                             */
  1060. X/*  LogOR                                                      */
  1061. X/*                                                             */
  1062. X/*  Do logical OR                                              */
  1063. X/*                                                             */
  1064. X/***************************************************************/
  1065. X#ifdef HAVE_PROTOS
  1066. XPRIVATE int LogOR(void)
  1067. X#else
  1068. Xstatic int LogOR()
  1069. X#endif
  1070. X{
  1071. X   Value v1, v2;
  1072. X   int r;
  1073. X
  1074. X   if (r = PopValStack(&v2)) return r;
  1075. X   if (r = PopValStack(&v1)) {
  1076. X      DestroyValue(&v2);
  1077. X      return r;
  1078. X   }
  1079. X
  1080. X   if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
  1081. X      v1.v.val = (v1.v.val || v2.v.val) ? 1 : 0;
  1082. X      return PushValStack(&v1);
  1083. X   }
  1084. X   DestroyValue(&v1); DestroyValue(&v2);
  1085. X   return E_BAD_TYPE;
  1086. X}
  1087. X
  1088. X/***************************************************************/
  1089. X/*                                                             */
  1090. X/*  LogAND                                                     */
  1091. X/*                                                             */
  1092. X/*  Do logical AND                                             */
  1093. X/*                                                             */
  1094. X/***************************************************************/
  1095. X#ifdef HAVE_PROTOS
  1096. XPRIVATE int LogAND(void)
  1097. X#else
  1098. Xstatic int LogAND()
  1099. X#endif
  1100. X{
  1101. X   Value v1, v2;
  1102. X   int r;
  1103. X
  1104. X   if (r = PopValStack(&v2)) return r;
  1105. X   if (r = PopValStack(&v1)) {
  1106. X      DestroyValue(&v2);
  1107. X      return r;
  1108. X   }
  1109. X
  1110. X   if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
  1111. X      v1.v.val = (v1.v.val && v2.v.val) ? 1 : 0;
  1112. X      return PushValStack(&v1);
  1113. X   }
  1114. X   DestroyValue(&v1); DestroyValue(&v2);
  1115. X   return E_BAD_TYPE;
  1116. X}
  1117. X
  1118. X/***************************************************************/
  1119. X/*                                                             */
  1120. X/*  UnMinus                                                    */
  1121. X/*                                                             */
  1122. X/*  Unary Minus                                                */
  1123. X/*                                                             */
  1124. X/***************************************************************/
  1125. X#ifdef HAVE_PROTOS
  1126. XPRIVATE int UnMinus(void)
  1127. X#else
  1128. Xstatic int UnMinus()
  1129. X#endif
  1130. X{
  1131. X   Value *v = &ValStack[ValStackPtr-1];
  1132. X   if (v->type != INT_TYPE) return E_BAD_TYPE;
  1133. X   v->v.val = -v->v.val;
  1134. X   return OK;
  1135. X}
  1136. X
  1137. X/***************************************************************/
  1138. X/*                                                             */
  1139. X/*  LogNot                                                     */
  1140. X/*                                                             */
  1141. X/*  Logical NOT                                                */
  1142. X/*                                                             */
  1143. X/***************************************************************/
  1144. X#ifdef HAVE_PROTOS
  1145. XPRIVATE int LogNot(void)
  1146. X#else
  1147. Xstatic int LogNot()
  1148. X#endif
  1149. X{
  1150. X   Value *v = &ValStack[ValStackPtr-1];
  1151. X   if (v->type != INT_TYPE) return E_BAD_TYPE;
  1152. X   if (v->v.val) v->v.val = 0; else v->v.val = 1;
  1153. X   return OK;
  1154. X}
  1155. X
  1156. X/***************************************************************/
  1157. X/*                                                             */
  1158. X/*  FindFunc                                                   */
  1159. X/*                                                             */
  1160. X/*  Find a function.                                           */
  1161. X/*                                                             */
  1162. X/***************************************************************/
  1163. X#ifdef HAVE_PROTOS
  1164. XPRIVATE Operator *FindFunc(char *name, Operator where[], int num)
  1165. X#else
  1166. Xstatic Operator *FindFunc(name, where, num)
  1167. Xchar *name;
  1168. XOperator where[];
  1169. Xint num;
  1170. X#endif
  1171. X{
  1172. X   int top=num-1, bot=0;
  1173. X   int mid, r;
  1174. X   while (top >= bot) {
  1175. X      mid = (top + bot) / 2;
  1176. X      r = StrCmpi(name, where[mid].name);
  1177. X      if (!r) return &where[mid];
  1178. X      else if (r > 0) bot = mid+1;
  1179. X      else top = mid-1;
  1180. X   }
  1181. X   return NULL;
  1182. X}
  1183. X    
  1184. X/***************************************************************/
  1185. X/*                                                             */
  1186. X/*  PrintValue                                                 */
  1187. X/*                                                             */
  1188. X/*  Print a value to stdout for debugging purposes.            */
  1189. X/*                                                             */
  1190. X/***************************************************************/
  1191. X#ifdef HAVE_PROTOS
  1192. XPUBLIC void PrintValue (Value *v, FILE *fp)
  1193. X#else
  1194. Xvoid PrintValue(v, fp)
  1195. XValue *v;
  1196. XFILE *fp;
  1197. X#endif
  1198. X{
  1199. X   int y, m, d;
  1200. X   char *s;
  1201. X
  1202. X   if (v->type == STR_TYPE) {
  1203. X      s=v->v.str;
  1204. X      putc('"', fp);
  1205. X      for (y=0; y<MAX_PRT_LEN && *s; y++) putc(*s++, fp);
  1206. X      putc('"',fp);
  1207. X      if (*s) fprintf(fp, "...");
  1208. X   }      
  1209. X   else if (v->type == INT_TYPE) fprintf(fp, "%d", v->v.val);
  1210. X   else if (v->type == TIM_TYPE) fprintf(fp, "%02d:%02d", v->v.val / 60, v->v.val % 60);
  1211. X   else if (v->type == DATE_TYPE) {
  1212. X      FromJulian(v->v.val, &y, &m, &d);
  1213. X      fprintf(fp, "%04d/%02d/%02d", y, m+1, d);
  1214. X   }
  1215. X   else fprintf(fp, "ERR");
  1216. X}
  1217. X
  1218. X/***************************************************************/
  1219. X/*                                                             */
  1220. X/*  CopyValue                                                  */
  1221. X/*                                                             */
  1222. X/*  Copy a value.                                              */
  1223. X/*                                                             */
  1224. X/***************************************************************/
  1225. X#ifdef HAVE_PROTOS
  1226. XPUBLIC int CopyValue(Value *dest, const Value *src)
  1227. X#else
  1228. Xint CopyValue(dest, src)
  1229. XValue *dest, *src;
  1230. X#endif
  1231. X{
  1232. X   dest->type = src->type;
  1233. X   if (src->type == STR_TYPE) {
  1234. X      dest->v.str = StrDup(src->v.str);
  1235. X      if (!dest->v.str) {
  1236. X     dest->type = ERR_TYPE;
  1237. X     return E_NO_MEM;
  1238. X      }
  1239. X      return OK;
  1240. X   } else {
  1241. X      dest->v.val = src->v.val;
  1242. X      return OK;
  1243. X   }
  1244. X}
  1245. SHAR_EOF
  1246. $TOUCH -am 1109141292 expr.c &&
  1247. chmod 0600 expr.c ||
  1248. echo "restore of expr.c failed"
  1249. set `wc -c expr.c`;Wc_c=$1
  1250. if test "$Wc_c" != "36192"; then
  1251.     echo original size 36192, current size $Wc_c
  1252. fi
  1253. fi
  1254. echo "End of part 4, continue with part 5"
  1255. exit 0
  1256.  
  1257. exit 0 # Just in case...
  1258.