This is general.c in view mode; [Download] [Up]
/* ** Astrolog (Version 4.10) File: general.c ** ** IMPORTANT NOTICE: the graphics database and chart display routines ** used in this program are Copyright (C) 1991-1994 by Walter D. Pullen ** (cruiser1@stein.u.washington.edu). 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. ** ** The main 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 personal use but not to sell them or profit from ** them in any way. ** ** The PostScript code within the core graphics routines are programmed ** and Copyright (C) 1992-1993 by Brian D. Willoughby ** (brianw@sounds.wa.com). Conditions are identical to those above. ** ** The extended accurate ephemeris databases and formulas are from the ** calculation routines in the program "Placalc" and are programmed and ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl ** (alois@azur.ch). The use of that source code is subject to ** regulations made by Astrodienst Zurich, and the code is not in the ** public domain. This copyright notice must not be changed or removed ** by any user of this program. ** ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991. ** X Window graphics initially programmed 10/23-29/1991. ** PostScript graphics initially programmed 11/29-30/1992. ** Last code change made 3/19/1994. */ #include "astrolog.h" /* ****************************************************************************** ** General Functions. ****************************************************************************** */ /* Swap two real floating point values. */ void SwapReal(d1, d2) real *d1, *d2; { real temp; temp = *d1; *d1 = *d2; *d2 = temp; } /* Return the length of a string. */ int StringLen(line) char *line; { int i; for (i = 0; *line++; i++) ; return i; } /* Return whether one string is greater than another. */ int StringCmp(s1, s2) char *s1, *s2; { while (*s1 && *s1 == *s2) s1++, s2++; return *s1 - *s2; } /* Determine the sign of a number: -1 if value negative, +1 if value */ /* positive, and 0 if it's zero. */ real Sgn(d) real d; { return d == 0.0 ? 0.0 : SGN2(d); } /* Given an x and y coordinate, return the angle formed by a line from the */ /* origin to this coordinate. This is just converting from rectangular to */ /* polar coordinates; however, we don't determine the radius here. */ real Angle(x, y) real x, y; { real a; if (x != 0.0) a = ATAN(y/x); else a = Sgn(y)*PI/2.0; if (a < 0.0) a += PI; if (y < 0.0) a += PI; return a; } /* Modulus function for floating point values. The modulus value itself */ /* has been specified earlier: it is usually either 360.0 or PI/2.0. */ real Mod(d) real d; { if (d >= modulus) /* In most cases, our value is only slightly */ d -= modulus; /* out of range, so we can test for it and */ else if (d < 0.0) /* avoid the more complicated arithmetic. */ d += modulus; if (d >= 0 && d < modulus) return d; return (d - floor(d/modulus)*modulus); } /* Integer division - like the "/" operator but always rounds result down. */ long Dvd(x, y) long x, y; { long z; if (y == 0) return x; z = x / y; if (((x >= 0) == (y >= 0)) || x-z*y == 0) return z; return z - 1; } /* ****************************************************************************** ** General Astrology Functions. ****************************************************************************** */ /* A similar modulus function: convert an integer to value from 1..12. */ int Mod12(i) int i; { while (i > SIGNS) i -= SIGNS; while (i < 1) i += SIGNS; return i; } /* Convert an inputed fractional degrees/minutes value to a true decimal */ /* degree quantity. For example, the user enters the decimal value "10.30" */ /* to mean 10 degrees and 30 minutes; this will return 10.5, i.e. 10 */ /* degrees and 30 minutes expressed as a floating point degree value. */ real DecToDeg(d) real d; { return Sgn(d)*(floor(dabs(d))+FRACT(dabs(d))*100.0/60.0); } /* This is the inverse of the above function. Given a true decimal value */ /* for a zodiac degree, adjust it so the degrees are in the integer part */ /* and the minute expressed as hundredths, e.g. 10.5 degrees -> 10.30 */ real DegToDec(d) real d; { return Sgn(d)*(floor(dabs(d))+FRACT(dabs(d))*60.0/100.0); } /* Return the shortest distance between two degrees in the zodiac. This is */ /* normally their difference, but we have to check if near the Aries point. */ real MinDistance(deg1, deg2) real deg1, deg2; { real i; i = dabs(deg1-deg2); return i < DEGHALF ? i : DEGREES - i; } /* This is just like the above routine, except the min distance value */ /* returned will either be positive or negative based on whether the */ /* second value is ahead or behind the first one in a circular zodiac. */ real MinDifference(deg1, deg2) real deg1, deg2; { real i; i = deg2 - deg1; if (dabs(i) < DEGHALF) return i; return Sgn(i)*(dabs(i) - DEGREES); } /* Return the degree of the midpoint between two zodiac positions, making */ /* sure we return the true midpoint closest to the positions in question. */ real Midpoint(deg1, deg2) real deg1, deg2; { real mid; mid = (deg1+deg2)/2.0; return MinDistance(deg1, mid) < DEGQUAD ? mid : Mod(mid+DEGHALF); } /* Given a planet and sign, determine whether: The planet rules the sign, */ /* the planet has its fall in the sign, the planet exalts in the sign, or */ /* is debilitated in the sign; and return an appropriate character. */ char Dignify(body, sign) int body, sign; { if (body > U_HI) return ' '; if (ruler1[body] == sign || ruler2[body] == sign) return 'R'; if (ruler1[body] == Mod12(sign+6) || ruler2[body] == Mod12(sign+6)) return 'F'; if (exalt[body] == sign) return 'e'; if (exalt[body] == Mod12(sign+6)) return 'd'; return '-'; } /* Determine the number of days in a particular month. The year is needed, */ /* too, because we have to check for leap years in the case of February. */ int DayInMonth(month, year) int month, year; { int d; if (month == _SEP || month == _APR || month == _JUN || month == _NOV) d = 30; else if (month != _FEB) d = 31; else { d = 28; if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0 || year <= G2JYEA)) d++; } return d; } /* Return the actual number of days in a particular month. Normally, this */ /* is the same as the above routine which determines the index of the last */ /* day of the month, but the values can differ when changing between */ /* calendar systems (Julian to Gregorian) in which one can jump over days. */ int DaysInMonth(month, year) int month, year; { int d; d = DayInMonth(month, year); if (year == G2JYEA && month == G2JMON) d -= (G2JDAY2 - G2JDAY1 - 1); return d; } /* Return the day of the week (Sunday is 0) of the specified given date. */ int DayOfWeek(month, day, year) int month, day, year; { int d; d = (int)((MdyToJulian(month, day, year) + 1) % 7); return d < 0 ? d+7 : d; } /* Given a day, and the month and year it falls in, add a number of days */ /* to it and return the new day index. As month changes are not checked for */ /* here, this is mostly just adding the offset to the day; however we need */ /* to check for calendar changes for when days in a month may be skipped. */ /* We do not check for month crossings. only for G2J. */ int AddDay(month, day, year, delta) int month, day, year, delta; { int d; d = day + delta; if (year == G2JYEA && month == G2JMON) { if (d > G2JDAY1 && d < G2JDAY2) d += SGN(delta)*(G2JDAY2-G2JDAY1-1); } return d; } /* Given an aspect and two objects making that aspect with each other, */ /* return the maximum orb allowed for such an aspect. Normally this only */ /* depends on the aspect itself, but some objects require narrow orbs, */ /* and some allow wider orbs, so check for these cases. */ real Orb(body1, body2, aspect) int body1, body2, aspect; { real orb, i; orb = aspectorb[aspect]; i = body1 > BASE ? 2.0 : planetorb[body1]; orb = MIN(orb, i); i = body2 > BASE ? 2.0 : planetorb[body2]; orb = MIN(orb, i); if (body1 <= BASE) orb += planetadd[body1]; if (body2 <= BASE) orb += planetadd[body2]; return orb; } /* ****************************************************************************** ** File IO Routines. ****************************************************************************** */ /* Exit the program, and do any cleanup necessary. Note that if we had */ /* a non-fatal error, and we are in the -Q loop mode, then we won't */ /* actually terminate the program, but drop back to the command line loop. */ void Terminate(value) int value; { if (value == _FORCE) { AnsiColor(WHITE); fprintf(stdout, "\n%s terminated.\n", appname); } if (value == _ERROR && (operation & DASHQ) > 0) return; if (ansi) fprintf(S, "%c[0m", ESCAPE); /* Get out of any Ansi color mode. */ exit(abs(value)); } /* Print a warning message given a string. This is called in non-fatal */ /* cases where we return to normal execution after printing the string. */ void PrintWarning(string) char *string; { AnsiColor(RED); fprintf(stderr, "%s\n", string); AnsiColor(DEFAULT); } /* Print an error message. This is called in more serious cases which halt */ /* running of the current chart sequence, which can terminate the program */ /* but isn't a fatal error in that we can still fall back to the -Q loop. */ void PrintError(string) char *string; { AnsiColor(RED); fprintf(stderr, "%s: %s\n", appname, string); Terminate(_ERROR); AnsiColor(DEFAULT); } /* Simplification for a commonly printed error message. */ void TooFew(option) char *option; { char string[STRING]; sprintf(string, "Too few options to switch -%s", option); PrintError(string); } /* Another simplification for a commonly printed error message. */ void BadVal(option, value) char *option; int value; { char string[STRING]; sprintf(string, "Value %d passed to switch -%s out of range.\n", value, option); PrintError(string); } /* A simple procedure used throughout Astrolog: Print a particular */ /* character on the screen 'n' times. */ void PrintTab(chr, count) char chr; int count; { int i; for (i = 0; i < count; i++) printc(chr); } /* Set an Ansi text color. */ void AnsiColor(col) int col; { /* Special case: If we are passed the value REVERSE, and ansi is not */ /* only on but set to a value > 1, then we'll enter reverse video mode. */ if (!ansi || (col == REVERSE && ansi < 2)) return; fprintf(S, "%c[", ESCAPE); if (col == DEFAULT) printc('0'); else if (col == REVERSE) { printc('7'); } else fprintf(S, "%c;%d", col > 7 ? '1' : '0', 30 + (col & 7)); printc('m'); } /* Print a zodiac position on the screen. This basically just prints the */ /* string returned from CharZodiac() below, except we take care of color. */ void PrintZodiac(deg) real deg; { AnsiColor(elemansi[(int) (deg / 30.0) & 3]); fprintf(S, "%s", CharZodiac(deg)); AnsiColor(DEFAULT); } /* Given a zodiac position, return a string containing it as it's */ /* formatted for display to the user. */ char *CharZodiac(deg) real deg; { static char zod[11]; int sign, d, m; real s; if (!(operation & DASHs0)) { /* Normally, we format the position in degrees/sign/minutes format: */ deg = Mod(deg + (seconds < 0 ? 1.0/60.0/60.0/2.0 : 1.0/60.0/2.0)); sign = (int) (deg / 30.0); d = (int) deg - sign*30; m = (int) (FRACT(deg)*60.0); sprintf(zod, "%2d%c%c%c%02d", d, SIGNAM(sign + 1), m); if (seconds < 0) { s = FRACT(deg)*60.0; s = FRACT(s)*60.0; sprintf(zod, "%s'%02d\"", zod, (int)s); } } else { /* However, if -s0 switch in effect, get position in hours/minutes: */ deg = Mod(deg + (seconds < 0 ? 1.0/4.0/60.0/2.0 : 1.0/4.0/2.0)); d = (int) (deg / 15.0); m = (int) ((deg - (real)d*15.0)*60.0/24.0); sprintf(zod, "%2dh,%02dm", d, m); if (seconds < 0) { s = FRACT(deg)*4.0; s = FRACT(s)*60.0; sprintf(zod, "%s,%02ds", zod, (int)s); } } return zod; } /* This is similar to formatting a zodiac degree, but here we return a */ /* string of a (signed) declination value in degrees and minutes. */ char *CharAltitude(deg) real deg; { static char alt[8]; int d, m, s; while (deg > DEGQUAD) /* Ensure declination is from -90..+90 degrees. */ deg -= DEGHALF; while (deg < -DEGQUAD) deg += DEGHALF; s = deg < 0.0; deg = dabs(deg) + 1.0/60.0/2.0; d = (int) deg; m = (int) (FRACT(deg)*60.0); sprintf(alt, "%c%2d%c%02d'", s ? '-' : '+', d, DEGR1, m); return alt; } /* Another string formatter, here we return a date string given a month, */ /* day, and year. We format with the day or month first based on whether */ /* the "European" date variable is set or not. The routine also takes a */ /* parameter to indicate how much the string should be abbreviated, if any. */ char *CharDate(mon, day, yea, full) int mon, day, yea, full; { static char dat[20]; if (full > FALSE) { if (eurodate) { if (full > TRUE) sprintf(dat, "%2d %c%c%c%5d", day, MONNAM(mon), yea); else sprintf(dat, "%d %s %d", day, monthname[mon], yea); } else { if (full > TRUE) sprintf(dat, "%c%c%c %2d%5d", MONNAM(mon), day, yea); else sprintf(dat, "%s %d, %d", monthname[mon], day, yea); } } else { if (eurodate) { if (full) sprintf(dat, "%2d-%2d-%2d", day, mon, yea%100); else sprintf(dat, "%2d-%2d-%4d", day, mon, yea); } else { if (full) sprintf(dat, "%2d/%2d/%2d", mon, day, yea%100); else sprintf(dat, "%2d/%2d/%4d", mon, day, yea); } } return dat; } /* Return a string containing the given time expressed as an hour and */ /* minute quantity. This is formatted in 24 hour or am/pm time based */ /* on whether the "European" time format flag is set or not. */ char *CharTime(hr, min) int hr, min; { static char tim[8]; if (eurotime) sprintf(tim, "%2d:%02d", hr, min); else sprintf(tim, "%2d:%02d%cm", Mod12(hr), min, hr < 12 ? 'a' : 'p'); return tim; } /* Return a string containing the given time zone, given as a real value */ /* having the hours before GMT in the integer part and minutes fractionally. */ char *CharZone(zon) real zon; { static char tim[7]; sprintf(tim, "%c%d:%02d", Zon > 0.0 ? '-' : '+', (int)dabs(Zon), (int)(FRACT(dabs(Zon))*100.0+ROUND/60.0)); return tim; } /* Nicely format the given longitude and latitude locations and return */ /* them in a string. Various parts of the program display a chart header, */ /* and this allows the similar computations to be coded only once. */ char *CharLocation(lon, lat, norm) real lon, lat, norm; { static char loc[14]; int i, j; i = (int) (FRACT(dabs(lon))*norm+ROUND); j = (int) (FRACT(dabs(lat))*norm+ROUND); sprintf(loc, "%3.0f%c%02d%c%3.0f%c%02d%c", floor(dabs(lon)), DEGR1, i, lon < 0.0 ? 'E' : 'W', floor(dabs(lat)), DEGR1, j, lat < 0.0 ? 'S' : 'N'); return loc; } #ifdef TIME /* Compute the date and time it is right now as the program is running */ /* using the computer's internal clock. We do this by getting the number */ /* of seconds which have passed since January 1, 1970 and going from there. */ /* The time return value filled is expressed in the given zone parameter. */ void GetTimeNow(Mon, Day, Yea, Tim, Zon) int *Mon, *Day, *Yea; real *Tim, Zon; { dword curtimer; int min, sec; real hr; time(&curtimer); sec = (int) (curtimer % 60); curtimer /= 60; min = (int) (curtimer % 60); curtimer /= 60; hr = (real) (curtimer % 24) - Zon; curtimer /= 24; while (hr < 0.0) { curtimer--; hr += 24.0; } while (hr >= 24.0) { curtimer++; hr -= 24.0; } #ifdef PC curtimer += 2415020L; /* Number of days between 1/1/1970 and 1/1/4713 BC. */ #else curtimer += 2440588L; /* Number of days in 70 years different than above. */ #endif JulianToMdy((real)curtimer, Mon, Day, Yea); *Tim = hr + (real) min / 100.0 + (real) sec / 6000.0; } #endif /* Stop and wait for the user to enter a line of text given a prompt to */ /* display and a string buffer to fill with it. */ void InputString(prompt, string) char *prompt, *string; { FILE *data; data = S; S = stdout; fprintf(S, "%s", prompt); AnsiColor(YELLOW); fprintf(S, " > "); AnsiColor(DEFAULT); if (gets(string) == NULL) /* Pressing control-D will terminate the */ Terminate(_FORCE); /* program (at least on some machines.) */ S = data; } /* Prompt the user for a floating point value, and make sure it conforms */ /* to the specified bounds before returning it. If a non-numeric value is */ /* entered, then assume it's the name of a month, and try to convert it */ /* to the appropriate number from 1 to 12; and also check for an "AM" or */ /* "PM" suffix to hour values, and adjust the number appropriately. */ real Input(prompt, low, high) char *prompt; real low, high; { char line[STRING], c; real x; int i, j; loop { InputString(prompt, line); i = StringLen(line); for (j = 0; j < i; j++) if (line[j] == ':') /* Convert all colons in the */ line[j] = '.'; /* entered line to periods. */ c = CAP(line[0]); /* If they entered a string, then check to see if it's a month name. */ if (c >= 'A' && c <= 'Z') { switch (c) { case 'J': x = CAP(line[1]) == 'U' ? /* January, */ (CAP(line[2]) == 'L' ? 7.0 : 6.0) : 1.0; break; /* June,July */ case 'F': x = 2.0; break; /* February */ case 'M': x = CAP(line[2]) == 'Y' ? 5.0 : 3.0; break; /* March,May */ case 'A': x = CAP(line[1]) == 'U' ? 8.0 : 4.0; break; /* April,August */ case 'S': x = 9.0; break; /* September */ case 'O': x = 10.0; break; /* October */ case 'N': x = 11.0; break; /* November */ case 'D': x = 12.0; break; /* December */ default: x = 0.0; } } else { sscanf(line, "%lf", &x); /* Convert entered line to number. */ i = StringLen(line)-1; if (i > 0 && CAP(line[i]) == 'M') i--; if (i > 0) { c = CAP(line[i]); if (c == 'A') /* Adjust value appropriately */ x = x >= 12.0 ? x-12.0 : x; /* if 'AM' or 'PM' suffix. */ else if (c == 'P') x = x >= 12.0 ? x : x+12.0; } } if (x >= low && x <= high) return x; sprintf(line, "Value out of range of from %.0f to %.0f.", low, high); PrintWarning(line); } } /* Given a string representing the complete pathname to a file, strip off */ /* all the path information leaving just the filename itself. This is called */ /* by the main program to determine the name of the Astrolog executable. */ char *ProcessProgname(name) char *name; { char *b, *c, *e; b = c = name; while (*c) { #ifdef PC *c = UNCAP(*c); /* Because DOS filenames are case insensitive. */ #endif c++; } e = c; while (c > b && *c != '.') c--; if (c > b) *c = 0; else c = e; while (c > b && *c != DIR_SEP) c--; if (c > b) name = c+1; return name; } /* This important procedure gets all the parameters defining the chart that */ /* will be worked with later. Given a "filename", it gets from it all the */ /* pertinent chart information. This is more than just reading from a file - */ /* the procedure also takes care of the cases of prompting the user for the */ /* information and using the time functions to determine the date now - the */ /* program considers these cases "virtual" files. Furthermore, when reading */ /* from a real file, we have to check if it was written in the -o0 format. */ bool InputData(filename) char *filename; { FILE *data; char name[STRING], c; int i; real k, l, m; /* If we are to read from the virtual file "set" then that means use a */ /* particular set of chart information generated earlier in the program. */ if (StringCmp(filename, "set") == 0) { autom = 1; SetCore(MonX, DayX, YeaX, TimX, ZonX, LonX, LatX); return TRUE; } #ifdef TIME /* If we are to read from the file "now" then that means use the time */ /* functions to calculate the present date and time. */ if (StringCmp(filename, "now") == 0) { autom = 1; ZZ = defzone; OO = deflong; AA = deflat; GetTimeNow(&MM, &DD, &YY, &TT, ZZ); return TRUE; } #endif /* If we are to read from the file "tty" then that means prompt the user */ /* for all the chart information. */ if (StringCmp(filename, "tty") == 0) { if (!noswitches) { /* Temporarily disable and internal redirection of output to a file */ /* because we always want user headers and prompts to be displayed. */ data = S; S = stdout; AnsiColor(WHITE); fprintf(S, "** %s version %s ", appname, VERSION); fprintf(S, "(See '%cHc' switch for copyrights and credits.) **\n", DASH); AnsiColor(DEFAULT); fprintf(S, " Invoke as '%s %cH' for list of command line options.\n", ProcessProgname(progname), DASH); S = data; } MM = (int)Input("Enter month of birth (e.g. '3', 'Mar')", 1.0, 12.0); DD = (int)Input("Enter day of birth (e.g. '1', '31') ", 1.0, (real) DayInMonth(MM, 0)); YY = (int)Input("Enter year of birth (e.g. '1994') ", -5000.0, 5000.0); if (YY >= 0 && YY <= 99) { sprintf(name, "Assuming first century A.D. is really meant instead of %d.", 1900 + YY); PrintWarning(name); } fprintf(stdout, "Subtract one hour if Daylight Saving time in effect.\n"); TT = Input("Enter time of birth (e.g. '18:30' '6:30pm')", -2.0, 24.0); fprintf(stdout, "Negative values indicate time zones east of Greenwich.\n"); ZZ = Input("Time zone in hours before GMT (5=Eastern, 8=Pacific)", -24.0, 24.0); fprintf(stdout, "Negative values indicate eastern and southern locations.\n"); OO = Input("Longitude west of place (i.e. DEG:MIN)", -DEGHALF, DEGHALF); AA = Input("Latitude north of place (i.e. DEG:MIN)", -DEGQUAD, DEGQUAD); printl(); return TRUE; } /* Now that the special cases are taken care of, we can assume we are */ /* to read from a real file. */ autom = 1; data = OpenFile(filename, 1); /* Read the chart parameters from a normal file. */ if ((c = getc(data)) != 'S') { ungetc(c, data); fscanf(data, "%d%d%d", &MM, &DD, &YY); fscanf(data, "%lf%lf%lf%lf", &TT, &ZZ, &OO, &AA); /* Read the actual chart positions from a file produced with the -o0. */ } else { /* Hack: A negative month value means the chart parameters are invalid, */ /* hence -o0 is in effect and we can assume the chart positions are */ /* already in memory so we don't have to calculate them later. */ MM = -1; for (i = 1; i <= BASE; i++) { fscanf(data, "%s%lf%lf%lf", name, &k, &l, &m); planet[i] = (l-1.0)*30.0+k+m/60.0; fscanf(data, "%s%lf%lf", name, &k, &l); planetalt[i] = k+l/60.0; ret[i] = DTOR(name[1] == 'D' ? 1.0 : -1.0); /* -o0 files from version 3.05 and before don't have the uranians in */ /* them. Be prepared to skip over them in old files for compatibility. */ if (i == OBJECTS) { while (getc(data) >= ' ') ; if ((c = getc(data)) != 'H') i = C_HI; else i = total; } } for (i = 1; i <= SIGNS/2; i++) { fscanf(data, "%s%lf%lf%lf", name, &k, &l, &m); house[i+6] = Mod((house[i] = Mod((l-1.0)*30.0+k+m/60.0))+DEGHALF); } } fclose(data); return TRUE; } /* Open the file indicated by the given string and return the file's stream */ /* pointer, or NULL if the file couldn't be found or opened. All parts of */ /* the program which open files to read call this routine. We look in */ /* several various locations and directories for the file before giving up. */ FILE *OpenFile(filename, filemode) char *filename; int filemode; { FILE *data; char name[STRING], mode[3]; #ifdef ENVIRON char *env; #endif /* Some file types we want to open as binary instead of Ascii. */ sprintf(mode, "r%s", filemode == 2 ? "b" : ""); /* First look for the file in the current directory. */ data = fopen(filename, mode); if (data != NULL) return data; #ifdef ENVIRON /* Next look for the file in the directory indicated by the version */ /* specific system environment variable. */ sprintf(name, "%s%s", ENVIRONVER, VERSION); env = getenv(name); if (env && *env) { sprintf(name, "%s%c%s", env, DIR_SEP, filename); data = fopen(name, mode); if (data != NULL) return data; } /* Next look in the directory in the general environment variable. */ env = getenv(ENVIRONALL); if (env && *env) { sprintf(name, "%s%c%s", env, DIR_SEP, filename); data = fopen(name, mode); if (data != NULL) return data; } /* Next look in the directory in the version prefix environment variable. */ env = getenv(ENVIRONVER); if (env && *env) { sprintf(name, "%s%c%s", env, DIR_SEP, filename); data = fopen(name, mode); if (data != NULL) return data; } #endif /* Finally look in one of several directories specified at compile time. */ sprintf(name, "%s%c%s", filemode == 0 ? DEFAULT_DIR : (filemode == 1 ? CHART_DIR : EPHE_DIR), DIR_SEP, filename); data = fopen(name, mode); if (data == NULL && filemode == 1) { /* If the file was never found, print an error (unless we were looking */ /* for a certain file type, e.g. the optional astrolog.dat file). */ sprintf(name, "File '%s' not found.", filename); PrintError(name); } return data; } /* general.c */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.