                                GEMENV v1.1
             Environment Variable Manager for the GEM Desktop
                    Public Domain Program by Ian Lepore


Overview


    Environment variables (referred to as "env vars" in much of this
    document) are a facility for storing information in character-string
    format for a duration that exceeds the run of a single program.  (My,
    that sounds stuffy.)  Many popular operating systems support env vars,
    among them MSDOS, Unix, and GEMDOS.  Unfortunately, while GEMDOS
    supports env vars, the GEM desktop doesn't, so it's a feature that goes
    largely unused on the ST.

    This program was written to bring to GEM desktop users all the power of
    environment variables that command-shell users have been enjoying for
    years.  With some software, such as the public domain Sozobon C
    compiler, env vars are required for the software to work correctly.


About Environment Variables


    Environment variables are stored in memory.  They are character strings
    with a format of "VARIABLE=VALUE".  Every program inherits a copy of
    the env vars owned by its parent (the program that started it).  DOS is
    responsible for making a copy of the parent's env data area at the time
    that it starts a new program.  It places a pointer to this copy in the
    program's basepage, so that the program can access the env var data
    through the pointer.

    When programming in C, there are two functions for accessing env vars,
    getenv() and putenv().  These functions retrieve or store a value in
    the env data area.  However, since each program receives a copy of its
    parent's env vars, using putenv() won't make permanent changes to the
    system's env data area; it can only affect the program's local copy.
    (GEMENV.PRG uses the ST cookie jar to work around this problem.)
    Therefore, the biggest use of env vars for most programs involves
    reading the values that are already there.  Programs such as compilers
    often read env vars to get device and path information such as where to
    search for #include files or runtime libraries.

    When using GEM, the desktop is the parent program to all programs that
    are started by clicking on a file.  These programs, therefore, inherit
    a copy of the desktop's env data area.  Unfortunately, the desktop
    doesn't keep much useful information in its env data area.  In fact,
    it's only a few bytes long, and contains the string "PATH=C:\" (or
    "PATH=A:\").  GEM itself uses this env var (PATH=) to locate RSC files
    when they are not found in the current default directory.


Using GEMENV


    The GEMENV program has two different modes, depending on whether it is
    run from the AUTO folder or from the desktop.  When run from AUTO, it
    prepares the system to support env vars, and loads the initial env vars
    from a file called ROOT.ENV, which must be in the root directory of the
    boot drive.  This process is described in greater detail in the section
    labeled "How GEMENV Works."

    When run from the desktop, the program does a check to see if it was
    installed from the AUTO folder at bootup time.  If not, the program
    will complain that a root environment doesn't exist.  In this case, you
    can still use the program to create an initial set of env vars and save
    them into a ROOT.ENV file, and then reboot to install them into the
    system.  (If you obtained this program as part of Ian's Sozobon C
    release, the Sozobon installation program will have created your
    initial ROOT.ENV file for you.)

    The main interface to the program is a large dialog box that lists all
    the current in-memory env vars and their values.  At the bottom of the
    dialog is a series of buttons providing access to the major functions
    of the program.  The displayed variables are also active buttons:
    clicking on a variable in the scolling area of the dialog will bring up
    the edit dialog which allows you to edit or delete a variable.

    Most of the buttons are self-explanatory, but several need further
    explanation.  The CANCEL button will allow you to exit the program
    without saving any changes you've done into the system env data area.
    The OK button saves all changes INTO MEMORY ONLY, and then exits.

    The OPTIONS button provides access to the LOAD FILE and SAVE FILE
    options.  It is possible to start the program, use the LOAD option to
    load a set of variables from a file, make changes to the variables, use
    the SAVE option to store them, and then exit via CANCEL so that the
    changes don't affect the current in-memory variables even though
    they've been stored to a file.  It can be handy to keep several
    different configurations stored in different files, especially if you
    use more than one C compiler.  Remember that at bootup time, GEMENV
    will only install itself if a ROOT.ENV file exists, so this file should
    contain your most common configuration.

    The OPTIONS dialog also provides a choice between PC FORMAT and TOS
    FORMAT environment variables.  This choice affects the way the
    variables are formatted in memory.  TOS FORMAT variables treat the
    variable's name and value as two separate strings, each with its own
    null terminator character (eg, "VARIABLE=\0VALUE\0").  The more common
    PC FORMAT stores the variable name and value as a single string (eg,
    "VARIABLE=VALUE\0").  It is recommended that you use PC FORMAT unless
    your software specifically requires TOS FORMAT.  (You may need to use
    TOS FORMAT if you're using TOS 1.0 -- I'm not sure about this yet.)

    GEMENV places few restrictions on the names or values stored in env
    vars.  The only hardcore restrictions are that variable names can't
    contain an '=' character (since that delimits the name from the value),
    and you can't store binary data in the name or value string.  (Mainly
    because GEMENV doesn't have a hex editor, but also because a binary
    zero in any position would be mistaken for the end of the value
    string.)  In general, the programs which use env vars dictate what the
    names of env vars must be and what types of data should appear in the
    value string.  Most programs require uppercase names, and some require
    uppercase values, but these are not universal standards.

    The GEM desktop really likes to see a PATH= variable in the set
    somewhere, and the value string must be a list of valid pathnames
    separated with semicolons between each name, and with the trailing
    backslash on each pathname.  Most ST software that uses a PATH=
    variable is also happy with this format.  (And, if you design your own
    software, you should stay compatible with this if you access the PATH=
    variable.)  As mentioned previously, GEM will search the paths listed
    in PATH= for .RSC files, If you want, you can put all your resource
    files in one place and put that pathname in the PATH= list.  This can
    also be handy when you have installed applications.  By putting all
    your installed aps and their RSC files into one path, and listing that
    path in the PATH= var, you'll never run into the "cannot load RSC file"
    glitches that installed aps are prone to.

    There will always be two special variables in your configuration which
    you cannot delete:  ENV$ROOTSIZE and ENV$OPTIONS.  ENV$OPTIONS stores
    the state of the buttons in the options dialog, so that whenever
    you use the program, the options will be as you last set them.  The
    ENV$ROOTSIZE variable tells the program how much memory to allocate for 
    env data area at bootup time.  Since you might add to the env vars at
    any time by running GEMENV, a little extra memory is always allocated
    when the ROOT.ENV file is loaded.  The program, by default, uses the
    size of the current env data, rounded up to the next 1k boundry.
    Typically, ENV$ROOTSIZE will always equal 1024 unless you have a lot of 
    vars.  You can manually change this value upwards if you need to, but
    it can never be made smaller than the current size rounded up to the
    next 1k (if you try, the program overrides you when you save.)

    The format of the ROOT.ENV file (and other .ENV files) is compatible
    with a normal text editor.  Each line contains a single entry in the
    format NAME=VALUE, and the lines are terminated with a CRLF sequence
    (or simply LF).  An installation utility program might want to create
    or modify the ROOT.ENV file, and that's fine, given that it follows a
    couple simple rules:

        - There MUST be an ENV$ROOTSIZE variable, and it MUST be an even
          multiple of 1024 bytes, and the value MUST be larger than the
          total size of all the env data in the file.
        - There MUST be an ENV$OPTIONS variable.  If one exists already,
          don't monkey with the values in it.  If you are creating a .ENV
          file from scratch, create this as ENV$OPTIONS=TYY.  If you use
          any other value beside TYY, don't come crying to me if the
          GEMENV fails to work correctly!

    If you are modifying an existing ROOT.ENV file programmatically, please
    be aware that the user may be fond of the values s/he currently has
    stored in the variables.  To add your software path, for example, don't
    just create a new PATH= variable, append your path to what's already
    there.  If for some reason you just can't take the time to code up a
    fancy modification program to handle the data that's already there,
    then at the very least create a ROOT.BAK for the user which contains
    the current data before you modify it.


How GEMENV Works


    As mentioned above, whenever a program starts, DOS provides that
    program a copy of its parent's env data area.  This applies to the
    process that TOS uses to start the GEM desktop as well.  After all the
    AUTO programs have finished running, TOS creates a basepage for the
    desktop using the standard DOS Pexec() call.  However, rather than
    loading a file from disk to execute, it jumps through the exec_os
    vector at $4FE in low memory after the basepage is created.

    When the GEMENV program is run from the AUTO folder it detects that
    fact by trying to do an appl_init() call and getting a bad status
    because GEM isn't available yet.  When this happens, it bypasses the
    normal dialog stuff it does when run from the desktop.  Instead, it
    loads the env vars from the ROOT.ENV file, and checks the value of the
    ENV$ROOTSIZE variable.  It allocates that much memory and stores the
    env vars (in TOS or PC format, as indicated by the ENV$OPTIONS var.)

    After the env data area is set up, GEMENV installs a cookie in the
    system cookie jar so that it can locate the installed data area later,
    when run from the desktop.  (If a cookie jar doesn't exist yet in the
    system, one is installed in the standard Atari-recommended fashion.)
    The cookie used is "ENV$", which isn't likely to conflict with any
    existing cookies (and GEMENV will work even if there is a conflict,
    although it's quite likely that the any other user of an "ENV$" cookie
    would die.)

    Next, the program saves the current contents of the exec_os system
    vector, and places a 'hook' into that vector, so that when TOS jumps
    through the vector to start the desktop, a routine in the resident
    portion of GEMENV will get control instead.  Once this is done, GEMENV
    issues a Ptermres() call to remain resident, keeping only a small
    program stub (about 300 bytes) and the env data itself.  The stub and
    the data are adjacent; GEMENV's installation will not fragment your
    free memory area like some TSRs do.

    When all the other AUTO programs are done, and TOS starts the desktop,
    the stub left resident by GEMENV gets control.  All this stub does is
    replace the environment data pointer in the current program basepage
    with a pointer to the data area loaded from the ROOT.ENV file.  It then
    restores the old value of the exec_os vector, then jumps through that
    vector.  Because the env data pointer in the desktop's basepage was
    changed, the desktop starts with the full contents of the ROOT.ENV data
    area instead of the minimal PATH=C:\ data area that TOS created for it.
    When the desktop starts other programs, it will pass copies of this
    full data area along to everyone else.

    Note that GEMENV does not leave any hooks in any system vectors once
    the desktop has started.  After replacing the env pointer and passing
    control along to the desktop, the resident portion of GEMENV never
    executes anything again; it just sits there providing a data area for
    everyone else.  In this sense, GEMENV is a passive TSR that is unlikely
    to interfere with anything else in the system.

    Also note that there are other programs in the world that use the
    exec_os vector.  Foremost among these are the replacement desktop
    programs that install themselves in place of the GEM desktop.  Because
    a replacement desktop truly steals the exec_os vector (rather than
    borrowing it and then jumping through it as GEMENV does), it may be
    necessary to play with the order of the files in your AUTO folder a bit
    to ensure that GEMENV can briefly gain control of the system.  In
    general, if GEMENV reports successfull installation while AUTO stuff is
    running, and then complains about no root env data when you run it from
    the desktop, try rearranging your AUTO folder.


Programming Notes


    It was mentioned earlier that your programs can't modify the system env
    data because they are really only modifying their own local copy of the
    env data when the putenv() function is used.  Your programs can,
    however, use the same technique that GEMENV uses to modify the main
    system env data area.

    This techique involves locating the root data area via the ENV$ cookie,
    then accessing that memory directly.  I'm not going to go into a long
    discussion of using the cookie jar; see the appropriate Atari docs for
    info on how to locate the ENV$ cookie.  The value of the ENV$ cookie is
    a 32-bit pointer to the following data structure:

         struct gemenv_control {
            long    magic;
            short   version;
            char    *pdata;
            long    sdata;
            long    reserved[4];
         };

    The 'magic' field must contain the value 0x04021959.  If it does not,
    someone else is also using ENV$ as a cookie, and you found them, not
    GEMENV.  In this case, just keep searching, you may find another ENV$
    cookie that points to the right magic value.

    The version word is the installed GEMENV version, encoded as 0xVVRR,
    where VV is the major version and RR is the release.  The current v1.1
    value is 0x0101.

    The pdata field is the pointer to the root env data area.  It is this
    pointer that you would use to access the env vars directly.  (But,
    NEVER EVER modify this pointer!  (Or, for that matter, anything else in
    this structure.)  Doing so will break GEMENV, and it will NOT cause the
    desktop to see a new set of env vars; that can only be done via the
    exec_os hook.)

    The sdata field is the size of the root env data area.  If you modify
    the root area, you must ensure that the modifications don't exceed this
    size.

    The reserved array is just that: reserved.  Right now, this will be
    four longwords of zeroes.  Someday, I'm going to provide a programmatic
    interface so that you don't have to manipulate the root data directly
    but can call gputenv() and ggetenv() to access the global data instead
    of your program's local copy.  When I do that, the vectors for the
    functions will go here.



Credits and Disclaimers


    I'd like to thank Mike Dorman for helping me test this program, and Bob
    Goff for both testing, and for obtaining the Atari docs on the cookie
    jar for me.  I'd like to thank David Stabb for inspiring the program,
    although I'm sure that by now he's quite forgotten how or why he was
    inspirational in this project. :-)

    This program, the executable code, documents, and source code are all
    in the public domain, and the author specifically releases any and all
    rights associated with it.  You are free to modify and/or use this work
    in any way you see fit.  You may include or distribute this work as a
    part of your own product or program, be it public domain, shareware, or
    commercial.  If you incorporate this into your own product, there is no
    need to include any reference to me or my work on it unless you feel
    like it.  I just want to see it distributed widely, and I want to see
    ST applications start to make better use of environment variables.

    This software is delivered on an as-is basis, and no warranties are
    made, including warranty of suitability for a particular purpose.  The
    author assumes no responsibility for the consequences of using this
    software, even if the consequences result from defects in this
    program.  Besides, I don't have enough money to make suing me worth
    your while. :-)


    Ian Lepore
    moderator, BIX atari.st and c.language conferences
    07/19/91

--------------------------------------------------------
                   < OK TO PORT >
This information comes from the atari.st conference 
on BIX (r), the BYTE Information Exchange.

For additional information about BIX, call 800-227-2983
or 603-924-7681.
---------------------------------------------------------

