/*************************************************************************
*									 *
*	 YAP Prolog 							 *
*									 *
*	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
*									 *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 2015-		 *
*									 *
**************************************************************************
*									 *
* File:		flags.c							 *
* Last rev:								 *
* mods:									 *
* comments:	abstract machine definitions				 *
*									 *
*************************************************************************/

/** @file flags.c

    @ingroup Flags
    @{
*/


// this is where  we define flags
#define INIT_FLAGS 1

#include "Yap.h"

static bool ro( Term inp );
static bool nat( Term inp );
static bool isatom( Term inp );
static bool boolean( Term inp );
//static bool string( Term inp );
//static bool  list_atom( Term inp );
static bool list_option( Term inp );
static bool argv( Term inp );
static bool os_argv( Term inp );
static bool agc_threshold( Term inp );
static bool gc_margin( Term inp );
static bool executable( Term inp );
static bool sys_thread_id(Term inp);
static bool mkprompt(Term inp);
static bool synerr(Term inp);
static bool indexer(Term inp);
static bool getenc(Term inp);
static bool typein( Term inp );
static bool dqf( Term t2 );

static void newFlag( Term fl, Term val );
static Int current_prolog_flag(USES_REGS1);
static Int set_prolog_flag(USES_REGS1);

#include "Yatom.h"
#include "yapio.h"
#include "eval.h"

#define YAP_FLAG( ID, NAME, WRITABLE, DEF, INIT, HELPER )    { NAME, WRITABLE, DEF, INIT, HELPER }

#define GZERO_FLAG {  NULL, false, NULL, NULL, NULL }
#define LZERO_FLAG {  NULL, false, NULL, NULL, NULL }

static flag_info global_flags_setup[] = {
#include "YapGFlagInfo.h"
  GZERO_FLAG
  };

static flag_info local_flags_setup[] = {
  #include "YapLFlagInfo.h"
  LZERO_FLAG
  };

static bool indexer( Term inp ) {
  if (inp == TermOff ||
      inp == TermSingle||
      inp == TermCompact||
      inp == TermMulti||
      inp == TermOn||
      inp == TermMax )
    return true;

  if (IsAtomTerm(inp)) {
    Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, inp, "set_prolog_flag index in {off,single,compact,multi,on,max}");
    return false;
  }
  Yap_Error(TYPE_ERROR_ATOM, inp, "set_prolog_flag in {dec10,error,fail,quiet}");
  return false;
}

static bool dqf1( ModEntry *new, Term t2 USES_REGS ) {
  new->flags &= ~(DBLQ_CHARS|DBLQ_CODES|DBLQ_ATOM|DBLQ_STRING);
  if (IsAtomTerm(t2) ) {
    if (t2 == TermString) {
      new->flags |=  DBLQ_STRING;
      return true;
    } else if (t2 == TermAtom) {
      new->flags |=  DBLQ_ATOM;
      return true;
    } else if (t2 == TermCodes) {
      new->flags |=  DBLQ_CODES;
      return true;
    } else if (t2 == TermChars) {
      new->flags |=  DBLQ_CHARS;
      return true;
    }
    /* bad argument, but still an atom */
    Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, t2, "bad option %s for backquoted string flag, use one string, arom, codes or chars", RepAtom(AtomOfTerm(t2))->StrOfAE);
    return false;
  } else {
    Yap_Error(TYPE_ERROR_ATOM, t2, "set_prolog_flag(double_quotes, %s), should be {string,atom,codes,chars}", RepAtom(AtomOfTerm(t2))->StrOfAE);
    return false;
  }
}

 static bool dqf( Term t2 ) {
  CACHE_REGS
  ModEntry *new = Yap_GetModuleEntry(CurrentModule);
  return dqf1( new, t2 PASS_REGS);
}


 
static bool bqf1( ModEntry *new, Term t2 USES_REGS ) {
  new->flags &=  ~(BCKQ_CHARS|BCKQ_CODES|BCKQ_ATOM|BCKQ_STRING);
   if (IsAtomTerm(t2)) {
     if (t2 == TermString) {
       new->flags |=  BCKQ_STRING;
       return true;
     } else if (t2 == TermAtom) {
       new->flags |=  BCKQ_ATOM;
       return true;
     } else if (t2 == TermCodes) {
       new->flags |=  BCKQ_CODES;
       return true;
     } else if (t2 == TermChars) {
       new->flags |=  BCKQ_CHARS;
       return true;
     }
     Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, t2, "bad option %s for backquoted string flag, use one string, arom, codes or chars", RepAtom(AtomOfTerm(t2))->StrOfAE);
    return false;
  } else {
    Yap_Error(TYPE_ERROR_ATOM, t2, "flag  %s is not module-scoped", RepAtom(AtomOfTerm(t2))->StrOfAE);
    return false;
  }
}
 
static bool isaccess( Term inp ) {
  if (inp == TermReadWrite ||
      inp == TermReadOnly )
    return true;

  if (IsAtomTerm(inp)) {
    Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, inp, "set_prolog_flag access in {read_write,read_only}");
    return false;
  }
  Yap_Error(TYPE_ERROR_ATOM, inp, "set_prolog_flag access in {read_write,read_only}");
  return false;
}

static bool isground( Term inp ) {
  return Yap_IsGroundTerm( inp );
}


static bool flagscope( Term inp ) {
  if (inp == TermGlobal ||
      inp == TermThread ||
      inp == TermModule)
    return true;

  if (IsAtomTerm(inp)) {
    Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, inp, "set_prolog_flag access in {global,module,thread}");
    return false;
  }
  Yap_Error(TYPE_ERROR_ATOM, inp, "set_prolog_flag access in {global,module,thread}");
  return false;
}


static bool mkprompt( Term inp ) {
    CACHE_REGS
  if (IsVarTerm(inp)) {
    return Yap_unify( inp, MkAtomTerm( Yap_LookupAtom( LOCAL_Prompt )) );
  }
  if (!IsAtomTerm(inp) ) {
    Yap_Error(TYPE_ERROR_ATOM, inp, "set_prolog_flag");
    return false;
  }
  strncpy( LOCAL_Prompt, (const char *)RepAtom( AtomOfTerm( inp ) )->StrOfAE, MAX_PROMPT );
  return true;
}

static bool getenc( Term inp ) {
    CACHE_REGS
  if (IsVarTerm(inp)) {
    return Yap_unify( inp, MkAtomTerm( Yap_LookupAtom( enc_name(LOCAL_encoding) )) );
  }
  if (!IsAtomTerm(inp) ) {
    Yap_Error(TYPE_ERROR_ATOM, inp, "set_prolog_flag");
    return false;
  }
  enc_id( ( char *)RepAtom( AtomOfTerm( inp ) )->StrOfAE );
  return true;
}

/*
static bool enablerl( Term inp ) {
    CACHE_REGS
  if (IsVarTerm(inp)) {
    return Yap_unify( inp, MkAtomTerm( Yap_LookupAtom( enc_name(LOCAL_encoding) )) );
  }
  if (!IsAtomTerm(inp) ) {
    Yap_Error(TYPE_ERROR_ATOM, inp, "set_prolog_flag");
    return false;
  }
  enc_id( RepAtom( AtomOfTerm( inp ) )->StrOfAE );
  return true;
}
*/

static bool typein( Term inp ) {
    CACHE_REGS
  if (IsVarTerm(inp)) {
    Term tin = CurrentModule;
    if (tin == PROLOG_MODULE)
     tin = TermProlog;
    return Yap_unify( inp, tin );
  }
  if (!IsAtomTerm(inp) ) {
    Yap_Error(TYPE_ERROR_ATOM, inp, "set_prolog_flag");
    return false;
  }
  CurrentModule = inp;
  if (inp == TermProlog) CurrentModule = PROLOG_MODULE;
  return true;
}

#if 0

static Int p_has_yap_or(USES_REGS1) {
#ifdef YAPOR
  return (TRUE);
#else
  return (FALSE);
#endif
}

static Int p_has_eam(USES_REGS1) {

#ifdef BEAM
  return (TRUE);
#else
  return (FALSE);
#endif
}

static Int p_has_jit(USES_REGS1) {
#ifdef HAS_JIT
  return (TRUE);
#else
  return (FALSE);
#endif
}

static bool tabling( Term inp ) {
      if (value == 0) { /* default */
      tab_ent_ptr tab_ent = GLOBAL_root_tab_ent;
      while (tab_ent) {
        TabEnt_mode(tab_ent) = TabEnt_flags(tab_ent);
        tab_ent = TabEnt_next(tab_ent);
      }
     yap_flags[TA BLING_MODE_FLAG] = 0;
    } else if (value == 1) { /* batched */
      tab_ent_ptr tab_ent = GLOBAL_root_tab_ent;
      while (tab_ent) {
        SetMode_Batched(TabEnt_mode(tab_ent));
        tab_ent = TabEnt_next(tab_ent);
      }
      SetMode_Batched(yap_flags[TABLING_MODE_FLAG]);
    } else if (value == 2) { /* local */
      tab_ent_ptr tab_ent = GLOBAL_root_tab_ent;
      while (tab_ent) {
        SetMode_Local(TabEnt_mode(tab_ent));
        tab_ent = TabEnt_next(tab_ent);
      }
      SetMode_Local(yap_flags[TABLING_MODE_FLAG]);
    } else if (value == 3) { /* exec_answers */
      tab_ent_ptr tab_ent = GLOBAL_root_tab_ent;
      while (tab_ent) {
        SetMode_ExecAnswers(TabEnt_mode(tab_ent));
        tab_ent = TabEnt_next(tab_ent);
      }
      SetMode_ExecAnswers(yap_flags[TABLING_MODE_FLAG]);
    } else if (value == 4) { /* load_answers */
      tab_ent_ptr tab_ent = GLOBAL_root_tab_ent;
      while (tab_ent) {
        SetMode_LoadAnswers(TabEnt_mode(tab_ent));
        tab_ent = TabEnt_next(tab_ent);
      }
      SetMode_LoadAnswers(yap_flags[TABLING_MODE_FLAG]);
    } else if (value == 5) { /* local_trie */
      tab_ent_ptr tab_ent = GLOBAL_root_tab_ent;
      while (tab_ent) {
        SetMode_LocalTrie(TabEnt_mode(tab_ent));
        tab_ent = TabEnt_next(tab_ent);
      }
      SetMode_LocalTrie(yap_flags[TABLING_MODE_FLAG]);
    } else if (value == 6) { /* global_trie */
      tab_ent_ptr tab_ent = GLOBAL_root_tab_ent;
      while (tab_ent) {
        SetMode_GlobalTrie(TabEnt_mode(tab_ent));
        tab_ent = TabEnt_next(tab_ent);
      }
      SetMode_GlobalTrie(yap_flags[TABLING_MODE_FLAG]);
    } else if (value == 7) { /* CoInductive */
      tab_ent_ptr tab_ent = GLOBAL_root_tab_ent;
      while (tab_ent) {
        SetMode_CoInductive(TabEnt_mode(tab_ent));
        tab_ent = TabEnt_next(tab_ent);
      }
      SetMode_CoInductive(yap_flags[TABLING_MODE_FLAG]);
    }
}

static bool string( Term inp ) {
  if (IsVarTerm(inp)) {
    Yap_Error(INSTANTIATION_ERROR, inp, "set_prolog_flag in \"...\"");
    return false;
  }
  if (IsStringTerm( inp ))
     return true;
  Term inp0  = inp;
  if (IsPairTerm(inp)) {
    Term hd = HeadOfTerm(inp);
   if (IsAtomTerm(hd)) {
      do {
       Term hd = HeadOfTerm(inp);
	if (!IsAtomTerm(hd)) {
	  Yap_Error(TYPE_ERROR_TEXT, inp0, "set_prolog_flag in \"...\"");
	  return false;
	}
      } while (IsPairTerm( inp ) );
    } else if (IsIntTerm(hd)) {
      do {
	Term hd = HeadOfTerm(inp);
 	if (!IsIntTerm(hd)) {
	  Yap_Error(TYPE_ERROR_TEXT, inp0, "set_prolog_flag in \"...\"");
	  return false;
	}
 	if (IntOfTerm(hd) < 0) {
	  Yap_Error(DOMAIN_ERROR_NOT_LESS_THAN_ZERO, inp0, "set_prolog_flag in 0...");
	  return false;
	}
      } while (IsPairTerm( inp ) );
    } else {
      Yap_Error(TYPE_ERROR_TEXT, inp0, "set_prolog_flag in \"...\"");
      return false;
    }
  }
  if ( inp != TermNil ) {
    Yap_Error(TYPE_ERROR_TEXT, inp0, "set_prolog_flag in \"...\"");
    return false;
  }
  return true;
}

static bool list_atom( Term inp ) {
  if (IsVarTerm(inp)) {
    Yap_Error(INSTANTIATION_ERROR, inp, "set_prolog_flag in \"...\"");
    return false;
  }
  Term inp0  = inp;
  if (IsPairTerm(inp)) {
    Term hd = HeadOfTerm(inp);
    do {
      if (!IsAtomTerm(hd)) {
	Yap_Error(TYPE_ERROR_ATOM, inp0, "set_prolog_flag in \"...\"");
	return false;
      }
    } while (IsPairTerm( inp ) );
  }
  if ( inp != TermNil ) {
    Yap_Error(TYPE_ERROR_LIST, inp0, "set_prolog_flag in [...]");
    return false;
  }
  return true;
}
#endif


static bool list_option( Term inp ) {
  if (IsVarTerm(inp)) {
    Yap_Error(INSTANTIATION_ERROR, inp, "set_prolog_flag in \"...\"");
    return false;
  }
  Term inp0  = inp;
    if (IsPairTerm(inp)) {
        do {
            Term hd = HeadOfTerm(inp);
            inp = TailOfTerm(inp);
            if (IsAtomTerm(hd)) {
                continue;
            }
            if(IsApplTerm(hd)) {
                Functor f = FunctorOfTerm(hd);
                if (!IsExtensionFunctor(f) &&
                    ArityOfFunctor(f) == 1 &&
                    Yap_IsGroundTerm(hd)) {
                    continue;
                }
                if (!Yap_IsGroundTerm(hd))
                    Yap_Error(INSTANTIATION_ERROR, hd, "set_prolog_flag in \"...\"");
                return false;
            }
        } while (IsPairTerm( inp ) );
        if ( inp == TermNil ) {
            return true;
        }
        Yap_Error(TYPE_ERROR_LIST, inp0, "set_prolog_flag in [...]");
        return false;
  } else /* lone option */ {
    if (IsAtomTerm(inp)) {
	 return true;
     } else if(IsApplTerm(inp)) {
       Functor f = FunctorOfTerm(inp);
       if (!IsExtensionFunctor(f) &&
	   ArityOfFunctor(f) == 1 &&
	   Yap_IsGroundTerm(ArgOfTerm(1, inp))) {
	 return true;
       }
     }

  }
  return false;
}

static bool agc_threshold( Term t ) {
  t = Deref(t);
  if (IsVarTerm(t)) {
    CACHE_REGS
    return Yap_unify(t, MkIntegerTerm(GLOBAL_AGcThreshold));
  } else if (!IsIntegerTerm(t)) {
    Yap_Error(TYPE_ERROR_INTEGER,t,"prolog_flag/2 agc_margin");
    return FALSE;
  } else {
    Int i = IntegerOfTerm(t);
    if (i<0) {
      Yap_Error(DOMAIN_ERROR_NOT_LESS_THAN_ZERO,t,"prolog_flag/2 agc_margin");
      return FALSE;
    } else {
      GLOBAL_AGcThreshold = i;
      return TRUE;
    }
  }
}

static bool gc_margin( Term t ) {
  t = Deref(t);
  if (IsVarTerm(t)) {
    return Yap_unify(t, Yap_GetValue(AtomGcMargin));
  } else if (!IsIntegerTerm(t)) {
    Yap_Error(TYPE_ERROR_INTEGER,t,"prolog_flag/2 agc_margin");
    return FALSE;
  } else {
    Int i = IntegerOfTerm(t);
    if (i<0) {
      Yap_Error(DOMAIN_ERROR_NOT_LESS_THAN_ZERO,t,"prolog_flag/2 gc_margin");
      return FALSE;
    } else {
      CACHE_REGS
      Yap_PutValue(AtomGcMargin, MkIntegerTerm( i ));
      return TRUE;
    }
  }
}

static Term mk_argc_list(USES_REGS1) {
  int i = 1;
  Term t = TermNil;
  while (i < GLOBAL_argc) {
    char *arg = GLOBAL_argv[i];
    /* check for -L -- */
    if (arg[0] == '-' && arg[1] == 'L') {
      arg += 2;
      while (*arg != '\0' && (*arg == ' ' || *arg == '\t'))
        arg++;
      if (*arg == '-' && arg[1] == '-' && arg[2] == '\0') {
        /* we found the separator */
        int j;
        for (j = GLOBAL_argc - 1; j > i + 1; --j) {
          t = MkPairTerm(MkAtomTerm(Yap_LookupAtom(GLOBAL_argv[j])), t);
        }
        return t;
      } else if (GLOBAL_argv[i + 1] && GLOBAL_argv[i + 1][0] == '-' &&
                 GLOBAL_argv[i + 1][1] == '-' &&
                 GLOBAL_argv[i + 1][2] == '\0') {
        /* we found the separator */
        int j;
        for (j = GLOBAL_argc - 1; j > i + 2; --j) {
          t = MkPairTerm(MkAtomTerm(Yap_LookupAtom(GLOBAL_argv[j])), t);
        }
        return t;
      }
    }
    if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') {
      /* we found the separator */
      int j;
      for (j = GLOBAL_argc - 1; j > i; --j) {
        t = MkPairTerm(MkAtomTerm(Yap_LookupAtom(GLOBAL_argv[j])), t);
      }
      return (t);
    }
    i++;
  }
  return (t);
}

static Term mk_os_argc_list(USES_REGS1) {
  int i = 0;
  Term t = TermNil;
  for (i = 0; i < GLOBAL_argc; i++) {
    char *arg = GLOBAL_argv[i];
    t = MkPairTerm(MkAtomTerm(Yap_LookupAtom(arg)), t);
  }
  return (t);
}

static bool argv(Term inp) {
  CACHE_REGS
    Term t = mk_argc_list(PASS_REGS1);
    if (IsAtomOrIntTerm(t))
	 GLOBAL_Flags[ARGV_FLAG].at = t;
       else {
	 GLOBAL_Flags[ARGV_FLAG].DBT = Yap_StoreTermInDB(t, 2);
       }
  return false;
}

static bool os_argv(Term inp) {
  CACHE_REGS
     Term t = mk_os_argc_list(PASS_REGS1);
   if (IsAtomOrIntTerm(t))
      GLOBAL_Flags[OS_ARGV_FLAG].at = t;
    else {
      GLOBAL_Flags[OS_ARGV_FLAG].DBT = Yap_StoreTermInDB(t, 2);
    }
  return false;
}




static FlagEntry *
GetFlagProp(Atom a )
{				/* look property list of atom a for kind  */
  AtomEntry *ae = RepAtom(a);
  FlagEntry *pp;

  READ_LOCK(ae->ARWLock);

  pp = RepFlagProp(ae->PropsOfAE);
  while (!EndOfPAEntr(pp) && pp->KindOfPE != FlagProperty)
    pp = RepFlagProp(pp->NextOfPE);
  READ_UNLOCK(ae->ARWLock);

  return pp;
}

static void
initFlag(flag_info *f, int fnum, bool global)
{
  Atom name = Yap_LookupAtom( f->name );
  AtomEntry *ae = RepAtom(name);
  WRITE_LOCK(ae->ARWLock);
  FlagEntry * fprop = RepFlagProp(Yap_GetAPropHavingLock( name, FlagProperty ) );
  if (fprop == NULL) {
    fprop = (FlagEntry *) Yap_AllocAtomSpace(sizeof(FlagEntry));
    if (fprop == NULL) {
      WRITE_UNLOCK(ae->ARWLock);
      Yap_Error(RESOURCE_ERROR_HEAP,TermNil,"not enough space for new Flag %s", ae->StrOfAE);          return;
    }
    fprop->KindOfPE = FlagProperty;
    fprop->FlagOfVE = fnum;
    fprop->rw = f->writable;
    fprop->global = global;
    fprop->type = f->def;
    fprop->helper = f->helper;
    AddPropToAtom(ae, AbsFlagProp(fprop));
  }
  WRITE_UNLOCK(ae->ARWLock);
}

static bool executable(Term inp) {
    CACHE_REGS
  if (GLOBAL_argv && GLOBAL_argv[0])
    Yap_TrueFileName(GLOBAL_argv[0], LOCAL_FileNameBuf, FALSE);
  else
    strncpy(LOCAL_FileNameBuf, Yap_FindExecutable(), YAP_FILENAME_MAX - 1);

  return Yap_unify(MkAtomTerm(Yap_LookupAtom(LOCAL_FileNameBuf)), inp);
}

static bool sys_thread_id(Term inp)
{
    CACHE_REGS
  int pid;
#ifdef HAVE_GETTID_SYSCALL
  pid = syscall(__NR_gettid);
#elif  defined(HAVE_GETTID_MACRO)
  pid = gettid();
#elif defined(__WINDOWS__)
  pid = GetCurrentThreadId();
#else
  pid = 0;
#endif

  return Yap_unify(MkIntegerTerm(pid), inp);
}


static bool setYapFlagInModule( Term tflag, Term t2, Term mod )
{
  CACHE_REGS
  FlagEntry *fv;
  ModEntry *new = Yap_GetModuleEntry(mod);
  if (!new)
    return false;
  fv = GetFlagProp( AtomOfTerm( tflag ) );
  if (!fv && !fv->global) {
    Yap_Error(DOMAIN_ERROR_PROLOG_FLAG, tflag, "trying to set unknown module flag");
    return false;
  }
  if (mod == USER_MODULE && !setYapFlag( tflag, t2) )
      return false;

      // module specific stuff now

  if (fv->FlagOfVE == UNKNOWN_FLAG) {
    if (t2 == TermError) {
      new->flags &= ~(UNKNOWN_FAIL|UNKNOWN_WARNING);
      new->flags |=  (UNKNOWN_ERROR);
      return true;
    } else if (t2 == TermFail) {
      new->flags &= ~(UNKNOWN_ERROR|UNKNOWN_WARNING);
      new->flags |=  (UNKNOWN_FAIL);
      return true;
    } else if (t2 == TermWarning) {
      new->flags &= ~(UNKNOWN_ERROR|UNKNOWN_FAIL);
      new->flags |=  (UNKNOWN_WARNING);
      return true;
    }
    Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, t2, "bad option  %s  for unknown flag, use one of error, fail or warning.", RepAtom(AtomOfTerm(tflag))->StrOfAE);
    return false;
  } else if (fv->FlagOfVE == DOUBLE_QUOTES_FLAG) {
    return dqf1( new, t2 PASS_REGS );
  } else if (fv->FlagOfVE == CHARACTER_ESCAPES_FLAG) {
    if (t2 == TermTrue) {
      new->flags |= M_CHARESCAPE;
      return true;
    } else if (t2 == TermFalse) {
      new->flags &= ~(M_CHARESCAPE);
      return true;
    }
    Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, t2, "bad option %s for character_escapes flag, use true or false", RepAtom(AtomOfTerm(tflag))->StrOfAE);
    return false;
  } else if (fv->FlagOfVE == BACKQUOTED_STRING_FLAG) {
    return bqf1( new, t2 PASS_REGS );;
  }
  // bad key?
  return false;
}

static Term  getYapFlagInModule( Term tflag, Term mod )
{
  FlagEntry *fv;
  ModEntry *new = Yap_GetModuleEntry(mod);
  if (!mod)
    return false;
  fv = GetFlagProp( AtomOfTerm( tflag ) );
  if (!fv && !fv->global) {
    Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, tflag, "trying to set unknown flag");
    return 0L;
  }
      // module specific stuff now

  if (fv->FlagOfVE == UNKNOWN_FLAG) {
    if (new->flags & UNKNOWN_ERROR)
      return TermError;
    if (new->flags & UNKNOWN_WARNING)
      return TermWarning;
    return TermFail;
  } else if (fv->FlagOfVE == CHARACTER_ESCAPES_FLAG) {
    if (new->flags & M_CHARESCAPE)
      return TermTrue;
  } else if (fv->FlagOfVE == BACKQUOTED_STRING_FLAG) {
      if (new->flags & BCKQ_CHARS)
        return TermChars;
      if (new->flags & BCKQ_CODES)
        return TermCodes;
      if (new->flags & BCKQ_ATOM)
        return TermAtom;
      return TermString;
    } else if (fv->FlagOfVE == DOUBLE_QUOTES_FLAG) {
      if (new->flags & DBLQ_CHARS)
        return TermChars;
      if (new->flags & DBLQ_CODES)
        return TermCodes;
      if (new->flags & DBLQ_ATOM)
        return TermAtom;
      return TermString;
    }
     Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, tflag, "flag  %s is not module-scoped", RepAtom(AtomOfTerm(tflag))->StrOfAE);
  return 0L;
}

static Int cont_yap_flag( USES_REGS1) {
  int i = IntOfTerm (EXTRA_CBACK_ARG (2, 1));
  int gmax = GLOBAL_flagCount;
  int lmax = LOCAL_flagCount;
  Term tflag = Deref(ARG1);
  EXTRA_CBACK_ARG (2, 1) = MkIntTerm (i+1);

  if (IsApplTerm( tflag ) && FunctorOfTerm(tflag) == FunctorModule) {
    Term modt = CurrentModule;
    tflag = Yap_StripModule( tflag, &modt );
    while (i != gmax &&
	   i != UNKNOWN_FLAG &&
	   i != CHARACTER_ESCAPES_FLAG &&
	   i !=  BACKQUOTED_STRING_FLAG) i++;
    if (i == gmax) cut_fail();
    EXTRA_CBACK_ARG (2, 1) = MkIntTerm (i+1);
    {
      Term lab = MkAtomTerm( Yap_LookupAtom( global_flags_setup[i].name ) ) ;
      Term val = Deref(ARG2);

      if (! Yap_unify(tflag, lab) )
	return false;
      if (IsVarTerm(val)) {
	Term oval = getYapFlagInModule( lab, modt );
	if (oval == 0)
	  return false;
	return Yap_unify( oval, val );
      } else {
	return setYapFlagInModule( tflag, val,  modt );
      }
    }
    return false;
  }
  if (i >= gmax ) {
    Yap_unify( ARG1, MkAtomTerm( Yap_LookupAtom(local_flags_setup[i-gmax].name ) ) );
    if (i == gmax+lmax-1)
      do_cut(0);
  }  else {
    Yap_unify( ARG1, MkAtomTerm( Yap_LookupAtom( global_flags_setup[i].name ) ) );
  }
  Term flag = getYapFlag( Deref(ARG1) );
  return Yap_unify( flag, ARG2 );
}

static Int yap_flag(USES_REGS1) {
  Term tflag = Deref(ARG1);
  if (IsVarTerm( tflag ) ) {
    EXTRA_CBACK_ARG (2, 1) = MkIntTerm (0);
    return cont_yap_flag( PASS_REGS1 );
  }
  if (IsApplTerm( tflag ) && FunctorOfTerm(tflag) == FunctorModule) {
    Term modt;
    tflag = Yap_StripModule( tflag, &modt );
    if (IsVarTerm(tflag)) {
     EXTRA_CBACK_ARG (2, 1) = MkIntTerm (0);
     return cont_yap_flag( PASS_REGS1 );
    }
    do_cut( 0 );

    if (!isatom(tflag)) return false;
    if (!isatom(modt)) return false;
    if (IsVarTerm(Deref(ARG2))) {
      Term flag = getYapFlagInModule( tflag, modt );
      if (flag == 0)
	return false;
      return Yap_unify( flag, ARG2 );
    } else {
      return setYapFlagInModule( tflag, Deref(ARG2), modt );
    }
  }

  do_cut( 0 );

  if (IsVarTerm(Deref(ARG2))) {
    Term flag = getYapFlag( Deref(ARG1) );
    if (flag == 0)
      return false;
    return Yap_unify( flag, ARG2 );
  }
  return set_prolog_flag( PASS_REGS1 );
}

static Int cont_prolog_flag(USES_REGS1) {
  int i = IntOfTerm (EXTRA_CBACK_ARG (3, 1));
  while (i < GLOBAL_flagCount+LOCAL_flagCount) {
    int gmax = GLOBAL_flagCount;
    int lmax = LOCAL_flagCount;
    Term flag, f;

    if (i >= gmax+lmax) {
      cut_fail();
    } else if (i >= gmax) {
      Yap_unify( ARG1, ( f = MkAtomTerm( Yap_LookupAtom(local_flags_setup[i-gmax].name ) ) ));
     }  else {
      Yap_unify( ARG1, (f = MkAtomTerm( Yap_LookupAtom( global_flags_setup[i].name ) ) ) );
    }
    EXTRA_CBACK_ARG (3, 1) = MkIntTerm (++i);
  flag = getYapFlag( f );
  if (! Yap_unify( f, ARG2 ) )
    return false;
  return setYapFlag( f, Deref(ARG3) );
  }
  cut_fail();
}

/** @pred prolog_flag(? _Flag_,- _OldValue_,+ _NewValue_)

Obtain the value for a YAP Prolog flag and then set it to a new
value. Equivalent to first calling current_prolog_flag/2 with the
second argument  _OldValue_ unbound and then calling
set_prolog_flag/2 with the third argument  _NewValue_.


*/
static Int prolog_flag(USES_REGS1) {
  if (IsVarTerm( Deref(ARG1) ) ) {
      EXTRA_CBACK_ARG (3, 1) = MkIntTerm (0);
      return cont_prolog_flag( PASS_REGS1 );
    }
  do_cut( 0 );
  if (IsVarTerm( Deref(ARG2) ) ) {
    Term flag = getYapFlag( Deref(ARG1) );
    if (flag == 0)
      return false;
    return Yap_unify( flag, ARG2 ) ;
  }
  return setYapFlag( Deref(ARG1), Deref(ARG3) );
}


/** @pred current_prolog_flag(? _Flag_,- _Value_) is iso

Obtain the value for a YAP Prolog flag. Equivalent to calling
yap_flag/2 with the second argument unbound, and unifying the
returned second argument with  _Value_.

*/
static Int current_prolog_flag( USES_REGS1 ) {
  Term tflag = Deref(ARG1);
  Term tout = 0;
  FlagEntry *fv;
  flag_term *tarr;

  if (IsVarTerm(tflag)) {
    EXTRA_CBACK_ARG (2, 1) = MkIntTerm (0);
    return cont_yap_flag( PASS_REGS1 );
  }
  do_cut( 0 );
  if (!IsAtomTerm(tflag)) {
    Yap_Error(TYPE_ERROR_ATOM, tflag, "current_prolog_flag/3");
    return (FALSE);
  }
  fv = GetFlagProp( AtomOfTerm( tflag ) );
  if (!fv) {
    // should itself depend on a flag
    return FALSE;
  }
  if (fv->global)
    tarr = GLOBAL_Flags;
  else
    tarr = LOCAL_Flags;
  tout = tarr[fv->FlagOfVE].at;
  if (IsVarTerm(tout))
    tout = Yap_FetchTermFromDB(tarr[fv->FlagOfVE].DBT);
  return (Yap_unify(ARG2, tout));
}

void Yap_setModuleFlags(ModEntry *new, ModEntry *cme)
{
  CACHE_REGS

  Atom at = new->AtomOfME;
  if (at == AtomProlog || CurrentModule == PROLOG_MODULE) {
      new->flags = M_SYSTEM | UNKNOWN_ERROR  |M_CHARESCAPE | DBLQ_CODES | BCKQ_STRING;
      if (at == AtomUser)
	new->flags =  UNKNOWN_ERROR  |M_CHARESCAPE | DBLQ_CODES | BCKQ_STRING;
 } else if (cme &&
	    cme->flags &&	    cme != new) {
    new->flags = cme->flags;
  } else {
    new->flags = ( UNKNOWN_ERROR  | M_CHARESCAPE | DBLQ_CODES | BCKQ_STRING
		   );
  }
  //printf("cme=%s new=%s flags=%x\n",cme,at->StrOfAE,new->flags);
}

bool setYapFlag( Term tflag, Term t2 )
{
  FlagEntry *fv;
  flag_term *tarr;
   if (IsVarTerm(tflag)) {
    Yap_Error(INSTANTIATION_ERROR, tflag, "yap_flag/2");
    return (FALSE);
  }
  if (IsApplTerm( tflag ) && FunctorOfTerm(tflag) == FunctorModule) {
    Term modt;
    tflag = Yap_StripModule( tflag, &modt );
    if (!isatom(tflag)) return false;
    if (!isatom(modt)) return false;
    return setYapFlagInModule( tflag, t2, modt );
  }
  if (!IsAtomTerm(tflag)) {
    Yap_Error(TYPE_ERROR_ATOM, tflag, "yap_flag/2");
    return (FALSE);
  }
  fv = GetFlagProp( AtomOfTerm( tflag ) );
  if (!fv) {
    Term fl = GLOBAL_Flags[USER_FLAGS_FLAG].at;
    if (fl == TermSilent) {
        CACHE_REGS
        Term t2 = Deref(ARG2);
     newFlag( tflag, t2);
    } else if (fl == TermWarning) {
      Yap_Warning("Flag %s does not exist", RepAtom(AtomOfTerm(fl))->StrOfAE);
    } else {
      Yap_Error(DOMAIN_ERROR_PROLOG_FLAG, fl, "trying to set unknown flag ~s", AtomName(AtomOfTerm(fl)));
    }
      return FALSE;
  }
  if (fv->global)
    tarr = GLOBAL_Flags;
  else {
      CACHE_REGS
      tarr = LOCAL_Flags;
  }
  if (!(fv->type(t2)))
    return false;
  if (fv->helper && !(fv->helper(t2)))
    return false;
  Term tout = tarr[fv->FlagOfVE].at;
  if (IsVarTerm(tout))
     Yap_PopTermFromDB( tarr[fv->FlagOfVE].DBT );
  if (IsAtomOrIntTerm(t2))
    tarr[fv->FlagOfVE].at = t2;
  else {
    tarr[fv->FlagOfVE].DBT = Yap_StoreTermInDB(t2, 2);
  }
  return true;
}

Term getYapFlag( Term tflag )
{
  FlagEntry *fv;
  flag_term *tarr;
   if (IsVarTerm(tflag)) {
    Yap_Error(INSTANTIATION_ERROR, tflag, "yap_flag/2");
    return (FALSE);
  }
  if (IsApplTerm( tflag ) && FunctorOfTerm(tflag) == FunctorModule) {
    Term modt;
    tflag = Yap_StripModule( tflag, &modt );
    if (!isatom(tflag)) return false;
    if (!isatom(modt)) return false;
    return getYapFlagInModule( tflag, modt );
  }
  if (!IsAtomTerm(tflag)) {
    Yap_Error(TYPE_ERROR_ATOM, tflag, "yap_flag/2");
    return (FALSE);
  }
  fv = GetFlagProp( AtomOfTerm( tflag ) );
  if (!fv) {
    Term fl = GLOBAL_Flags[USER_FLAGS_FLAG].at;
    if (fl == TermSilent) {
        return false;
    } else if (fl == TermWarning) {
      Yap_Warning("Flag ~s does not exist", RepAtom(AtomOfTerm(fl))->StrOfAE);
    } else {
      Yap_Error(DOMAIN_ERROR_PROLOG_FLAG, fl, "trying to read unknown flag %s",
		RepAtom(AtomOfTerm(fl))->StrOfAE);
    }
    return FALSE;
  }
  if (fv->global)
    tarr = GLOBAL_Flags;
  else {
      CACHE_REGS
      tarr = LOCAL_Flags;
  }
  Term tout = tarr[fv->FlagOfVE].at;
  if (IsVarTerm(tout))
    return Yap_FetchTermFromDB( tarr[fv->FlagOfVE].DBT );
  else
    return tout;
}

/** @pred set_prolog_flag(+ _Flag_,+ _Value_) is iso

Set the value for YAP Prolog flag `Flag`. Equivalent to
calling yap_flag/2 with both arguments bound.

*/
static Int set_prolog_flag(USES_REGS1) {
  Term tflag = Deref(ARG1), t2 = Deref(ARG2);
  return setYapFlag( tflag, t2 );
 }

/**   @pred source

After executing this goal, YAP keeps information on the source
of the predicates that will be consulted. This enables the use of
[listing/0](@ref listing), `listing/1` and [clause/2](@ref clause) for those
clauses.

The same as `source_mode(_,on)` or as declaring all newly defined
static procedures as `public`.
*/
static Int source(USES_REGS1) {
  setBooleanGlobalPrologFlag(SOURCE_FLAG, true);
  return true;
}

/** @pred no_source
 The opposite to `source`.

The same as `source_mode(_,off)`.

*/
static Int no_source(USES_REGS1) {
  setBooleanGlobalPrologFlag(SOURCE_FLAG, false);
  return true;
}


/**
   @pred source_mode(- _O_,+ _N_)

The state of source mode can either be on or off. When the source mode
is on, all clauses are kept both as compiled code and in a "hidden"
database.  _O_ is unified with the previous state and the mode is set
according to  _N_.

*/
static Int
source_mode( USES_REGS1 )
{
  Term targ;
  bool current = trueGlobalPrologFlag(SOURCE_FLAG);
  if (current && !Yap_unify_constant( ARG1, TermTrue ) )
    return false;
  if (!current && !Yap_unify_constant( ARG1, TermFalse) )
    return false;
  targ = Deref(ARG2);
  setYapFlag( TermSource, ARG2 );
  return true;
}


static bool
setInitialValue( bool bootstrap, flag_func f, const char *s,flag_term *tarr )
{
  errno = 0;

  if (f == boolean) {
    if (!bootstrap) { return 0; }
    if (!strcmp(s, "true")) {
      tarr->at = TermTrue;
      return true;
    }
    if (!strcmp(s, "false")) {
      tarr->at =  TermFalse;
       return true;
   }
    if (!strcmp(s, "on")) {
      tarr->at =  TermTrue;
      return true;
    }
    if (!strcmp(s, "off")) {
      tarr->at =  TermFalse;
      return true;
    }
    Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, TermNil, "~s should be either true (on) or false (off)", s);
    return false;
  } else if (f == nat) {
    if (!bootstrap) { return 0; }
    UInt r = strtoul(s, NULL, 10);
    Term t;
    if (errno) {
      Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, TermNil, "~s should be a positive integer)", s);
      return false;
    }
    CACHE_REGS
      t= MkIntegerTerm(r);
    if (IsIntTerm(t))
      tarr->at =  t;
    else {
      tarr->DBT = Yap_StoreTermInDB(t, 2);
    }
    return true;
  } else if (f == at2n) {
    if (!bootstrap) { return 0; }
    if (!strcmp(s, "INT_MAX")) {
      tarr->at = MkIntTerm(Int_MAX);
      return true;
    }
    if (!strcmp(s, "MAX_THREADS")) {
      tarr->at =  MkIntTerm(MAX_THREADS);
       return true;
    }
    if (!strcmp(s, "MAX_WORKERS")) {
      tarr->at =  MkIntTerm(MAX_WORKERS);
       return true;
    }
    if (!strcmp(s, "INT_MIN")) {
      tarr->at =  MkIntTerm(Int_MIN);
       return true;
    }
    if (!strcmp(s, "YAP_NUMERIC_VERSION")) {
      tarr->at =  MkIntTerm(atol(YAP_NUMERIC_VERSION));
       return true;
    }
    if (!strcmp(s, "YAP_NUMERIC_VERSION")) {
      tarr->at =  MkIntTerm(atol(YAP_NUMERIC_VERSION));
       return true;
    }
    Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, TermNil, "~s should be either true (on) or false (off)", s);
    return false;
  } else if (f == isatom) {
    if (!bootstrap) { return false; }
    Atom r = Yap_LookupAtom(s);
    if (errno) {
      Yap_Error(DOMAIN_ERROR_OUT_OF_RANGE, TermNil, "~s should be a positive integer)", s);
      tarr->at =  TermNil;
    }
    tarr->at =  MkAtomTerm( r );
    return true;
  } else {
    Term t0;
    if (bootstrap) { return false; }
    CACHE_REGS
    t0 = Yap_StringToTerm(s, strlen(s)+1, &LOCAL_encoding, 1200, NULL);
    if (!t0)
      return false;
    if (IsAtomTerm(t0) || IsIntTerm(t0)) {
      // do yourself flags
      if (t0 == MkAtomTerm(AtomQuery)) {
	f(TermNil);
      } else {
	tarr->at =  t0;
      }
    } else {
      tarr->DBT = Yap_StoreTermInDB(t0, 2);
    }
    return true;
  }
}

#define    PROLOG_FLAG_PROPERTY_DEFS() \
  PAR( "access", isaccess, PROLOG_FLAG_PROPERTY_ACCESS, "read_write" ),	\
    PAR( "type", isground, PROLOG_FLAG_PROPERTY_TYPE, "term" ),			\
    PAR( "scope", flagscope, PROLOG_FLAG_PROPERTY_SCOPE, "global" ),		\
    PAR( "keep", boolean, PROLOG_FLAG_PROPERTY_KEEP, "false" ),			\
    PAR( NULL, ok, PROLOG_FLAG_PROPERTY_END, 0 )

#define PAR(x,y,z,w) z

 typedef enum prolog_flag_property_enum_choices
  {
    PROLOG_FLAG_PROPERTY_DEFS()
  } prolog_flag_property_choices_t;

#undef PAR

#define PAR(x,y,z, w) { x , y, z, w }


 static const param2_t prolog_flag_property_defs[] =
  {
    PROLOG_FLAG_PROPERTY_DEFS()
  };
#undef PAR



static Int
do_prolog_flag_property (Term tflag, Term opts USES_REGS)
{				/* Init current_prolog_flag */
  FlagEntry *fv;
  xarg *args;
  prolog_flag_property_choices_t i;
  bool rc = true;
  args = Yap_ArgList2ToVector ( opts, prolog_flag_property_defs, PROLOG_FLAG_PROPERTY_END  );
  if (args == NULL) {
    return FALSE;
  }
  if (!IsAtomTerm(tflag)) {
    if (IsApplTerm( tflag ) && FunctorOfTerm(tflag) == FunctorModule) {
      Term modt = CurrentModule;
      tflag = Yap_YapStripModule( tflag, &modt );
    } else {
      Yap_Error(TYPE_ERROR_ATOM, tflag, "yap_flag/2");
      return (FALSE);
    }
  }
  fv = GetFlagProp( AtomOfTerm( tflag ) );
  for (i=0; i < PROLOG_FLAG_PROPERTY_END; i ++) {
    if (args[i].used) {
      switch (i) {
      case PROLOG_FLAG_PROPERTY_ACCESS:
	if (fv->rw)
	  rc = rc &&
	    Yap_unify(TermReadWrite, args[PROLOG_FLAG_PROPERTY_ACCESS].tvalue);
	else
	  rc = rc &&
	    Yap_unify(TermReadOnly, args[PROLOG_FLAG_PROPERTY_ACCESS].tvalue);
	break;
      case PROLOG_FLAG_PROPERTY_TYPE:
	if (fv->type == boolean)
	  rc = rc &&
	    Yap_unify(TermBoolean, args[PROLOG_FLAG_PROPERTY_TYPE].tvalue);
	else if  (fv->type == isatom)
	  rc = rc &&
	    Yap_unify(TermAtom, args[PROLOG_FLAG_PROPERTY_TYPE].tvalue);
	else if  (fv->type == nat)
	  rc = rc &&
	    
	    Yap_unify(TermInteger, args[PROLOG_FLAG_PROPERTY_TYPE].tvalue);
	else if  (fv->type == isfloat)
	  rc = rc &&
	    Yap_unify(TermFloat, args[PROLOG_FLAG_PROPERTY_TYPE].tvalue);
	else
	  rc = rc &&
	    Yap_unify(TermTerm, args[PROLOG_FLAG_PROPERTY_TYPE].tvalue);
	break;
     case PROLOG_FLAG_PROPERTY_KEEP:
       rc = rc && false;
       break;
     case PROLOG_FLAG_PROPERTY_SCOPE:
       if (fv->global) {
	 if (fv->FlagOfVE == UNKNOWN_FLAG ||
	     fv->FlagOfVE == CHARACTER_ESCAPES_FLAG ||
	     fv->FlagOfVE ==  BACKQUOTED_STRING_FLAG)
	   Yap_unify(TermModule, args[PROLOG_FLAG_PROPERTY_SCOPE].tvalue);
	  rc = rc &&
	    Yap_unify(TermGlobal, args[PROLOG_FLAG_PROPERTY_SCOPE].tvalue);
       } else
	  rc = rc &&
	    Yap_unify(TermThread, args[PROLOG_FLAG_PROPERTY_SCOPE].tvalue);
       break;
      case PROLOG_FLAG_PROPERTY_END:
	/* break; */
	Yap_Error(DOMAIN_ERROR_PROLOG_FLAG, opts, "Flag not supported by YAP");
      }
    }
  }
  // UNLOCK(GLOBAL_Prolog_Flag[sno].prolog_flaglock);
  return rc;
}

static Int
cont_prolog_flag_property (USES_REGS1)
{				/* current_prolog_flag */
  int i = IntOfTerm (EXTRA_CBACK_ARG (2, 1));

  while (i < GLOBAL_flagCount+LOCAL_flagCount) {
    int gmax = GLOBAL_flagCount;
    int lmax = LOCAL_flagCount;
    Term lab;

    if (i >= gmax+lmax) {
      cut_fail();
    } else if (i >= gmax) {
      lab = MkAtomTerm( Yap_LookupAtom( local_flags_setup[i-gmax].name ) ) ;
     }  else {
      if (i == UNKNOWN_FLAG ||
	  i == CHARACTER_ESCAPES_FLAG ||
	  i ==  BACKQUOTED_STRING_FLAG) {
	Term labs[2];
	labs[0] = MkVarTerm();
  	labs[1] = MkAtomTerm( Yap_LookupAtom( global_flags_setup[i].name ) ) ;
	lab = Yap_MkApplTerm(FunctorModule, 2, labs);
      } else {
	lab =  MkAtomTerm( Yap_LookupAtom( global_flags_setup[i].name ) ) ;
      }
    }
    EXTRA_CBACK_ARG (2, 1) = MkIntTerm (++i);
    Yap_unify(ARG1, lab);
    return do_prolog_flag_property(lab, Deref(ARG2) PASS_REGS);
  }
  cut_fail();
}

/** @pred prolog_flag_property(+ _Flag_,+ _Prooperties_)

Report a property for a YAP Prolog flag.  _Properties_ include

   * `type(+_Type_)` with _Type_ one of `boolean`, `integer`, `float`, `atom`
and `term` (that is, any ground term)

   * `access(+_Access_)` with  _Access_ one of `read_only` or `read_write`

   * `scope(+_Scope_) the flag aplies to a `thread`, to a `module`, or is `global` to the system.

*/
static Int
prolog_flag_property (USES_REGS1)
{				/* Init current_prolog_flag */
  Term t1 = Deref(ARG1);
  /* make valgrind happy by always filling in memory */
  EXTRA_CBACK_ARG (2, 1) = MkIntTerm (0);
  if (IsVarTerm(t1)) {
    return (cont_prolog_flag_property (PASS_REGS1));
  } else {
    if (IsApplTerm( t1 ) && FunctorOfTerm(t1) == FunctorModule) {
      Term modt;
      t1 = Yap_StripModule( t1, &modt );
      if (IsAtomTerm( modt )) {
	Int rc;
	rc = cont_prolog_flag_property( PASS_REGS1 );

	return rc;
      }
    }  else if (IsAtomTerm(t1)) {
      do_cut(0);
      return do_prolog_flag_property( t1, Deref(ARG2) PASS_REGS);
    } else {
      Yap_Error(TYPE_ERROR_ATOM, t1, "prolog_flag_property/2");
    }
  }
  return false;
}


static void
newFlag( Term fl, Term val )
{
    flag_info f;
    int i =     GLOBAL_flagCount;

  GLOBAL_flagCount ++;
  f.name = (char *)RepAtom(AtomOfTerm(fl))->StrOfAE;
  f.writable = true;
  f.helper = 0;
  f.def = ok;
  initFlag(&f, i , true);
    if (IsAtomOrIntTerm(val)) {
        GLOBAL_Flags[i].at = val;
        } else {
        GLOBAL_Flags[i].DBT = Yap_StoreTermInDB(val, 2);
        }
}

static Int
do_create_prolog_flag( USES_REGS1 )
{
  FlagEntry *fv;
  xarg *args;
  prolog_flag_property_choices_t i;
  Term tflag = Deref(ARG1), tval = Deref(ARG2), opts = Deref(ARG3);

  args = Yap_ArgList2ToVector ( opts, prolog_flag_property_defs, PROLOG_FLAG_PROPERTY_END  );
  if (args == NULL) {
    return FALSE;
  }
  fv = GetFlagProp( AtomOfTerm( tflag ) );
  if (fv) {
    if (args[PROLOG_FLAG_PROPERTY_KEEP].used && args[PROLOG_FLAG_PROPERTY_KEEP].tvalue == TermTrue)
      return true;
  } else {
    newFlag( tflag, tval );
    fv = GetFlagProp( AtomOfTerm( tflag ) );
  }
  for (i=0; i < PROLOG_FLAG_PROPERTY_END; i ++) {
    if (args[i].used) {
      switch (i) {
     case PROLOG_FLAG_PROPERTY_KEEP:
       break;
      case PROLOG_FLAG_PROPERTY_ACCESS:
	if (args[PROLOG_FLAG_PROPERTY_ACCESS].tvalue == TermReadWrite)
	  fv->rw = true;
	else
	  fv->rw = false;
	break;
      case PROLOG_FLAG_PROPERTY_TYPE:
	{
	  Term ttype = args[PROLOG_FLAG_PROPERTY_TYPE].tvalue;
	if (ttype == TermBoolean)
	  fv->type = boolean;
	else if (ttype == TermInteger)
	  fv->type = isatom;
	else if (ttype == TermFloat)
	  fv->type = isfloat;
	else
	  fv->type = isground;
	}
	break;
     case PROLOG_FLAG_PROPERTY_SCOPE:
 	return
	  false;
      case PROLOG_FLAG_PROPERTY_END:
	break;
      }
    }
  }
  //UNLOCK(GLOBAL_Prolog_Flag[sno].prolog_flaglock);
  return true;

}

/**
 * Init System Prolog flags. This is done in two phases:
 *   early on, it takes care of the atomic flags that are required by other modules;
 * later, it looks at flags that are structured terms
 *
 * @param bootstrap: wether this is done before stack initialization, or afterwards.
 * Complex terms can only be built in the second step.
 */

void
Yap_InitFlags( bool bootstrap) {
    CACHE_REGS
      tr_fr_ptr tr0 = TR;
      flag_info *f =
    global_flags_setup;
  GLOBAL_flagCount = 0;
  if (bootstrap) {
    GLOBAL_Flags = (union flagTerm *)Yap_AllocCodeSpace(sizeof(union flagTerm)*(2*sizeof(global_flags_setup)/sizeof(flag_info)));
  }
  while (f->name != NULL) {
    bool itf = setInitialValue( bootstrap, f->def, f->init, GLOBAL_Flags+GLOBAL_flagCount );
    if (itf) {
      initFlag( f, GLOBAL_flagCount, true);
    }
    GLOBAL_flagCount ++;
   f++;
  }
  LOCAL_flagCount = 0;
  int nflags = sizeof(local_flags_setup)/sizeof(flag_info);
  if (bootstrap)
    LOCAL_Flags = (union flagTerm *)Yap_AllocCodeSpace(sizeof(union flagTerm)*nflags);
  f =
    local_flags_setup;
  while (f->name != NULL) {
    bool itf = setInitialValue( bootstrap, f->def, f->init, LOCAL_Flags+LOCAL_flagCount );
    //    Term itf = Yap_StringToTerm(f->init, strlen(f->init)+1, LOCAL_encoding, 1200, &tp);
    if (itf) {
      initFlag( f, LOCAL_flagCount, false);
    }
    LOCAL_flagCount ++;
    f++;
  }
  if (!bootstrap) {
    Yap_InitCPredBack("current_prolog_flag", 2, 1, current_prolog_flag, cont_yap_flag, 0);
    TR = tr0;
    /** @pred prolog_flag(? _Flag_,- _Value__)

	Obtain the value for a YAP Prolog flag, same as current_prolog_flag/2_.
    */
    Yap_InitCPredBack("prolog_flag", 3, 1, prolog_flag, cont_yap_flag, 0);
    Yap_InitCPredBack("prolog_flag", 2, 1, prolog_flag, cont_yap_flag, 0);
    Yap_InitCPred("set_prolog_flag", 2, set_prolog_flag, SyncPredFlag);
    Yap_InitCPred("$create_prolog_flag", 3, do_create_prolog_flag, SyncPredFlag);
    Yap_InitCPredBack("yap_flag", 2, 1, yap_flag,cont_yap_flag, 0);
    Yap_InitCPredBack("prolog_flag_property", 2, 1, prolog_flag_property,cont_prolog_flag_property, 0);
    Yap_InitCPred("source", 0, source,SyncPredFlag);
    Yap_InitCPred("no_source", 0, no_source,SyncPredFlag);
    Yap_InitCPred("source_mode", 2, source_mode, SyncPredFlag);
  }
}


  /* Accessing and changing the flags for a predicate */