/*
 * File: lmisc.c
 *  Contents: create, keywd, limit, llist
 */

#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"
#include "..\h\keyword.h"
#include "..\h\version.h"



/*
 * create - return an entry block for a co-expression.
 */

OpBlock(create,1,"create",0)

Ocreate(entryp, cargp)
word *entryp;
register dptr cargp;
   {

#ifdef Coexpr
   register struct b_coexpr *sblkp;
   register struct b_refresh *rblkp;
   register dptr dp, ndp, dsp;
   register word *newsp;
   int na, nl, i;
   struct b_proc *cproc;

   /*
    * Get a new co-expression stack and initialize.
    */
   if ((sblkp = alccoexp()) == NULL) 
      RunErr(0, NULL);

   /*
    * Icon stack starts at word after co-expression stack block.  C stack
    *  starts at end of stack region on machines with down-growing C stacks
    *  and somewhere in the middle of the region.
    *
    * The C stack is aligned on a doubleword boundary.	For upgrowing
    *  stacks, the C stack starts in the middle of the stack portion
    *  of the static block.  For downgrowing stacks, the C stack starts
    *  at the end of the static block.
    */
   newsp = (word *)((char *)sblkp + sizeof(struct b_coexpr));

#ifdef UpStack
   sblkp->cstate[0] =
      ((word)((char *)sblkp + (stksize - sizeof(*sblkp))/2)
       &~(WordSize*StackAlign-1));
#else					/* UpStack */
   sblkp->cstate[0] =
	((word)((char *)sblkp + stksize - WordSize)&~(WordSize*StackAlign-1));
#endif					/* UpStack */

#ifdef CoProcesses
   sblkp->cstate[1] = 0;
#endif


   sblkp->es_argp = (dptr )newsp;
   /*
    * Calculate number of arguments and number of local variables.
    *  na is nargs + 1 to include Arg0.
    */
   na = pfp->pf_nargs + 1;
   cproc = (struct b_proc *)BlkLoc(argp[0]);
   nl = (int)cproc->ndynam;

   /*
    * Get a refresh block for the new co-expression.
    */
   if (blkreq((word)sizeof(struct b_refresh) +
         (na + nl) * sizeof(struct descrip)) == Error) 
      RunErr(0, NULL);
   rblkp = alcrefresh(entryp, na, nl);
   sblkp->freshblk.dword = D_Refresh;
   BlkLoc(sblkp->freshblk) = (union block *) rblkp;

   /*
    * Copy current procedure frame marker into refresh block.
    */
   rblkp->pfmkr = *pfp;
   rblkp->pfmkr.pf_pfp = 0;

   /*
    * Copy arguments into refresh block and onto new stack.
    */
   dp = &argp[0];
   ndp = &rblkp->elems[0];
   dsp = (dptr)newsp;
   for (i = 1; i <= na; i++) {
      *dsp++ = *dp;
      *ndp++ = *dp++;
      }

   /*
    * Copy procedure frame to new stack and point dsp to word after frame.
    */
   *((struct pf_marker *)dsp) = *pfp;
   sblkp->es_pfp = (struct pf_marker *)dsp;
   sblkp->es_pfp->pf_pfp = 0;
   dsp = (dptr)((word *)dsp + Vwsizeof(*pfp));
   sblkp->es_ipc.opnd = entryp;
   sblkp->es_gfp = 0;
   sblkp->es_efp = 0;
   sblkp->es_ilevel = 0;
   sblkp->tvalloc = NULL;

   /*
    * Copy locals to new stack and refresh block.
    */
   dp = &(pfp->pf_locals)[0];
   for (i = 1; i <= nl; i++) {
      *dsp++ = *dp;
      *ndp++ = *dp++;
      }
   /*
    * Push two null descriptors on the stack.
    */
   *dsp++ = nulldesc;
   *dsp++ = nulldesc;

   sblkp->es_sp = (word *)dsp - 1;

   /*
    * Return the new co-expression.
    */
   Arg0.dword = D_Coexpr;
   BlkLoc(Arg0) = (union block *) sblkp;
   Return;
#else					/* Coexpr */
   RunErr(-401, NULL);
#endif					/* Coexpr */

   }

/*
 * keywd - process keyword.
 */

char *feattab[] = {
#if AMIGA
   "Amiga",
#endif					/* AMIGA */
#if ATARI_ST
   "Atari ST",
#endif					/* ATARI_ST */
#if VM
   "CMS",
#endif					/* VM */
#if HIGHC_386
   "MS-DOS/386",
#endif					/* HIGHC_386 */
#if MACINTOSH
   "Macintosh",
#endif					/* MACINTOSH */
#if MSDOS
   "MS-DOS",
#endif					/* MSDOS */
#if MVS
   "MVS",
#endif					/* MVS */
#if OS2
   "OS/2",
#endif					/* OS2 */
#if PORT
   "PORT",
#endif					/* PORT */
#if UNIX
   "UNIX",
#endif					/* VM */
#if VMS
   "VMS",
#endif					/* VMS */
#if !EBCDIC
   "ASCII",
#else					/* EBCDIC */
   "EBCDIC",
#endif					/* EBCDIC */
#ifdef IconCalling
   "calling to Icon",
#endif					/* IconCalling */
#ifdef Coexpr
   "co-expressions",
#endif					/* Coexpr */
#ifdef Header
   "direct execution",
#endif					/* Header */
#ifdef EnvVars
   "environment variables",
#endif					/* EnvVars */
#ifdef TraceBack
   "error trace back",
#endif					/* TraceBack */
#ifdef EvalTrace
   "evaluation tracing",
#endif					/* EvalTrace */
#ifdef ExecImages
   "executable images",
#endif					/* ExecImages */
#ifndef FixedRegions
   "expandable regions",
#endif					/* FixedRegions */
#ifdef ExternalFunctions
   "external functions",
#endif					/* ExternalFunctions */
#ifdef FixedRegions
   "fixed regions",
#endif					/* FixedRegions */
#ifdef KeyboardFncs
   "keyboard functions",
#endif					/* KeyboardFncs */
#ifdef LargeInts
   "large integers",
#endif					/* LargeInts */
#ifdef MathFncs
   "math functions",
#endif					/* MathFncs */
#ifdef MemMon
   "memory monitoring",
#endif					/* MEMMON */
#ifdef Pipes
   "pipes",
#endif					/* Pipes */
#ifdef RecordIO
   "record I/O",
#endif					/* RecordIO */
#ifdef StrInvoke
   "string invocation",
#endif					/* StrInvoke */
#ifdef SystemFnc
   "system function",
#endif					/* SystemFnc */
#ifdef DosFncs
   "MS-DOS extensions",
#endif					/* DosFncs */
   ""
   };

LibDcl(keywd,0,"&keywd")
   {
   register int hour;
   register word i;
   register char *merid;
   char **p;
   char sbuf[MaxCvtLen];
   extern word coll_stat, coll_str, coll_blk, coll_tot;
   long runtim;
   struct cal_time ct;

#if MACINTOSH && MPW
/* #pragma unused(nargs) */
#endif					/* MACINTOSH && MPW */

   /*
    * This is just plug and chug code.	For whatever keyword is desired,
    *  the appropriate value is dug out of the system and made into
    *  a suitable Icon value.
    *
    * A few special cases are worth noting:
    *  &pos, &random, &trace - built-in trapped variables are returned
    */
   switch ((int)IntVal(Arg0)) {
      case K_ASCII:
         Arg0.dword = D_Cset;
         BlkLoc(Arg0) = (union block *) &k_ascii;
         break;
      case K_CLOCK:
         if (strreq((word)8) == Error) 
            RunErr(0, NULL);
         getitime(&ct);
         sprintf(sbuf,"%02d:%02d:%02d", ct.hour, ct.minute, ct.second);
         StrLen(Arg0) = 8;
         StrLoc(Arg0) = alcstr(sbuf,(word)8);
         break;
      case K_COLLECTIONS:
         MakeInt(coll_tot, &Arg0);
         Suspend;
         MakeInt(coll_stat, &Arg0);
         Suspend;
         MakeInt(coll_str, &Arg0);
         Suspend;
         MakeInt(coll_blk, &Arg0);
         Return;


      case K_CSET:
         Arg0.dword = D_Cset;
         BlkLoc(Arg0) = (union block *) &k_cset;
         break;
      case K_CURRENT:
         Arg0 = k_current;
         break;
      case K_DATE:
         if (strreq((word)10) == Error) 
            RunErr(0, NULL);
         getitime(&ct);
         sprintf(sbuf, "%04d/%02d/%02d", ct.year, ct.month_no, ct.mday);
         StrLen(Arg0) = 10;
         StrLoc(Arg0) = alcstr(sbuf,(word)10);
         break;
      case K_DATELINE:
         getitime(&ct);
         if ((hour = ct.hour) >= 12) {
            merid = "pm";
            if (hour > 12)
               hour -= 12;
            }
         else {
            merid = "am";
            if (hour < 1)
               hour += 12;
            }
         sprintf(sbuf, "%s, %s %d, %d  %d:%02d %s", ct.wday, ct.month_nm,
            ct.mday, ct.year, hour, ct.minute, merid);
         if (strreq(i = strlen(sbuf)) == Error) 
            RunErr(0, NULL);
         StrLen(Arg0) = i;
         StrLoc(Arg0) = alcstr(sbuf, i);
         break;
      case K_DIGITS:
         Arg0.dword = D_Cset;
         BlkLoc(Arg0) = (union block *)&k_digits;
         break;


      case K_ERROR:
         Arg0.dword = D_Tvkywd;
         BlkLoc(Arg0) = (union block *)&tvky_err;
         break;

      case K_ERRORNUMBER:
         if (k_errornumber == 0)
            Fail;
         MakeInt((k_errornumber > 0 ? k_errornumber : -k_errornumber), &Arg0);
         break;
      case K_ERRORTEXT:
         if (k_errornumber == 0)
            Fail;
         StrLoc(Arg0) = k_errortext;
         StrLen(Arg0) = strlen(k_errortext);
         break;
      case K_ERRORVALUE:
         if (k_errornumber <= 0)
            Fail;
         Arg0 = k_errorvalue;
         break;
      case K_ERROUT:
         Arg0.dword = D_File;
         BlkLoc(Arg0) = (union block *)&k_errout;
         break;
      case K_FEATURES:
         p = feattab;
         for(;;) {
            StrLen(Arg0) = strlen(*p);
            if (StrLen(Arg0) == 0)
               Fail;
            StrLoc(Arg0) = *p;
            Suspend;
            p++;
            }
      case K_FILE:
         StrLoc(Arg0) = findfile(ipc.opnd);
         StrLen(Arg0) = strlen(StrLoc(Arg0));
         break;


      case K_HOST:
         iconhost(sbuf);
         if (strreq(i = strlen(sbuf)) == Error) 
            RunErr(0, NULL);
         StrLen(Arg0) = i;
         StrLoc(Arg0) = alcstr(sbuf, i);
         break;
      case K_INPUT:
         Arg0.dword = D_File;
         BlkLoc(Arg0) = (union block *)&k_input;
         break;
      case K_LCASE:
         Arg0.dword = D_Cset;
         BlkLoc(Arg0) = (union block *)&k_lcase;
         break;
      case K_LETTERS:
         Arg0.dword = D_Cset;
         BlkLoc(Arg0) = (union block *)&k_letters;
         break;
      case K_LEVEL:
         MakeInt(k_level, &Arg0);
         break;
      case K_LINE:
         MakeInt(findline(ipc.opnd), &Arg0);
         break;
      case K_MAIN:
         Arg0 = k_main;
         break;
      case K_OUTPUT:
         Arg0.dword = D_File;
         BlkLoc(Arg0) = (union block *)&k_output;
         break;
      case K_POS:
         Arg0.dword = D_Tvkywd;
         BlkLoc(Arg0) = (union block *) &tvky_pos;
         break;
      case K_RANDOM:
         Arg0.dword = D_Tvkywd;
         BlkLoc(Arg0) = (union block *) &tvky_ran;
         break;
      case K_REGIONS:

#ifdef FixedRegions
         Arg0 = zerodesc;
#else					/* FixedRegions */
         MakeInt(DiffPtrs(statend,statbase) - mstksize, &Arg0);
#endif					/* FixedRegions */

         Suspend;
         MakeInt(DiffPtrs(strend,strbase), &Arg0);
         Suspend;
         MakeInt(DiffPtrs(blkend,blkbase), &Arg0);
         Return;

      case K_SOURCE:

#ifndef Coexpr
         Arg(0) = k_main;
#else					/* Coexpr */
 	 Arg0.dword = D_Coexpr;
 	 BlkLoc(Arg0) =
            (union block *)topact((struct b_coexpr *)BlkLoc(k_current));
#endif					/* Coexpr */

         break;
      case K_STORAGE:

#ifdef FixedRegions
         Arg0 = zerodesc;
#else					/* FixedRegions */
         MakeInt(DiffPtrs(statfree,statbase) - mstksize, &Arg0);
#endif					/* FixedRegions */

         Suspend;
         MakeInt(DiffPtrs(strfree,strbase), &Arg0);
         Suspend;
         MakeInt(DiffPtrs(blkfree,blkbase), &Arg0);
         Return;
      case K_SUBJECT:
         Arg0.dword = D_Tvkywd;
         BlkLoc(Arg0) = (union block *) &tvky_sub;
         break;
      case K_TIME:
         runtim = millisec();
         MakeInt(runtim, &Arg0);
         break;
      case K_TRACE:
         Arg0.dword = D_Tvkywd;
         BlkLoc(Arg0) = (union block *)&tvky_trc;
         break;
      case K_UCASE:
         Arg0.dword = D_Cset;
         BlkLoc(Arg0) = (union block *)&k_ucase;
         break;
      case K_VERSION:
         if (strreq(i = strlen(Version)) == Error) 
            RunErr(0, NULL);
         StrLen(Arg0) = i;
         StrLoc(Arg0) = Version;
         break;
      default:
         syserr("keyword: unknown keyword type.");
      }
   Return;
   }


/*
 * limit - explicit limitation initialization.
 */


#ifdef WATERLOO_C_V3_0
struct b_iproc Blimit = {
	T_Proc,
	Vsizeof(struct b_proc),
	Olimit,
	2,
	-1,
	0,
	0,
	{sizeof(BackSlash)-1,BackSlash}}; Olimit(nargs,cargp,sptr) register dptr cargp;
#else					/* WATERLOO_C_V3_0 */
LibDcl(limit,2,BackSlash)
#endif					/* WATERLOO_C_V3_0 */

   {

#if MACINTOSH
#if MPW
/* #pragma unused(nargs) */
#endif					/* MPW */
#endif					/* MACINTOSH */

   /*
    * The limit is both passed and returned in Arg0.  The limit must
    *  be an integer.  If the limit is 0, the expression being evaluated
    *  fails.  If the limit is < 0, it is an error.  Note that the
    *  result produced by limit is ultimately picked up by the lsusp
    *  function.
    */
   if (DeRef(Arg0) == Error) 
      RunErr(0, NULL);

   switch (cvint(&Arg0)) {

      case T_Integer:
         break;

      default:
         RunErr(101, &Arg0);
      }

   if (IntVal(Arg0) < 0) 
      RunErr(205, &Arg0);
   if (IntVal(Arg0) == 0)
      Fail;
   Return;
   }


/*
 * [ ... ] - create an explicitly specified list.
 */

LibDcl(llist,-1,"[...]")
   {
   register word i;
   register struct b_list *hp;
   register struct b_lelem *bp;
   word nslots;

   nslots = nargs;
   if (nslots == 0)
      nslots = MinListSlots;

   if (blkreq((word)sizeof(struct b_list) + sizeof(struct b_lelem) +
         nslots * sizeof(struct descrip)) == Error) 
      RunErr(0, NULL);

   /*
    * Allocate the list and a list block.
    */
   hp = alclist((word)nargs);
   bp = alclstb(nslots, (word)0, (word)nargs);

   /*
    * Make the list block just allocated into the first and last blocks
    *  for the list.
    */
   hp->listhead = hp->listtail = (union block *)bp;
   /*
    * Dereference each argument in turn and assign it to a list element.
    */
   for (i = 1; i <= nargs; i++) {
      if (DeRef(Arg(i)) == Error) 
         RunErr(0, NULL);
      bp->lslots[i-1] = Arg(i);
      }
   /*
    * Point Arg0 at the new list and return it.
    */
   ArgType(0) = D_List;
   Arg(0).vword.bptr = (union block *)hp;
   Return;
   }
