/** 
   -- Almost-Non-Volatile Environment Options
   
    Copyright (C) Tektronix, Inc. 2001 - 2001. All rights reserved.
  
    @see     GNU LGPL
    @author  Tektronix CTE              @(#) %derived_by: guidod %
    @version %version: bln_mpt1!1.32 %
      (%date_modified: Wed Jul 17 15:48:59 2002 %)
  
    @description
         the openfirmware standard specifies some means to add
         options to a non-volatile ram-area (nvram) that are used
         in the bootup sequence of the forth-based bootsystem.
         Here we add an internal API for looking for bootup options,
         an internal API to add bootup options through commandline
         processing, and an external API to change the bootup options
         for a => COLD reboot or an => APPLICATION specific sequence.
         The external API will try to follow openfirmware as closely
         as possible without having a real non-volatile ram-area.
         Instead there is a session-struct that can be changed and
         from which multiple forth-threads can be instantiated
         later using those options. The forth-instantion processing
         is not supposed to scan for commandline options, which is
         a mere necessity in embedded enviroments where option
         transferal is done in a completly different way and where
         the forth thread is never killed but just stopped or
         restarted for various reasons. Even that there is no real
         nvram we add a cold-options reboot-area in this wordset.
         The option-ram is organized just along a normal dictionary
         just need to add a wordlist-handlestruct to find the
         definitions in this dictlike ram-portion.
 */
/*@{*/
#if defined(__version_control__) && defined(__GNUC__)
static char* id __attribute__((unused)) = 
"@(#) $Id: %full_filespec:  option-ext.c~bln_mpt1!1.32:csrc:bln_12xx!1 % $";
#endif

#define _P4_SOURCE 1

#include <pfe/pfe-base.h>
#include <pfe/option-ext.h>

#include <stdlib.h>
#include <string.h>

#include <pfe/logging.h>

#ifdef __vxworks
#include <sysSymTbl.h>
#else
#include <errno.h>
#endif
#include <ctype.h>

#ifndef _export
typedef p4_Session p4_Options;
#endif

#define OPT (opt->opt)

#define IS_VALUE_RT(X) (*P4_TO_CODE(X) == PFX(p4_value_RT) \
                     || *P4_TO_CODE(X) == PFX(p4_constant_RT))

FCode_RT (p4_string_RT)
{   FX_USE_BODY_ADDR 
{
    p4char* str = (p4char*) FX_POP_BODY_ADDR;
    FX_PUSH (str);
    FX_PUSH (strlen(str));
}
}
static FCode (p4_string) 
{ /* dummy */ }
P4RUNTIME1(p4_string, p4_string_RT);
/* ready for FX_GET_RT optimization */
/*
   returns the CFA of the option if found
 */
_export p4xt 
p4_search_option (const p4char* nm, int l, p4_Options* opt)
{
    auto char upper[32];

    if (l < 32) 
    
{ memcpy (upper, nm, l); p4_upper (upper, l); }
else
{ *(int*)upper = 0; }
{ /* compare with dict-sub:search_thread called by p4_search_wordlist */
        p4char* t = OPT.link;
        while (t)
        
{
            if (!(*_FFA(t) & P4xSMUDGED) && NFACNT(*t) == l) 
            
{ 
                if (! memcmp (nm, t+1, l) || ! memcmp (upper, t+1, l))
                    return p4_name_from (t);

                /* omitted extra strncmpi here... and no warning... */
            }
t = *p4_name_to_link (t); }
}
return 0; }
/*
   create a new header in the option-dict and return
   the xt. Unlike other header-creations, here we never
   smudge the name, and the caller is responsible to
   setup the value of the CFA. If no header could be
   created then the function will return null - check for that!
 */
_export p4xt
p4_create_option (const p4char* name, int len, int size, p4_Options* opt)
{
    /* compare with dict-sub:p4_header_comma */

    if (len == 0 || len > NFACNTMAX 
      || OPT.dictlimit < OPT.dp + len + 2*sizeof(p4char) + 4*sizeof(p4cell) )
        return 0; /* invalid or dict exhausted */

# if defined PFE_WITH_FFA
    OPT.dp += 2; OPT.dp += len; while (! P4_ALIGNED(OPT.dp)) OPT.dp++;
    memmove (OPT.dp-len, name, len);
    OPT.last = OPT.dp-len -1;
    *OPT.last = len;
    OPT.last[-1] = '\x80';
# elif defined PFE_WITH_FIG
    OPT.dp += 1; OPT.dp += len; while (! P4_ALIGNED(OPT.dp)) OPT.dp++;
    memmove (OPT.dp-len, name, len);
    OPT.last = OPT.dp-len -1;
    *OPT.last = len;
    *OPT.last |= '\x80';
#else
    OPT.last = OPT.dp++;
    if (name != OPT.dp) memcpy (OPT.dp, name, len);
    *OPT.last = len;
    *OPT.last |= '\x80';
    OPT.dp += len; while (! P4_ALIGNED(OPT.dp)) OPT.dp++;
#endif

    *((pfe_lfa_t*)(OPT.dp))++ = OPT.link;
    OPT.link = OPT.last;
    return ((p4xt)(OPT.dp))++; 
}
/*
   search the option value in the option-ram, if nothing
   is found then return the argument default. The option-ram
   is not changed.
   (in vxworks/k12xx: lookup also p4__default_<optionname> datasymbol)
   (in posixish os: lookup also PFE_<OPTIONNAME> environment variable)
 */
_export p4cell
p4_search_option_value (const p4char* nm, int l, 
                        p4cell defval, p4_Options* opt)
{
    p4xt xt = p4_search_option (nm, l, opt);
    if (xt && IS_VALUE_RT(xt)) return *P4_TO_BODY(xt);
    /* else return deval; */
    if (l >= 32) return defval;

    
{ /* generic option passing via vx-start symbols-settings */
#      ifdef __vxworks 
        long* symval;
        const p4char prefix[] = "p4__default_";
#      else
        const p4char prefix[] = "pfe_default_";
#      endif
#       define strlen_prefix (sizeof(prefix)-1)
        p4char symbol[strlen_prefix+32+5];
        p4char* s;

        if (*nm == '/') 
{ 
            memcpy (&symbol[0], prefix, strlen_prefix);
            memcpy (&symbol[strlen_prefix], nm+1, l-1);
            memcpy (&symbol[strlen_prefix+l-1], "_size", 6);
        }
else
{
            memcpy (&symbol[0], prefix, sizeof(prefix)-1);
            memcpy (&symbol[strlen_prefix], nm, l);
            /*    */ symbol[strlen_prefix+l] = '\0';
        }
/* forth-symbols may contain non-alnums, need to sanitize */ for (s=symbol; *s; s++) if (! isalnum(*(p4char*)s)) *s = '_'; # ifdef __vxworks if (symFindByName (sysSymTbl, symbol, (char**) &symval, 0) == OK) if (symval)
{
                P4_info4 ("seen '%.*s' = %ld (%s)", l, nm, *symval, symbol);
                return *symval;
            }
# else # ifndef _toupper # define _toupper(X) toupper(X) # endif for (s=symbol; *s; s++) if (islower(*s)) *s = _toupper(*s); if ((s=getenv(symbol)))
{
            int newval;  errno = 0; newval = atoi (s);
            if (! errno)
            
{
                P4_info4 ("seen '%.*s' = %d (%s)", l, nm, newval, symbol);
                return newval;
            }
}
# endif P4_info4 ("keep '%.*s' = %ld (%s not found)", l, nm, (long)defval, symbol); }
return defval; }
/*
   return the value of an option that is already in the
   option-ram. If no option is found, add the default we
   give as an argument, and return this parameter as the result.
 */
_export p4cell
p4_create_option_value (const p4char* nm, int l, 
                        p4cell defval, p4_Options* opt)
{
    p4xt xt = p4_search_option (nm, l, opt);
    if (xt && IS_VALUE_RT(xt))
        return *P4_TO_BODY(xt);
    else
{
        xt = p4_create_option (nm, l, sizeof(p4cell), opt);
        if (! xt) return defval; 
	P4_XT_VALUE(xt) = FX_GET_RT (p4_value);
        return (( *(p4cell*)OPT.dp = defval ));
    }
}
/*
   change the value of an option that is already in the
   option-ram. If no option-var is found, create that option
   and set the var to the argument value. Return the xt, or 
   null if the option-ram is filled up.
 */
_export p4xt
p4_change_option_value (const p4char* nm, int l, 
                        p4cell defval, p4_Options* opt)
{
    p4xt xt = p4_search_option (nm, l, opt);
    if (! xt || ! IS_VALUE_RT(xt))
    
{
        xt = p4_create_option (nm, l, sizeof(p4cell), opt);
        if (! xt) return 0; 
	P4_XT_VALUE(xt) = FX_GET_RT (p4_value);
    }
*P4_TO_BODY(xt) = defval; return xt; }
/*
   search the option value in the option-ram, if nothing
   is found then return the argument default. The option-ram
   is not changed.
 */
_export const p4char*
p4_search_option_string (const p4char* nm, int l, 
                         const p4char* defval, p4_Options* opt)
{
    p4xt xt = p4_search_option (nm, l, opt);
    if (!xt || *P4_TO_CODE(xt) != PFX(p4_string_RT)) return defval;
    return ((void*) P4_TO_BODY(xt));
}
/*
   return the value of an option that is already in the
   option-ram. If no option is found, add the default we
   give as an argument. The string is zero-terminated (!!)
 */
_export const p4char*
p4_create_option_string (const p4char* nm, int l, 
                         const p4char* defval, p4_Options* opt)
{
    p4xt xt = p4_search_option (nm, l, opt);
    if (xt && *P4_TO_CODE(xt) == PFX(p4_string_RT))
        return ((void*) P4_TO_BODY(xt));
    else
{
        xt = p4_create_option (nm, l, sizeof(strlen(defval))+1, opt);
        if (! xt) return defval; 
	P4_XT_VALUE(xt) = FX_GET_RT (p4_string);
        memcpy (OPT.dp, defval, strlen(defval) +1);
        return ((void*) P4_TO_BODY(xt));
    }
}
/*
   change the value of an option that is already in the
   option-ram. If no option var is found, create that var
   and set it to the argument string. The string is 
   zero-terminated. The return is the xt or zero if
   no option-var could be created.
 */
_export p4xt
p4_change_option_string (const p4char* nm, int l, 
                         const p4char* defval, p4_Options* opt)
{
    p4xt xt = p4_search_option (nm, l, opt);
    if (! xt || *P4_TO_CODE(xt) != PFX(p4_string_RT)
      || (*P4_TO_LINK(xt) && strlen(defval)+1 
        > ((p4char*)_FFA(*P4_TO_LINK(xt)) - (p4char*)P4_TO_BODY(xt))))
    
{
        xt = p4_create_option (nm, l, strlen(defval)+1, opt);
        if (! xt) return 0; 
	P4_XT_VALUE(xt) = FX_GET_RT (p4_string);
    }
memcpy ((void*)P4_TO_BODY(xt), defval, strlen(defval) +1); return xt; }
/* ---------------------------------------------------------------- */
/*
   a string shall be converted into a cell that should flag the
   size of something like the dictionary or stack. The second
   argument gives an idea about the default if no size-specifier
   has been provided. This routine does match the arg-option
   processing of gforth - had an e-mail exchange with anton ertl 
   about this. Does return null on any errors.
 */
_export p4ucell
p4_convsize (const char* s, p4ucell elemsize)
{/* converts s of the format [0-9]+[bekMGT]? (e.g. 25k) into the number
    of bytes.  the letter at the end indicates the unit, where e stands
    for the element size. default is e */
    char *endp;
    p4ucell n,m;

    m = elemsize;
    n = strtoul(s,&endp,0);
    if (endp != NULL) 
{
        if (*endp == 'b' || *endp == 'c')
        
{ m = 1; if (*++endp == 'e') m *= elemsize; }
else if (*endp == 'k' || *endp == 'K')
{ m = 1024; if (*++endp == 'e') m *= elemsize; }
else if (*endp == 'M')
{ m = 1024*1024; if (*++endp == 'e') m *= elemsize; }
else if (*endp == 'G')
{ m = 1024*1024*1024; if (*++endp == 'e') m *= elemsize;  }
else if (*endp == 'T')
{
#         if (PFE_SIZEOF_VOIDP > 4)
            
{ m = 1024L*1024*1024*1024; endp++; if (*++endp) m *= elemsize; }
# else P4_fail1 ("size specification \"%s\" " "too large for this machine\n", endp); return 0; # endif }
if (*endp != 'e' && *endp != 'b' && *endp != 'B' && *endp != 0)
{
            P4_fail2 ("cannot grok size specification %s: "
                      "invalid unit \"%s\"\n", s, endp);
            return 0;
        }
}
return m * n; }
P4_LISTWORDS (option) =
{
    P4_INTO ("ENVIRONMENT", 0),
    P4_OCON ("OPTION-EXT",	2001 ),
}
; P4_COUNTWORDS (option, "Option Words For Almost-Non-Volatile Environment");
/*@}*/
/* 
   Local variables:
   c-file-style: "stroustrup"
   End:
 */