ecd@cs.umn.edu@ncs-med.UUCP (Elwood C. Downey) (03/11/90)
Posting-number: Volume 11, Issue 3 Submitted-by: ecd@cs.umn.edu@ncs-med.UUCP (Elwood C. Downey) Archive-name: ephem4.12/part02 # This is the first line of a "shell archive" file. # This means it contains several files that can be extracted into # the current directory when run with the sh shell, as follows: # sh < this_file_name # This is file 2. echo x compiler.c sed -e 's/^X//' << 'EOFxEOF' > compiler.c X/* module to compile and execute a c-style arithmetic expression. X * public entry points are compile_expr() and execute_expr(). X * X * one reason this is so nice and tight is that all opcodes are the same size X * (an int) and the tokens the parser returns are directly usable as opcodes, X * for the most part. constants and variables are compiled as an opcode X * with an offset into the auxiliary opcode tape, opx. X */ X X#include <math.h> X#include "screen.h" X X/* parser tokens and opcodes, as necessary */ X#define HALT 0 /* good value for HALT since program is inited to 0 */ X/* binary operators (precedences in table, below) */ X#define ADD 1 X#define SUB 2 X#define MULT 3 X#define DIV 4 X#define AND 5 X#define OR 6 X#define GT 7 X#define GE 8 X#define EQ 9 X#define NE 10 X#define LT 11 X#define LE 12 X/* unary op, precedence in NEG_PREC #define, below */ X#define NEG 13 X/* symantically operands, ie, constants, variables and all functions */ X#define CONST 14 X#define VAR 15 X#define ABS 16 /* add functions if desired just like this is done */ X/* purely tokens - never get compiled as such */ X#define LPAREN 255 X#define RPAREN 254 X#define ERR (-1) X X/* precedence of each of the binary operators. X * in case of a tie, compiler associates left-to-right. X * N.B. each entry's index must correspond to its #define! X */ Xstatic int precedence[] = {0,5,5,6,6,2,1,4,4,3,3,4,4}; X#define NEG_PREC 7 /* negation is highest */ X X/* execute-time operand stack */ X#define MAX_STACK 16 Xstatic double stack[MAX_STACK], *sp; X X/* space for compiled opcodes - the "program". X * opcodes go in lower 8 bits. X * when an opcode has an operand (as CONST and VAR) it is really in opx[] and X * the index is in the remaining upper bits. X */ X#define MAX_PROG 32 Xstatic int program[MAX_PROG], *pc; X#define OP_SHIFT 8 X#define OP_MASK 0xff X X/* auxiliary operand info. X * the operands (all but lower 8 bits) of CONST and VAR are really indeces X * into this array. thus, no point in making this any longer than you have X * bits more than 8 in your machine's int to index into it, ie, make X * MAX_OPX <= 1 << ((sizeof(int)-1)*8) X * also, the fld's must refer to ones being flog'd, so not point in more X * of these then that might be used for plotting and srching combined. X */ X#define MAX_OPX 16 Xtypedef union { X double opu_f; /* value when opcode is CONST */ X int opu_fld; /* rcfpack() of field when opcode is VAR */ X} OpX; Xstatic OpX opx[MAX_OPX]; Xstatic int opxidx; X X/* these are global just for easy/rapid access */ Xstatic int parens_nest; /* to check that parens end up nested */ Xstatic char *err_msg; /* caller provides storage; we point at it with this */ Xstatic char *cexpr, *lcexpr; /* pointers that move along caller's expression */ Xstatic int good_prog; /* != 0 when program appears to be good */ X X/* compile the given c-style expression. X * return 0 and set good_prog if ok, X * else return -1 and a reason message in errbuf. X */ Xcompile_expr (ex, errbuf) Xchar *ex; Xchar *errbuf; X{ X int instr; X X /* init the globals. X * also delete any flogs used in the previous program. X */ X cexpr = ex; X err_msg = errbuf; X pc = program; X opxidx = 0; X parens_nest = 0; X do { X instr = *pc++; X if ((instr & OP_MASK) == VAR) X flog_delete (opx[instr >> OP_SHIFT].opu_fld); X } while (instr != HALT); X X pc = program; X if (compile(0) == ERR) { X sprintf (err_msg + strlen(err_msg), " at \"%.10s\"", lcexpr); X good_prog = 0; X return (-1); X } X *pc++ = HALT; X good_prog = 1; X return (0); X} X X/* execute the expression previously compiled with compile_expr(). X * return 0 with *vp set to the answer if ok, else return -1 with a reason X * why not message in errbuf. X */ Xexecute_expr (vp, errbuf) Xdouble *vp; Xchar *errbuf; X{ X int s; X X err_msg = errbuf; X sp = stack + MAX_STACK; /* grows towards lower addresses */ X pc = program; X s = execute(vp); X if (s < 0) X good_prog = 0; X return (s); X} X X/* this is a way for the outside world to ask whether there is currently a X * reasonable program compiled and able to execute. X */ Xprog_isgood() X{ X return (good_prog); X} X X/* get and return the opcode corresponding to the next token. X * leave with lcexpr pointing at the new token, cexpr just after it. X * also watch for mismatches parens and proper operator/operand alternation. X */ Xstatic Xnext_token () X{ X static char toomt[] = "More than %d terms"; X static char badop[] = "Illegal operator"; X int tok = ERR; /* just something illegal */ X char c; X X while ((c = *cexpr) == ' ') X cexpr++; X lcexpr = cexpr++; X X /* mainly check for a binary operator */ X switch (c) { X case '\0': --cexpr; tok = HALT; break; /* keep returning HALT */ X case '+': tok = ADD; break; /* compiler knows when it's really unary */ X case '-': tok = SUB; break; /* compiler knows when it's really negate */ X case '*': tok = MULT; break; X case '/': tok = DIV; break; X case '(': parens_nest++; tok = LPAREN; break; X case ')': X if (--parens_nest < 0) { X sprintf (err_msg, "Too many right parens"); X return (ERR); X } else X tok = RPAREN; X break; X case '|': X if (*cexpr == '|') { cexpr++; tok = OR; } X else { sprintf (err_msg, badop); return (ERR); } X break; X case '&': X if (*cexpr == '&') { cexpr++; tok = AND; } X else { sprintf (err_msg, badop); return (ERR); } X break; X case '=': X if (*cexpr == '=') { cexpr++; tok = EQ; } X else { sprintf (err_msg, badop); return (ERR); } X break; X case '!': X if (*cexpr == '=') { cexpr++; tok = NE; } X else { sprintf (err_msg, badop); return (ERR); } X break; X case '<': X if (*cexpr == '=') { cexpr++; tok = LE; } X else tok = LT; X break; X case '>': X if (*cexpr == '=') { cexpr++; tok = GE; } X else tok = GT; X break; X } X X if (tok != ERR) X return (tok); X X /* not op so check for a constant, variable or function */ X if (isdigit(c) || c == '.') { X if (opxidx > MAX_OPX) { X sprintf (err_msg, toomt, MAX_OPX); X return (ERR); X } X opx[opxidx].opu_f = atof (lcexpr); X tok = CONST | (opxidx++ << OP_SHIFT); X skip_double(); X } else if (isalpha(c)) { X /* check list of functions */ X if (strncmp (lcexpr, "abs", 3) == 0) { X cexpr += 2; X tok = ABS; X } else { X /* not a function, so assume it's a variable */ X int fld; X if (opxidx > MAX_OPX) { X sprintf (err_msg, toomt, MAX_OPX); X return (ERR); X } X fld = parse_fieldname (); X if (fld < 0) { X sprintf (err_msg, "Unknown field"); X return (ERR); X } else { X if (flog_add (fld) < 0) { /* register with field logger */ X sprintf (err_msg, "Sorry; too many fields"); X return (ERR); X } X opx[opxidx].opu_fld = fld; X tok = VAR | (opxidx++ << OP_SHIFT); X } X } X } X X return (tok); X} X X/* move cexpr on past a double. X * allow sci notation. X * no need to worry about a leading '-' or '+' but allow them after an 'e'. X * TODO: this handles all the desired cases, but also admits a bit too much X * such as things like 1eee2...3. geeze; to skip a double right you almost X * have to go ahead and crack it! X */ Xstatic Xskip_double() X{ X int sawe = 0; /* so we can allow '-' or '+' right after an 'e' */ X X while (1) { X char c = *cexpr; X if (isdigit(c) || c=='.' || (sawe && (c=='-' || c=='+'))) { X sawe = 0; X cexpr++; X } else if (c == 'e') { X sawe = 1; X cexpr++; X } else X break; X } X} X X/* call this whenever you want to dig out the next (sub)expression. X * keep compiling instructions as long as the operators are higher precedence X * than prec, then return that "look-ahead" token that wasn't (higher prec). X * if error, fill in a message in err_msg[] and return ERR. X */ Xstatic Xcompile (prec) Xint prec; X{ X int expect_binop = 0; /* set after we have seen any operand. X * used by SUB so it can tell if it really X * should be taken to be a NEG instead. X */ X int tok = next_token (); X X while (1) { X int p; X if (tok == ERR) X return (ERR); X if (pc - program >= MAX_PROG) { X sprintf (err_msg, "Program is too long"); X return (ERR); X } X X /* check for special things like functions, constants and parens */ X switch (tok & OP_MASK) { X case HALT: return (tok); X case ADD: X if (expect_binop) X break; /* procede with binary addition */ X /* just skip a unary positive(?) */ X tok = next_token(); X continue; X case SUB: X if (expect_binop) X break; /* procede with binary subtract */ X tok = compile (NEG_PREC); X *pc++ = NEG; X expect_binop = 1; X continue; X case ABS: /* other funcs would be handled the same too ... */ X /* eat up the function parenthesized argument */ X if (next_token() != LPAREN || compile (0) != RPAREN) { X sprintf (err_msg, "Function arglist error"); X return (ERR); X } X /* then handled same as ... */ X case CONST: /* handled same as... */ X case VAR: X *pc++ = tok; X tok = next_token(); X expect_binop = 1; X continue; X case LPAREN: X if (compile (0) != RPAREN) { X sprintf (err_msg, "Unmatched left paren"); X return (ERR); X } X tok = next_token(); X expect_binop = 1; X continue; X case RPAREN: X return (RPAREN); X } X X /* everything else is a binary operator */ X p = precedence[tok]; X if (p > prec) { X int newtok = compile (p); X if (newtok == ERR) X return (ERR); X *pc++ = tok; X expect_binop = 1; X tok = newtok; X } else X return (tok); X } X} X X/* "run" the program[] compiled with compile(). X * if ok, return 0 and the final result, X * else return -1 with a reason why not message in err_msg. X */ Xstatic Xexecute(result) Xdouble *result; X{ X int instr; X X do { X instr = *pc++; X switch (instr & OP_MASK) { X /* put these in numberic order so hopefully even the dumbest X * compiler will choose to use a jump table, not a cascade of ifs. X */ X case HALT: break; /* outer loop will stop us */ X case ADD: sp[1] = sp[1] + sp[0]; sp++; break; X case SUB: sp[1] = sp[1] - sp[0]; sp++; break; X case MULT: sp[1] = sp[1] * sp[0]; sp++; break; X case DIV: sp[1] = sp[1] / sp[0]; sp++; break; X case AND: sp[1] = sp[1] && sp[0] ? 1 : 0; sp++; break; X case OR: sp[1] = sp[1] || sp[0] ? 1 : 0; sp++; break; X case GT: sp[1] = sp[1] > sp[0] ? 1 : 0; sp++; break; X case GE: sp[1] = sp[1] >= sp[0] ? 1 : 0; sp++; break; X case EQ: sp[1] = sp[1] == sp[0] ? 1 : 0; sp++; break; X case NE: sp[1] = sp[1] != sp[0] ? 1 : 0; sp++; break; X case LT: sp[1] = sp[1] < sp[0] ? 1 : 0; sp++; break; X case LE: sp[1] = sp[1] <= sp[0] ? 1 : 0; sp++; break; X case NEG: *sp = -*sp; break; X case CONST: *--sp = opx[instr >> OP_SHIFT].opu_f; break; X case VAR: X if (flog_get (opx[instr >> OP_SHIFT].opu_fld, --sp) < 0) { X sprintf (err_msg, "Bug! VAR field not logged"); X return (-1); X } X break; X case ABS: *sp = fabs (*sp); break; X default: X sprintf (err_msg, "Bug! bad opcode: 0x%x", instr); X return (-1); X } X if (sp < stack) { X sprintf (err_msg, "Runtime stack overflow"); X return (-1); X } else if (sp - stack > MAX_STACK) { X sprintf (err_msg, "Bug! runtime stack underflow"); X return (-1); X } X } while (instr != HALT); X X /* result should now be on top of stack */ X if (sp != &stack[MAX_STACK - 1]) { X sprintf (err_msg, "Bug! stack has %d items",MAX_STACK-(sp-stack)); X return (-1); X } X *result = *sp; X return (0); X} X Xstatic Xisdigit(c) Xchar c; X{ X return (c >= '0' && c <= '9'); X} X Xstatic Xisalpha (c) Xchar c; X{ X return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); X} X X/* starting with lcexpr pointing at a string expected to be a field name, X * return an rcfpack(r,c,0) of the field else -1 if bad. X * when return, leave lcexpr alone but move cexpr to just after the name. X */ Xstatic Xparse_fieldname () X{ X int r = -1, c = -1; /* anything illegal */ X char *fn = lcexpr; /* likely faster than using the global */ X char f0, f1; X char *dp; X X /* search for first thing not an alpha char. X * leave it in f0 and leave dp pointing to it. X */ X dp = fn; X while (isalpha(f0 = *dp)) X dp++; X X /* crack the new field name. X * when done trying, leave dp pointing at first char just after it. X * set r and c if we recognized it. X */ X if (f0 == '.') { X /* planet.column pair. X * first crack the planet portion (pointed to by fn): set r. X * then the second portion (pointed to by dp+1): set c. X */ X f0 = fn[0]; X f1 = fn[1]; X switch (f0) { X case 'j': X r = R_JUPITER; X break; X case 'm': X if (f1 == 'a') r = R_MARS; X else if (f1 == 'e') r = R_MERCURY; X else if (f1 == 'o') r = R_MOON; X break; X case 'n': X r = R_NEPTUNE; X break; X case 'p': X r = R_PLUTO; X break; X case 's': X if (f1 == 'a') r = R_SATURN; X else if (f1 == 'u') r = R_SUN; X break; X case 'u': X r = R_URANUS; X break; X case 'x': X r = R_OBJX; X break; X case 'v': X r = R_VENUS; X break; X } X X /* now crack the column (stuff after the dp) */ X dp++; /* point at good stuff just after the decimal pt */ X f0 = dp[0]; X f1 = dp[1]; X switch (f0) { X case 'a': X if (f1 == 'l') c = C_ALT; X else if (f1 == 'z') c = C_AZ; X break; X case 'd': X c = C_DEC; X break; X case 'e': X if (f1 == 'd') c = C_EDIST; X else if (f1 == 'l') c = C_ELONG; X break; X case 'h': X if (f1 == 'l') { X if (dp[2] == 'a') c = C_HLAT; X else if (dp[2] == 'o') c = C_HLONG; X } else if (f1 == 'r' || f1 == 'u') c = C_TUP; X break; X case 'j': X c = C_JUPITER; X break; X case 'm': X if (f1 == 'a') c = C_MARS; X else if (f1 == 'e') c = C_MERCURY; X else if (f1 == 'o') c = C_MOON; X break; X case 'n': X c = C_NEPTUNE; X break; X case 'p': X if (f1 == 'h') c = C_PHASE; X else if (f1 == 'l') c = C_PLUTO; X break; X case 'r': X if (f1 == 'a') { X if (dp[2] == 'z') c = C_RISEAZ; X else c = C_RA; X } else if (f1 == 't') c = C_RISETM; X break; X case 's': X if (f1 == 'a') { X if (dp[2] == 'z') c = C_SETAZ; X else c = C_SATURN; X } else if (f1 == 'd') c = C_SDIST; X else if (f1 == 'i') c = C_SIZE; X else if (f1 == 't') c = C_SETTM; X else if (f1 == 'u') c = C_SUN; X break; X case 't': X if (f1 == 'a') c = C_TRANSALT; X else if (f1 == 't') c = C_TRANSTM; X break; X case 'u': X c = C_URANUS; X break; X case 'v': X if (f1 == 'e') c = C_VENUS; X else if (f1 == 'm') c = C_MAG; X break; X } X X /* now skip dp on past the column stuff */ X while (isalpha(*dp)) X dp++; X } else { X /* no decimal point; some field in the top of the screen */ X f0 = fn[0]; X f1 = fn[1]; X switch (f0) { X case 'd': X if (f1 == 'a') r = R_DAWN, c = C_DAWNV; X else if (f1 == 'u') r = R_DUSK, c = C_DUSKV; X break; X case 'n': X r = R_LON, c = C_LONV; X break; X } X } X X cexpr = dp; X if (r <= 0 || c <= 0) return (-1); X return (rcfpack (r, c, 0)); X} EOFxEOF len=`wc -c < compiler.c` if expr $len != 15015 > /dev/null then echo Length of compiler.c is $len but it should be 15015. fi echo x eq_ecl.c sed -e 's/^X//' << 'EOFxEOF' > eq_ecl.c X#include <stdio.h> X#include <math.h> X#include "astro.h" X X#define EQtoECL 1 X#define ECLtoEQ (-1) X X/* given the modified Julian date, mjd, and an equitorial ra and dec, each in X * radians, find the corresponding geocentric ecliptic latitude, *lat, and X * longititude, *lng, also each in radians. X * correction for the effect on the angle of the obliquity due to nutation is X * included. X */ Xeq_ecl (mjd, ra, dec, lat, lng) Xdouble mjd, ra, dec; Xdouble *lat, *lng; X{ X ecleq_aux (EQtoECL, mjd, ra, dec, lng, lat); X} X X/* given the modified Julian date, mjd, and a geocentric ecliptic latitude, X * *lat, and longititude, *lng, each in radians, find the corresponding X * equitorial ra and dec, also each in radians. X * correction for the effect on the angle of the obliquity due to nutation is X * included. X */ Xecl_eq (mjd, lat, lng, ra, dec) Xdouble mjd, lat, lng; Xdouble *ra, *dec; X{ X ecleq_aux (ECLtoEQ, mjd, lng, lat, ra, dec); X} X Xstatic Xecleq_aux (sw, mjd, x, y, p, q) Xint sw; /* +1 for eq to ecliptic, -1 for vv. */ Xdouble mjd, x, y; /* sw==1: x==ra, y==dec. sw==-1: x==lng, y==lat. */ Xdouble *p, *q; /* sw==1: p==lng, q==lat. sw==-1: p==ra, q==dec. */ X{ X static double lastmjd; /* last mjd calculated */ X static double seps, ceps; /* sin and cos of mean obliquity */ X double sx, cx, sy, cy, ty; X X if (mjd != lastmjd) { X double eps; X double deps, dpsi; X obliquity (mjd, &eps); /* mean obliquity for date */ X nutation (mjd, &deps, &dpsi); X eps += deps; X seps = sin(eps); X ceps = cos(eps); X lastmjd = mjd; X } X X sy = sin(y); X cy = cos(y); /* always non-negative */ X if (fabs(cy)<1e-20) cy = 1e-20; /* insure > 0 */ X ty = sy/cy; X cx = cos(x); X sx = sin(x); X *q = asin((sy*ceps)-(cy*seps*sx*sw)); X *p = atan(((sx*ceps)+(ty*seps*sw))/cx); X if (cx<0) *p += PI; /* account for atan quad ambiguity */ X range (p, 2*PI); X} EOFxEOF len=`wc -c < eq_ecl.c` if expr $len != 1891 > /dev/null then echo Length of eq_ecl.c is $len but it should be 1891. fi echo x flog.c sed -e 's/^X//' << 'EOFxEOF' > flog.c X/* this is a simple little package to manage the saving and retrieving of X * field values, which we call field logging or "flogs". a flog consists of a X * field location, ala rcfpack(), and its value as a double. you can reset the X * list of flogs, add to and remove from the list of registered fields and log X * a field if it has been registered. X * X * this is used by the plotting and searching facilities of ephem to maintain X * the values of the fields that are being plotted or used in search X * expressions. X * X * a field can be in use for more than one X * thing at a time (eg, all the X plot values may the same time field, or X * searching and plotting might be on at one time using the same field) so X * we consider the field to be in use as long a usage count is > 0. X */ X X#include "screen.h" X X#define NFLOGS 32 X Xtypedef struct { X int fl_usagecnt; /* number of "users" logging to this field */ X int fl_fld; /* an rcfpack(r,c,0) */ X double fl_val; X} FLog; X Xstatic FLog flog[NFLOGS]; X X/* add fld to the list. if already there, just increment usage count. X * return 0 if ok, else -1 if no more room. X */ Xflog_add (fld) Xint fld; X{ X FLog *flp, *unusedflp = 0; X X /* scan for fld already in list, or find an unused one along the way */ X for (flp = &flog[NFLOGS]; --flp >= flog; ) { X if (flp->fl_usagecnt > 0) { X if (flp->fl_fld == fld) { X flp->fl_usagecnt++; X return (0); X } X } else X unusedflp = flp; X } X if (unusedflp) { X unusedflp->fl_fld = fld; X unusedflp->fl_usagecnt = 1; X return (0); X } X return (-1); X} X X/* decrement usage count for flog for fld. if goes to 0 take it out of list. X * ok if not in list i guess... X */ Xflog_delete (fld) Xint fld; X{ X FLog *flp; X X for (flp = &flog[NFLOGS]; --flp >= flog; ) X if (flp->fl_fld == fld && flp->fl_usagecnt > 0) { X if (--flp->fl_usagecnt <= 0) { X flp->fl_usagecnt = 0; X } X break; X } X} X X/* if plotting or searching is active then X * if rcfpack(r,c,0) is in the fld list, set its value to val. X * return 0 if ok, else -1 if not in list. X */ Xflog_log (r, c, val) Xint r, c; Xdouble val; X{ X if (plot_ison() || srch_ison()) { X FLog *flp; X int fld = rcfpack (r, c, 0); X for (flp = &flog[NFLOGS]; --flp >= flog; ) X if (flp->fl_fld == fld && flp->fl_usagecnt > 0) { X flp->fl_val = val; X return(0); X } X return (-1); X } else X return (0); X} X X/* search for fld in list. if find it return its value. X * return 0 if found it, else -1 if not in list. X */ Xflog_get (fld, vp) Xint fld; Xdouble *vp; X{ X FLog *flp; X X for (flp = &flog[NFLOGS]; --flp >= flog; ) X if (flp->fl_fld == fld && flp->fl_usagecnt > 0) { X *vp = flp->fl_val; X return (0); X } X return (-1); X} EOFxEOF len=`wc -c < flog.c` if expr $len != 2680 > /dev/null then echo Length of flog.c is $len but it should be 2680. fi echo x formats.c sed -e 's/^X//' << 'EOFxEOF' > formats.c X/* basic formating routines. X * all the screen oriented printing should go through here. X */ X X#include <stdio.h> X#include <math.h> X#include "astro.h" X#include "screen.h" X Xextern char *strcpy(); X X/* suppress screen io if this is true, but always flog stuff. X */ Xstatic int f_scrnoff; Xf_on () X{ X f_scrnoff = 0; X} Xf_off () X{ X f_scrnoff = 1; X} X X/* draw n blanks at the given cursor position. */ Xf_blanks (r, c, n) Xint r, c, n; X{ X if (f_scrnoff) X return; X c_pos (r, c); X while (--n >= 0) X putchar (' '); X} X X/* print the given value, v, in "sexadecimal" format at [r,c] X * ie, in the form A:m.P, where A is a digits wide, P is p digits. X * if p == 0, then no decimal point either. X */ Xf_sexad (r, c, a, p, mod, v) Xint r, c; Xint a, p; /* left space, min precision */ Xint mod; /* don't let whole portion get this big */ Xdouble v; X{ X char astr[32], str[32]; X long dec; X double frac; X int visneg; X X (void) flog_log (r, c, v); X X if (f_scrnoff) X return; X X if (v >= 0.0) X visneg = 0; X else { X if (v <= -0.5/60.0*pow(10.0,-1.0*p)) { X v = -v; X visneg = 1; X } else { X /* don't show as negative if less than the precision showing */ X v = 0.0; X visneg = 0; X } X } X X dec = v; X frac = (v - dec)*60.0; X sprintf (str, "59.%.*s5", p, "999999999"); X if (frac >= atof (str)) { X dec += 1; X frac = 0.0; X } X dec %= mod; X if (dec == 0 && visneg) X strcpy (str, "-0"); X else X sprintf (str, "%ld", visneg ? -dec : dec); X X /* would just do this if Turbo-C 2.0 %?.0f" worked: X * sprintf (astr, "%*s:%0*.*f", a, str, p == 0 ? 2 : p+3, p, frac); X */ X if (p == 0) X sprintf (astr, "%*s:%02d", a, str, (int)(frac+0.5)); X else X sprintf (astr, "%*s:%0*.*f", a, str, p+3, p, frac); X f_string (r, c, astr); X} X X/* print the given value, t, in sexagesimal format at [r,c] X * ie, in the form T:mm:ss, where T is nd digits wide. X * N.B. we assume nd >= 2. X */ Xf_sexag (r, c, nd, t) Xint r, c, nd; Xdouble t; X{ X char tstr[32]; X int h, m, s; X int tisneg; X X (void) flog_log (r, c, t); X if (f_scrnoff) X return; X dec_sex (t, &h, &m, &s, &tisneg); X if (h == 0 && tisneg) X sprintf (tstr, "%*s-0:%02d:%02d", nd-2, "", m, s); X else X sprintf (tstr, "%*d:%02d:%02d", nd, tisneg ? -h : h, m, s); X f_string (r, c, tstr); X} X X/* print angle ra, in radians, in ra hours as hh:mm.m at [r,c] X * N.B. we assume ra is >= 0. X */ Xf_ra (r, c, ra) Xint r, c; Xdouble ra; X{ X f_sexad (r, c, 2, 1, 24, radhr(ra)); X} X X/* print time, t, as hh:mm:ss */ Xf_time (r, c, t) Xint r, c; Xdouble t; X{ X f_sexag (r, c, 2, t); X} X X/* print time, t, as +/-hh:mm:ss (don't show leading +) */ Xf_signtime (r, c, t) Xint r, c; Xdouble t; X{ X f_sexag (r, c, 3, t); X} X X/* print time, t, as hh:mm */ Xf_mtime (r, c, t) Xint r, c; Xdouble t; X{ X f_sexad (r, c, 2, 0, 24, t); X} X X/* print angle, a, in rads, as degress at [r,c] in form ddd:mm */ Xf_angle(r, c, a) Xint r, c; Xdouble a; X{ X f_sexad (r, c, 3, 0, 360, raddeg(a)); X} X X/* print angle, a, in rads, as degress at [r,c] in form dddd:mm:ss */ Xf_gangle(r, c, a) Xint r, c; Xdouble a; X{ X f_sexag (r, c, 4, raddeg(a)); X} X X/* print the given modified Julian date, jd, as the starting date at [r,c] X * in the form mm/dd/yyyy. X */ Xf_date (r, c, jd) Xint r, c; Xdouble jd; X{ X char dstr[32]; X int m, y; X double d, tmp; X X /* shadow to the plot subsystem as years. */ X mjd_year (jd, &tmp); X (void) flog_log (r, c, tmp); X if (f_scrnoff) X return; X X mjd_cal (jd, &m, &d, &y); X X sprintf (dstr, "%2d/%02d/%04d", m, (int)(d), y); X f_string (r, c, dstr); X} X Xf_char (row, col, c) Xint row, col; Xchar c; X{ X if (f_scrnoff) X return; X c_pos (row, col); X putchar (c); X} X Xf_string (r, c, s) Xint r, c; Xchar *s; X{ X if (f_scrnoff) X return; X c_pos (r, c); X fputs (s, stdout); X} X Xf_double (r, c, fmt, f) Xint r, c; Xchar *fmt; Xdouble f; X{ X char str[80]; X (void) flog_log (r, c, f); X sprintf (str, fmt, f); X f_string (r, c, str); X} X X/* print prompt line */ Xf_prompt (p) Xchar *p; X{ X c_pos (R_PROMPT, C_PROMPT); X c_eol (); X c_pos (R_PROMPT, C_PROMPT); X fputs (p, stdout); X} X X/* clear from [r,c] to end of line, if we are drawing now. */ Xf_eol (r, c) Xint r, c; X{ X if (!f_scrnoff) { X c_pos (r, c); X c_eol(); X } X} X X/* print a message and wait for op to hit any key */ Xf_msg (m) Xchar *m; X{ X f_prompt (m); X (void) read_char(); X} X X/* crack a line of the form X?X?X into its components, X * where X is an integer and ? can be any character except '0-9' or '-', X * such as ':' or '/'. X * only change those fields that are specified: X * eg: ::10 only changes *s X * 10 only changes *d X * 10:0 changes *d and *m X * if see '-' anywhere, first non-zero component will be made negative. X */ Xf_sscansex (bp, d, m, s) Xchar *bp; Xint *d, *m, *s; X{ X char c; X int *p = d; X int *nonzp = 0; X int sawneg = 0; X int innum = 0; X X while (c = *bp++) X if (c >= '0' && c <= '9') { X if (!innum) { X *p = 0; X innum = 1; X } X *p = *p*10 + (c - '0'); X if (*p && !nonzp) X nonzp = p; X } else if (c == '-') { X sawneg = 1; X } else if (c != ' ') { X /* advance to next component */ X p = (p == d) ? m : s; X innum = 0; X } X X if (sawneg && nonzp) X *nonzp = -*nonzp; X} X X/* crack a floating date string, bp, of the form m/d/y, where d may be a X * floating point number, into its components. X * leave any component unspecified unchanged. X * actually, the slashes may be anything but digits or a decimal point. X * this is functionally the same as f_sscansex() exept we allow for X * the day portion to be real, and we don't handle negative numbers. X * maybe someday we could make a combined one and use it everywhere. X */ Xf_sscandate (bp, m, d, y) Xchar *bp; Xint *m, *y; Xdouble *d; X{ X char *bp0, c; X X bp0 = bp; X while ((c = *bp++) && (c >= '0' && c <= '9')) X continue; X if (bp > bp0+1) X *m = atoi (bp0); X if (c == '\0') X return; X bp0 = bp; X while ((c = *bp++) && (c >= '0' && c <= '9' || c == '.')) X continue; X if (bp > bp0+1) X *d = atof (bp0); X if (c == '\0') X return; X bp0 = bp; X while (c = *bp++) X continue; X if (bp > bp0+1) X *y = atoi (bp0); X} X X/* just like dec_sex() but makes the first non-zero element negative if X * x is negative (instead of returning a sign flag). X */ Xf_dec_sexsign (x, h, m, s) Xdouble x; Xint *h, *m, *s; X{ X int n; X dec_sex (x, h, m, s, &n); X if (n) { X if (*h) X *h = -*h; X else if (*m) X *m = -*m; X else X *s = -*s; X } X} X X/* return 1 if bp looks like a decimal year; else 0. X * any number greater than 12 is assumed to be a year, or any string X * with exactly one decimal point, an optional minus sign, and nothing X * else but digits. X */ Xdecimal_year (bp) Xchar *bp; X{ X char c; X int ndig = 0, ndp = 0, nneg = 0, nchar = 0; X int n = atoi(bp); X X while (c = *bp++) { X nchar++; X if (c >= '0' && c <= '9') X ndig++; X else if (c == '.') X ndp++; X else if (c == '-') X nneg++; X } X X return (n > 12 || (ndp == 1 && nneg <= 1 && nchar == ndig+ndp+nneg)); X} EOFxEOF len=`wc -c < formats.c` if expr $len != 6850 > /dev/null then echo Length of formats.c is $len but it should be 6850. fi echo x io.c sed -e 's/^X//' << 'EOFxEOF' > io.c X/* this file (in principle) contains all the device-dependent code for X * handling screen movement and reading the keyboard. public routines are: X * c_pos(r,c), c_erase(), c_eol(); X * chk_char(), read_char(), read_line (buf, max); and X * byetty(). X * N.B. we assume output may be performed by printf(), putchar() and X * fputs(stdout). since these are buffered we flush first in read_char(). X */ X X/* explanation of various conditional #define options: X * UNIX: uses termcap for screen management. X * USE_NDELAY: does non-blocking tty reads with fcntl(O_NDELAY); otherwise X * this is done with ioctl(..,FIONREAD..). Use which ever works on your X * system. X * TURBO_C: compiles for Turbo C 2.0. I'm told it works for Lattice and X * Microsoft too. X * USE_ANSISYS: default PC cursor control uses direct BIOS calls (thanks to X * Mr. Doug McDonald). If your PC does not work with this, however, add X * "device ANSI.SYS" to your config.sys file and build ephem with X * USE_ANSISYS. X */ X X#define UNIX X/* #define USE_NDELAY */ X/* #define TURBO_C */ X/* #define USE_ANSISYS */ X X#include <stdio.h> X#include "screen.h" X X#ifdef UNIX X#include <sgtty.h> X#include <signal.h> X#ifdef USE_NDELAY X#include <fcntl.h> X#endif X Xextern char *tgoto(); Xstatic char *cm, *ce, *cl, *kl, *kr, *ku, *kd; /* curses sequences */ Xstatic int tloaded; Xstatic int ttysetup; Xstatic struct sgttyb orig_sgtty; X X/* move cursor to row, col, 1-based. X * we assume this also moves a visible cursor to this location. X */ Xc_pos (r, c) Xint r, c; X{ X if (!tloaded) tload(); X fputs (tgoto (cm, c-1, r-1), stdout); X} X X/* erase entire screen. */ Xc_erase() X{ X if (!tloaded) tload(); X fputs (cl, stdout); X} X X/* erase to end of line */ Xc_eol() X{ X if (!tloaded) tload(); X fputs (ce, stdout); X} X X#ifdef USE_NDELAY Xstatic char sav_char; /* one character read-ahead for chk_char() */ X#endif X X/* return 0 if there is a char that may be read without blocking, else -1 */ Xchk_char() X{ X#ifdef USE_NDELAY X if (!ttysetup) setuptty(); X if (sav_char) X return (0); X fcntl (0, F_SETFL, O_NDELAY); /* non-blocking read. FNDELAY on BSD */ X if (read (0, &sav_char, 1) != 1) X sav_char = 0; X return (sav_char ? 0 : -1); X#else X long n; X if (!ttysetup) setuptty(); X ioctl (0, FIONREAD, &n); X return (n > 0 ? 0 : -1); X#endif X} X X/* read the next char, blocking if necessary, and return it. don't echo. X * map the arrow keys if we can too into hjkl X */ Xread_char() X{ X char c; X if (!ttysetup) setuptty(); X fflush (stdout); X#ifdef USE_NDELAY X fcntl (0, F_SETFL, 0); /* blocking read */ X if (sav_char) { X c = sav_char; X sav_char = 0; X } else X#endif X read (0, &c, 1); X c = chk_arrow (c & 0177); /* just ASCII, please */ X return (c); X} X X/* used to time out of a read */ Xstatic got_alrm; Xstatic Xon_alrm() X{ X got_alrm = 1; X} X X/* see if c is the first of any of the curses arrow key sequences. X * if it is, read the rest of the sequence, and return the hjkl code X * that corresponds. X * if no match, just return c. X */ Xstatic Xchk_arrow (c) Xregister char c; X{ X register char *seq; X X if (c == *(seq = kl) || c == *(seq = kd) || c == *(seq = ku) X || c == *(seq = kr)) { X char seqa[32]; /* maximum arrow escape sequence ever expected */ X unsigned l = strlen(seq); X seqa[0] = c; X if (l > 1) { X extern unsigned alarm(); X /* cautiously read rest of arrow sequence */ X got_alrm = 0; X signal (SIGALRM, on_alrm); X alarm(2); X read (0, seqa+1, l-1); X alarm(0); X if (got_alrm) X return (c); X } X seqa[l] = '\0'; X if (strcmp (seqa, kl) == 0) X return ('h'); X if (strcmp (seqa, kd) == 0) X return ('j'); X if (strcmp (seqa, ku) == 0) X return ('k'); X if (strcmp (seqa, kr) == 0) X return ('l'); X } X return (c); X} X X/* do whatever might be necessary to get the screen and/or tty back into shape. X */ Xbyetty() X{ X ioctl (0, TIOCSETP, &orig_sgtty); X#ifdef USE_NDELAY X fcntl (0, F_SETFL, 0); /* be sure to go back to blocking read */ X#endif X} X Xstatic Xtload() X{ X extern char *getenv(), *tgetstr(); X extern char *UP, *BC; X char *egetstr(); X static char tbuf[512]; X char rawtbuf[1024]; X char *tp; X char *ptr; X X if (!(tp = getenv ("TERM"))) { X printf ("Must have addressable cursor\n"); X exit(1); X } X X if (!ttysetup) setuptty(); X if (tgetent (rawtbuf, tp) != 1) { X printf ("Can't find termcap for %s\n", tp); X exit (1); X } X ptr = tbuf; X ku = egetstr ("ku", &ptr); X kd = egetstr ("kd", &ptr); X kl = egetstr ("kl", &ptr); X kr = egetstr ("kr", &ptr); X cm = egetstr ("cm", &ptr); X ce = egetstr ("ce", &ptr); X cl = egetstr ("cl", &ptr); X UP = egetstr ("up", &ptr); X if (!tgetflag ("bs")) X BC = egetstr ("bc", &ptr); X tloaded = 1; X} X X/* like tgetstr() but discard curses delay codes, for now anyways */ Xstatic char * Xegetstr (name, sptr) Xchar *name; Xchar **sptr; X{ X extern char *tgetstr(); X register char c, *s; X X s = tgetstr (name, sptr); X while (((c = *s) >= '0' && c <= '9') || c == '*') X s += 1; X return (s); X} X X/* set up tty for char-by-char read, non-blocking */ Xstatic Xsetuptty() X{ X struct sgttyb sg; X X ioctl (0, TIOCGETP, &orig_sgtty); X sg = orig_sgtty; X sg.sg_flags &= ~ECHO; /* do our own echoing */ X sg.sg_flags &= ~CRMOD; /* leave CR and LF unchanged */ X sg.sg_flags |= XTABS; /* no tabs with termcap */ X sg.sg_flags |= CBREAK; /* wake up on each char but can still kill */ X ioctl (0, TIOCSETP, &sg); X ttysetup = 1; X} X#endif X X#ifdef TURBO_C X#ifdef USE_ANSISYS X#define ESC '\033' X/* position cursor. X * (ANSI: ESC [ r ; c f) (r/c are numbers given in ASCII digits) X */ Xc_pos (r, c) Xint r, c; X{ X printf ("%c[%d;%df", ESC, r, c); X} X X/* erase entire screen. (ANSI: ESC [ 2 J) */ Xc_erase() X{ X printf ("%c[2J", ESC); X} X X/* erase to end of line. (ANSI: ESC [ K) */ Xc_eol() X{ X printf ("%c[K", ESC); X} X#else X#include <dos.h> Xunion REGS rg; X X/* position cursor. X */ Xc_pos (r, c) Xint r, c; X{ X rg.h.ah = 2; X rg.h.bh = 0; X rg.h.dh = r-1; X rg.h.dl = c-1; X int86(16,&rg,&rg); X} X X/* erase entire screen. */ Xc_erase() X{ X int cur_cursor, i; X rg.h.ah = 3; X rg.h.bh = 0; X int86(16,&rg,&rg); X cur_cursor = rg.x.dx; X for(i = 0; i < 25; i++){ X c_pos(i+1,1); X rg.h.ah = 10; X rg.h.bh = 0; X rg.h.al = 32; X rg.x.cx = 80; X int86(16,&rg,&rg); X } X rg.h.ah = 2; X rg.h.bh = 0; X rg.x.dx = cur_cursor; X int86(16,&rg,&rg); X X} X X/* erase to end of line.*/ Xc_eol() X{ X int cur_cursor, i; X rg.h.ah = 3; X rg.h.bh = 0; X int86(16,&rg,&rg); X cur_cursor = rg.x.dx; X rg.h.ah = 10; X rg.h.bh = 0; X rg.h.al = 32; X rg.x.cx = 80 - rg.h.dl; X int86(16,&rg,&rg); X rg.h.ah = 2; X rg.h.bh = 0; X rg.x.dx = cur_cursor; X int86(16,&rg,&rg); X X} X#endif X X/* return 0 if there is a char that may be read without blocking, else -1 */ Xchk_char() X{ X return (kbhit() == 0 ? -1 : 0); X} X X/* read the next char, blocking if necessary, and return it. don't echo. X * map the arrow keys if we can too into hjkl X */ Xread_char() X{ X int c; X fflush (stdout); X c = getch(); X if (c == 0) { X /* get scan code; convert to direction hjkl if possible */ X c = getch(); X switch (c) { X case 0x4b: c = 'h'; break; X case 0x50: c = 'j'; break; X case 0x48: c = 'k'; break; X case 0x4d: c = 'l'; break; X } X } X return (c); X} X X/* do whatever might be necessary to get the screen and/or tty back into shape. X */ Xbyetty() X{ X} X#endif X X/* read up to max chars into buf, with cannonization. X * add trailing '\0' (buf is really max+1 chars long). X * return count of chars read (not counting '\0'). X * assume cursor is already positioned as desired. X * if type END when n==0 then return -1. X */ Xread_line (buf, max) Xchar buf[]; Xint max; X{ X static char erase[] = "\b \b"; X int n, c; X int done; X X#ifdef UNIX X if (!ttysetup) setuptty(); X#endif X X for (done = 0, n = 0; !done; ) X switch (c = read_char()) { /* does not echo */ X case cntrl('h'): /* backspace or */ X case 0177: /* delete are each char erase */ X if (n > 0) { X fputs (erase, stdout); X n -= 1; X } X break; X case cntrl('u'): /* line erase */ X while (n > 0) { X fputs (erase, stdout); X n -= 1; X } X break; X case '\r': /* EOL */ X done++; X break; X default: /* echo and store, if ok */ X if (n == 0 && c == END) X return (-1); X if (n >= max) X putchar (cntrl('g')); X else if (c >= ' ') { X putchar (c); X buf[n++] = c; X } X } X X buf[n] = '\0'; X return (n); X} EOFxEOF len=`wc -c < io.c` if expr $len != 8533 > /dev/null then echo Length of io.c is $len but it should be 8533. fi echo x main.c sed -e 's/^X//' << 'EOFxEOF' > main.c X/* main "ephem" program. X * ------------------------------------------------------------------- X * Copyright (c) 1990 by Elwood Charles Downey X * X * Permission is granted to make and distribute copies of this program X * free of charge, provided the copyright notice and this permission X * notice are preserved on all copies. All other rights reserved. X * ------------------------------------------------------------------- X * set options. X * init screen and circumstances. X * enter infinite loop updating screen and allowing operator input. X */ X X#include <stdio.h> X#include <signal.h> X#include <math.h> X#include "astro.h" X#include "circum.h" X#include "screen.h" X Xextern char *getenv(); Xextern char *strcpy(); X X/* shorthands for fields of a Now structure, now. X * first undo the ones for a Now pointer from circum.h. X */ X#undef mjd X#undef lat X#undef lng X#undef tz X#undef temp X#undef pressure X#undef height X#undef epoch X#undef tznm X X#define mjd now.n_mjd X#define lat now.n_lat X#define lng now.n_lng X#define tz now.n_tz X#define temp now.n_temp X#define pressure now.n_pressure X#define height now.n_height X#define epoch now.n_epoch X#define tznm now.n_tznm X Xstatic char *cfgfile = "ephem.cfg"; /* default config filename */ X Xstatic Now now; /* where when and how, right now */ Xstatic double tminc; /* hrs to inc time by each loop; RTC means use clock */ Xstatic int nstep; /* steps to go before stopping */ Xstatic int optwi; /* set when want to display dawn/dusk/len-of-night */ Xstatic int oppl; /* mask of (1<<planet) bits; set when want to show it */ X Xmain (ac, av) Xint ac; Xchar *av[]; X{ X void bye(); X static char freerun[] = X "Running... press any key to stop to make changes."; X static char prmpt[] = X"Move to another field, RETURN to change this field, ? for help, or q to run"; X static char hlp[] = X "arrow keys move to field; any key stops running; ^d exits; ^l redraws"; X int curr = R_NSTEP, curc = C_NSTEPV; /* must start somewhere */ X int sflag = 0; /* not silent, by default */ X int one = 1; /* use a variable so optimizer doesn't get disabled */ X int srchdone = 0; /* true when search funcs say so */ X int newcir = 2; /* set when circumstances change - means don't tminc */ X X while ((--ac > 0) && (**++av == '-')) { X char *s; X for (s = *av+1; *s != '\0'; s++) X switch (*s) { X case 's': /* no credits "silent" (don't publish this) */ X sflag++; X break; X case 'c': /* set name of config file to use */ X if (--ac <= 0) usage("-c but no config file"); X cfgfile = *++av; X break; X default: X usage("Bad - option"); X } X } X X if (!sflag) X credits(); X X /* fresh screen. X * crack config file, THEN args so args may override. X */ X c_erase(); X read_cfgfile (cfgfile); X read_fieldargs (ac, av); X X /* set up to clean up screen and tty if interrupted */ X signal (SIGINT, bye); X X /* update screen forever (until QUIT) */ X while (one) { X X nstep -= 1; X X /* recalculate everything and update all the fields */ X redraw_screen (newcir); X mm_newcir (0); X X /* let searching functions change tminc and check for done */ X srchdone = srch_eval (mjd, &tminc) < 0; X print_tminc(0); /* to show possibly new search increment */ X X /* update plot file, now that all fields are up to date and X * search function has been evaluated. X */ X plot(); X X /* stop loop to allow op to change parameters: X * if a search evaluation converges (or errors out), X * or if steps are done, X * or if op hits any key. X */ X newcir = 0; X if (srchdone || nstep <= 0 || (chk_char()==0 && read_char()!=0)) { X int fld; X X /* update screen with the current stuff if stopped during X * unattended plotting since last redraw_screen() didn't. X */ X if (plot_ison() && nstep > 0) X redraw_screen (1); X X /* return nstep to default of 1 */ X if (nstep <= 0) { X nstep = 1; X print_nstep (0); X } X X /* change fields until END. X * update all time fields if any are changed X * and print NEW CIRCUMSTANCES if any have changed. X * QUIT causes bye() to be called and we never return. X */ X while(fld = sel_fld(curr,curc,alt_menumask()|F_CHG,prmpt,hlp)) { X if (chg_fld ((char *)0, fld)) { X mm_now (&now, 1); X mm_newcir(1); X newcir = 1; X } X curr = unpackr (fld); X curc = unpackc (fld); X } X if (nstep > 1) X f_prompt (freerun); X } X X /* increment time only if op didn't change cirumstances */ X if (!newcir) X inc_mjd (&now, tminc); X } X X return (0); X} X X/* draw all the stuff on the screen, using the current menu. X * if how_much == 0 then just update fields that need it; X * if how_much == 1 then redraw all fields; X * if how_much == 2 then erase the screen and redraw EVERYTHING. X */ Xredraw_screen (how_much) Xint how_much; X{ X if (how_much == 2) X c_erase(); X X /* print the single-step message if this is the last loop */ X if (nstep < 1) X print_updating(); X X if (how_much == 2) { X mm_borders(); X mm_labels(); X srch_prstate(1); X plot_prstate(1); X alt_labels(); X } X X /* if just updating changed fields while plotting unattended then X * suppress most screen updates except X * always show nstep to show plot loops to go and X * always show tminc to show search convergence progress. X */ X print_nstep(how_much); X print_tminc(how_much); X if (how_much == 0 && plot_ison() && nstep > 0) X f_off(); X X /* print all the time-related fields */ X mm_now (&now, how_much); X X if (optwi) X mm_twilight (&now, how_much); X X /* print solar system body info */ X print_bodies (how_much); X X f_on(); X} X X/* clean up and exit for sure. X */ Xvoid Xbye() X{ X c_erase(); X byetty(); X exit (0); X} X Xstatic Xusage(why) Xchar *why; X{ X /* don't advertise -s (silent) option */ X c_erase(); X f_string (1, 1, why); X f_string (2, 1, "usage: [-c <configfile>] [field=value] ...\r\n"); X byetty(); X exit (1); X} X X/* read cfg file, fn, if present. X * if errors in file, call usage() (which exits). X * try $HOME/.ephemrc as last resort. X * skip blank lines and lines that begin with '#', '*', ' ' or '\t'. X */ Xstatic Xread_cfgfile(fn) Xchar *fn; X{ X char buf[128]; X FILE *fp; X X fp = fopen (fn, "r"); X if (!fp) { X char *home = getenv ("HOME"); X if (home) { X sprintf (buf, "%s/.ephemrc", home); X fp = fopen (buf, "r"); X if (!fp) X return; /* oh well */ X fn = buf; /* save fn for error report */ X } X } X X while (fgets (buf, sizeof(buf), fp)) { X switch (buf[0]) { X case '#': case '*': case ' ': case '\t': case '\n': X continue; X } X buf[strlen(buf)-1] = '\0'; /* discard trailing \n */ X if (crack_fieldset (buf) < 0) { X char why[128]; X sprintf (why, "Unknown field spec in %s: %s\n", fn, buf); X usage (why); X } X } X fclose (fp); X} X X/* process the field specs from the command line. X * if trouble call usage() (which exits). X */ Xstatic Xread_fieldargs (ac, av) Xint ac; /* number of such specs */ Xchar *av[]; /* array of strings in form <field_name value> */ X{ X while (--ac >= 0) { X char *fs = *av++; X if (crack_fieldset (fs) < 0) { X char why[128]; X sprintf (why, "Unknown command line field spec: %s", fs); X usage (why); X } X } X} X X/* process a field spec in buf, either from config file or argv. X * return 0 if recognized ok, else -1. X */ Xstatic Xcrack_fieldset (buf) Xchar *buf; X{ X if (strncmp ("LAT", buf, 3) == 0) X (void) chg_fld (buf+4, rcfpack (R_LAT,C_LATV,0)); X else if (strncmp ("LONG", buf, 4) == 0) X (void) chg_fld (buf+5, rcfpack (R_LONG,C_LONGV,0)); X else if (strncmp ("UT", buf, 2) == 0) X (void) chg_fld (buf+3, rcfpack (R_UT,C_UTV,0)); X else if (strncmp ("UD", buf, 2) == 0) X (void) chg_fld (buf+3, rcfpack (R_UD,C_UD,0)); X else if (strncmp ("TZONE", buf, 5) == 0) X (void) chg_fld (buf+6, rcfpack (R_TZONE,C_TZONEV,0)); X else if (strncmp ("TZNAME", buf, 6) == 0) X (void) chg_fld (buf+7, rcfpack (R_TZN,C_TZN,0)); X else if (strncmp ("HEIGHT", buf, 6) == 0) X (void) chg_fld (buf+7, rcfpack (R_HEIGHT,C_HEIGHTV,0)); X else if (strncmp ("NSTEP", buf, 5) == 0) X (void) chg_fld (buf+6, rcfpack (R_NSTEP,C_NSTEPV,0)); X else if (strncmp ("STPSZ", buf, 5) == 0) X (void) chg_fld (buf+6, rcfpack (R_STPSZ,C_STPSZV,0)); X else if (strncmp ("TEMP", buf, 4) == 0) X (void) chg_fld (buf+5, rcfpack (R_TEMP,C_TEMPV,0)); X else if (strncmp ("PRES", buf, 4) == 0) X (void) chg_fld (buf+5, rcfpack (R_PRES,C_PRESV,0)); X else if (strncmp ("EPOCH", buf, 5) == 0) X (void) chg_fld (buf+6, rcfpack (R_EPOCH,C_EPOCHV,0)); X else if (strncmp ("JD", buf, 2) == 0) X (void) chg_fld (buf+3, rcfpack (R_JD,C_JDV,0)); X else if (strncmp ("OBJX", buf, 4) == 0) X (void) objx_define (buf+5); X else if (strncmp ("PROPTS", buf, 6) == 0) { X char *bp = buf+7; X if (buf[6] != '+') X optwi = oppl = 0; X while (*bp) X switch (*bp++) { X case 'T': optwi = 1; break; X case 'S': oppl |= (1<<SUN); break; X case 'M': oppl |= (1<<MOON); break; X case 'e': oppl |= (1<<MERCURY); break; X case 'v': oppl |= (1<<VENUS); break; X case 'm': oppl |= (1<<MARS); break; X case 'j': oppl |= (1<<JUPITER); break; X case 's': oppl |= (1<<SATURN); break; X case 'u': oppl |= (1<<URANUS); break; X case 'n': oppl |= (1<<NEPTUNE); break; X case 'p': oppl |= (1<<PLUTO); break; X case 'x': oppl |= (1<<OBJX); objx_on(); break; X } X } else X return (-1); X return (0); X} X X/* change the field at rcpk according to the optional string input at bp. X * if bp is != 0 use it, else issue read_line() and use buffer. X * then sscanf the buffer and update the corresponding (global) variable(s) X * or do whatever a pick at that field should do. X * return 1 if we change a field that invalidates any of the times or X * to update all related fields. X */ Xstatic Xchg_fld (bp, rcpk) Xchar *bp; Xint rcpk; X{ X char buf[NC]; X int deghrs = 0, mins = 0, secs = 0; X int new = 0; X X /* switch on just the row/col portion */ X switch (unpackrc(rcpk)) { X case rcfpack (R_ALTM, C_ALTM, 0): X if (altmenu_setup() == 0) { X print_updating(); X alt_nolabels(); X clrall_bodies(); X alt_labels(); X print_bodies(1); X } X break; X case rcfpack (R_JD, C_JDV, 0): X if (!bp) { X static char p[] = "Julian Date (or n for Now): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X if (bp[0] == 'n' || bp[0] == 'N') X time_fromsys (&now); X else X mjd = atof(bp) - 2415020L; X set_t0 (&now); X new = 1; X break; X case rcfpack (R_UD, C_UD, 0): X if (!bp) { X static char p[] = "utc date (m/d/y, or year.d, or n for Now): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X if (bp[0] == 'n' || bp[0] == 'N') X time_fromsys (&now); X else { X if (decimal_year(bp)) { X double y = atof (bp); X year_mjd (y, &mjd); X } else { X double day, newmjd0; X int month, year; X mjd_cal (mjd, &month, &day, &year); /* init with now */ X f_sscandate (bp, &month, &day, &year); X cal_mjd (month, day, year, &newmjd0); X /* if don't give a fractional part to days X * then retain current hours. X */ X if ((long)day == day) X mjd = newmjd0 + mjd_hr(mjd)/24.0; X else X mjd = newmjd0; X } X } X set_t0 (&now); X new = 1; X break; X case rcfpack (R_UT, C_UTV, 0): X if (!bp) { X static char p[] = "utc time (h:m:s, or n for Now): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X if (bp[0] == 'n' || bp[0] == 'N') X time_fromsys (&now); X else { X double newutc = (mjd-mjd_day(mjd)) * 24.0; X f_dec_sexsign (newutc, °hrs, &mins, &secs); X f_sscansex (bp, °hrs, &mins, &secs); X sex_dec (deghrs, mins, secs, &newutc); X mjd = mjd_day(mjd) + newutc/24.0; X } X set_t0 (&now); X new = 1; X break; X case rcfpack (R_LD, C_LD, 0): X if (!bp) { X static char p[] = "local date (m/d/y, or year.d, n for Now): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X if (bp[0] == 'n' || bp[0] == 'N') X time_fromsys (&now); X else { X if (decimal_year(bp)) { X double y = atof (bp); X year_mjd (y, &mjd); X mjd += tz/24.0; X } else { X double day, newlmjd0; X int month, year; X mjd_cal (mjd-tz/24.0, &month, &day, &year); /* now */ X f_sscandate (bp, &month, &day, &year); X cal_mjd (month, day, year, &newlmjd0); X /* if don't give a fractional part to days X * then retain current hours. X */ X if ((long)day == day) X mjd = newlmjd0 + mjd_hr(mjd-tz/24.0)/24.0; X else X mjd = newlmjd0; X mjd += tz/24.0; X } X } X set_t0 (&now); X new = 1; X break; X case rcfpack (R_LT, C_LT, 0): X if (!bp) { X static char p[] = "local time (h:m:s, or n for Now): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X if (bp[0] == 'n' || bp[0] == 'N') X time_fromsys (&now); X else { X double newlt = (mjd-mjd_day(mjd)) * 24.0 - tz; X range (&newlt, 24.0); X f_dec_sexsign (newlt, °hrs, &mins, &secs); X f_sscansex (bp, °hrs, &mins, &secs); X sex_dec (deghrs, mins, secs, &newlt); X mjd = mjd_day(mjd-tz/24.0) + (newlt + tz)/24.0; X } X set_t0 (&now); X new = 1; X break; X case rcfpack (R_LST, C_LSTV, 0): X if (!bp) { X static char p[] = "local sidereal time (h:m:s, or n for Now): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X if (bp[0] == 'n' || bp[0] == 'N') X time_fromsys (&now); X else { X double lst, utc; X now_lst (&now, &lst); X f_dec_sexsign (lst, °hrs, &mins, &secs); X f_sscansex (bp, °hrs, &mins, &secs); X sex_dec (deghrs, mins, secs, &lst); X lst -= radhr(lng); /* convert to gst */ X range (&lst, 24.0); X gst_utc (mjd_day(mjd), lst, &utc); X mjd = mjd_day(mjd) + utc/24.0; X } X set_t0 (&now); X new = 1; X break; X case rcfpack (R_TZN, C_TZN, 0): X if (!bp) { X static char p[] = "timezone abbreviation (3 char max): "; X f_prompt (p); X if (read_line (buf, 3) <= 0) X break; X bp = buf; X } X strcpy (tznm, bp); X new = 1; X break; X case rcfpack (R_TZONE, C_TZONEV, 0): X if (!bp) { X static char p[] = "hours behind utc: "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X f_dec_sexsign (tz, °hrs, &mins, &secs); X f_sscansex (bp, °hrs, &mins, &secs); X sex_dec (deghrs, mins, secs, &tz); X new = 1; X break; X case rcfpack (R_LONG, C_LONGV, 0): X if (!bp) { X static char p[] = "longitude (+ west) (d:m:s): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X f_dec_sexsign (-raddeg(lng), °hrs, &mins, &secs); X f_sscansex (bp, °hrs, &mins, &secs); X sex_dec (deghrs, mins, secs, &lng); X lng = degrad (-lng); /* want - radians west */ X new = 1; X break; X case rcfpack (R_LAT, C_LATV, 0): X if (!bp) { X static char p[] = "latitude (+ north) (d:m:s): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X f_dec_sexsign (raddeg(lat), °hrs, &mins, &secs); X f_sscansex (bp, °hrs, &mins, &secs); X sex_dec (deghrs, mins, secs, &lat); X lat = degrad (lat); X new = 1; X break; X case rcfpack (R_HEIGHT, C_HEIGHTV, 0): X if (!bp) { X static char p[] = "height above sea level (ft): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X sscanf (bp, "%lf", &height); X height /= 2.093e7; /* convert ft to earth radii above sea level */ X new = 1; X break; X case rcfpack (R_NSTEP, C_NSTEPV, 0): X if (!bp) { X static char p[] = "number of steps to run: "; X f_prompt (p); X if (read_line (buf, 8) <= 0) X break; X bp = buf; X } X sscanf (bp, "%d", &nstep); X print_nstep (0); X break; X case rcfpack (R_TEMP, C_TEMPV, 0): X if (!bp) { X static char p[] = "temperature (deg.F): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X sscanf (bp, "%lf", &temp); X temp = 5./9.*(temp - 32.0); /* want degs C */ X new = 1; X break; X case rcfpack (R_PRES, C_PRESV, 0): X if (!bp) { X static char p[] = X "atmos pressure (in. Hg; 0 for no refraction correction): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X sscanf (bp, "%lf", &pressure); X pressure *= 33.86; /* want mBar */ X new = 1; X break; X case rcfpack (R_EPOCH, C_EPOCHV, 0): X if (!bp) { X static char p[] = "epoch (year, or e for Equinox of Date): "; X f_prompt (p); X if (read_line (buf, PW-strlen(p)) <= 0) X break; X bp = buf; X } X if (bp[0] == 'e' || bp[0] == 'E') X epoch = EOD; X else { X double e; X e = atof(bp); X year_mjd (e, &epoch); X } X new = 1; X break; X case rcfpack (R_STPSZ, C_STPSZV, 0): X if (!bp) { X static char p[] = X "step size increment (h:m:s, or <x>d for x days, or r for RTC): "; X f_prompt (p); X if (read_line (buf, PW-sizeof(p)) <= 0) X break; X bp = buf; X } X if (bp[0] == 'r' || bp[0] == 'R') X tminc = RTC; X else { X int last = strlen (bp) - 1; X if (bp[last] == 'd') { X /* ends in d so treat as a number of days */ X double x; X sscanf (bp, "%lf", &x); X tminc = x * 24.0; X } else { X if (tminc == RTC) X deghrs = mins = secs = 0; X else X f_dec_sexsign (tminc, °hrs, &mins, &secs); X f_sscansex (bp, °hrs, &mins, &secs); X sex_dec (deghrs, mins, secs, &tminc); X } X } X print_tminc(0); X set_t0 (&now); X break; X case rcfpack (R_PLOT, C_PLOT, 0): X plot_setup(); X if (plot_ison()) X new = 1; X break; X case rcfpack (R_WATCH, C_WATCH, 0): X watch (&now, tminc, oppl); X /* set new reference time to what watch left it. X * no need to set new since watch just did a redraw. X */ X set_t0 (&now); X break; X case rcfpack (R_DAWN, C_DAWN, 0): X case rcfpack (R_DUSK, C_DUSK, 0): X case rcfpack (R_LON, C_LON, 0): X if (optwi ^= 1) { X print_updating(); X mm_twilight (&now, 1); X } else { X f_blanks (R_DAWN, C_DAWNV, 5); X f_blanks (R_DUSK, C_DUSKV, 5); X f_blanks (R_LON, C_LONV, 5); X } X break; X case rcfpack (R_SRCH, C_SRCH, 0): X srch_setup(); X if (srch_ison()) X new = 1; X break; X case rcfpack (R_SUN, C_OBJ, 0): X if ((oppl ^= (1<<SUN)) & (1<<SUN)) { X print_updating(); X alt_body (SUN, 1, &now); X } else X alt_nobody (SUN); X break; X case rcfpack (R_MOON, C_OBJ, 0): X if ((oppl ^= (1<<MOON)) & (1<<MOON)) { X print_updating(); X alt_body (MOON, 1, &now); X } else X alt_nobody (MOON); X break; X case rcfpack (R_MERCURY, C_OBJ, 0): X if ((oppl ^= (1<<MERCURY)) & (1<<MERCURY)) { X print_updating(); X alt_body (MERCURY, 1, &now); X } else X alt_nobody (MERCURY); X break; X case rcfpack (R_VENUS, C_OBJ, 0): X if ((oppl ^= (1<<VENUS)) & (1<<VENUS)) { X print_updating(); X alt_body (VENUS, 1, &now); X } else X alt_nobody (VENUS); X break; X case rcfpack (R_MARS, C_OBJ, 0): X if ((oppl ^= (1<<MARS)) & (1<<MARS)) { X print_updating(); X alt_body (MARS, 1, &now); X } else X alt_nobody (MARS); X break; X case rcfpack (R_JUPITER, C_OBJ, 0): X if ((oppl ^= (1<<JUPITER)) & (1<<JUPITER)) { X print_updating(); X alt_body (JUPITER, 1, &now); X } else X alt_nobody (JUPITER); X break; X case rcfpack (R_SATURN, C_OBJ, 0): X if ((oppl ^= (1<<SATURN)) & (1<<SATURN)) { X print_updating(); X alt_body (SATURN, 1, &now); X } else X alt_nobody (SATURN); X break; X case rcfpack (R_URANUS, C_OBJ, 0): X if ((oppl ^= (1<<URANUS)) & (1<<URANUS)) { X print_updating(); X alt_body (URANUS, 1, &now); X } else X alt_nobody (URANUS); X break; X case rcfpack (R_NEPTUNE, C_OBJ, 0): X if ((oppl ^= (1<<NEPTUNE)) & (1<<NEPTUNE)) { X print_updating(); X alt_body (NEPTUNE, 1, &now); X } else X alt_nobody (NEPTUNE); X break; X case rcfpack (R_PLUTO, C_OBJ, 0): X if ((oppl ^= (1<<PLUTO)) & (1<<PLUTO)) { X print_updating(); X alt_body (PLUTO, 1, &now); X } else X alt_nobody (PLUTO); X break; X case rcfpack (R_OBJX, C_OBJ, 0): X /* this might change which columns are used so erase all when X * returns and redraw if still on. X */ X objx_setup (); X alt_nobody (OBJX); X if (objx_ison()) { X oppl |= 1 << OBJX; X print_updating(); X alt_body (OBJX, 1, &now); X } else X oppl &= ~(1 << OBJX); /* already erased; just clear flag */ X break; X } X X return (new); X} X Xstatic Xprint_tminc(force) Xint force; X{ X static double last; X X if (force || tminc != last) { X if (tminc == RTC) X f_string (R_STPSZ, C_STPSZV, " RT CLOCK"); X else if (fabs(tminc) >= 24.0) X f_double (R_STPSZ, C_STPSZV, "%6.4g dy", tminc/24.0); X else X f_signtime (R_STPSZ, C_STPSZV, tminc); X last = tminc; X } X} X Xstatic Xprint_bodies (force) Xint force; X{ X int p; X X for (p = nxtbody(-1); p != -1; p = nxtbody(p)) X if (oppl & (1<<p)) X alt_body (p, force, &now); X} X Xstatic Xclrall_bodies () X{ X int p; X X for (p = nxtbody(-1); p != -1; p = nxtbody(p)) X if (oppl & (1<<p)) X alt_nobody (p); X} X Xprint_updating() X{ X f_prompt ("Updating..."); X} X Xstatic Xprint_nstep(force) Xint force; X{ X static int last; X X if (force || nstep != last) { X char buf[16]; X sprintf (buf, "%8d", nstep); X f_string (R_NSTEP, C_NSTEPV, buf); X last = nstep; X } X} EOFxEOF len=`wc -c < main.c` if expr $len != 21224 > /dev/null then echo Length of main.c is $len but it should be 21224. fi