/*
** Astrolog (Version 3.05) File: driver.c
**
** IMPORTANT: The planetary calculation routines used in this program
** have been Copyrighted and the core of this program is basically a
** conversion to C of the routines created by James Neely as listed in
** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
** available from Matrix Software. The copyright gives us permission to
** use the routines for our own purposes but not to sell them or profit
** from them in any way.
**
** IN ADDITION: the graphics database and chart display routines used in
** this program are Copyright (C) 1991-1993 by Walter D. Pullen. Permission
** is granted to freely use and distribute these routines provided one
** doesn't sell, restrict, or profit from them in any way. Modification
** is allowed provided these notices remain with any altered or edited
** versions of the program.
*/

#include "astrolog.h"

char *filenameout, **extralines;
int prog = FALSE, extracount = 0;


/*
*******************************************************************************
** Option processing
*******************************************************************************
*/

/* Print out a command switch or keypress info line to the screen, as done  */
/* with the -H switch or 'H' key in a graphic window. This is just printing */
/* out the string, except in Ansi mode we set the proper colors: Red for    */
/* header lines, Green for individual switches or keys, and White for the   */
/* rest of the line telling what it does. We also prefix each switch with   */
/* either Unix's '-' or PC's '/', whichever is appropriate for the system.  */

void Prints(string)
char *string;
{
  int dash;
  char c;

  dash = string[1];
  AnsiColor(*string != ' ' ? RED : (dash == '_' || dash == 'P' ? GREEN : -1));
  while ((c = *string) && c != ':' &&
    (dash != 'P' || (c != ' ' || *(string+1) != 't'))) {
    if (c != '_')
      putchar(c);
    else
      putchar(DASH);
    string++;
    }
  if (*string)
    putchar(*string++);
  AnsiColor(-1);
  while (c = *string)
    {
    if (c != '_')
      putchar(c);
    else
      putchar(DASH);
    string++;
    }
  putchar('\n');
}


/* Print a list of every command switch that can be passed to the program, */
/* and a description of what it does. This is what the -H switch prints.   */

void DisplaySwitches()
{
  sprintf(string, "%s command switches (version %s) (%s):", appname, VERSION,
    DATE);
  Prints(string);
  Prints(" _H: Display this help list.");
  Prints(" _H0: Display names of zodiac signs and houses.");
  Prints(" _O: Display available planets and other celestial objects.");
  Prints(" _O0: Like _O but ignore object restrictions.");
  Prints(" _A: Display available aspects, their angles, and present orbs.");
  Prints(" _I0: Display meanings of signs, houses, planets, and aspects.\n");
  Prints("Switches which determine the type of chart to display:");
  Prints(" _v: Display list of object positions (chosen by default).");
  Prints(" _v0: Like _v but express velocities as absolute degree speed.");
  Prints(" _w [<rows>]: Display chart in a graphic house wheel format.");
  Prints(" _w0 [..]: Like _w but reverse order of objects in houses 4..9.");
  Prints(" _g: Display aspect and midpoint grid among planets.");
  Prints(" _g0: Like _g but flag aspect configurations (e.g. Yod's) too.");
  Prints(" _g0: For comparison charts, show midpoints instead of aspects.");
  Prints(" _ga: Like _g but indicate applying instead of difference orbs.");
  Prints(" _m: Display all object midpoints in sorted zodiac order.");
  Prints(" _m0: Like _m but list aspects ordered by influence instead.");
  Prints(" _Z: Display planet locations with respect to the local horizon.");
#ifdef GRAPH
  Prints(" _Z0: For graphics charts, like _Z but have a polar center.");
#endif
  Prints(" _S: Display x,y,z coordinate positions of planets in space.");
  Prints(" _j: Display astrological influences of each object in chart.");
  Prints(" _j0: Like _j but include influences of each sign as well.");
  Prints(" _L [<step>]: Display astro-graph locations of planetary angles.");
  Prints(" _L0 [..]: Like _L but display list of latitude crossings too.");
  Prints(" _d: Print all aspects and changes occurring in a day.");
  Prints(" _dm: Like _d but print all aspects for the entire month.");
  Prints(" _dp <month> <year>: Print aspects within progressed chart.");
  Prints(" _E: Display planetary ephemeris for given month.");
  Prints(" _Ey: Display planetary ephemeris for entire year.");
  Prints(" _e: Print all options for chart (i.e. _v_w_g0_m_Z_S_j0_L0_d_E).");
  Prints(
    " _T <month> <year>: Compute all transits to natal planets in month.");
  Prints(
    " _Tp <month> <year>: Compute progressions in month for chart.");
  Prints(" _T[p]y: <year>: Compute transits/progressions for entire year.");
#ifdef TIME
  Prints(" _Tn[y]: Compute transits to natal planets for current time now.");
#endif
  Prints(" _I: Print interpretation of selected charts.\n");
  Prints("Switches which affect how the chart parameters are obtained:");
#ifdef TIME
  Prints(" _n: Compute chart for this exact moment using current time.");
  Prints(" _n[d,m,y]: Compute chart for start of current day, month, year.");
#endif
  Prints(" _a <month> <date> <year> <time> <zone> <long> <lat>:");
  Prints("     Compute chart automatically given specified data.");
  Prints(" _z: Assume Daylight time (change default zone appropriately).");
  Prints(" _z <zone>: Change the default time zone (for _d_q_T_E options).");
  Prints(" _l <long> <lat>: Change the default longitude & latitude.");
  Prints(" _q <month> <date> <year> <time>: Compute chart with defaults.");
  Prints(" _qd <month> <date> <year>: Compute chart for noon on date.");
  Prints(" _qm <month> <year>: Compute chart for first of month.");
  Prints(" _qy <year>: Compute chart for first day of year.");
  Prints(" _i <file>: Compute chart based on info in file.");
  Prints(" _o <file> [..]: Write parameters of current chart to file.");
  Prints(" _o0 <file> [..]: Like _o but output planet/house positions.\n");
  Prints("Switches which affect what information is used in a chart:");
  Prints(" _R [<obj1> [<obj2> ..]]: Restrict specific bodies from displays.");
  Prints(" _R0 [<obj1> ..]: Like _R but restrict everything first.");
  Prints(" _R[C,u,U]: Restrict all minor cusps, all uranians, or stars.");
  Prints(" _RT[0,C,u,U] [..]: Restrict transiting planets in _T lists.");
  Prints(" _C: Include non-angular house cusps in charts.");
  Prints(" _u: Include transneptunian/uranian bodies in charts.");
  Prints(" _U: Include locations of fixed background stars in charts.");
  Prints(" _U[z,l,n,b]: Order by azimuth, altitude, name, or brightness.");
  Prints(" _A <0-18>: Specify the number of aspects to use in charts.");
  Prints(" _Ao <aspect> <orb>: Specify maximum orb for an aspect.");
  Prints(" _Am <planet> <orb>: Specify maximum orb allowed to a planet.");
  Prints(" _Ad <planet> <orb>: Specify orb addition given to a planet.\n");
  Prints("Switches which affect how a chart is computed:");
  Prints(" _c <value>: Select a different default system of houses.");
  Prints("     (0 = Placidus, 1 = Koch, 2 = Equal, 3 = Campanus,");
  Prints("     4 = Meridian, 5 = Regiomontanus, 6 = Porphry, 7 = Morinus,");
  Prints("     8 = Topocentric, 9 = None.)");
  Prints(" _s: Compute a sidereal instead of the normal tropical chart.");
  Prints(" _s0: Display locations as right ascension instead of degrees.");
  Prints(" _h [<objnum>]: Compute positions centered on specified object.");
  Prints(" _p <month> <day> <year>: Cast 2ndary progressed chart for date.");
  Prints(" _p0 <month> <day> <year>: Cast solar arc chart for date.");
#ifdef TIME
  Prints(" _p[0]n: Cast progressed chart based on current date now.");
#endif
  Prints(" _pd <days>: Set no. of days to progress / day (default 365.25).");
  Prints(" _x <1-360>: Cast harmonic chart based on specified factor.");
  Prints(" _1 [<objnum>]: Cast chart with specified object on Ascendant.");
  Prints(" _2 [<objnum>]: Cast chart with specified object on Midheaven.");
  Prints(" _3: Display objects in their zodiac decan positions.");
  Prints(" _f: Display houses as sign positions (flip them).");
  Prints(" _G: Display houses based on geographic location only.");
  Prints(" _F <objnum> <sign> <deg>: Force object's position to be value.");
  Prints(" _+ [<days>]: Cast chart for specified no. of days in the future.");
  Prints(" _- [<days>]: Cast chart for specified no. of days in the past.\n");
  Prints("Switches for relationship and comparison charts:");
  Prints(" _r <file1> <file2>: Compute a relationship synastry chart.");
  Prints(" _rc <file1> <file2>: Compute a composite chart.");
  Prints(" _rm <file1> <file2>: Compute a time space midpoint chart.");
  Prints(" _rd <file1> <file2>: Print number of days between files' dates.");
  Prints(" _rb <file1> <file2>: Display biorhythm for file1 at time file2.");
  Prints(" _r0 <file1> <file2>: Keep the charts separate in comparison.");
#ifdef TIME
  Prints(" _t <file>: Display current house transits for particular chart.");
  Prints(" _t[b,d] <file>: Print biorhythm/datediff for current time now.");
#endif
  Prints("\nSwitches to access graphics options:");
  Prints(" _k: Display text charts using Ansi characters and color.");

  /* If graphics features are compiled in, call an additional procedure to */
  /* display the command switches offered dealing with the graphics stuff. */

#ifdef GRAPH
  XDisplaySwitches();
#endif
}


/* Print out a list of the various objects - planets, asteroids, house     */
/* cusps, stars - recognized by the program, and their index values. This  */
/* is displayed when the -O switch is invoked. For some objects, display   */
/* additional information, e.g. ruling signs for planets, brightnesses and */
/* positions in the sky for fixed stars, etc.                              */

void PrintObjects(all)
int all;
{
  int i, j;
  real Off;

  if (!(operation & DASHC))
    for (i = C_LO; i <= C_HI; i++)    /* Set up restrictions properly: Minor */
      ignore[i] = TRUE;               /* cusps and uranians included only if */
  if (!(operation & DASHu))           /* -C and -u switches are in effect.   */
    for (i = U_LO; i <= U_HI; i++)
      ignore[i] = TRUE;
  printf("%s planets and objects:\n", appname);
  printf("No. Name       Rule Co-Rule Fall Co-Fall Exalt Debilitate\n\n");
  for (i = 1; i <= BASE; i++) if (all || !ignore[i]) {
    AnsiColor(objectansi[i]);
    printf("%2d %-12s", i, objectname[i]);
    if (i <= OBJECTS) {                      /* Print rulerships, etc */
      if (ruler1[i]) {                       /* for the planets.      */
        j = ruler2[i];
        printf("%c%c%c  %c%c%c     ", SIGNAM(ruler1[i]),
          j ? signname[j][0] : ' ', j ? signname[j][1] : ' ',
          j ? signname[j][2] : ' ');
        printf("%c%c%c  %c%c%c     ", SIGNAM(Mod12(ruler1[i]+6)),
          j ? signname[Mod12(j+6)][0] : ' ',
          j ? signname[Mod12(j+6)][1] : ' ',
          j ? signname[Mod12(j+6)][2] : ' ');
        printf("%c%c%c   %c%c%c", SIGNAM(exalt[i]),
          SIGNAM(Mod12(exalt[i]+6)));
      }
    } else if (i <= C_HI)
      printf("Minor House Cusp #%d", i-OBJECTS);
    else
      printf("Uranian #%d", i-U_LO+1);
    putchar('\n');
  }

  /* Now, if -U in effect, read in and display stars in specified order. */

  if (all || universe) {
    Off = ProcessInput(TRUE);
    CastStar(operation & DASHs ? 0.0 : -Off);
    for (i = S_LO; i <= S_HI; i++) if (all | !ignore[i]) {
      j = BASE+starname[i-BASE];
      AnsiColor(objectansi[j]);
      printf("%2d %-12s", i, objectname[j]);
      printf("Star #%2d    ", i-BASE);
      PrintMinute(planet[j]);
      printf("  ");
      PrintAltitude(planetalt[j]);
      printf(" %5.2f\n", starbright[j-BASE]);
    }
  }
}


/* Print out a list of all the aspects recognized by the program, and info  */
/* about them: their names, index numbers, degree angles, present orbs, and */
/* the description of their glyph. This gets displayed when the -A switch   */
/* is invoked (without any argument).                                       */

void PrintAspects()
{
  int i;

  printf("%s aspects:\nNo. Name         Abbrev. Angle    Orb", appname);
  printf("          Description of glyph\n\n");
  for (i = 1; i <= ASPECTS; i++) {
    AnsiColor(aspectansi[i]);
    printf("%2d %-15s(%s) %6.2f +/- %1.0f degrees - %s\n", i, aspectname[i],
      aspectabbrev[i], aspectangle[i], aspectorb[i], aspectglyph[i]);
  }
}


/* Print out a list of the 12 signs and houses of the zodiac, and their    */
/* standard and traditional names, as done when the -H0 switch is invoked. */

void PrintSigns()
{
  int i;

  printf("%s signs and houses:\n", appname);
  printf("Sign        English name      House Traditional name\n\n");
  for (i = 1; i <= SIGNS; i++) {
    AnsiColor(elemansi[i-1 & 3]);
    printf("%-12sthe %-14s%2d%s  House of %s\n", signname[i], signenglish[i],
      i, post[i], housetradition[i]);
  }
}


/* Read in a set of default program values used in the program, such as */
/* present location, time zone, the system of houses to use, the number */
/* of aspects and what orbs to use, and so on. These values are always  */
/* read in at the beginning of program execution.                       */

/* The NEXTDEFAULT macro means to skip all comments in the file until we   */
/* reach the beginning of the next set of data, delimited with a '=' sign. */

#define NEXTDEFAULT while(getc(data) != '=');

int InputDefaults()
{
  FILE *data;
  char name[STRING];
  int i, j;

  filename = DEFAULT_INFOFILE;     /* First lets open the info file.      */
  data = fopen(filename, "r");     /* Look for file in current directory. */
  if (data == NULL) {
    sprintf(name, "%s%s", DEFAULT_DIR, filename);   /* Look for file in   */
    data = fopen(name, "r");                        /* default directory. */
    if (data == NULL)          /* If file not found anywhere, then forget */
      return FALSE;            /* it and use the compile time defaults.   */
  }
  fscanf(data, "%s", name); fscanf(data, "%s", name);
  if (name[0] != '3' || name [1] != '.' || name[2] != '0' ||
    (name[3] != '0' && name[3] != '5')) {
    fprintf(stderr, "%s: Bad information in default parameter file '%s'.\n",
      appname, filename);
    fprintf(stderr,
      "Delete this file or obtain one compatible with current version.\n");
    Terminate(1);
  }
  NEXTDEFAULT; fscanf(data, "%lf", &defzone);        /* Time zone    */
  NEXTDEFAULT; fscanf(data, "%lf", &deflong);        /* Longitude    */
  NEXTDEFAULT; fscanf(data, "%lf", &deflat);         /* Latitude     */
  NEXTDEFAULT; fscanf(data, "%d",  &aspects);        /* # of aspects   */
  NEXTDEFAULT; fscanf(data, "%d",  &housesystem);    /* House type     */
  NEXTDEFAULT; fscanf(data, "%d",  &ansi);           /* Ansi text?     */
  NEXTDEFAULT; fscanf(data, "%d",  &divisions);      /* For -d and -T. */
  NEXTDEFAULT; fscanf(data, "%d",  &wheelrows);      /* For -w charts. */
  NEXTDEFAULT; fscanf(data, "%d",  &smartcusp);      /* Logical -T display?  */
  NEXTDEFAULT; fscanf(data, "%d",  &column80);       /* Clip text at col 80? */
  NEXTDEFAULT;
  for (i = 1; i <= OBJECTS; i++) {         /* Object restrictions         */
    fscanf(data, "%d", &j);
    ignore[i] = j > 0;
  }
  NEXTDEFAULT;
  for (i = 1; i <= OBJECTS; i++) {         /* Transit object restrictions */
    fscanf(data, "%d", &j);
    ignore2[i] = j > 0;
  }
  NEXTDEFAULT;
  for (i = 1; i <= ASPECTS; i++)           /* Orbs for aspects  */
    fscanf(data, "%lf", &aspectorb[i]);
  NEXTDEFAULT;
  for (i = 1; i <= OBJECTS; i++)           /* Orbs for planets  */
    fscanf(data, "%lf", &planetorb[i]);
  NEXTDEFAULT;
  for (i = 1; i <= OBJECTS; i++)           /* Extra planet orbs */
    fscanf(data, "%lf", &planetadd[i]);
  NEXTDEFAULT; fscanf(data, "%lf", &objectinf[OBJECTS+1]);   /* Rules sign  */
  NEXTDEFAULT; fscanf(data, "%lf", &objectinf[OBJECTS+2]);   /* Exalts in   */
  NEXTDEFAULT; fscanf(data, "%lf",  &houseinf[SIGNS+1]);     /* Rules house */
  NEXTDEFAULT; fscanf(data, "%lf",  &houseinf[SIGNS+2]);     /* Exalts in   */
  NEXTDEFAULT;
  for (i = 1; i <= OBJECTS; i++)
    fscanf(data, "%lf", &objectinf[i]);    /* Influence of each object */
  for (i = 1; i <= SIGNS; i++)
    fscanf(data, "%lf", &houseinf[i]);     /* Influence of each house  */
  for (i = 1; i <= ASPECTS; i++)
    fscanf(data, "%lf", &aspectinf[i]);    /* Influence of each aspect */
  fclose(data);
  return TRUE;
}


/* Take the current chart information, and write it out to the file   */
/* as indicated by the -o switch. This is only executed at the end of */
/* program execution if the -o switch is in effect.                   */

void OutputData()
{
  FILE *data;
  int i, j;
  real k;

  data = fopen(filenameout, "w");    /* Create and open the file for output. */
  if (data == NULL) {
    fprintf(stderr, "%s: File %s can not be created.\n", appname, filenameout);
    Terminate(1);
  }
  if (!(operation & DASHo0)) {

    /* Write the chart information to the file. */

    if (Mon < 1) {
      fprintf(stderr, "\n%s: Can't output chart with no time/space to file.\n",
        appname);
      fclose(data);
      Terminate(1);
    }
    fprintf(data, "%.0f\n%.0f\n%.0f\n%.2f\n%.2f\n%.2f\n%.2f\n",
      Mon, Day, Yea, Tim, Zon, Lon, Lat);
  } else {

    /* However, if the -o0 switch is in effect, then write the actual */
    /* positions of the planets and houses to the file instead.       */

    for (i = 1; i <= OBJECTS; i++) {
      j = (int) planet[i];
      fprintf(data, "%c%c%c: %2d %2d %10.7f\n", OBJNAM(i),
        j%30, j/30+1, FRACT(planet[i])*60.0);                /* Position */
      k = planetalt[i];
      fprintf(data, "[%c]: %3d %12.8f\n",                    /* Altitude */
        ret[i] >= 0.0 ? 'D' : 'R', (int)(Sgn(k)*
        floor(dabs(k))), (k-(real)(int)k)*60.0);        /* Retrograde? */
    }
    for (i = 1; i <= SIGNS/2; i++) {   /* Write first six cusp positions */ 
      j = (int) house[i];
      fprintf(data, "H_%c: %2d %2d %10.7f\n",
        'a'+i-1, j%30, j/30+1, FRACT(house[i])*60.0);
    }
  }

  /* Now write any extra strings that were on the command line after the -o */
  /* specification but before the next switch, to the file as comments.     */

  for (i = 1; i < extracount; i++) {
    extralines++;
    fprintf(data, "%s\n", extralines[1]);
  }
  fclose(data);
}


/* Initialize an Ansi color array with the color to print each object in. */

void InitColors()
{
  int i;

  objectansi[0] = elemansi[1];
  for (i = 1; i <= 10; i++)
    objectansi[i] = elemansi[(ruler1[i]-1)%4];
  for (i = 11; i <= 15; i++)
    objectansi[i] = MAGENTA;
  for (i = 16; i <= 20; i++)
    objectansi[i] = DKCYAN;
  objectansi[18] = elemansi[1]; objectansi[19] = elemansi[0];
  objectansi[21] = elemansi[2]; objectansi[22] = elemansi[3];
  objectansi[23] = elemansi[1]; objectansi[24] = elemansi[2];
  for (i = U_LO; i <= U_HI; i++)
    objectansi[i] = PURPLE;
  for (i = S_LO; i <= S_HI; i++)
    objectansi[i] = starbright[i-BASE] < 1.0 ? ORANGE : MAROON;
}


/* This is the dispatch procedure for the entire program. After all the   */
/* command switches have been processed, this routine is called to        */
/* actually call the various routines to generate and display the charts. */

void Action()
{
  int i;

  AnsiColor(-1);

  /* First let's adjust the restriction status of the minor cusps, uranians, */
  /* and fixed stars based on whether -C, -u, and -U switches are in effect. */

  if (!(operation & DASHC))
    for (i = C_LO; i <= C_HI; i++)
      ignore[i] = TRUE;
  if (!(operation & DASHu))
    for (i = U_LO; i <= U_HI; i++)
      ignore[i] = TRUE;
  if (!universe)
    for (i = S_LO; i <= S_HI; i++)
      ignore[i] = TRUE;

  if (Delta != 0.0) {                   /* If -+ or -- switches in effect   */
    JD = MdyToJulian(M, D+Delta, Y);    /* then add the specified delta     */
    JulianToMdy(JD, &M, &D, &Y);        /* value to date before proceeding. */
  }

    /* Here we either do a normal chart or some kind of relationship chart. */

  if (!relation) {
    if (!autom)            /* If chart information not in memory yet, */
      InputData("tty");    /* then prompt the user for the time, etc. */
    Mon = M; Day = D; Yea = Y; Tim = F; Zon = X; Lon = L5; Lat = LA;
    CastChart(TRUE);
  } else
    CastRelation();
#ifdef GRAPH
  if (operation & DASHX)    /* If any of the X window switches in effect, */
    XAction();              /* then go make a graphics chart...           */
  else
#endif
    PrintChart(prog);       /* Otherwise print chart on text screen.      */

  if (operation & DASHo)    /* If -o switch in effect, then write */
    OutputData();           /* the chart information to a file.   */
}


/* This routine is called by the main program to actually prompt the user */
/* for the command switches and parameters, to be entered one by one in   */
/* the same format as they would be on a Unix command line. This is only  */
/* executed for those non-Unix systems which don't allow passing of the   */
/* command line to the program. The result of this routine is passed back */
/* to the main program which then processes it just like in a Unix        */
/* system. (Not very graceful, but it works and gets the job done.)       */

#ifndef SWITCHES
#define MAXSWITCHES 30
int InputSwitches(argv)
char argv[MAXSWITCHES][12];
{
  int argc = 0;

  AnsiColor(WHITE);
  printf("** %s version %s (%s) **\n", appname, VERSION, ADDRESS);
  AnsiColor(-1);
  printf("Enter all switch parameters below. (Enter %cH for help.)\n", DASH);
  printf("Press return after each switch or number parameter input.\n");
  printf("Input a '.' on a line by itself when done.\n\n");
  do {
    argc++;
    printf("Input parameter string #%2d: ", argc);
    if (gets(argv[argc]) == (char *) NULL) {
      printf("\n%s terminated.\n", appname);
      Terminate(2);
    }
  } while (argc < MAXSWITCHES && (argv[argc][0] != '.' ||
      argv[argc][1] != 0));
  putchar('\n');
  return argc;
}
#endif


/*
*******************************************************************************
** Main program
*******************************************************************************
*/

/* The main program, the starting point for Astrolog, follows. This section */
/* basically consists of looping through and processing each switch on the  */
/* command line, before actually calling a routine to do the neat stuff.    */

int main(argc, argv)
int argc;
char **argv;
{
  int pos, i;
  real k;
  char cpos, *c;
#ifdef TIME
  struct tm curtime;
  long curtimer;
#endif
#ifndef SWITCHES
  char strings[MAXSWITCHES][12];
  char *pointers[MAXSWITCHES];
#endif

  InputDefaults();
#ifndef SWITCHES
  for (i = 0; i < MAXSWITCHES; i++)
    pointers[i] = strings[i];
  argc = InputSwitches(strings);
  argv = pointers;
#endif
  InitColors();

  argc--; argv++;
  while (argc) {
    pos  = 1 + (argv[0][0] == '-' || argv[0][0] == '/');
    cpos = argv[0][pos];
    switch (argv[0][pos-1]) {
    case 'H':
      if (cpos != '0')
        DisplaySwitches();
      else
        PrintSigns();
      Terminate(0);
    case 'O':
      PrintObjects(cpos == '0');
      Terminate(0);
    case 'R':
      if (cpos == 'T') {
        c = (char PTR)ignore2;
        pos++;
        cpos = argv[0][pos];
      } else
        c = (char PTR)ignore;
      if (cpos == '0')
        for (i = 1; i <= total; i++)
          c[i] = TRUE;
      else if (cpos == 'C')
        for (i = C_LO; i <= C_HI; i++)
          c[i] = !c[i];
      else if (cpos == 'u')
        for (i = U_LO; i <= U_HI; i++)
          c[i] = !c[i];
      else if (cpos == 'U')
        for (i = S_LO; i <= S_HI; i++)
          c[i] = !c[i];
      else if (argc <= 1 || (!atoi(argv[1]))) {
        for (i = 11; i <= 15; i++)
          c[i] = !c[i];
        c[17] = !c[17]; c[20] = !c[20];
      }
      while (argc > 1 && (i = atoi(argv[1])))
        if (i < 1 || i > total)
          BadVal("R", i);
        else {
          c[i] = !c[i];
          argc--; argv++;
        }
      break;
    case 'u':
      operation ^= DASHu;
      break;
    case 'U':
      if (cpos == 'n' || cpos == 'b' || cpos == 'z' || cpos == 'l')
        universe = cpos;
      else
        universe = !universe;
      break;
    case 'C':
      operation ^= DASHC;
      break;
    case 'A':
      if (argc <= 1) {
        PrintAspects();
        Terminate(0);
      }
      if (cpos != 'o' && cpos != 'm' && cpos != 'd') {
        aspects = atoi(argv[1]);
        if (aspects < 0 || aspects > ASPECTS)
          BadVal("A", aspects);
        argc--; argv++;
      } else {
        if (argc <= 2)
          TooFew("A");
        i = atoi(argv[1]);
        if (i < 1 || i > (cpos == 'o' ? ASPECTS : OBJECTS)) {
          fprintf(stderr, "%s: Value %d passed to switch -A%c out of range.\n",
            appname, i, cpos);
          Terminate(1);
        }
        k = atof(argv[2]);
        if (k < -360.0 || k > 360.0) {
          fprintf(stderr,
            "%s: Value %.0f passed to switch -A%c out of range.\n",
            appname, k, cpos);
          Terminate(1);
        }
        if (cpos == 'm')
          planetorb[i] = k;
        else if (cpos == 'd')
          planetadd[i] = k;
        else
          aspectorb[i] = k;
        argc -= 2; argv += 2;
      }
      break;
    case 's':
      if (cpos != '0')
        operation ^= DASHs;
      else
        operation ^= DASHs0;
      break;
    case 'h':
      if (argc > 1 && (centerplanet = atoi(argv[1]))) {
        argc--; argv++;
      } else
        centerplanet = 1;
      if (centerplanet < 0 || centerplanet == 2 || (centerplanet >= THINGS &&
        centerplanet < U_LO) || centerplanet > U_HI)
        BadVal("h", centerplanet);
      c = objectname[0];
      objectname[0] = objectname[centerplanet];
      objectname[centerplanet] = c;
      if (centerplanet < 2)
        centerplanet = 1-centerplanet;
      break;
    case 'c':
      if (argc <= 1)
        TooFew("c");
      housesystem = atoi(argv[1]);
      if (housesystem < 0 || housesystem >= SYSTEMS)
        BadVal("c", housesystem);
      argc--; argv++;
      break;
    case 'x':
      if (argc <= 1)
        TooFew("x");
      multiplyfactor = atoi(argv[1]);
      if (multiplyfactor < 1 || multiplyfactor > DEGREES)
        BadVal("x", multiplyfactor);
      argc--; argv++;
      break;
    case '1':
      if (argc > 1 && (onasc = atoi(argv[1]))) {
        argc--; argv++;
      } else
        onasc = 1;
      if (onasc < 1 || onasc > TOTAL)
        BadVal("1", onasc);
      break;
    case '2':
      if (argc > 1 && (onasc = atoi(argv[1]))) {
        argc--; argv++;
      } else
        onasc = 1;
      if (onasc < 1 || onasc > TOTAL)
        BadVal("2", onasc);
      onasc = -onasc;
      break;
    case 'f':
      operation ^= DASHf;
      break;
    case '3':
      operation ^= DASH3;
      break;
    case 'G':
      operation ^= DASHG;
      break;
    case 'p':
      if (cpos == '0') {
        operation |= DASHp0;
        cpos = (argv[0][++pos]);
      }
      progress = TRUE;
#ifdef TIME
      if (cpos == 'n') {
        curtimer = (long) time((long *) 0);
        curtime = *localtime(&curtimer);
        Mon = (real) curtime.tm_mon + 1.0;
        Day = (real) curtime.tm_mday;
        Yea = (real) curtime.tm_year + 1900.0;
        Jdp = MdyToJulian(Mon, Day, Yea);
        break;
      }
#endif
      if (cpos == 'd') {
        if (argc <= 1)
          TooFew("pd");
        progday = atof(argv[1]);
        if (progday == 0.0)
          BadVal2("pd", progday);
        argc--; argv++;
        break;
      }
      if (argc <= 3)
        TooFew("p");
      Mon = atof(argv[1]);
      if (Mon < 1.0 || Mon > 12.0)
        BadVal2("p", Mon);
      Day = atof(argv[2]);
      Yea = atof(argv[3]);
      if (Day < 1.0 || Day > (real)DayInMonth((int)Mon, (int)Yea))
        BadVal2("p", Day);
      Jdp = MdyToJulian(Mon, Day, Yea);
      argc -= 3; argv += 3;
      break;
    case 'F':
      if (argc <= 3)
        TooFew("F");
      i = atoi(argv[1]);
      if (i < 1 || i > TOTAL)
        BadVal("F", i);
      force[i] = atof(argv[2])*30.0-30.0+DecToDeg(atof(argv[3]));
      if (force[i] < 0.0 || force[i] >= DEGREES)
        BadVal2("F", force[i]);
      else
        force[i] += DEGREES;
      argc -= 3; argv += 3;
      break;
    case '+':
      if (argc > 1 && (k = atof(argv[1])) != 0.0) {
        argc--; argv++;
        Delta += k;
      } else
        Delta += 1.0;
      break;
    case '-': case '\0':
      if (argc > 1 && (k = atof(argv[1])) != 0.0) {
        argc--; argv++;
        Delta -= k;
      } else
        Delta -= 1.0;
      break;
    case 'v':
      if (cpos == '0')
        exdisplay ^= DASHv0;
      todisplay ^= DASHv;
      break;
    case 'w':
      if (cpos == '0')
        exdisplay ^= DASHw0;
      if (argc > 1 && (i = atoi(argv[1]))) {
        wheelrows = i;
        argc--; argv++;
      }
      if (wheelrows < 1 || wheelrows > WHEELROWS)
        BadVal("w", wheelrows);
      todisplay ^= DASHw;
      break;
    case 'g':
      if (cpos == '0')
        exdisplay ^= DASHg0;
      else if (cpos == 'a') {
        exdisplay ^= DASHga;
        if (argv[0][pos+1] == '0')
          exdisplay ^= DASHg0;
      }
      todisplay ^= DASHg;
      break;
    case 'm':
      if (cpos == '0')
        exdisplay ^= DASHm0;
      todisplay ^= DASHm;
      break;
    case 'Z':
      if (cpos == '0')
        exdisplay ^= DASHZ0;
      todisplay ^= DASHZ;
      break;
    case 'S':
      todisplay ^= DASHS;
      break;
    case 'j':
      if (cpos == '0')
        exdisplay ^= DASHj0;
      todisplay ^= DASHj;
      break;
    case 'L':
      if (cpos == '0')
        exdisplay ^= DASHL0;
      if (argc > 1 && (i = atoi(argv[1]))) {
        graphstep = i;
        argc--; argv++;
      }
      if (graphstep < 1 || 160%graphstep > 0)
        BadVal("L", graphstep);
      todisplay ^= DASHL;
      break;
    case 'd':
      if (cpos == 'p') {
        if (argc <= 2)
          TooFew("dp");
        prog = TRUE;
        exdisplay ^= DASHdm;
        Mon2 = atof(argv[1]);
        Yea2 = atof(argv[2]);
        if (Mon2 < 0.0 || Mon2 > 12.0)
          BadVal2("dp", Mon2);
        argc -= 2; argv += 2;
      } else if (cpos == 'm')
        exdisplay ^= DASHdm;
#ifdef X11
      else if (cpos == 'i') {
        if (argc <= 1)
          TooFew("display");
        dispname = argv[1];
      }
#endif
      todisplay ^= DASHd;
      break;
    case 'E':
      if (cpos == 'y')
        exdisplay ^= DASHEy;
      todisplay ^= DASHE;
      break;
    case 'T':
#ifdef TIME
      if (cpos == 'n') {
        curtimer = (long) time((long *) 0);
        curtime = *localtime(&curtimer);
        todisplay ^= DASHT;
        if (argv[0][pos+1] == 'y')
          Mon2 = 0.0;
        else {
          Mon2 = (real) curtime.tm_mon + 1.0;
          if (Mon2 < 1.0 || Mon2 > 12.0)
            BadVal2("Tn", Mon2);
        }
        Yea2 = (real) curtime.tm_year + 1900.0;
        break;
      }
#endif
      if (i = cpos == 'p')
        prog = TRUE;
      i = (argv[0][pos+i] == 'y');
      if (argc <= 2-i)
        TooFew("T");
      todisplay ^= DASHT;
      if (i)
        Mon2 = 0.0;
      else {
        Mon2 = atof(argv[1]);
        if (Mon2 < 1.0 || Mon2 > 12.0)
          BadVal2("T", Mon2);
      }
      Yea2 = atof(argv[2-i]);
      argc -= 2-i; argv += 2-i;
      break;
    case 'e':
      todisplay ^= DASHe;
      exdisplay ^= DASHg0 | DASHj0 | DASHL0;
      break;
    case 'I':
      if (cpos == '0') {
        ChartLocation(TRUE);
        ChartGrid(TRUE);
        Terminate(0);
      }
      interpret = !interpret;
      break;
#ifdef TIME
    case 'n':
      InputData("now");
      if (cpos == 'd')
        F = 0.0;
      else if (cpos == 'm') {
        D = 1.0; F = 0.0;
      } else if (cpos == 'y') {
        M = D = 1.0; F = 0.0;
      }
      break;
#endif
    case 'l':
      if (argc <= 2)
        TooFew("l");
      deflong = atof(argv[1]);
      deflat  = atof(argv[2]);
      if (deflong < -180.0 || deflong > 180.0)
        BadVal2("l", deflong);
      if (deflat < -90 || deflat > 90)
        BadVal2("l", deflat);
      argc -= 2; argv += 2;
      break;
    case 'z':
      if (argc <= 1 || (atoi(argv[1]) == 0 && argv[1][0] != '0'))
        defzone--;
      else {
        defzone = atof(argv[1]);
        if (defzone < -24.0 || defzone > 24.0)
          BadVal2("z", defzone);
        argc--; argv++;
      }
      break;
    case 'a':
      if (argc <= 7)
        TooFew("a");
      autom = TRUE;
      M = atof(argv[1]); D = atof(argv[2]); Y = atof(argv[3]);
      F = atof(argv[4]); X = atof(argv[5]);
      L5 = atof(argv[6]); LA = atof(argv[7]);
      if (M < 1.0 || M > 12.0)
        BadVal2("a", M);
      if (D < 1.0 || D > (real)DayInMonth((int)M, (int)Y))
        BadVal2("a", D);
      if (F < -2.0 || F > 24.0)
        BadVal2("a", F);
      if (X < -24.0 || X > 24.0)
        BadVal2("a", M);
      if (L5 < -180.0 || L5 > 180.0)
        BadVal2("a", L5);
      if (LA < -90 || LA > 90)
        BadVal2("a", LA);
      argc -= 7; argv += 7;
      break;
    case 'q':
      i = (cpos == 'd') + 2*(cpos == 'm') + 3*(cpos == 'y');
      if (argc <= 4-i)
        TooFew("q");
      autom = TRUE;
      M = i > 2 ? 1.0 : atof(argv[1]);
      D = i > 1 ? 1.0 : atof(argv[2-(i>2)]); Y = atof(argv[4-i-(i<1)]);
      F = i == 0 ? atof(argv[4]) : (i > 1 ? 0.0 : 12.0);
      X = defzone; L5 = deflong; LA = deflat;
      if (M < 1.0 || M > 12.0)
        BadVal2("q", M);
      if (D < 1.0 || D > (real)DayInMonth((int)M, (int)Y))
        BadVal2("q", D);
      if (F < -2.0 || F > 24.0)
        BadVal2("q", F);
      argc -= 4-i; argv += 4-i;
      break;
    case 'i':
      if (argc <= 1)
        TooFew("i");
      InputData(argv[1]);
      argc--; argv++;
      break;
    case 'o':
      if (argc <= 1)
        TooFew("o");
      if (cpos == '0')
        operation ^= DASHo0;
      operation ^= DASHo;
      filenameout = argv[1];
      extralines = argv;
      do {
        argc--; argv++;
        extracount++;
      } while (argc > 1 && (argv[1][0] != '-' || argv[1][0] != '/'));
      break;
    case 'r':
      if (argc <= 2)
        TooFew("r");
      if (cpos == 'c')
        relation = DASHrc;
      else if (cpos == 'm')
        relation = DASHrm;
      else if (cpos == 'd')
        relation = DASHrd;
      else if (cpos == 'b')
        relation = DASHrb;
      else if (cpos == '0')
        relation = DASHr0;
      else
        relation = DASHr;
      filename = argv[1]; filename2 = argv[2];
      argc -= 2; argv += 2;
      break;
#ifdef TIME
    case 't':
      if (argc <= 1)
        TooFew("t");
      if (cpos == 'd')
        relation = DASHrd;
      else if (cpos == 'b')
        relation = DASHrb;
      else
        relation = DASHr0;
      filename = argv[1]; filename2 = "now";
      argc--; argv++;
      break;
#endif
    case 'k':
      ansi = !ansi;
      break;
#ifdef GRAPH
    case 'X':
      i = XProcess(argc, argv, pos);
      operation |= DASHX;
      argc -= i; argv += i;
      break;
#endif
    default:
      fprintf(stderr, "%s: Unknown switch '%s'\n", appname, argv[0]);
      Terminate(1);
    }
    argc--; argv++;
  }
  Action();
  Terminate(0);
}

/* driver.c */
