home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume33 / remind / part04 < prev    next >
Encoding:
Text File  |  1992-11-10  |  37.6 KB  |  1,252 lines

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