'===========================================================================
' Subject: MODIFY ENVIRONMENT SETTINGS        Date: 12-09-96 (00:21)       
'  Author: Jack Hudgions                      Code: QB, PDS                
'  Origin: FidoNet QUIK_BAS Echo            Packet: DOS.ABC
'===========================================================================
'***********************************************************************
'* MODULE Environ
'*
'* EXTERNAL ROUTINE(S)
'*    QBX.LIB
'*       SUB InterruptX (IntNum%, RegsX AS RegTypeX, RegsX AS RegTypeX)
'*
'* CREDIT(S)
'*    Douglas Lusher, Fidonet QuickBASIC, 07-11-94
'*
'* MODIFICATIONS:
'*    Tue, 07-20-94 - Generally cleaned up the code.  Modified the
'*    following routines:
'*
'*       FUNCTION MasterEnvInt$
'*          Removed ERROR statement, added ErrCode% parameter and
'*          assigned unique error codes for each error condition.
'*
'*          Changed "Tmp$ = SPACE$(128)" to "Tmp$ = SPACE(256)" to
'*          support 4DOS which allows environment variables > 128 bytes.
'*          Actually, 4DOS allows environment variables somewhat < 256
'*          bytes, but this is good enough. :)
'*
'*       FUNCTION MasterEnvSet%
'*          Changed from SUB to FUNCTION - ErrCode% parameter no longer
'*          needed.
'*
'*       FUNCTION MasterEnvStr$
'*          Removed ERROR statement, added ErrCode% parameter and
'*          assigned unique error codes for each error condition.
'*
'*          Changed "Tmp$ = SPACE$(128)" to "Tmp$ = SPACE(256)" to
'*          support 4DOS which allows environment variables > 128 bytes.
'***********************************************************************
' additional modifications by Jack Hudgions 02/01/95:
'   changed MasterEnvSet Function as suggested by Mark Northcutt.
DEFINT A-Z

'$INCLUDE: 'qbx.bi'

DECLARE FUNCTION MasterEnvFree% ()
DECLARE FUNCTION MasterEnvInt$ (StringNum%, ErrCode%)
DECLARE FUNCTION MasterEnvSeg& ()
DECLARE FUNCTION MasterEnvSet% (Env$)
DECLARE FUNCTION MasterEnvSize% ()
DECLARE FUNCTION MasterEnvStr$ (DefStr$, ErrCode%)

'A demo:
PRINT "Master Environment info:"

PRINT "      Size ="; MasterEnvSize%
PRINT "      Used ="; MasterEnvSize% - MasterEnvFree%
PRINT "      Free ="; MasterEnvFree%
PRINT "   Segment = "; HEX$(MasterEnvSeg&)
PRINT
PRINT "   Current environment variables are:"

DO
   StringNum% = StringNum% + 1
   Environment$ = MasterEnvInt$(StringNum%, ErrCode%)
   IF ErrCode% = 0 THEN
      EqualPtr% = INSTR(Environment$, "=")
      EnvName$ = LEFT$(Environment$, EqualPtr% - 1)
      EnvVal$ = MID$(Environment$, EqualPtr% + 1)

      PRINT "      "; UCASE$(EnvName$)
      PRINT "         "; LEFT$(EnvVal$, 67);

      IF LEN(EnvVal$) > 67 THEN
     PRINT "..."
     PRINT "         ..."; MID$(EnvVal$, 68)
      ELSE
     PRINT
      END IF
   END IF
LOOP UNTIL ErrCode% > 0

PRINT : INPUT "   Enter an environment variable to retrieve: ", DefStr$

Environment$ = MasterEnvStr$(DefStr$, ErrCode)
SELECT CASE ErrCode%
   CASE 0: PRINT "      "; DefStr$; "="; Environment$
   CASE 2: PRINT "      ERROR - you entered a '=' character!"
   CASE 3: PRINT "      ERROR - you entered a NULL character!"
END SELECT

PRINT
INPUT "   Enter an environment variable name to modify: ", EnvName$

IF LEN(EnvName$) THEN
   INPUT "                                Enter new value: ", EnvVal$
   IF LEN(EnvVal$) THEN
      Env$ = EnvName$ + "=" + EnvVal$
      ErrCode% = MasterEnvSet%(Env$)
   END IF
END IF

PRINT : PRINT "Type 'SET' at the DOS prompt to see the new values"

END

'***********************************************************************
'* FUNCTION MasterEnvFree%
'*
'* PURPOSE
'*    Returns the amount of free space in the master environment.
'*
'* INTERNAL ROUTINE(S)
'*    FUNCTION MasterEnvSeg& ()
'*    FUNCTION MasterEnvSize% ()
'***********************************************************************
FUNCTION MasterEnvFree%
   EnvPtr% = -1                              'Pointer into environment

   DEF SEG = MasterEnvSeg&                   'Set segment to Master Env.
   DO
      DO
     EnvPtr% = EnvPtr% + 1               'Examine next character
      LOOP WHILE PEEK(EnvPtr%)               'Loop until a double NULL
   LOOP WHILE PEEK(EnvPtr% + 1)              '  (terminates the envir.)
   DEF SEG                                   'Restore default segment

   'Assign return value
   MasterEnvFree% = MasterEnvSize% - (EnvPtr% + 2)
END FUNCTION

'***********************************************************************
'* FUNCTION MasterEnvInt$
'*
'* PURPOSE
'*    Returns an environment string specified by StringNum%.
'*
'*    ErrCode% return values:
'*       1  StringNum% < 1
'*       2  StringNum% > the number of environment variables
'*
'* INTERNAL ROUTINE(S)
'*    FUNCTION MasterEnvSeg& ()
'***********************************************************************
FUNCTION MasterEnvInt$ (StringNum%, ErrCode%)
   MasterEnvInt$ = ""                        'Initialize some variables
   EnvPtr% = -1                              'Pointer into environment
   Count% = 0                                '# of environ. vars. found
   ErrCode% = 0                              'Return value

   IF StringNum% < 1 THEN
      ErrCode% = 1                           'Must be >= 1
      EXIT FUNCTION                          'Bail out
   END IF

   DEF SEG = MasterEnvSeg&                   'Set segment to Master Env.

   DO
      IF PEEK(EnvPtr% + 1) = 0 THEN          'StringNum% > # of
     ErrCode% = 2                        '  environment variables
     EXIT DO                             'Bail out
      END IF

      Count% = Count + 1                     'Next env. variable
      IF Count% < StringNum% THEN            '
     DO                                  'Find end of current var.
        EnvPtr% = EnvPtr% + 1            'Examine next character
        IF PEEK(EnvPtr%) = 0 THEN        'NULL (end) found
           EXIT DO                       '  exit loop
        END IF
     LOOP
      ELSE                                   'Found specified env. var.
     Tmp$ = SPACE$(256)                  'This is where we'll
                         '  hold the result
     StrPtr% = 0
     DO                                  'Find end of env. variable
        EnvPtr% = EnvPtr% + 1            'Examine next character
        EnvCh% = PEEK(EnvPtr%)
        IF EnvCh% = 0 THEN               'Loop until
           EXIT DO                       '  NULL is found
        END IF

        StrPtr% = StrPtr% + 1            'Insert character
        MID$(Tmp$, StrPtr%, 1) = CHR$(EnvCh%)
     LOOP

     MasterEnvInt$ = LEFT$(Tmp$, StrPtr%)'Assign return value
     EXIT DO
      END IF
   LOOP

   DEF SEG                                   'Restore default segment
END FUNCTION

'***********************************************************************
'* FUNCTION MasterEnvSeg&
'*
'* PURPOSE
'*    Uses (an apparently undocumented) feature of DOS ISR 21H, Function
'*    35H (Get Interrupt Vector) to return the segment of the Master
'*    Environment.
'*
'* EXTERNAL ROUTINE(S)
'*    SUB InterruptX (IntNum%, InReg AS RegTypeX, OutReg AS RegTypeX)
'***********************************************************************
FUNCTION MasterEnvSeg& STATIC
   DIM RegsX AS RegTypeX

   RegsX.ax = &H352E
   InterruptX &H21, RegsX, RegsX

   DEF SEG = RegsX.es
   MasterEnvSeg& = PEEK(&H2C) + PEEK(&H2D) * 256&
   DEF SEG                                   'Restore default segment
END FUNCTION

'***********************************************************************
'* FUNCTION MasterEnvSet%
'*
'* PURPOSE
'*    Sets the specified environment string (Env$) in the master
'*    environment.  Returns 1 if Env$ is empty, if Env$ contains a NULL,
'*    or if Env$ does not contain a "=".  Returns 2 if the result
'*    (after adding/changing Env$) is too long to fit into the maximum
'*    Master Environment size.
'*
'* INTERNAL ROUTINE(S)
'*    FUNCTION MasterEnvSeg& ()
'*    FUNCTION MasterEnvSize% ()
'***********************************************************************
FUNCTION MasterEnvSet% (Env$)
   null$ = CHR$(0)

   IF LEN(Env$) = 0 THEN                     'Is it set?
      MasterEnvSet% = 1                      '  no, exit
      EXIT FUNCTION                          '  with error
   END IF

   IF INSTR(Env$, null$) THEN                'Does it have a null?
      MasterEnvSet% = 2                      '  Yes, exit
      EXIT FUNCTION                          '  with error.
   END IF

   EqualPtr% = INSTR(Env$, "=")              'Find the "="
   IF EqualPtr% <= 1 THEN                    'Was it found?
      MasterEnvSet% = 3                      '  No, exit
      EXIT FUNCTION                          '  with error
   END IF

   EVar$ = UCASE$(LEFT$(Env$, EqualPtr%))    'Grab the environment name
   EnvVal$ = MID$(Env$, EqualPtr% + 1)       'Grab the environment value

   EnvSize% = MasterEnvSize%
   EnvSeg& = MasterEnvSeg&

   Tmp$ = SPACE$(EnvSize%)
   DEF SEG = EnvSeg&
   FOR EqualPtr% = 1 TO LEN(Tmp$)            'Copy the env. to a string
      MID$(Tmp$, EqualPtr%, 1) = CHR$(PEEK(EqualPtr% - 1))
   NEXT
   DEF SEG                                   'Restore default segment

   'Chop it off at the end of the last environment string
   Tmp$ = LEFT$(Tmp$, INSTR(Tmp$, null$ + null$))

   IF LEN(Tmp$) = 1 THEN                     'If the environment happens
      Tmp$ = ""                              '  to be empty
   END IF

'   EnvVarPtr% = INSTR(Tmp$, EVar$)           'Is Env$ is in the_
' environ?
' Mark's modification begin.
   EnvVarPtr% = INSTR(Tmp$, null$ + EVar$) + 1'Is Env$ is in the_
' environ?
   IF EnvVarPtr% = 0 THEN
       EnvVarPtr% = INSTR(Tmp$, EVar$)     'if null+var is not there,_
' maybe it'i
       IF EnvVarPtr% > 1 THEN EnvVarPtr% = 0'if not #1 then found a_
' substr later
   END IF
' Mark's modification end.
   IF EnvVarPtr% THEN
      'Find the beginning of the next environment variable
      NextPtr% = INSTR(EnvVarPtr%, Tmp$, null$) + 1

      IF NextPtr% > LEN(Tmp$) THEN           'EVar$ is the last var. in
     Tmp$ = LEFT$(Tmp$, EnvVarPtr% - 1)  '  the environ, so keep
      ELSE                                   '  everything before it.
     'EVar$ isn't the last variable so move everything after it up
     Tmp$ = LEFT$(Tmp$, EnvVarPtr% - 1) + MID$(Tmp$, NextPtr%)
      END IF
   END IF

   IF LEN(EnvVal$) THEN                      'Are we setting it,
      'Add Env$ to the end of the envir. and terminate with two nulls
      Tmp$ = Tmp$ + EVar$ + EnvVal$ + null$ + null$
      IF LEN(Tmp$) > EnvSize% THEN           'Is the result too long?
     MasterEnvSet% = 2                   'Yes, exit with
     EXIT FUNCTION                       '  error
      END IF
   ELSE                                      'Or removing it?
      'If EnvVal$ is empty then all we wanted to do
      '  was remove the variable from the environment
      Tmp$ = Tmp$ + null$

      IF LEN(Tmp$) = 1 THEN                  'If this happened to be the
     Tmp$ = Tmp$ + null$                 '  last environ. var., an
      END IF                                 '  extra null is needed to
   END IF                                    '  terminate.

   DEF SEG = EnvSeg&
   FOR Ptr% = 1 TO LEN(Tmp$)                 'Copy the string back into
      POKE Ptr% - 1, ASC(MID$(Tmp$, Ptr%, 1))'  the environment
   NEXT
   DEF SEG                                   'Restore default segment

   MasterEnvSet% = 0                         'Everything OK
END FUNCTION

'***********************************************************************
'* FUNCTION MasterEnvSize%
'*
'* PURPOSE
'*    Returns the size of the master environment in bytes.
'***********************************************************************
FUNCTION MasterEnvSize%
   DEF SEG = MasterEnvSeg& - 1               'Set segment to Master Env.
   MasterEnvSize% = (PEEK(3) + PEEK(4) * 256) * 16
   DEF SEG                                   'Restore default segment
END FUNCTION

'***********************************************************************
'* FUNCTION MasterEnvStr$
'*
'* PURPOSE
'*    Returns an environment string specified by DefStr$.
'*
'*    ErrCode% return values:
'*       0     Success
'*       1     DefStr$ is empty
'*       2     DefStr$ contains a "="
'*       3     DefStr$ contains an embedded NULL
'*
'* INTERNAL ROUTINE(S)
'*    FUNCTION MasterEnvSeg& ()
'***********************************************************************
FUNCTION MasterEnvStr$ (DefStr$, ErrCode%)
   IF LEN(DefStr$) = 0 THEN
      ErrCode% = 1                           'String is empty
      EXIT FUNCTION                          'Bail out
   END IF

   IF INSTR(DefStr$, "=") THEN
      ErrCode% = 2                           'Invalid environment string
      EXIT FUNCTION                          '  (contains a "="), bail
   END IF                                    '  out.

   IF INSTR(DefStr$, CHR$(0)) THEN
      ErrCode% = 3                           'Invalid environment string
      EXIT FUNCTION                          '  (contains a NULL), bail
   END IF                                    '  out.

   Tmp$ = UCASE$(DefStr$) + "="
   DefLen% = LEN(Tmp$)
   REDIM DefCh%(1 TO DefLen%)                'Fill DefCh%()
   FOR StrPtr% = 1 TO DefLen%                '  with given environ. var.
      DefCh%(StrPtr%) = ASC(MID$(Tmp$, StrPtr%, 1))
   NEXT

   MasterEnvStr$ = ""                        'Initialize some variables
   Found% = 0
   EnvPtr% = -1

   DEF SEG = MasterEnvSeg&                   'Set segment to Master Env.

   DO
      IF PEEK(EnvPtr% + 1) = 0 THEN          'Found terminating NULL
     EXIT DO                             'Bail out
      END IF

      StrPtr% = 0
      DO                                     'Find match for DefStr$
     StrPtr% = StrPtr% + 1               '  (DefCh%()) in environ.
     IF StrPtr% > DefLen% THEN           'Longer than our env. var.
        GOSUB SkipString                 'It isn't this one,
        EXIT DO                          '  skip it
     END IF

     EnvPtr% = EnvPtr% + 1               'Pointer into environment
     EnvCh% = PEEK(EnvPtr%)              'Get next byte in environ.
     IF EnvCh% = DefCh%(StrPtr%) THEN    'Do the chars. match?
        IF StrPtr% = DefLen% THEN        'Is the length the same?
           Found% = -1                   'Found it!
           EXIT DO                       'Bail out
        END IF
     ELSE
        GOSUB SkipString                 'It isn't this one,
        EXIT DO                          '  skip it
     END IF
      LOOP

      IF Found% THEN                         'If we found it,
     Tmp$ = SPACE$(256)                  'New copy will go here
     StrPtr% = 0
     DO UNTIL EnvCh% = 0                 'Grab the value
        EnvPtr% = EnvPtr% + 1            '  and insert
        EnvCh% = PEEK(EnvPtr%)           '  it in
        StrPtr% = StrPtr% + 1            '  Tmp$
        MID$(Tmp$, StrPtr%, 1) = CHR$(EnvCh%)
     LOOP

     MasterEnvStr$ = LEFT$(Tmp$, StrPtr%)
     EXIT DO
      END IF
   LOOP
   DEF SEG                                   'Restore default segment
   ErrCode% = 0                              'Success
   EXIT FUNCTION                             'All done

SkipString:                                  'Skip current environ. var.
   DO UNTIL EnvCh% = 0                       'Look for terminating NULL
      EnvPtr% = EnvPtr% + 1
      EnvCh% = PEEK(EnvPtr%)
   LOOP
RETURN
END FUNCTION
