ftp.nice.ch/pub/next/unix/calendars/remind.3.0.13.N.bs.tar.gz#/remind-3.0.13/funcs.c

This is funcs.c in view mode; [Download] [Up]

/***************************************************************/
/*                                                             */
/*  FUNCS.C                                                    */
/*                                                             */
/*  This file contains the built-in functions used in          */
/*  expressions.                                               */
/*                                                             */
/*  This file is part of REMIND.                               */
/*  Copyright (C) 1992, 1993, 1994 by David F. Skoll.          */
/*                                                             */
/***************************************************************/
#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <string.h>
#include <ctype.h>
#include <math.h>
#ifdef UNIX
#ifdef HAVE_UNISTD
#include <unistd.h>
#else
#include <sys/file.h>
#endif
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#if defined(__MSDOS__) || defined(__OS2__)
#include <io.h>
#define R_OK 4
#define W_OK 2
#define X_OK 1
#endif
#ifndef R_OK
#define R_OK 4
#define W_OK 2
#define X_OK 1
#endif
#include "types.h"
#include "globals.h"
#include "protos.h"
#include "err.h"
#include "expr.h"
#include "version.h"

/* Function prototypes */
PRIVATE	int	FAbs		ARGS ((void));
PRIVATE	int	FAccess		ARGS ((void));
PRIVATE int     FArgs		ARGS ((void));
PRIVATE	int	FAsc		ARGS ((void));
PRIVATE	int	FBaseyr		ARGS ((void));
PRIVATE	int	FChar		ARGS ((void));
PRIVATE	int	FChoose		ARGS ((void));
PRIVATE	int	FCoerce		ARGS ((void));
PRIVATE	int	FDate		ARGS ((void));
PRIVATE	int	FDay		ARGS ((void));
PRIVATE	int	FDaysinmon	ARGS ((void));
PRIVATE	int	FDefined	ARGS ((void));
PRIVATE	int	FDosubst	ARGS ((void));
PRIVATE	int	FEasterdate	ARGS ((void));
PRIVATE int	FFiledate	ARGS ((void));
PRIVATE	int	FFiledir	ARGS ((void));
PRIVATE	int	FFilename	ARGS ((void));
PRIVATE	int	FGetenv		ARGS ((void));
PRIVATE int     FHebdate	ARGS ((void));
PRIVATE int     FHebday		ARGS ((void));
PRIVATE int     FHebmon		ARGS ((void));
PRIVATE int     FHebyear	ARGS ((void));
PRIVATE	int	FHour		ARGS ((void));
PRIVATE	int	FIif		ARGS ((void));
PRIVATE	int	FIndex		ARGS ((void));
PRIVATE	int	FIsdst		ARGS ((void));
PRIVATE	int	FIsomitted	ARGS ((void));
PRIVATE	int	FLanguage	ARGS ((void));
PRIVATE	int	FMax		ARGS ((void));
PRIVATE	int	FMin		ARGS ((void));
PRIVATE	int	FMinute		ARGS ((void));
PRIVATE	int	FMinsfromutc	ARGS ((void));
PRIVATE	int	FMoondate	ARGS ((void));
PRIVATE	int	FMoonphase	ARGS ((void));
PRIVATE	int	FMoontime	ARGS ((void));
PRIVATE	int	FMon		ARGS ((void));
PRIVATE	int	FMonnum		ARGS ((void));
PRIVATE	int	FOrd		ARGS ((void));
PRIVATE	int	FOstype 	ARGS ((void));
PRIVATE	int	FPlural		ARGS ((void));
PRIVATE	int	FSgn		ARGS ((void));
PRIVATE int	FPsmoon		ARGS ((void));
PRIVATE int	FPsshade	ARGS ((void));
PRIVATE	int	FShell		ARGS ((void));
PRIVATE	int	FStrlen		ARGS ((void));
PRIVATE	int	FSubstr		ARGS ((void));
PRIVATE	int	FSunrise	ARGS ((void));
PRIVATE	int	FSunset		ARGS ((void));
PRIVATE	int	FTime		ARGS ((void));
PRIVATE	int	FTrigdate	ARGS ((void));
PRIVATE	int	FTrigtime	ARGS ((void));
PRIVATE	int	FTrigvalid	ARGS ((void));
PRIVATE	int	FTypeof		ARGS ((void));
PRIVATE	int	FUpper		ARGS ((void));
PRIVATE	int	FValue		ARGS ((void));
PRIVATE	int	FVersion	ARGS ((void));
PRIVATE	int	FWkday		ARGS ((void));
PRIVATE	int	FWkdaynum	ARGS ((void));
PRIVATE	int	FYear		ARGS ((void));
PRIVATE int	FIsleap         ARGS ((void));
PRIVATE int	FLower          ARGS ((void));
PRIVATE int	FNow            ARGS ((void));
PRIVATE int	FRealnow            ARGS ((void));
PRIVATE int	FRealtoday      ARGS ((void));
PRIVATE int	FToday          ARGS ((void));
PRIVATE int	FTrigger        ARGS ((void));
PRIVATE int	CheckArgs       ARGS ((Operator *f, int nargs));
PRIVATE int	CleanUpAfterFunc ARGS ((void));
PRIVATE int	SunStuff	ARGS ((int rise, double cosz, int jul));

#if defined(__MSDOS__) || defined(__BORLANDC__)
PRIVATE FILE *os_popen  ARGS((char *cmd, char *mode));
PRIVATE int   os_pclose ARGS((FILE *fp));
#define POPEN os_popen
#define PCLOSE os_pclose

#if defined(_MSC_VER)
#define popen _popen
#define pclose _pclose
#endif

#elif defined(_MSC_VER)
#define POPEN _popen
#define PCLOSE _pclose

#else
#define POPEN popen
#define PCLOSE pclose
#endif

/* "Overload" the struct Operator definition */
#define NO_MAX 127
#define MINARGS prec
#define MAXARGS type

/* Sigh - we use a global var. to hold the number of args supplied to
   function being called */
static int Nargs;

/* Use a global var. to hold function return value */
static Value RetVal;

/* Temp string buffer */
static char Buffer[32];

/* Caches for extracting months, days, years from dates - may
   improve performance slightly. */
static int CacheJul = -1;
static int CacheYear, CacheMon, CacheDay;

static int CacheHebJul = -1;
static int CacheHebYear, CacheHebMon, CacheHebDay;

/* We need access to the value stack */
extern Value ValStack[];
extern int ValStackPtr;

/* Macro for accessing arguments from the value stack - args are numbered
   from 0 to (Nargs - 1) */
#define ARG(x) (ValStack[ValStackPtr - Nargs + (x)])

/* Macro for copying a value while destroying original copy */
#define DCOPYVAL(x, y) ( (x) = (y), (y).type = ERR_TYPE )

/* Convenience macros */
#define UPPER(c) (islower(c) ? toupper(c) : c)
#define LOWER(c) (isupper(c) ? tolower(c) : c)

/* The array holding the built-in functions. */
Operator Func[] = {
/*	Name		minargs maxargs	func   */

    {   "abs",		1,	1,	FAbs	},
    {   "access",       2,      2,      FAccess },
    {   "args",         1,      1,      FArgs   },
    {   "asc",		1,	1,	FAsc	},
    {   "baseyr",	0,	0,	FBaseyr	},
    {   "char",		1,	NO_MAX,	FChar	},
    {   "choose",	2,	NO_MAX, FChoose },
    {   "coerce",	2,	2,	FCoerce },
    {   "date",		3,	3,	FDate	},
    {   "day",		1,	1,	FDay	},
    {   "daysinmon",	2,	2,	FDaysinmon },
    {   "defined",	1,	1,	FDefined },
    {   "dosubst",	1,	3,	FDosubst },
    {   "easterdate",	1,	1,	FEasterdate },
    {	"filedate",	1,	1,	FFiledate },
    {	"filedir",	0,	0,	FFiledir },
    {   "filename",	0,	0,	FFilename },
    {   "getenv",	1,	1,	FGetenv },
    {   "hebdate",	2,	5,	FHebdate },
    {   "hebday",	1,	1,	FHebday },
    {   "hebmon",	1,	1,	FHebmon },
    {   "hebyear",	1,	1,	FHebyear },
    {   "hour",		1,	1,	FHour	},
    {   "iif",		1,	NO_MAX,	FIif	},
    {   "index",	2,	3,	FIndex 	},
    {   "isdst",	0,	2,	FIsdst },
    {   "isleap",	1,	1,	FIsleap },
    {   "isomitted",	1,	1,	FIsomitted },
    {   "language",     0,      0,      FLanguage },
    {   "lower",	1,	1,	FLower	},
    {   "max",		1,	NO_MAX,	FMax	},
    {   "min",		1,	NO_MAX, FMin	},
    {   "minsfromutc",	0,	2,	FMinsfromutc },
    {   "minute",	1,	1,	FMinute },
    {   "mon",		1,	1,	FMon	},
    {   "monnum",	1,	1,	FMonnum },
    {	"moondate",	1,	3,	FMoondate },
    {	"moonphase",	0,	2,	FMoonphase },
    {	"moontime",	1,	3,	FMoontime },
    {   "now",		0,	0,	FNow	},
    {   "ord",		1,	1,	FOrd	},
    {   "ostype",       0,      0,      FOstype },
    {   "plural",	1,	3,	FPlural },
    {	"psmoon",	1,	4,	FPsmoon},
    {	"psshade",	1,	1,	FPsshade},
    {   "realnow",      0,      0,      FRealnow},
    {   "realtoday",    0,      0,      FRealtoday },
    {   "sgn",		1,	1,	FSgn	},
    {   "shell",	1,	1,	FShell	},
    {   "strlen",	1,	1,	FStrlen	},
    {   "substr",	2,	3,	FSubstr	},
    {   "sunrise",	0,	1,	FSunrise},
    {   "sunset",	0,	1,	FSunset },
    {   "time",		2,	2,	FTime	},
    {   "today",	0,	0,	FToday	},
    {   "trigdate",	0,	0,	FTrigdate },
    {   "trigger",	1,	3,	FTrigger },
    {   "trigtime",	0,	0,	FTrigtime },
    {   "trigvalid",	0,	0,	FTrigvalid },
    {   "typeof",       1,      1,      FTypeof },
    {   "upper",	1,	1,	FUpper	},
    {   "value",	1,	2,	FValue	},
    {   "version",      0,      0,      FVersion },
    {   "wkday",	1,	1,	FWkday	},
    {   "wkdaynum",	1,	1,	FWkdaynum },
    {   "year",		1,	1,	FYear	}
};

/* Need a variable here - Func[] array not really visible to outside. */
int NumFuncs = sizeof(Func) / sizeof(Operator) ;

/***************************************************************/
/*                                                             */
/*  CallFunc                                                   */
/*                                                             */
/*  Call a function given a pointer to it, and the number      */
/*  of arguments supplied.                                     */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int CallFunc(Operator *f, int nargs)
#else
int CallFunc(f, nargs)
Operator *f;
int nargs;
#endif
{
   register int r = CheckArgs(f, nargs);
   int i;

   Nargs = nargs;

   RetVal.type = ERR_TYPE;
   if (DebugFlag & DB_PRTEXPR) {
      fprintf(ErrFp, "%s(", f->name);
      for (i=0; i<nargs; i++) {
	 PrintValue(&ARG(i), ErrFp);
	 if (i<nargs-1) fprintf(ErrFp, ", ");
      }
      fprintf(ErrFp, ") => ");
      if (r) {
	 fprintf(ErrFp, "%s\n", ErrMsg[r]);
	 return r;
      }
   }
   if (r) {
      Eprint("%s(): %s", f->name, ErrMsg[r]);
      return r;
   }

   r = (*(f->func))();
   if (r) {
      DestroyValue(RetVal);
      if (DebugFlag & DB_PRTEXPR)
	 fprintf(ErrFp, "%s\n", ErrMsg[r]);
      else
	 Eprint("%s(): %s", f->name, ErrMsg[r]);
      return r;
   }
   if (DebugFlag & DB_PRTEXPR) {
      PrintValue(&RetVal, ErrFp);
      fprintf(ErrFp, "\n");
   }
   r = CleanUpAfterFunc();
   return r;
}

/***************************************************************/
/*                                                             */
/*  CheckArgs                                                  */
/*                                                             */
/*  Check that the right number of args have been supplied     */
/*  for a function.                                            */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int CheckArgs(Operator *f, int nargs)
#else
static int CheckArgs(f, nargs)
Operator *f;
int nargs;
#endif
{
   if (nargs < f->MINARGS) return E_2FEW_ARGS;
   if (nargs > f->MAXARGS && f->MAXARGS != NO_MAX) return E_2MANY_ARGS;
   return OK;
}
/***************************************************************/
/*                                                             */
/*  CleanUpAfterFunc                                           */
/*                                                             */
/*  Clean up the stack after a function call - remove          */
/*  args and push the new value.                               */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int CleanUpAfterFunc(void)
#else
static int CleanUpAfterFunc()
#endif
{
   Value v;
   int i;

   for (i=0; i<Nargs; i++) {
      PopValStack(v);
      DestroyValue(v);
   }
   PushValStack(RetVal);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  RetStrVal                                                  */
/*                                                             */
/*  Return a string value from a function.                     */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int RetStrVal(const char *s)
#else
static int RetStrVal(s)
char *s;
#endif
{
   RetVal.type = STR_TYPE;
   if (!s) {
      RetVal.v.str = (char *) malloc(1);
      if (RetVal.v.str) *RetVal.v.str = 0;
   } else
      RetVal.v.str = StrDup(s);

   if (!RetVal.v.str) {
      RetVal.type = ERR_TYPE;
      return E_NO_MEM;
   }
   return OK;
}


/***************************************************************/
/*                                                             */
/*  FStrlen - string length                                    */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FStrlen(void)
#else
static int FStrlen()
#endif
{
   Value *v = &ARG(0);
   if (v->type != STR_TYPE) return E_BAD_TYPE;
   RetVal.type = INT_TYPE;
   RetVal.v.val = strlen(v->v.str);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FBaseyr - system base year                                 */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FBaseyr(void)
#else
static int FBaseyr()
#endif
{
   RetVal.type = INT_TYPE;
   RetVal.v.val = BASE;
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FDate - make a date from year, month, day.                 */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FDate(void)
#else
static int FDate()
#endif
{
   int y, m, d;
   if (ARG(0).type != INT_TYPE ||
       ARG(1).type != INT_TYPE ||
       ARG(2).type != INT_TYPE) return E_BAD_TYPE;
   y = ARG(0).v.val;
   m = ARG(1).v.val - 1;
   d = ARG(2).v.val;

   if (!DateOK(y, m, d)) return E_BAD_DATE;

   RetVal.type = DATE_TYPE;
   RetVal.v.val = Julian(y, m, d);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FCoerce - type coercion function.                          */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FCoerce(void)
#else
static int FCoerce()
#endif
{
   char *s;

   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
   s = ARG(0).v.str;

   /* Copy the value of ARG(1) into RetVal, and make ARG(1) invalid so
      it won't be destroyed */
   DCOPYVAL(RetVal, ARG(1));

   if (! StrCmpi(s, "int")) return DoCoerce(INT_TYPE, &RetVal);
   else if (! StrCmpi(s, "date")) return DoCoerce(DATE_TYPE, &RetVal);
   else if (! StrCmpi(s, "time")) return DoCoerce(TIM_TYPE, &RetVal);
   else if (! StrCmpi(s, "string")) return DoCoerce(STR_TYPE, &RetVal);
   else return E_CANT_COERCE;
}

/***************************************************************/
/*                                                             */
/*  FMax - select maximum from a list of args.                 */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FMax(void)
#else
static int FMax()
#endif
{
   Value *maxptr;
   int i;
   char type;

   maxptr = &ARG(0);
   type = maxptr->type;

   for (i=1; i<Nargs; i++) {
      if (ARG(i).type != type) return E_BAD_TYPE;
      if (type != STR_TYPE) {
	 if (ARG(i).v.val > maxptr->v.val) maxptr = &ARG(i);
      } else {
	 if (strcmp(ARG(i).v.str, maxptr->v.str) > 0) maxptr = &ARG(i);
      }
   }
   DCOPYVAL(RetVal, *maxptr);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FMin - select minimum from a list of args.                 */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FMin(void)
#else
static int FMin()
#endif
{
   Value *minptr;
   int i;
   char type;

   minptr = &ARG(0);
   type = minptr->type;

   for (i=1; i<Nargs; i++) {
      if (ARG(i).type != type) return E_BAD_TYPE;
      if (type != STR_TYPE) {
	 if (ARG(i).v.val < minptr->v.val) minptr = &ARG(i);
      } else {
	 if (strcmp(ARG(i).v.str, minptr->v.str) < 0) minptr = &ARG(i);
      }
   }
   DCOPYVAL(RetVal, *minptr);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FAsc - ASCII value of first char of string                 */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FAsc(void)
#else
static int FAsc()
#endif
{
   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
   RetVal.type = INT_TYPE;
   RetVal.v.val = *(ARG(0).v.str);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FChar - build a string from ASCII values                   */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FChar(void)
#else
static int FChar()
#endif
{

   int i, len;

/* Special case of one arg - if given ascii value 0, create empty string */
   if (Nargs == 1) {
      if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
      if (ARG(0).v.val < -128) return E_2LOW;
      if (ARG(0).v.val > 255) return E_2HIGH;
      len = ARG(0).v.val ? 2 : 1;
      RetVal.v.str = (char *) malloc(len);
      if (!RetVal.v.str) return E_NO_MEM;
      RetVal.type = STR_TYPE;
      *(RetVal.v.str) = ARG(0).v.val;
      if (len>1) *(RetVal.v.str + 1) = 0;
      return OK;
   }

   RetVal.v.str = (char *) malloc(Nargs + 1);
   if (!RetVal.v.str) return E_NO_MEM;
   RetVal.type = STR_TYPE;
   for (i=0; i<Nargs; i++) {
     if (ARG(i).type != INT_TYPE) {
        free(RetVal.v.str);
	RetVal.type = ERR_TYPE;
        return E_BAD_TYPE;
     }
     if (ARG(i).v.val < -128 || ARG(i).v.val == 0) {
        free(RetVal.v.str);
	RetVal.type = ERR_TYPE;
        return E_2LOW;
     }
     if (ARG(i).v.val > 255) {
        free(RetVal.v.str);
	RetVal.type = ERR_TYPE;
        return E_2HIGH;
     }
     *(RetVal.v.str + i) = ARG(i).v.val;
   }
   *(RetVal.v.str + Nargs) = 0;
   return OK;
}
/***************************************************************/
/*                                                             */
/*  Functions for extracting the components of a date.         */
/*                                                             */
/*  FDay - get day of month                                    */
/*  FMonnum - get month (1-12)                                 */
/*  FYear - get year                                           */
/*  FWkdaynum - get weekday num (0 = Sun)                      */
/*  FWkday - get weekday (string)                              */
/*  FMon - get month (string)                                  */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FDay(void)
#else
static int FDay()
#endif
{
   int y, m, d;
   if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
   if (ARG(0).v.val == CacheJul)
      d = CacheDay;
   else {
      FromJulian(ARG(0).v.val, &y, &m, &d);
      CacheJul = ARG(0).v.val;
      CacheYear = y;
      CacheMon = m;
      CacheDay = d;
   }
   RetVal.type = INT_TYPE;
   RetVal.v.val = d;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FMonnum(void)
#else
static int FMonnum()
#endif
{
   int y, m, d;
   if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
   if (ARG(0).v.val == CacheJul)
      m = CacheMon;
   else {
      FromJulian(ARG(0).v.val, &y, &m, &d);
      CacheJul = ARG(0).v.val;
      CacheYear = y;
      CacheMon = m;
      CacheDay = d;
   }
   RetVal.type = INT_TYPE;
   RetVal.v.val = m+1;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FYear(void)
#else
static int FYear()
#endif
{
   int y, m, d;
   if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
   if (ARG(0).v.val == CacheJul)
      y = CacheYear;
   else {
      FromJulian(ARG(0).v.val, &y, &m, &d);
      CacheJul = ARG(0).v.val;
      CacheYear = y;
      CacheMon = m;
      CacheDay = d;
   }
   RetVal.type = INT_TYPE;
   RetVal.v.val = y;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FWkdaynum(void)
#else
static int FWkdaynum()
#endif
{
   if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
   RetVal.type = INT_TYPE;

   /* Correct so that 0 = Sunday */
   RetVal.v.val = (ARG(0).v.val+1) % 7;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FWkday(void)
#else
static int FWkday()
#endif
{
   char *s;

   if (ARG(0).type != DATE_TYPE && ARG(0).type != INT_TYPE) return E_BAD_TYPE;
   if (ARG(0).type == INT_TYPE) {
      if (ARG(0).v.val < 0) return E_2LOW;
      if (ARG(0).v.val > 6) return E_2HIGH;
      /* Convert 0=Sun to 0=Mon */
      ARG(0).v.val--;
      if (ARG(0).v.val < 0) ARG(0).v.val = 6;
      s = DayName[ARG(0).v.val];
   } else s = DayName[ARG(0).v.val % 7];
   return RetStrVal(s);
}

#ifdef HAVE_PROTOS
PRIVATE int FMon(void)
#else
static int FMon()
#endif
{
   char *s;
   int y, m, d;

   if (ARG(0).type != DATE_TYPE && ARG(0).type != INT_TYPE)
      return E_BAD_TYPE;
   if (ARG(0).type == INT_TYPE) {
      m = ARG(0).v.val - 1;
      if (m < 0) return E_2LOW;
      if (m > 11) return E_2HIGH;
   } else {
      if (ARG(0).v.val == CacheJul)
         m = CacheMon;
      else {
         FromJulian(ARG(0).v.val, &y, &m, &d);
         CacheJul = ARG(0).v.val;
         CacheYear = y;
         CacheMon = m;
         CacheDay = d;
      }
   }
   s = MonthName[m];
   return RetStrVal(s);
}

/***************************************************************/
/*                                                             */
/*  FHour - extract hour from a time                           */
/*  FMinute - extract minute from a time                       */
/*  FTime - create a time from hour and minute                 */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FHour(void)
#else
static int FHour()
#endif
{
   if (ARG(0).type != TIM_TYPE) return E_BAD_TYPE;
   RetVal.type = INT_TYPE;
   RetVal.v.val = ARG(0).v.val / 60;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FMinute(void)
#else
static int FMinute()
#endif
{
   if (ARG(0).type != TIM_TYPE) return E_BAD_TYPE;
   RetVal.type = INT_TYPE;
   RetVal.v.val = ARG(0).v.val % 60;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FTime(void)
#else
static int FTime()
#endif
{
   int h, m;

   if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;

   h = ARG(0).v.val;
   m = ARG(1).v.val;
   if (h<0 || m<0) return E_2LOW;
   if (h>23 || m>59) return E_2HIGH;
   RetVal.type = TIM_TYPE;
   RetVal.v.val = h*60 + m;
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FAbs - absolute value                                      */
/*  FSgn - signum function                                     */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FAbs(void)
#else
static int FAbs()
#endif
{
   int v;

   if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
   v = ARG(0).v.val;
   RetVal.type = INT_TYPE;
   RetVal.v.val = (v < 0) ? (-v) : v;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FSgn(void)
#else
static int FSgn()
#endif
{
   int v;

   if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
   v = ARG(0).v.val;
   RetVal.type = INT_TYPE;
   if (v>0) RetVal.v.val = 1;
   else if (v<0) RetVal.v.val = -1;
   else RetVal.v.val = 0;
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FOrd - returns a string containing ordinal number.         */
/*                                                             */
/*  EG - ord(2) == "2nd", etc.                                 */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FOrd(void)
#else
static int FOrd()
#endif
{
   int t, u, v;
   char *s;

   if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;

   v = ARG(0).v.val;
   t = v % 100;
   if (t < 0) t = -t;
   u = t % 10;
   s = "th";
   if (u == 1 && t != 11) s = "st";
   if (u == 2 && t != 12) s = "nd";
   if (u == 3 && t != 13) s = "rd";
   sprintf(Buffer, "%d%s", v, s);
   return RetStrVal(Buffer);
}

/***************************************************************/
/*                                                             */
/*  FPlural - pluralization function                           */
/*                                                             */
/*  plural(n) -->  "" or "s"                                   */
/*  plural(n, str) --> "str" or "strs"                         */
/*  plural(n, str1, str2) --> "str1" or "str2"                 */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FPlural(void)
#else
static int FPlural()
#endif
{
   if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;

   switch(Nargs) {
      case 1:
	 if (ARG(0).v.val == 1) return RetStrVal("");
	 else return RetStrVal("s");

      case 2:
	 if (ARG(1).type != STR_TYPE) return E_BAD_TYPE;
	 if (ARG(0).v.val == 1) {
	    DCOPYVAL(RetVal, ARG(1));
	    return OK;
	 }
	 RetVal.type = STR_TYPE;
	 RetVal.v.str = (char *) malloc(strlen(ARG(1).v.str)+2);
	 if (!RetVal.v.str) {
	    RetVal.type = ERR_TYPE;
	    return E_NO_MEM;
	 }
	 strcpy(RetVal.v.str, ARG(1).v.str);
	 strcat(RetVal.v.str, "s");
	 return OK;

      default:
	 if (ARG(1).type != STR_TYPE || ARG(2).type != STR_TYPE)
	    return E_BAD_TYPE;
	 if (ARG(0).v.val == 1) DCOPYVAL(RetVal, ARG(1));
	 else DCOPYVAL(RetVal, ARG(2));
	 return OK;
   }
}

/***************************************************************/
/*                                                             */
/*  FChoose                                                    */
/*  Choose the nth value from a list of value.  If n<1, choose */
/*  first.  If n>N, choose Nth value.  Indexes always start    */
/*  from 1.                                                    */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FChoose(void)
#else
static int FChoose()
#endif
{
   int v;

   if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
   v = ARG(0).v.val;
   if (v < 1) v = 1;
   if (v > Nargs-1) v = Nargs-1;
   DCOPYVAL(RetVal, ARG(v));
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FVersion - version of Remind                               */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FVersion(void)
#else
static int FVersion()
#endif
{
   return RetStrVal(VERSION);
}

/***************************************************************/
/*                                                             */
/*  FOstype - the type of operating system                     */
/*  (UNIX, OS/2, or MSDOS)                                     */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FOstype(void)
#else
static int FOstype()
#endif
{
#ifdef UNIX
   return RetStrVal("UNIX");
#else
#ifdef __OS2__
   return RetStrVal(OS2MODE ? "OS/2" : "MSDOS");
#else
   return RetStrVal("MSDOS");
#endif
#endif
}

/***************************************************************/
/*                                                             */
/*  FUpper - convert string to upper-case                      */
/*  FLower - convert string to lower-case                      */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FUpper(void)
#else
static int FUpper()
#endif
{
   char *s;

   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
   DCOPYVAL(RetVal, ARG(0));
   s = RetVal.v.str;
   while (*s) { *s = UPPER(*s); s++; }
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FLower(void)
#else
static int FLower()
#endif
{
   char *s;

   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
   DCOPYVAL(RetVal, ARG(0));
   s = RetVal.v.str;
   while (*s) { *s = LOWER(*s); s++; }
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FToday - return the system's notion of "today"             */
/*  Frealtoday - return today's date as read from OS.          */
/*  FNow - return the system time (or time on cmd line.)       */
/*  FRealnow - return the true system time                     */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FToday(void)
#else
static int FToday()
#endif
{
   RetVal.type = DATE_TYPE;
   RetVal.v.val = JulianToday;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FRealtoday(void)
#else
static int FRealtoday()
#endif
{
   RetVal.type = DATE_TYPE;
   RetVal.v.val = RealToday;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FNow(void)
#else
static int FNow()
#endif
{
   RetVal.type = TIM_TYPE;
   RetVal.v.val = (int) ( SystemTime(0) / 60L );
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FRealnow(void)
#else
static int FRealnow()
#endif
{
   RetVal.type = TIM_TYPE;
   RetVal.v.val = (int) ( SystemTime(1) / 60L );
   return OK;
}
/***************************************************************/
/*                                                             */
/*  FGetenv - get the value of an environment variable.        */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FGetenv(void)
#else
static int FGetenv()
#endif
{
   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
   return RetStrVal(getenv(ARG(0).v.str));
}

/***************************************************************/
/*                                                             */
/*  FValue                                                     */
/*                                                             */
/*  Get the value of a variable.  If a second arg is supplied, */
/*  it is returned if variable is undefined.                   */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FValue(void)
#else
static int FValue()
#endif
{
   Var *v;

   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
   switch(Nargs) {
      case 1:
	 return GetVarValue(ARG(0).v.str, &RetVal, NULL);

      case 2:
         v = FindVar(ARG(0).v.str, 0);
	 if (!v) {
	    DCOPYVAL(RetVal, ARG(1));
	    return OK;
	 } else {
	    return CopyValue(&RetVal, &v->v);
	 }
   }
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FDefined                                                   */
/*                                                             */
/*  Return 1 if a variable is defined, 0 if it is not.         */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FDefined(void)
#else
static int FDefined()
#endif
{
   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;

   RetVal.type = INT_TYPE;

   if (FindVar(ARG(0).v.str, 0))
      RetVal.v.val = 1;
   else
      RetVal.v.val = 0;
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FTrigdate and FTrigtime                                    */
/*                                                             */
/*  Date and time of last trigger.  These are stored in global */
/*  vars.                                                      */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FTrigdate(void)
#else
static int FTrigdate()
#endif
{
   RetVal.type = DATE_TYPE;
   RetVal.v.val = LastTriggerDate;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FTrigvalid(void)
#else
static int FTrigvalid()
#endif
{
   RetVal.type = INT_TYPE;
   RetVal.v.val = LastTrigValid;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FTrigtime(void)
#else
static int FTrigtime()
#endif
{
   RetVal.type = TIM_TYPE;
   RetVal.v.val = LastTriggerTime;
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FDaysinmon                                                 */
/*                                                             */
/*  Returns the number of days in mon,yr                       */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FDaysinmon(void)
#else
static int FDaysinmon()
#endif
{
   if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;

   if (ARG(0).v.val > 12 || ARG(0).v.val < 1 ||
       ARG(1).v.val < BASE || ARG(1).v.val > BASE+YR_RANGE)
          return E_DOMAIN_ERR;

   RetVal.type = INT_TYPE;
   RetVal.v.val = DaysInMonth(ARG(0).v.val-1, ARG(1).v.val);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FIsleap                                                    */
/*                                                             */
/*  Return 1 if year is a leap year, zero otherwise.           */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FIsleap(void)
#else
static int FIsleap()
#endif
{
   int y, m, d;

   if (ARG(0).type != INT_TYPE && ARG(0).type != DATE_TYPE) return E_BAD_TYPE;

   /* If it's a date, extract the year */
   if (ARG(0).type == DATE_TYPE)
      FromJulian(ARG(0).v.val, &y, &m, &d);
   else
      y = ARG(0).v.val;

   RetVal.type = INT_TYPE;
   RetVal.v.val = IsLeapYear(y);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FTrigger                                                   */
/*                                                             */
/*  Put out a date in a format suitable for triggering.        */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FTrigger(void)
#else
static int FTrigger()
#endif
{
   int y, m, d;
   int date, time;
   char buf[40];

   if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
   date = ARG(0).v.val;
   if (Nargs > 2) {
      if (ARG(2).type != INT_TYPE) return E_BAD_TYPE;
      if (ARG(1).type != TIM_TYPE) return E_BAD_TYPE;
      if (ARG(2).v.val) {
         UTCToLocal(ARG(0).v.val, ARG(1).v.val, &date, &time);
      } else {
         date = ARG(0).v.val;
	 time = ARG(1).v.val;
      }
   }
   FromJulian(date, &y, &m, &d);
   if (Nargs > 1) {
      if (ARG(1).type != TIM_TYPE) return E_BAD_TYPE;
      if (Nargs == 2) time = ARG(1).v.val;
      sprintf(buf, "%d %s %d AT %02d:%02d", d, EnglishMonthName[m], y,
              time/60, time%60);
   } else {
      sprintf(buf, "%d %s %d", d, EnglishMonthName[m], y);
   }
   return RetStrVal(buf);
}

/***************************************************************/
/*                                                             */
/*  FShell                                                     */
/*                                                             */
/*  The shell function.                                        */
/*                                                             */
/*  If run is disabled, will not be executed.                  */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FShell(void)
#else
static int FShell()
#endif
{
   char buf[SHELLSIZE+1];
   int ch, len;
   FILE *fp;
   char *s;

   if (RunDisabled) return E_RUN_DISABLED;
   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
   s = buf;
   len = 0;
   fp = POPEN(ARG(0).v.str, "r");
   if (!fp) return E_IO_ERR;
   while (len < SHELLSIZE) {
      ch = getc(fp);
      if (ch == EOF) {
	 break;
      }
      if (isspace(ch)) *s++ = ' ';
      else            *s++ = ch;
      len++;
   }
   *s = 0;

   /* Delete trailing newline (converted to space) */
   if (s > buf && *(s-1) == ' ') *(s-1) = 0;
#if defined(__MSDOS__) || defined(__OS2__)
   if (s-1 > buf && *(s-2) == ' ') *(s-2) = 0;
#endif
   PCLOSE(fp);
   return RetStrVal(buf);
}

/***************************************************************/
/*                                                             */
/*  FIsomitted                                                 */
/*                                                             */
/*  Is a date omitted?                                         */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FIsomitted(void)
#else
static int FIsomitted()
#endif
{
   if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
   RetVal.type = INT_TYPE;
   RetVal.v.val = IsOmitted(ARG(0).v.val, 0);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FSubstr                                                    */
/*                                                             */
/*  The substr function.  We destroy the value on the stack.   */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FSubstr(void)
#else
static int FSubstr()
#endif
{
   char *s, *t;
   int start, end;

   if (ARG(0).type != STR_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;
   if (Nargs == 3 && ARG(2).type != INT_TYPE) return E_BAD_TYPE;

   s = ARG(0).v.str;
   start = 1;
   while (start < ARG(1).v.val) {
      if (!*s) break;
      s++;
      start++;
   }
   if (Nargs == 2 || !*s) return RetStrVal(s);
   end = start;
   t = s;
   while (end <= ARG(2).v.val) {
      if (!*s) break;
      s++;
      end++;
   }
   *s = 0;
   return RetStrVal(t);
}

/***************************************************************/
/*                                                             */
/*  FIndex                                                     */
/*                                                             */
/*  The index of one string embedded in another.               */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FIndex(void)
#else
static int FIndex()
#endif
{
   char *s;
   int start;

   if (ARG(0).type != STR_TYPE || ARG(1).type != STR_TYPE ||
       (Nargs == 3 && ARG(2).type != INT_TYPE)) return E_BAD_TYPE;

   s = ARG(0).v.str;

/* If 3 args, bump up the start */
   if (Nargs == 3) {
      start = 1;
      while (start < ARG(2).v.val) {
         if (!*s) break;
	 s++;
	 start++;
      }
  }

/* Find the string */
   s = strstr(s, ARG(1).v.str);
   RetVal.type = INT_TYPE;
   if (!s) {
      RetVal.v.val = 0;
      return OK;
   }
   RetVal.v.val = (s - ARG(0).v.str) + 1;
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FIif                                                       */
/*                                                             */
/*  The IIF function.                                          */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FIif(void)
#else
static int FIif()
#endif
{
   int istrue;
   int arg;

   if (!(Nargs % 2)) return E_IIF_ODD;

   for (arg=0; arg<Nargs-1; arg += 2) {
      if (ARG(arg).type != STR_TYPE && ARG(arg).type != INT_TYPE)
         return E_BAD_TYPE;

      if (ARG(arg).type == INT_TYPE)
         istrue = ARG(arg).v.val;
      else
         istrue = *(ARG(arg).v.str);

      if (istrue) {
         DCOPYVAL(RetVal, ARG(arg+1));
	 return OK;
      }
   }

   DCOPYVAL(RetVal, ARG(Nargs-1));
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FFilename                                                  */
/*                                                             */
/*  Return name of current file                                */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FFilename(void)
#else
static int FFilename()
#endif
{
   return RetStrVal(FileName);
}

/***************************************************************/
/*                                                             */
/*  FFiledir                                                   */
/*                                                             */
/*  Return directory of current file                           */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FFiledir(void)
#else
static int FFiledir()
#endif
{
   char TmpBuf[LINELEN];  /* Should be _POSIX_PATH_MAX ? */
   char *s;

   strcpy(TmpBuf, FileName);
   s = TmpBuf + strlen(TmpBuf) - 1;
   if (s < TmpBuf) return RetStrVal(".");
#if defined(__OS2__) || defined(__MSDOS__)
   /* Both '\\' and '/' can be part of path; handle drive letters. */
   while (s > TmpBuf && !strchr("\\/:", *s)) s--;
   if (*s == ':') { s[1] = '.'; s += 2; }
   if (s > TmpBuf) *s = '/';
#else
   while (s > TmpBuf && *s != '/') s--;
#endif
   if (*s == '/') {
         *s = 0;
	 return RetStrVal(TmpBuf);
   } else return RetStrVal(".");
}
/***************************************************************/
/*                                                             */
/*  FAccess                                                    */
/*                                                             */
/*  The UNIX access() system call.                             */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FAccess(void)
#else
static int FAccess()
#endif
{
   int amode;
   char *s;

   if (ARG(0).type != STR_TYPE ||
       (ARG(1).type != INT_TYPE && ARG(1).type != STR_TYPE)) return E_BAD_TYPE;

   if (ARG(1).type == INT_TYPE) amode = ARG(1).v.val;
   else {
      amode = 0;
      s = ARG(1).v.str;
      while (*s) {
         switch(*s++) {
	    case 'r':
	    case 'R': amode |= R_OK; break;
	    case 'w':
	    case 'W': amode |= W_OK; break;
	    case 'x':
	    case 'X': amode |= X_OK; break;
         }
      }
   }
   RetVal.type = INT_TYPE;
   RetVal.v.val = access(ARG(0).v.str, amode);
   return OK;
}

#if defined(__MSDOS__) || defined(__BORLANDC__)
/***************************************************************/
/*                                                             */
/*  popen and pclose                                           */
/*                                                             */
/*  These are some rather brain-dead kludges for MSDOS.        */
/*  They are just sufficient for the shell() function, and     */
/*  should NOT be viewed as general-purpose replacements       */
/*  for the UNIX system calls.                                 */
/*                                                             */
/***************************************************************/
#ifdef __TURBOC__
#pragma argsused
#endif

static char *TmpFile;
#ifdef HAVE_PROTOS
PRIVATE FILE *os_popen(char *cmd, char *mode)
#else
static FILE *os_popen(cmd, mode)
char *cmd, *mode;
#endif
{
   char *s;

#if defined(__OS2__) && !defined(__BORLANDC__)
   if (OS2MODE)
     return(popen(cmd, mode));
#endif

   TmpFile = tmpnam(NULL);
   if (!TmpFile) return NULL;
   s = (char *) malloc(strlen(cmd) + 3 + strlen(TmpFile) + 1);
   if (!s) return NULL;
   strcpy(s, cmd);
   strcat(s, " > ");
   strcat(s, TmpFile);
   system(s);
   free(s);
   return fopen(TmpFile, "r");
}

#ifdef HAVE_PROTOS
PRIVATE int os_pclose(FILE *fp)
#else
static int os_pclose(fp)
FILE *fp;
#endif
{
#if defined(__OS2__) && !defined(__BORLANDC__)
  if (OS2MODE)
    return(pclose(fp));
#endif

   unlink(TmpFile);
   return fclose(fp);
}

#endif

/***************************************************************/
/*                                                             */
/*  FTypeof                                                    */
/*                                                             */
/*  Implement the typeof() function.                           */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FTypeof(void)
#else
static int FTypeof()
#endif
{
   switch(ARG(0).type) {
      case INT_TYPE:  return RetStrVal("INT");
      case DATE_TYPE: return RetStrVal("DATE");
      case TIM_TYPE:  return RetStrVal("TIME");
      case STR_TYPE:  return RetStrVal("STRING");
      default:        return RetStrVal("ERR");
   }
}

/***************************************************************/
/*                                                             */
/*  FLanguage                                                  */
/*                                                             */
/*  Implement the language() function.                         */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FLanguage(void)
#else
static int FLanguage()
#endif
{
   return RetStrVal(L_LANGNAME);
}

/***************************************************************/
/*                                                             */
/*  FArgs                                                      */
/*                                                             */
/*  Implement the args() function.                             */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FArgs(void)
#else
static int FArgs()
#endif
{
   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
   RetVal.type = INT_TYPE;
   RetVal.v.val = UserFuncExists(ARG(0).v.str);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FDosubst                                                   */
/*                                                             */
/*  Implement the dosubst() function.                          */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FDosubst(void)
#else
static int FDosubst()
#endif
{
   int jul, tim, r;
   char TmpBuf[LINELEN];

   jul = NO_DATE;
   tim = NO_TIME;
   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
   if (Nargs >= 2) {
      if (ARG(1).type != DATE_TYPE) return E_BAD_TYPE;
      jul = ARG(1).v.val;
      if (Nargs >= 3) {
         if (ARG(2).type != TIM_TYPE) return E_BAD_TYPE;
	 tim = ARG(2).v.val;
      }
   }

   if ((r=DoSubstFromString(ARG(0).v.str, TmpBuf, jul, tim))) return r;
   return RetStrVal(TmpBuf);
}

/***************************************************************/
/*                                                             */
/*  FHebdate                                                   */
/*  FHebday						       */
/*  FHebmon						       */
/*  FHebyear                                                   */
/*                                                             */
/*  Hebrew calendar support functions                          */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FHebdate(void)
#else
static int FHebdate()
#endif
{
   int year, day, mon, jahr;
   int mout, dout;
   int ans, r;
   int adarbehave;

   if (ARG(0).type != INT_TYPE || ARG(1).type != STR_TYPE) return E_BAD_TYPE;
   day = ARG(0).v.val;
   mon = HebNameToNum(ARG(1).v.str);
   if (mon < 0) return E_BAD_HEBDATE;
   if (Nargs == 2) {
      r = GetNextHebrewDate(JulianToday, mon, day, 0, 0, &ans);
      if (r) return r;
      RetVal.type = DATE_TYPE;
      RetVal.v.val = ans;
      return OK;
   }
   if (Nargs == 5) {
      if (ARG(4).type != INT_TYPE) return E_BAD_TYPE;
      adarbehave = ARG(4).v.val;
      if (adarbehave < 0) return E_2LOW;
      if (adarbehave > 2) return E_2HIGH;
   } else adarbehave = 0;

   if (Nargs == 4) {
      if (ARG(3).type != INT_TYPE) return E_BAD_TYPE;
      jahr = ARG(3).v.val;
      if (jahr < 0) return E_2LOW;
      if (jahr > 2) {
         r = ComputeJahr(jahr, mon, day, &jahr);
	 if (r) return r;
      }
   } else jahr = 0;


   if (ARG(2).type == INT_TYPE) {
      year = ARG(2).v.val;
      r = GetValidHebDate(year, mon, day, 0, &mout, &dout, jahr);
      if (r) return r;
      r = HebToJul(year, mout, dout);
      if (r<0) return E_DATE_OVER;
      RetVal.v.val = r;
      RetVal.type = DATE_TYPE;
      return OK;
   } else if (ARG(2).type == DATE_TYPE) {
      r = GetNextHebrewDate(ARG(2).v.val, mon, day, jahr, adarbehave, &ans);
      if (r) return r;
      RetVal.v.val = ans;
      RetVal.type = DATE_TYPE;
      return OK;
   } else return E_BAD_TYPE;
}

#ifdef HAVE_PROTOS
PRIVATE int FHebday(void)
#else
static int FHebday()
#endif
{
   int y, m, d;

   if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
   if (ARG(0).v.val == CacheHebJul)
      d = CacheHebDay;
   else {
      JulToHeb(ARG(0).v.val, &y, &m, &d);
      CacheHebJul = ARG(0).v.val;
      CacheHebYear = y;
      CacheHebMon = m;
      CacheHebDay = d;
   }
   RetVal.type = INT_TYPE;
   RetVal.v.val = d;
   return OK;
}

#ifdef HAVE_PROTOS
PRIVATE int FHebmon(void)
#else
static int FHebmon()
#endif
{
   int y, m, d;

   if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
   if (ARG(0).v.val == CacheHebJul) {
      m = CacheHebMon;
      y = CacheHebYear;
   } else {
      JulToHeb(ARG(0).v.val, &y, &m, &d);
      CacheHebJul = ARG(0).v.val;
      CacheHebYear = y;
      CacheHebMon = m;
      CacheHebDay = d;
   }
   return RetStrVal(HebMonthName(m, y));
}

#ifdef HAVE_PROTOS
PRIVATE int FHebyear(void)
#else
static int FHebyear()
#endif
{
   int y, m, d;

   if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
   if (ARG(0).v.val == CacheHebJul)
      y = CacheHebYear;
   else {
      JulToHeb(ARG(0).v.val, &y, &m, &d);
      CacheHebJul = ARG(0).v.val;
      CacheHebYear = y;
      CacheHebMon = m;
      CacheHebDay = d;
   }
   RetVal.type = INT_TYPE;
   RetVal.v.val = y;
   return OK;
}
/****************************************************************/
/*                                                              */
/*  FEasterdate - calc. easter Sunday from a year.              */
/*                                                              */
/*    from The Art of Computer Programming Vol 1.               */
/*            Fundamental Algorithms                            */
/*    by Donald Knuth.                                          */
/*                                                              */
/* Donated by Michael Salmon - thanks!                          */
/*                                                              */
/* I haven't examined this in detail, but I *think* int         */
/* arithmetic is fine, even on 16-bit machines.                 */
/*                                                              */
/****************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FEasterdate(void)
#else
static int FEasterdate()
#endif
{
   int y, m, d;
   int g, c, x, z, e, n;
   if (ARG(0).type == INT_TYPE) {
      y = ARG(0).v.val;
      if (y < BASE) return E_2LOW;
      else if (y > BASE+YR_RANGE) return E_2HIGH;
   } else if (ARG(0).type == DATE_TYPE) {
      FromJulian(ARG(0).v.val, &y, &m, &d);  /* We just want the year */
   } else return E_BAD_TYPE;

   do {
      g = (y % 19) + 1;  /* golden number */
      c = (y / 100) + 1; /* century */
      x = (3 * c)/4 - 12;        /* correction for non-leap year centuries */
      z = (8 * c + 5)/25 - 5;    /* special constant for moon sync */
      d = (5 * y)/4 - x - 10;    /* find sunday */
      e = (11 * g + 20 + z - x) % 30;    /* calc epact */
      if ( e < 0 ) e += 30;
      if ( e == 24 || (e == 25 && g > 11)) e++;
      n = 44 - e;                        /* find full moon */
      if ( n < 21 ) n += 30;     /* after 21st */
      d = n + 7 - (d + n)%7;     /* calc sunday after */
      if (d <= 31) m = 2;
      else
      {
         d = d - 31;
         m = 3;
      }

      RetVal.type = DATE_TYPE;
      RetVal.v.val = Julian(y, m, d);
      y++; } while (ARG(0).type == DATE_TYPE && RetVal.v.val < ARG(0).v.val);

   return OK;
}
/***************************************************************/
/*                                                             */
/*  FIsdst and FMinsfromutc                                    */
/*                                                             */
/*  Check whether daylight savings time is in effect, and      */
/*  get minutes from UTC.                                      */
/*                                                             */
/***************************************************************/
PRIVATE int FTimeStuff ARGS ((int wantmins));
#ifdef HAVE_PROTOS
PRIVATE int FIsdst(void)
#else
static int FIsdst()
#endif
{
   return FTimeStuff(0);
}

#ifdef HAVE_PROTOS
PRIVATE int FMinsfromutc(void)
#else
static int FMinsfromutc()
#endif
{
   return FTimeStuff(1);
}

#ifdef HAVE_PROTOS
PRIVATE int FTimeStuff(int wantmins)
#else
static int FTimeStuff(wantmins)
int wantmins;
#endif
{
   int jul, tim;
   int mins, dst;

   jul = JulianToday;
   tim = 0;

   if (Nargs >= 1) {
      if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
      jul = ARG(0).v.val;
      if (Nargs >= 2) {
         if (ARG(1).type != TIM_TYPE) return E_BAD_TYPE;
	 tim = ARG(1).v.val;
      }
   }

   if (CalcMinsFromUTC(jul, tim, &mins, &dst)) return E_MKTIME_PROBLEM;
   RetVal.type = INT_TYPE;
   if (wantmins) RetVal.v.val = mins; else RetVal.v.val = dst;

   return OK;
}

/***************************************************************/
/*                                                             */
/*  Sunrise and sunset functions.                              */
/*                                                             */
/*  Algorithm from "Almanac for computers for the year 1978"   */
/*  by L. E. Doggett, Nautical Almanac Office, USNO.           */
/*                                                             */
/*  This code also uses some ideas found in programs written   */
/*  by Michael Schwartz and Marc T. Kaufman.                   */
/*                                                             */
/***************************************************************/
#ifdef PI
#undef PI
#endif
#define PI 3.14159265358979323846
#define DEGRAD (PI/180.0)
#define RADDEG (180.0/PI)

#ifdef HAVE_PROTOS
PRIVATE int SunStuff(int rise, double cosz, int jul)
#else
static int SunStuff(rise, cosz, jul)
int rise;
double cosz;
int jul;
#endif
{
   int year, mon, day;
   int jan0;
   int mins, hours;

   double M, L, tanA, sinDelta, cosDelta, a, a_hr, cosH, t, H, T;
   double latitude, longdeg, UT, local;

/* Get offset from UTC */
   if (CalculateUTC) {
      if (CalcMinsFromUTC(jul, 12*60, &mins, NULL)) {
         Eprint(ErrMsg[E_MKTIME_PROBLEM]);
	 return NO_TIME;
      }
   } else mins = MinsFromUTC;

/* Get latitude and longitude */
   longdeg = (double) LongDeg + (double) LongMin / 60.0
		      + (double) LongSec / 3600.0;

   latitude = DEGRAD * ((double) LatDeg + (double) LatMin / 60.0
		         + (double) LatSec / 3600.0);


   FromJulian(jul, &year, &mon, &day);
   jan0 = jul - Julian(year, 0, 1);

/* Following formula on page B6 exactly... */
   t = (double) jan0;
   if (rise) t += (6.0 + longdeg/15.0) / 24.0;
   else      t += (18.0 + longdeg/15.0) / 24.0;

/* Mean anomaly of sun for 1978 ... how accurate for other years??? */
   M = 0.985600 * t - 3.251;  /* In degrees */

/* Sun's true longitude */
   L = M + 1.916*sin(DEGRAD*M) + 0.02*sin(2*DEGRAD*M) + 282.565;
   if (L > 360.0) L -= 360.0;

/* Tan of sun's right ascension */
   tanA = 0.91746 * tan(DEGRAD*L);
   a = RADDEG * atan(tanA);

/* Move a into same quadrant as L */
   if (0.0 <= L && L < 90.0) {
      if (a < 0.0) a += 180.0;
   } else if (90.0 <= L && L < 180.0) {
      a += 180.0;
   } else if (180.0 <= L && L < 270.0) {
      a += 180.0;
   } else {
      if (a > 0.0) a += 180.0;
   }
/*   if (fabs(a - L) > 90.0)
      a += 180.0; */

   if (a > 360.0)
      a -= 360.0;
   a_hr = a / 15.0;

/* Sine of sun's declination */
   sinDelta = 0.39782 * sin(DEGRAD*L);
   cosDelta = sqrt(1 - sinDelta*sinDelta);

/* Cosine of sun's local hour angle */
   cosH = (cosz - sinDelta * sin(latitude)) / (cosDelta * cos(latitude));

   if (cosH > 1.0 || cosH < -1.0) return NO_TIME;

   H = RADDEG * acos(cosH);
   if (rise) H = 360.0 - H;

   T = H / 15.0 + a_hr - 0.065710*t - 6.620;
   if (T >= 24.0) T -= 24.0;
   else if (T < 0.0) T+= 24.0;

   UT = T + longdeg / 15.0;


   local = UT + (double) mins / 60.0;
   if (local < 0.0) local += 24.0;
   else if (local >= 24.0) local -= 24.0;

   hours = (int) local;
   mins = (int) ((local - hours) * 60.0);

   return hours*60 + mins;
}

/***************************************************************/
/*                                                             */
/*  Sunrise and Sunset functions.                              */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FSun(int rise)
#else
static int FSun(rise)
int rise;
#endif
{
   int jul = JulianToday;
   static double cosz = -0.014543897;  /* for sunrise and sunset */
   int r;

   if (Nargs >= 1) {
      if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
      jul = ARG(0).v.val;
   }
   
   r = SunStuff(rise, cosz, jul);
   if (r == NO_TIME) {
      RetVal.v.val = 0;
      RetVal.type = INT_TYPE;
   } else {
      RetVal.v.val = r;
      RetVal.type = TIM_TYPE;
   }
   return OK;
}
      
#ifdef HAVE_PROTOS
PRIVATE int FSunrise(void)
#else
static int FSunrise()
#endif
{
   return FSun(1);
}
#ifdef HAVE_PROTOS
PRIVATE int FSunset(void)
#else
static int FSunset()
#endif
{
   return FSun(0);
}

/***************************************************************/
/*                                                             */
/*  FFiledate                                                  */
/*                                                             */
/*  Return modification date of a file                         */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FFiledate(void)
#else
static int FFiledate()
#endif
{
   struct stat statbuf;
   struct tm *t1;

   RetVal.type = DATE_TYPE;

   if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;

   if (stat(ARG(0).v.str, &statbuf)) {
      RetVal.v.val = 0;
      return OK;
   }

#ifdef __TURBOC__
   t1 = localtime( (time_t *) &(statbuf.st_mtime) );
#else
   t1 = localtime(&(statbuf.st_mtime));
#endif

   if (t1->tm_year + 1900 < BASE)
      RetVal.v.val=0;
   else
      RetVal.v.val=Julian(t1->tm_year+1900, t1->tm_mon, t1->tm_mday);

   return OK;
}

/***************************************************************/
/*                                                             */
/*  FPsshade                                                   */
/*                                                             */
/*  Canned PostScript code for shading a calendar square       */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FPsshade(void)
#else
static int FPsshade()
#endif
{
   char psbuff[256];
   char *s = psbuff;
   if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
   if (ARG(0).v.val < 0) return E_2LOW;
   if (ARG(0).v.val > 100) return E_2HIGH;

   sprintf(s, "/_A LineWidth 2 div def ");
   s += strlen(s);
   sprintf(s, "_A _A moveto ");
   s += strlen(s);
   sprintf(s, "BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto ");
   s += strlen(s);
   sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARG(0).v.val);
   return RetStrVal(psbuff);
}

/***************************************************************/
/*                                                             */
/*  FPsmoon                                                    */
/*                                                             */
/*  Canned PostScript code for generating moon phases          */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FPsmoon(void)
#else
static int FPsmoon()
#endif
{
   char psbuff[512];
   char sizebuf[30];
   char fontsizebuf[30];
   char *s = psbuff;
   char *extra = NULL;
   int size = -1;
   int fontsize = -1;

   if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
   if (ARG(0).v.val < 0) return E_2LOW;
   if (ARG(0).v.val > 3) return E_2HIGH;
   if (Nargs > 1) {
      if (ARG(1).type != INT_TYPE) return E_BAD_TYPE;
      if (ARG(1).v.val < -1) return E_2LOW;
      size = ARG(1).v.val;
      if (Nargs > 2) {
	 if (ARG(2).type != STR_TYPE) return E_BAD_TYPE;
	 extra = ARG(2).v.str;
	 if (Nargs > 3) {
	    if (ARG(3).type != INT_TYPE) return E_BAD_TYPE;
	    if (ARG(3).v.val <= 0) return E_2LOW;
	    fontsize = ARG(3).v.val;
	 }
      }
   }
   if (size > 0) {
      sprintf(sizebuf, "%d", size);
   } else {
      strcpy(sizebuf, "DaySize 2 div");
   }

   if (fontsize > 0) {
      sprintf(fontsizebuf, "%d", fontsize);
   } else {
      strcpy(fontsizebuf, "EntrySize");
   }

   sprintf(s, "gsave 0 setgray newpath Border %s add BoxHeight Border sub %s sub",
           sizebuf, sizebuf);
   s += strlen(s);
   sprintf(s, " %s 0 360 arc closepath", sizebuf);
   s += strlen(s);
   switch(ARG(0).v.val) {
      case 0:
	 sprintf(s, " fill");
	 s += strlen(s);
	 break;

      case 2:
	 sprintf(s, " stroke");
	 s += strlen(s);
	 break;

      case 1:
         sprintf(s, " stroke");
	 s += strlen(s);
         sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub",
                 sizebuf, sizebuf);
	 s += strlen(s);
	 sprintf(s, " %s 90 270 arc closepath fill", sizebuf);
	 s += strlen(s);
	 break;

      default:
         sprintf(s, " stroke");
	 s += strlen(s);
         sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub",
                 sizebuf, sizebuf);
	 s += strlen(s);
	 sprintf(s, " %s 270 90 arc closepath fill", sizebuf);
	 s += strlen(s);
	 break;
   }
   if (extra) {
      sprintf(s, " Border %s add %s add Border add BoxHeight border sub %s sub %s sub moveto /EntryFont findfont %s scalefont setfont (%s) show",
	      sizebuf, sizebuf, sizebuf, sizebuf, fontsizebuf, extra);
      s += strlen(s);
   }

   sprintf(s, " grestore");
   return RetStrVal(psbuff);
}

/***************************************************************/
/*                                                             */
/*  FMoonphase                                                 */
/*                                                             */
/*  Phase of moon for specified date/time.                     */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int FMoonphase(void)
#else
static int FMoonphase()
#endif
{
   int date, time;

   switch(Nargs) {
      case 0:
         date = JulianToday;
	 time = 0;
	 break;
      case 1:
         if (ARG(0).type != DATE_TYPE) return E_BAD_TYPE;
	 date = ARG(0).v.val;
	 time = 0;
	 break;
      case 2:
         if (ARG(0).type != DATE_TYPE && ARG(1).type != TIM_TYPE) return E_BAD_TYPE;
	 date = ARG(0).v.val;
	 time = ARG(1).v.val;
	 break;

      default: return E_SWERR;
   }

   RetVal.type = INT_TYPE;
   RetVal.v.val = MoonPhase(date, time);
   return OK;
}

/***************************************************************/
/*                                                             */
/*  FMoondate                                                  */
/*                                                             */
/*  Hunt for next occurrence of specified moon phase           */
/*                                                             */
/***************************************************************/
PRIVATE int MoonStuff ARGS ((int want_time));
#ifdef HAVE_PROTOS
PRIVATE int FMoondate(void)
#else
static int FMoondate()
#endif
{
   return MoonStuff(0);
}

#ifdef HAVE_PROTOS
PRIVATE int FMoontime(void)
#else
static int FMoontime()
#endif
{
   return MoonStuff(1);
}

#ifdef HAVE_PROTOS
PRIVATE int MoonStuff(int want_time)
#else
static int MoonStuff(want_time)
int want_time;
#endif
{
   int startdate, starttim;
   int d, t;

   startdate = JulianToday;
   starttim = 0;

   if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
   if (ARG(0).v.val < 0) return E_2LOW;
   if (ARG(0).v.val > 3) return E_2HIGH;
   if (Nargs >= 2) {
      if (ARG(1).type != DATE_TYPE) return E_BAD_TYPE;
      startdate = ARG(1).v.val;
      if (Nargs >= 3) {
         if (ARG(2).type != TIM_TYPE) return E_BAD_TYPE;
	 starttim = ARG(2).v.val;
      }
   }

   HuntPhase(startdate, starttim, ARG(0).v.val, &d, &t);
   if (want_time) {
      RetVal.type = TIM_TYPE;
      RetVal.v.val = t;
   } else {
      RetVal.type = DATE_TYPE;
      RetVal.v.val = d;
   }
   return OK;
}


These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.