Jump to content

Using SYSLEVEL Files in Your Applications: Difference between revisions

From EDM2
Prokushev (talk | contribs)
No edit summary
 
Prokushev (talk | contribs)
Formatting issues
Line 12: Line 12:
for our own purposes...
for our own purposes...


<p><h3>Why?</h3>
=== Why? ===


This registering ability proves its usefulness when writing (relatively)
This registering ability proves its usefulness when writing (relatively)
Line 24: Line 24:
component presence.
component presence.


<p>Another interest is that it helps component writers in creating CSD or
Another interest is that it helps component writers in creating CSD or
component updates.  As each SYSLEVEL file maintains a current CSD level as
component updates.  As each SYSLEVEL file maintains a current CSD level as
well as a previous CSD level field, it's easy to check what to update, and
well as a previous CSD level field, it's easy to check what to update, and
what to leave alone.
what to leave alone.


<p>It's also useful while maintaining a computer network.  It would provide
It's also useful while maintaining a computer network.  It would provide
the maintainer an up-to-date information database.
the maintainer an up-to-date information database.


<p><h3>Contents</h3>
=== Contents ===


This article contains two parts.  The first one describes the SYSLEVEL file
This article contains two parts.  The first one describes the SYSLEVEL file
Line 38: Line 38:
one for Rexx).
one for Rexx).


<p>
== A Voyage to Syslevel ==
<h2>A Voyage to Syslevel</h2>


When you start SYSLEVEL.EXE, it scans all your drives for SYSLEVEL files.
When you start SYSLEVEL.EXE, it scans all your drives for SYSLEVEL files.
Line 47: Line 46:
purpose, we'll have to decrypt SYSLEVELs' file headers, and find out how
purpose, we'll have to decrypt SYSLEVELs' file headers, and find out how
(and where) to store our own data.
(and where) to store our own data.
<pre>
C:\MMOS2\INSTALL\SYSLEVEL.MPM
C:\MMOS2\INSTALL\SYSLEVEL.MPM
                              Multimedia Presentation Manager 2
                            Multimedia Presentation Manager 2
Extensions systŠme 1.10    ID de composant 562137400
Extensions systŠme 1.10    ID de composant 562137400
Type MMPM\2
Type MMPM\2
Niveau de modifications en cours : UN00000
Niveau de modifications en cours : UN00000
Niveau de modifications ant‚rieur : UN00000
Niveau de modifications ant‚rieur : UN00000
 
</pre>
<font size=2>
Figure 1.  Syslevel output sample (French version, sorry :-) )
Figure 1.  Syslevel output sample (French version, sorry :-) )
</font>


<h3>On the File Header</h3>
=== On the File Header ===


A SYSLEVEL file header looks like the following:
A SYSLEVEL file header looks like the following:
<pre>
 
typedef struct _SYSLEVELHEADER {
typedef struct _SYSLEVELHEADER {
  unsigned char h_magic[2];
    unsigned char h_magic[2];
  unsigned char h_name[9];
    unsigned char h_name[9];
  unsigned char h_reserved1[4];
    unsigned char h_reserved1[4];
  unsigned char h_updated;
    unsigned char h_updated;
  unsigned char h_reserved2[17];
    unsigned char h_reserved2[17];
  ULONG        h_data;
    ULONG        h_data;
} SYSLEVELHEADER;
} SYSLEVELHEADER;
</pre>
<font size=2>


Figure 2.  The SYSLEVELHEADER structure.
Figure 2.  The SYSLEVELHEADER structure.
</font><p>
 
<dl>
<dl>
<dt><b>h_magic</b><dd>is the magic cookie.  Its value is 0xFFFF.
<dt><b>h_magic</b><dd>is the magic cookie.  Its value is 0xFFFF.
 
<dt><b>h_name</b><dd>is the string "SYSLEVEL", in uppercase and null-terminated.
<p><dt><b>h_name</b><dd>is the string "SYSLEVEL", in uppercase and null-terminated.
<dt><b>h_reserved1</b><dd>meaning unknown.  Set to zero.
 
<dt><b>h_updated</b><dd>was 1 for OS2, GRE and MPM, and 0 for TLK.  My guess: set it to
<p><dt><b>h_reserved1</b><dd>meaning unknown.  Set to zero.
 
<p><dt><b>h_updated</b><dd>was 1 for OS2, GRE and MPM, and 0 for TLK.  My guess: set it to
zero.
zero.
<dt><b>h_reserved2</b><dd>meaning unknown.  Set to zero.
<dt><b>h_data</b><dd>points to the beginning of the file's data structure (offset from
the beginning of the file), which is described below.
</dl>


<p><dt><b>h_reserved2</b><dd>meaning unknown.  Set to zero.
While I realize there's still many holes in there, it appears to fulfill
 
<p><dt><b>h_data</b><dd>points to the beginning of the file's data structure (offset from
the beginning of the file), which is described below.
</dl><p>While I realize there's still many holes in there, it appears to fulfill
our needs; namely, we are now able to create files recognized by
our needs; namely, we are now able to create files recognized by
SYSLEVEL.EXE.  Isn't that great?
SYSLEVEL.EXE.  Isn't that great?


<p>But it's time to learn more...
But it's time to learn more...




<p><h3>The SYSLEVEL Data Structure</h3>
=== The SYSLEVEL Data Structure ===


In the previous section, we have seen that the file's data structure starts
In the previous section, we have seen that the file's data structure starts
at offset h_data.  This structure (aka. the SYSLEVEL data structure) looks
at offset h_data.  This structure (aka. the SYSLEVEL data structure) looks
like the following:
like the following:
<pre>
 
typedef struct _SYSLEVELDATA {
typedef struct _SYSLEVELDATA {
  unsigned char d_reserved1[2];
    unsigned char d_reserved1[2];
  unsigned char d_kind;
    unsigned char d_kind;
  unsigned char d_version[2];
    unsigned char d_version[2];
  unsigned char d_reserved2[2];
    unsigned char d_reserved2[2];
  unsigned char d_clevel[7];
    unsigned char d_clevel[7];
  unsigned char d_reserved3;
    unsigned char d_reserved3;
  unsigned char d_plevel[7];
    unsigned char d_plevel[7];
  unsigned char d_reserved4;
    unsigned char d_reserved4;
  unsigned char d_title[80];
    unsigned char d_title[80];
  unsigned char d_cid[9];
    unsigned char d_cid[9];
  unsigned char d_revision;
    unsigned char d_revision;
  unsigned char d_type[1];
    unsigned char d_type[1];
} SYSLEVELDATA;
} SYSLEVELDATA;
</pre>
 
<font size=2>Figure 3.  The SYSLEVELDATA structure.</font>
Figure 3.  The SYSLEVELDATA structure.


<dl>
<dl>
<dt><b>d_reserved1</b><dd>set to zero...
<dt><b>d_reserved1</b><dd>set to zero...
<dt><b>d_kind</b><dd>  specifies the component "kind".  It can be any of the following:
* SLK_BASE (0)  "Base version"
* SLK_EXTENSION (2)  "System extension"
* SLK_STANDARD (15)  Neither "Base version" nor "System extension"... :-)


<p><dt><b>d_kind</b><dd>  specifies the component "kind".  It can be any of the following:
<blockquote>
SLK_BASE (0)  "Base version"
<br>SLK_EXTENSION (2)  "System extension"
<br>SLK_STANDARD (15)  Neither "Base version" nor "System extension"... :-)
<p>
Any other value is equivalent to SLK_STANDARD (except 1, which is
Any other value is equivalent to SLK_STANDARD (except 1, which is
equivalent to SLK_EXTENSION).
equivalent to SLK_EXTENSION).
</blockquote>


<p><dt><b>d_version</b><dd>  specifies the component version.  Its format is a bit strange:
<dt><b>d_version</b><dd>  specifies the component version.  Its format is a bit strange:
the four high-order bits of <b>d_version[0]</b> contains the major version number,
the four high-order bits of <b>d_version[0]</b> contains the major version number,
the four low-order bits contains the first minor version digit, and
the four low-order bits contains the first minor version digit, and
<b>d_version[1]</b> contains the second minor version digit.
<b>d_version[1]</b> contains the second minor version digit.


<p>For example, if <b>d_version[0]</b> is 0x21 and d_version[1] is 0x02, you will get
For example, if <b>d_version[0]</b> is 0x21 and d_version[1] is 0x02, you will get
"2.12".
"2.12".


[You can have a revision level, as in "2.12.a".  See <b>d_revision</b> below.]


<p>[You can have a revision level, as in "2.12.a".  See <b>d_revision</b> below.]
<dt><b>d_reserved2</b><dd>  oh no, not again.  Set to zero.


<p><dt><b>d_reserved2</b><dd>  oh no, not again.  Set to zero.
<dt><b>d_clevel</b><dd>  specifies the current CSD level.  It's a seven-byte string,
 
<p><dt><b>d_clevel</b><dd>  specifies the current CSD level.  It's a seven-byte string,
usually in the form "AAC####", where AA is a two-byte string, C a
usually in the form "AAC####", where AA is a two-byte string, C a
country-dependent code, and #### a number.  For example, on my home
country-dependent code, and #### a number.  For example, on my home
Line 155: Line 140:
time).
time).


<p>It's just a suggested usage and is not enforced in any way. As long as you
It's just a suggested usage and is not enforced in any way. As long as you
use seven printable characters, it should work.
use seven printable characters, it should work.


<p><dt><b>d_reserved3</b><dd>  guess what?  You lose!  Set to 0x5F this time.
<dt><b>d_reserved3</b><dd>  guess what?  You lose!  Set to 0x5F this time.


<p><dt><b>d_plevel</b><dd>  specifies the previous CSD level.  If there was no previous
<dt><b>d_plevel</b><dd>  specifies the previous CSD level.  If there was no previous
level; set it equal to the current level, d_clevel.
level; set it equal to the current level, d_clevel.


<dt><b>d_reserved4</b><dd>  is just like d_reserved3; set to 0x5F


<p><dt><b>d_reserved4</b><dd>  is just like d_reserved3; set to 0x5F
<dt><b>d_title</b><dd>  specifies the component name.  It's a null-terminated string.
 
<p><dt><b>d_title</b><dd>  specifies the component name.  It's a null-terminated string.


<p><dt><b>d_cid</b><dd>  specifies the component ID.  It's not null-terminated.
<dt><b>d_cid</b><dd>  specifies the component ID.  It's not null-terminated.


<p><dt><b>d_revision</b><dd>  specifies the component revision.  It does not appear in the
<dt><b>d_revision</b><dd>  specifies the component revision.  It does not appear in the
version field if sets to zero.  If it's not null, it will be concatenated
version field if sets to zero.  If it's not null, it will be concatenated
with major and minor version number, as in "1.2.3" (where 1 is the major
with major and minor version number, as in "1.2.3" (where 1 is the major
Line 178: Line 162:
will get an "A", and so on.
will get an "A", and so on.


<p><dt><b>d_type</b><dd>  specifies the component type.  It's an optional, null-terminated
<dt><b>d_type</b><dd>  specifies the component type.  It's an optional, null-terminated
string. This field allows you to store additional informations for your
string. This field allows you to store additional informations for your
component. For example, this field is used to differentiate the blue and
component. For example, this field is used to differentiate the blue and
Line 187: Line 171:




<p>
== Home, Sweet Home ==
<h2>Home, Sweet Home</h2>


This walk was fun, but all good things must come to an end.  So we are
This walk was fun, but all good things must come to an end.  So we are
Line 194: Line 177:
knowledge, isn't it?
knowledge, isn't it?


<p>Two APIs are available, a C one and a REXX one.
Two APIs are available, a C one and a REXX one.


<p><h3>The SYSLEVEL C API</h3>
=== The SYSLEVEL C API ===


This section describes the API which provides access to the syslevel files.
This section describes the API which provides access to the syslevel files.
Line 203: Line 186:
favorite library.  It has been tested with GCC/2, but it should work with
favorite library.  It has been tested with GCC/2, but it should work with
any other C compiler...
any other C compiler...
<pre>
APIRET LvlOpenLevelFile(PSZ pszName, PHFILE phFile, ULONG ulOpenMode, PSZ
pszCID)
APIRET LvlCloseLevelFile(HFILE hFile)


APIRET LvlQueryLevelFile (PSZ pszName, PSZ pszCID, PVOID pBuffer, ULONG
APIRET LvlOpenLevelFile(PSZ pszName, PHFILE phFile, ULONG ulOpenMode, PSZ pszCID)
ulBufSize)
APIRET LvlCloseLevelFile(HFILE hFile)
APIRET LvlQueryLevelFile (PSZ pszName, PSZ pszCID, PVOID pBuffer, ULONG ulBufSize)
APIRET LvlQueryLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)
APIRET LvlWriteLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)


APIRET LvlQueryLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG
==== Design considerations ====
ulBufSize, PULONG pulSize)
 
APIRET LvlWriteLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG
ulBufSize, PULONG pulSize)
</pre>
<h4>Design considerations</h4>


This API has been modeled upon the profile (Prf) functions.
This API has been modeled upon the profile (Prf) functions.


 
==== Examples ====
<p><h4>Examples</h4>


The following two figures show how to check for the existence of a
The following two figures show how to check for the existence of a
component, and if a component needs updating, respectively.
component, and if a component needs updating, respectively.
<pre>
:
#include &lt;level.h&gt;
:
int main()
{
  HFILE hFile;
  :
  if(hFile = LvlOpenLevelFile("DBM",
                              OLF_SCANDISKS | OLF_CHECKID,
                              "123456789"))
    /* present */
    :
  else
    /* not present */
    :
  :
  LvlCloseLevelFile(hFile);
  :
}
</pre>
<font size=2>Figure 4.  Syslevel API usage sample - Checking for the existence of a
component</font>


<p><pre>
:
:
#include &lt;level.h&gt;
#include &lt;level.h&gt;
:
int main()
{
  HFILE hFile;
  :
  if(hFile = LvlOpenLevelFile("DBM",
                              OLF_SCANDISKS | OLF_CHECKID,
                              "123456789"))
    /* present */
    :
  else
    /* not present */
    :
  :
  LvlCloseLevelFile(hFile);
  :
}


:
Figure 4.  Syslevel API usage sample - Checking for the existence of a component
BOOL _needUpdate(HFILE hFile)
{
  CHAR achCSD[7];
  ULONG ulCount;


  if(LvlQueryLevelData(hFile, QLD_CURRENTCSD, achCSD, 7, &ulCount))
:
    /* shall we update it? */
#include &lt;level.h&gt;
    return strncmp(achCSD, "DBF0099",7) &lt;= 0; /* for example */
  else
:
    /* an error occurred */
BOOL _needUpdate(HFILE hFile)
    :
{
}
  CHAR achCSD[7];
:
  ULONG ulCount;
</pre>
<font size=2>Figure 5.  Syslevel API usage sample - Shall we update component?</font>
  if(LvlQueryLevelData(hFile, QLD_CURRENTCSD, achCSD, 7, &ulCount))
    /* shall we update it? */
    return strncmp(achCSD, "DBF0099",7) &lt;= 0; /* for example */
  else
    /* an error occurred */
    :
}
:


<p><h3>The SYSLEVEL REXX API</h3>
Figure 5.  Syslevel API usage sample - Shall we update component?
 
=== The SYSLEVEL REXX API ===


Well, it's not really an API, it's in fact a single function.  The
Well, it's not really an API, it's in fact a single function.  The
Line 279: Line 255:
MAKESYSL.CMD, which can be used to create SYSLEVEL files.
MAKESYSL.CMD, which can be used to create SYSLEVEL files.


<p>This function has been modeled upon the STREAM function.
This function has been modeled upon the STREAM function.


<p>RexxLvl()
RexxLvl()


<p><h4>Example</h4>
==== Example ====


The following example is a REXX script.  It allows you to easily create
The following example is a REXX script.  It allows you to easily create
syslevel files.
syslevel files.


<pre>
/* makesysl.cmd                                          940327 */
/* makesysl.cmd                                          940327 */
say 'Component name:';                  parse pull name
say 'Component ID:';                    parse pull ID
say 'Component type (optional):';      parse pull type
say 'Component version (as x.y[.z]):';  parse pull version
say 'Current CSD Level:';              parse pull CSD
say 'Component extension:';            parse pull ext
file = RexxLvl("n", ext, ID)
call RexxLvl "s", file, "n", name
call RexxLvl "s", file, "c", csd
call RexxLvl "s", file, "v", version
call RexxLvl "s", file, "t", type
rc = RexxLvl("c", file)
if rc = 0 then say 'SYSLEVEL.'ext' created.'


say 'Component name:';                  parse pull name
Figure 6. Creating a syslevel file in REXX.
say 'Component ID:';                    parse pull ID
say 'Component type (optional):';      parse pull type
say 'Component version (as x.y[.z]):';  parse pull version
say 'Current CSD Level:';              parse pull CSD
say 'Component extension:';            parse pull ext


file = RexxLvl("n", ext, ID)
== Summary ==
call RexxLvl "s", file, "n", name
call RexxLvl "s", file, "c", csd
call RexxLvl "s", file, "v", version
call RexxLvl "s", file, "t", type
rc = RexxLvl("c", file)
 
if rc = 0 then say 'SYSLEVEL.'ext' created.'
</pre>
<font size=2>Figure 6.  Creating a syslevel file in REXX.</font>
 
<p><h2>Summary</h2>


Now you have enough information to use the SYSLEVEL files well.  However,
Now you have enough information to use the SYSLEVEL files well.  However,
Line 316: Line 291:
require them, again and again, until you get satisfaction.  :-)
require them, again and again, until you get satisfaction.  :-)


<p>As a sidenote, the information exposed in the first part of this article is
As a sidenote, the information exposed in the first part of this article is
the result of my own exploration.  If you find any inaccuracy or if you
the result of my own exploration.  If you find any inaccuracy or if you
have more information, please, let me know.
have more information, please, let me know.


<p>
=== kind ===
<h3>kind</h3>
<dl>
<dl>
<dt><b>SLK_BASE</b><dd>denotes a "base" component.
<dt><b>SLK_BASE</b><dd>denotes a "base" component.


<dt><b>SLK_EXTENSION</b><dd>denotes a system extension.


<p><dt><b>SLK_EXTENSION</b><dd>denotes a system extension.
<dt><b>SLK_STANDARD</b><dd>denotes a "standard" component.  It's the suggested
 
<p><dt><b>SLK_STANDARD</b><dd>denotes a "standard" component.  It's the suggested
component kind value.
component kind value.
</dl>
</dl>


<p><h3>ulOpenMode - input</h3>
=== ulOpenMode - input ===
<dl>
<dl>
<dt><b>ULONG</b><dd>field that describes the mode of the open function.
<dt><b>ULONG</b><dd>field that describes the mode of the open function.


<p><dt><b>OLF_OPEN</b><dd>open the specified file.  The default behavior is to scan the
<dt><b>OLF_OPEN</b><dd>open the specified file.  The default behavior is to scan the
current disk, starting from the current directory.  It returns the first
current disk, starting from the current directory.  It returns the first
SYSLEVEL file whose extension matches pszName  You can override this by
SYSLEVEL file whose extension matches pszName  You can override this by
combining OLF_OPEN with OLF_SCANDISK or OLF_CHECKID.
combining OLF_OPEN with OLF_SCANDISK or OLF_CHECKID.


<p><dt><b>OLF_SCANDISKS</b><dd>scans all disks (starting from C:).  Use this flag to
<dt><b>OLF_SCANDISKS</b><dd>scans all disks (starting from C:).  Use this flag to
modify the default OLF_OPEN behavior.
modify the default OLF_OPEN behavior.


<p><dt><b>OLF_CHECKID</b><dd>finds file(s) whose ID matches the specified one.  Use this
<dt><b>OLF_CHECKID</b><dd>finds file(s) whose ID matches the specified one.  Use this
flag to override the default OLF_OPEN behavior.
flag to override the default OLF_OPEN behavior.


 
<dt><b>OLF_CREATE</b><dd>creates the specified file in the current directory.  A valid
<p><dt><b>OLF_CREATE</b><dd>creates the specified file in the current directory.  A valid
pszName and ID should be provided.
pszName and ID should be provided.
</dl>
</dl>


<p><h3>ulWhat - input</h3>
=== ulWhat - input ===


The following flags can be used when querying (or writing) a SYSLEVEL file.
The following flags can be used when querying (or writing) a SYSLEVEL file.


<p><dl>
<dl>
<dt><b>QLD_MAJORVERSION</b><dd>Query (or update) the major version field.  It's a
<dt><b>QLD_MAJORVERSION</b><dd>Query (or update) the major version field.  It's a
one-character field. It should be in the range '0'-'9'.  The value is
one-character field. It should be in the range '0'-'9'.  The value is
Line 362: Line 334:
should be at least 1.)
should be at least 1.)


<p><dt><b>QLD_MINORVERSION</b><dd>Query (or update) the minor version field.  It's a
<dt><b>QLD_MINORVERSION</b><dd>Query (or update) the minor version field.  It's a
two-character field. It should be in range '00'-'99'.  The value is placed
two-character field. It should be in range '00'-'99'.  The value is placed
in (or taken from) the first two chars of pBuffer.  (Buffer size should be
in (or taken from) the first two chars of pBuffer.  (Buffer size should be
at least 2.)
at least 2.)


<p><dt><b>QLD_REVISION</b><dd>Query (or update) the revision field.  It's should fit in a
<dt><b>QLD_REVISION</b><dd>Query (or update) the revision field.  It's should fit in a
character.  If it's '0', there's no revision available.  It can be a letter
character.  If it's '0', there's no revision available.  It can be a letter
as well as a digit.  The value is placed in (or taken from) the first
as well as a digit.  The value is placed in (or taken from) the first
character of pBuffer.  (Buffer size should be at least 1.)
character of pBuffer.  (Buffer size should be at least 1.)


<p><dt><b>QLD_KIND</b><dd>Query (or update) the kind field.  The value is placed in (or
<dt><b>QLD_KIND</b><dd>Query (or update) the kind field.  The value is placed in (or
taken from) the first character of *pBuffer.  (Buffer size should be at
taken from) the first character of *pBuffer.  (Buffer size should be at
least 1.)
least 1.)


 
<dt><b>QLD_CURRENTCSD</b><dd>Query (or update) the current CSD level (when you update
<p><dt><b>QLD_CURRENTCSD</b><dd>Query (or update) the current CSD level (when you update
this field, its old value is copied to the old CSD level field).  It's a
this field, its old value is copied to the old CSD level field).  It's a
seven-character field, and it does not have to be null-terminated.  The
seven-character field, and it does not have to be null-terminated.  The
Line 383: Line 354:
(Buffer size should be at least 7.)
(Buffer size should be at least 7.)


<p><dt><b>QLD_PREVIOUSCSD</b><dd>Query the previous CSD level.  You can't update this
<dt><b>QLD_PREVIOUSCSD</b><dd>Query the previous CSD level.  You can't update this
field.  The value is placed in the first seven chars of pBuffer.  (Buffer
field.  The value is placed in the first seven chars of pBuffer.  (Buffer
size should be at least 7.)
size should be at least 7.)
Line 390: Line 361:
returned value.
returned value.


<p><dt><b>QLD_TITLE</b><dd>Query (or update) the component title field.  It's an
<dt><b>QLD_TITLE</b><dd>Query (or update) the component title field.  It's an
eighty-character string (required ending null included).  The value is
eighty-character string (required ending null included).  The value is
placed in (or taken from) the first eighty characters of pBuffer.  On
placed in (or taken from) the first eighty characters of pBuffer.  On
Line 397: Line 368:
eighty characters.
eighty characters.


<p><dt><b>QLD_ID</b><dd>Query (or update) the component ID field.  It's a nine-character
<dt><b>QLD_ID</b><dd>Query (or update) the component ID field.  It's a nine-character
field. It does not have to be null-terminated.  The value is placed in (or
field. It does not have to be null-terminated.  The value is placed in (or
taken from) the first nine characters of pBuffer.  (Buffer size should be
taken from) the first nine characters of pBuffer.  (Buffer size should be
Line 405: Line 376:
value.
value.


<p><dt><b>QLD_TYPE</b><dd>Query (or update) the component type field.
<dt><b>QLD_TYPE</b><dd>Query (or update) the component type field.
</dl>
</dl>


<p><h2>LvlOpenLevelFile</h2>
== LvlOpenLevelFile ==
 
APIRET LvlOpenLevelFile(PSZ pszName, PHFILE phFile, ULONG ulOpenMode, PSZ pszCID)


APIRET LvlOpenLevelFile(PSZ pszName, PHFILE phFile, ULONG ulOpenMode, PSZ
Use this to find, open or create a SYSLEVEL file.
pszCID)


<p>Use this to find, open or create a SYSLEVEL file.
<p>
It returns the following values:
It returns the following values:
<pre>
0    NO_ERROR
2    ERROR_FILE_NOT_FOUND
3    ERROR_PATH_NOT_FOUND
4    ERROR_TOO_MANY_OPEN_FILES
5    ERROR_ACCESS_DENIED
12    ERROR_INVALID_ACCESS
26    ERROR_NOT_DOS_DISK
32    ERROR_SHARING_VIOLATION
36    ERROR_SHARING_BUFFER_EXCEEDED
82    ERROR_CANNOT_MAKE
87    ERROR_INVALID_PARAMETER
99    ERROR_DEVICE_IN_USE
108  ERROR_DRIVE_LOCKED
110  ERROR_OPEN_FAILED
112  ERROR_DISK_FULL
206  ERROR_FILENAME_EXCEED_RANGE
231  ERROR_PIPE_BUSY
</pre>


<p><h3>pszName - input</h3>
0    NO_ERROR
2    ERROR_FILE_NOT_FOUND
3    ERROR_PATH_NOT_FOUND
4    ERROR_TOO_MANY_OPEN_FILES
5    ERROR_ACCESS_DENIED
12    ERROR_INVALID_ACCESS
26    ERROR_NOT_DOS_DISK
32    ERROR_SHARING_VIOLATION
36    ERROR_SHARING_BUFFER_EXCEEDED
82    ERROR_CANNOT_MAKE
87    ERROR_INVALID_PARAMETER
99    ERROR_DEVICE_IN_USE
108  ERROR_DRIVE_LOCKED
110  ERROR_OPEN_FAILED
112  ERROR_DISK_FULL
206  ERROR_FILENAME_EXCEED_RANGE
231  ERROR_PIPE_BUSY
 
=== pszName - input ===


The syslevel file extension.  It's a three-letter, null-terminated string.
The syslevel file extension.  It's a three-letter, null-terminated string.
Line 442: Line 411:
where xxx  is pszName's value.
where xxx  is pszName's value.


<p>
== LvlCloseLevelFile ==
<h2>LvlCloseLevelFile</h3>


APIRET LvlCloseLevelFile(HFILE hFile)
APIRET LvlCloseLevelFile(HFILE hFile)


<p>Use this to close a SYSLEVEL file.  It's just a DosClose alias, for API
Use this to close a SYSLEVEL file.  It's just a DosClose alias, for API
consistence.
consistence.
<p>
 
It returns the following values:
It returns the following values:
<pre>
0  NO_ERROR
2  ERROR_FILE_NOT_FOUND
5  ERROR_ACCESS_DENIED
6  ERROR_INVALID_HANDLE
</pre>


<p><h2>LvlQueryLevelData</h2>
0  NO_ERROR
2  ERROR_FILE_NOT_FOUND
5  ERROR_ACCESS_DENIED
6  ERROR_INVALID_HANDLE
== LvlQueryLevelData ==


APIRET LvlQueryLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG
APIRET LvlQueryLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)
ulBufSize, PULONG pulSize)
 
Use this to query an already opened SYSLEVEL file.


<p>Use this to query an already opened SYSLEVEL file.
<p>
It returns the following values:
It returns the following values:
<pre>
0    NO_ERROR
6    ERROR_INVALID_HANDLE
87    ERROR_INVALID_PARAMETER
122  ERROR_INSUFFICIENT_BUFFER


</pre>
0    NO_ERROR
6    ERROR_INVALID_HANDLE
87    ERROR_INVALID_PARAMETER
122  ERROR_INSUFFICIENT_BUFFER
When it returns ERROR_INSUFFICIENT_BUFFER, *pulSize contains the minimum
When it returns ERROR_INSUFFICIENT_BUFFER, *pulSize contains the minimum
requested size.
requested size.


<p>
=== pBuffer - input/output ===
<h3>pBuffer - input/output</h3>


  Address of the buffer that contains the data to write/read.
  Address of the buffer that contains the data to write/read.


<p>
=== pulSize - output ===
<h3>pulSize - output</h3>


Address of the variable to receive the number of bytes actually read or
Address of the variable to receive the number of bytes actually read or
written.
written.


<p>
== LvlWriteLevelData ==
<h2>LvlWriteLevelData</h2>
 
APIRET LvlWriteLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)
 
Use this to update an already opened SYSLEVEL file.


APIRET LvlWriteLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG
It returns the following values:
ulBufSize, PULONG pulSize)


<p>Use this to update an already opened SYSLEVEL file.
0    NO_ERROR
6    ERROR_INVALID_HANDLE
19    ERROR_WRITE_PROTECT
29    ERROR_WRITE_FAULT
87    ERROR_INVALID_PARAMETER
122  ERROR_INSUFFICIENT_BUFFER


<p>It returns the following values:
<pre>
0    NO_ERROR
6    ERROR_INVALID_HANDLE
19    ERROR_WRITE_PROTECT
29    ERROR_WRITE_FAULT
87    ERROR_INVALID_PARAMETER
122  ERROR_INSUFFICIENT_BUFFER
</pre>
When it returns ERROR_INSUFFICIENT_BUFFER, *pulSize contains the minimum
When it returns ERROR_INSUFFICIENT_BUFFER, *pulSize contains the minimum
requested size.
requested size.


<p>
=== ulBufSize - input ===
<h3>ulBufSize - input</h3>


The length, in bytes, of pBuffer.  This is the number of bytes to be read
The length, in bytes, of pBuffer.  This is the number of bytes to be read
or write.
or write.


<p>
=== phFile - output ===
<h3>phFile - output</h3>


Address of the handle for the file.
Address of the handle for the file.


 
=== pszCID - input ===
<p>
<h3>pszCID - input</h3>


It's the component ID string.  It's required when you specify the
It's the component ID string.  It's required when you specify the
Line 528: Line 485:
name, but you can't have multiple syslevel files with the same name and ID.
name, but you can't have multiple syslevel files with the same name and ID.


<p>If you are not using the OLF_CHECKID flag, set pszCID to NULL.
If you are not using the OLF_CHECKID flag, set pszCID to NULL.


<p>
== RexxLvl() ==
<h2>RexxLvl()</h2>


<img src="rexx-lvl.gif">
[Images:rexx-lvl.gif]


<p>RexxLvl returns a string describing the state of, or the result of an
RexxLvl returns a string describing the state of, or the result of an
operation upon, a SYSLEVEL file.  This function is used to request
operation upon, a SYSLEVEL file.  This function is used to request
information on the state of a SYSLEVEL file, or to carry out some specific
information on the state of a SYSLEVEL file, or to carry out some specific
operation on the SYSLEVEL file.
operation on the SYSLEVEL file.


<p>The first argument can be one of the following strings (of which only the
The first argument can be one of the following strings (of which only the
first letter is needed) which describes the action to be carried out:
first letter is needed) which describes the action to be carried out:
<p>
 
<dl>
<dl>
<dt><b>"c[lose]"</b><dd>closes the specified syslevel file.  File is a filename
<dt><b>"c[lose]"</b><dd>closes the specified syslevel file.  File is a filename
returned by a previous RexxLvl OPEN or NEW call.
returned by a previous RexxLvl OPEN or NEW call.


 
<dt><b>"e[num]"</b><dd>enumerates the matching SYSLEVEL files.  Extension is (an
<p><dt><b>"e[num]"</b><dd>enumerates the matching SYSLEVEL files.  Extension is (an
optional) SYSLEVEL file extension.  Id is (an optional) component ID.  It
optional) SYSLEVEL file extension.  Id is (an optional) component ID.  It
returns the number of matching files, or 'ERROR'.  The corresponding
returns the number of matching files, or 'ERROR'.  The corresponding
Line 554: Line 509:
them back.
them back.


<p><dt><b>"n[ew]"</b><dd>creates a new SYSLEVEL file, in the current directory. Extension
<dt><b>"n[ew]"</b><dd>creates a new SYSLEVEL file, in the current directory. Extension
is the SYSLEVEL file extension, and id is the component ID.  It returns a
is the SYSLEVEL file extension, and id is the component ID.  It returns a
file name to be used by subsequent RexxLvl calls.
file name to be used by subsequent RexxLvl calls.


<p><dt><b>"o[pen]"</b><dd>opens the specified SYSLEVEL file.  Extension is the SYSLEVEL
<dt><b>"o[pen]"</b><dd>opens the specified SYSLEVEL file.  Extension is the SYSLEVEL
file extension, and id is the component ID.  It returns a file name to be
file extension, and id is the component ID.  It returns a file name to be
used by subsequent RexxLvl calls, 'NOTFOUND' if the required file was not
used by subsequent RexxLvl calls, 'NOTFOUND' if the required file was not
Line 565: Line 520:
Note:  It returns the first matching SYSLEVEL file.
Note:  It returns the first matching SYSLEVEL file.


<p><dt><b>"q[uery]"</b><dd>queries a SYSLEVEL file field.  File is a filename returned by
<dt><b>"q[uery]"</b><dd>queries a SYSLEVEL file field.  File is a filename returned by
a previous RexxLvl OPEN or NEW call. Field is the field to be queried.  It
a previous RexxLvl OPEN or NEW call. Field is the field to be queried.  It
can be any of the following (of which only the first letter is needed):
can be any of the following (of which only the first letter is needed):


<p><dt><b>"n[ame]"</b><dd>component name.  Its length cannot exceed 79 characters.
<dt><b>"n[ame]"</b><dd>component name.  Its length cannot exceed 79 characters.


<p><dt><b>"i[d]"</b><dd>component ID. It's a nine-digit string.
<dt><b>"i[d]"</b><dd>component ID. It's a nine-digit string.


<p><dt><b>"k[ind]"</b><dd>component kind.  It can be 0 (if the component is a base one), 1
<dt><b>"k[ind]"</b><dd>component kind.  It can be 0 (if the component is a base one), 1
or 2 (if the component is a system extension) or 15 otherwise.
or 2 (if the component is a system extension) or 15 otherwise.


<p><dt><b>"v[ersion]"</b><dd>component version.  It should follows the 'x.yy[.z]' format.
<dt><b>"v[ersion]"</b><dd>component version.  It should follows the 'x.yy[.z]' format.
 


<p><dt><b>"t[ype]"</b><dd>component type.  It's a string.
<dt><b>"t[ype]"</b><dd>component type.  It's a string.


<p><dt><b>"c[csd]"</b><dd>current CSD level.  It's a seven-byte string.
<dt><b>"c[csd]"</b><dd>current CSD level.  It's a seven-byte string.


<p><dt><b>"p[csd]"</b><dd>previous CSD level.  It's a seven-byte string.
<dt><b>"p[csd]"</b><dd>previous CSD level.  It's a seven-byte string.
<br>
<br>
It returns the field value or 'ERROR'.
It returns the field value or 'ERROR'.


<p><dt><b>"s[et]"</b><dd>sets the specified SYSLEVEL file field.  File is a filename
<dt><b>"s[et]"</b><dd>sets the specified SYSLEVEL file field.  File is a filename
returned by a previous RexxLvl OPEN or NEW call.  Field is the field to be
returned by a previous RexxLvl OPEN or NEW call.  Field is the field to be
set.  It can be any of the values taken by the field parameter of the
set.  It can be any of the values taken by the field parameter of the
Line 595: Line 549:
</dl>
</dl>


<p>
== LvlQueryLevelFile ==
<h2>LvlQueryLevelFile</h2>
 
APIRET LvlQueryLevelFile(PSZ pszName, PSZ pszCID, PVOID pBuffer, ULONG ulBufSize)


APIRET LvlQueryLevelFile(PSZ pszName, PSZ pszCID, PVOID pBuffer, ULONG
ulBufSize)


<p>
Use this to query/find existing SYSLEVEL files.  This function enumerates
Use this to query/find existing SYSLEVEL files.  This function enumerates
all the SYSLEVEL files present in the system and returns the names as a
all the SYSLEVEL files present in the system and returns the names as a
Line 609: Line 561:
enumeration to the corresponding SYSLEVEL files.
enumeration to the corresponding SYSLEVEL files.


<p>It returns the following values:
It returns the following values:
<pre>
 
0    NO_ERROR
0    NO_ERROR
87    ERROR_INVALID_PARAMETER
87    ERROR_INVALID_PARAMETER
122  ERROR_INSUFFICIENT_BUFFER
122  ERROR_INSUFFICIENT_BUFFER
</pre>
 
When it returns ERROR_INSUFFICIENT_BUFFER, the first ulBufSize bytes of
When it returns ERROR_INSUFFICIENT_BUFFER, the first ulBufSize bytes of
pBuffer are correctly filled.
pBuffer are correctly filled.

Revision as of 09:10, 21 February 2005

Written by Martin Lafaix

Introduction

What's that?

Determining a computer software configuration has always been a tricky process. It would be great to have a standard way for registering applications, so that we could easily query the system configuration. But wait a minute! OS/2 provides the SYSLEVEL command, which does just that (it even tells us the various component versions)! If only we could use it for our own purposes...

Why?

This registering ability proves its usefulness when writing (relatively) large projects or components. For example, if you write a public toolkit or tools other programs may use, it would be a good idea to register them. (OS/2 already uses it this way: on my home system, the syslevel command tells me that MMPM/2 and the Developer's toolkit are present - the same thing occurs if you have Communication Manager, etc.) When distributing such a tool, tell your users your component name and ID; it would then be easy for them to write an installation procedure which checks your component presence.

Another interest is that it helps component writers in creating CSD or component updates. As each SYSLEVEL file maintains a current CSD level as well as a previous CSD level field, it's easy to check what to update, and what to leave alone.

It's also useful while maintaining a computer network. It would provide the maintainer an up-to-date information database.

Contents

This article contains two parts. The first one describes the SYSLEVEL file format. The second part describes the two proposed APIs (one for C, and one for Rexx).

A Voyage to Syslevel

When you start SYSLEVEL.EXE, it scans all your drives for SYSLEVEL files. These files are named SYSLEVEL.xxx (where xxx is any three-letters extension), and have a specific header. When one such file is found, it is displayed, as shown in figure 1. So, to be able to use SYSLEVEL for our own purpose, we'll have to decrypt SYSLEVELs' file headers, and find out how (and where) to store our own data.

C:\MMOS2\INSTALL\SYSLEVEL.MPM
                             Multimedia Presentation Manager 2
Extensions systŠme 1.10     ID de composant 562137400
Type MMPM\2
Niveau de modifications en cours : UN00000
Niveau de modifications ant‚rieur : UN00000

Figure 1. Syslevel output sample (French version, sorry :-) )

On the File Header

A SYSLEVEL file header looks like the following:

typedef struct _SYSLEVELHEADER {
   unsigned char h_magic[2];
   unsigned char h_name[9];
   unsigned char h_reserved1[4];
   unsigned char h_updated;
   unsigned char h_reserved2[17];
   ULONG         h_data;
} SYSLEVELHEADER;

Figure 2. The SYSLEVELHEADER structure.

h_magic
is the magic cookie. Its value is 0xFFFF.
h_name
is the string "SYSLEVEL", in uppercase and null-terminated.
h_reserved1
meaning unknown. Set to zero.
h_updated
was 1 for OS2, GRE and MPM, and 0 for TLK. My guess: set it to zero.
h_reserved2
meaning unknown. Set to zero.
h_data
points to the beginning of the file's data structure (offset from the beginning of the file), which is described below.

While I realize there's still many holes in there, it appears to fulfill our needs; namely, we are now able to create files recognized by SYSLEVEL.EXE. Isn't that great?

But it's time to learn more...


The SYSLEVEL Data Structure

In the previous section, we have seen that the file's data structure starts at offset h_data. This structure (aka. the SYSLEVEL data structure) looks like the following:

typedef struct _SYSLEVELDATA {
   unsigned char d_reserved1[2];
   unsigned char d_kind;
   unsigned char d_version[2];
   unsigned char d_reserved2[2];
   unsigned char d_clevel[7];
   unsigned char d_reserved3;
   unsigned char d_plevel[7];
   unsigned char d_reserved4;
   unsigned char d_title[80];
   unsigned char d_cid[9];
   unsigned char d_revision;
   unsigned char d_type[1];
} SYSLEVELDATA;

Figure 3. The SYSLEVELDATA structure.

d_reserved1
set to zero...
d_kind
specifies the component "kind". It can be any of the following:
  • SLK_BASE (0) "Base version"
  • SLK_EXTENSION (2) "System extension"
  • SLK_STANDARD (15) Neither "Base version" nor "System extension"... :-)
Any other value is equivalent to SLK_STANDARD (except 1, which is equivalent to SLK_EXTENSION).
d_version
specifies the component version. Its format is a bit strange: the four high-order bits of d_version[0] contains the major version number, the four low-order bits contains the first minor version digit, and d_version[1] contains the second minor version digit. For example, if d_version[0] is 0x21 and d_version[1] is 0x02, you will get "2.12". [You can have a revision level, as in "2.12.a". See d_revision below.]
d_reserved2
oh no, not again. Set to zero.
d_clevel
specifies the current CSD level. It's a seven-byte string, usually in the form "AAC####", where AA is a two-byte string, C a country-dependent code, and #### a number. For example, on my home computer, I get "XRF2010" for the base operating system (It's the French version), and "XR02100" for the developer's toolkit (US version, this time). It's just a suggested usage and is not enforced in any way. As long as you use seven printable characters, it should work.
d_reserved3
guess what? You lose! Set to 0x5F this time.
d_plevel
specifies the previous CSD level. If there was no previous level; set it equal to the current level, d_clevel.
d_reserved4
is just like d_reserved3; set to 0x5F
d_title
specifies the component name. It's a null-terminated string.
d_cid
specifies the component ID. It's not null-terminated.
d_revision
specifies the component revision. It does not appear in the version field if sets to zero. If it's not null, it will be concatenated with major and minor version number, as in "1.2.3" (where 1 is the major version, 2 the minor and 3 the revision). If you prefer a letter in place of a digit, just increment the revision number by 0x10 for an upper-case letter, or by 0x30 for a lower-case. That is, if d_revision is 0x11, you will get an "A", and so on.
d_type
specifies the component type. It's an optional, null-terminated string. This field allows you to store additional informations for your component. For example, this field is used to differentiate the blue and salmon distribution of OS/2 (a type of '0-2' indicates the 'salmon' one).

Well, enough said on this exploration. It's time to go back to much safer places...


Home, Sweet Home

This walk was fun, but all good things must come to an end. So we are back. And it's time to write some functions to comfort our precious new knowledge, isn't it?

Two APIs are available, a C one and a REXX one.

The SYSLEVEL C API

This section describes the API which provides access to the syslevel files. The corresponding code is in CLEVEL.ZIP. It's just an include file, level.h and a small C file, level.c which can be included, say, in your favorite library. It has been tested with GCC/2, but it should work with any other C compiler...

APIRET LvlOpenLevelFile(PSZ pszName, PHFILE phFile, ULONG ulOpenMode, PSZ pszCID)

APIRET LvlCloseLevelFile(HFILE hFile)

APIRET LvlQueryLevelFile (PSZ pszName, PSZ pszCID, PVOID pBuffer, ULONG ulBufSize)

APIRET LvlQueryLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)

APIRET LvlWriteLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)

Design considerations

This API has been modeled upon the profile (Prf) functions.

Examples

The following two figures show how to check for the existence of a component, and if a component needs updating, respectively.

:
#include <level.h>
:
int main()
{
  HFILE hFile;
  :
  if(hFile = LvlOpenLevelFile("DBM",
                              OLF_SCANDISKS | OLF_CHECKID,
                              "123456789"))
    /* present */
    :
  else
    /* not present */
    :
  :
  LvlCloseLevelFile(hFile);
  :
}

Figure 4. Syslevel API usage sample - Checking for the existence of a component

:
#include <level.h>

:
BOOL _needUpdate(HFILE hFile)
{
  CHAR achCSD[7];
  ULONG ulCount;

  if(LvlQueryLevelData(hFile, QLD_CURRENTCSD, achCSD, 7, &ulCount))
    /* shall we update it? */
    return strncmp(achCSD, "DBF0099",7) <= 0; /* for example */
  else
    /* an error occurred */
    :
}
:

Figure 5. Syslevel API usage sample - Shall we update component?

The SYSLEVEL REXX API

Well, it's not really an API, it's in fact a single function. The corresponding code is in RLEVEL.ZIP. It's just a REXX file, named REXXLVL.CMD. Put it somewhere along your PATH. The zip file also contains MAKESYSL.CMD, which can be used to create SYSLEVEL files.

This function has been modeled upon the STREAM function.

RexxLvl()

Example

The following example is a REXX script. It allows you to easily create syslevel files.

/* makesysl.cmd                                          940327 */

say 'Component name:';                  parse pull name
say 'Component ID:';                    parse pull ID
say 'Component type (optional):';       parse pull type
say 'Component version (as x.y[.z]):';  parse pull version
say 'Current CSD Level:';               parse pull CSD
say 'Component extension:';             parse pull ext

file = RexxLvl("n", ext, ID)
call RexxLvl "s", file, "n", name
call RexxLvl "s", file, "c", csd
call RexxLvl "s", file, "v", version
call RexxLvl "s", file, "t", type
rc = RexxLvl("c", file)

if rc = 0 then say 'SYSLEVEL.'ext' created.'

Figure 6. Creating a syslevel file in REXX.

Summary

Now you have enough information to use the SYSLEVEL files well. However, the advertised advantages would become a reality only if we use SYSLEVEL files extensively. So, if you like these perspectives, use them, ask them, require them, again and again, until you get satisfaction.  :-)

As a sidenote, the information exposed in the first part of this article is the result of my own exploration. If you find any inaccuracy or if you have more information, please, let me know.

kind

SLK_BASE
denotes a "base" component.
SLK_EXTENSION
denotes a system extension.
SLK_STANDARD
denotes a "standard" component. It's the suggested component kind value.

ulOpenMode - input

ULONG
field that describes the mode of the open function.
OLF_OPEN
open the specified file. The default behavior is to scan the current disk, starting from the current directory. It returns the first SYSLEVEL file whose extension matches pszName You can override this by combining OLF_OPEN with OLF_SCANDISK or OLF_CHECKID.
OLF_SCANDISKS
scans all disks (starting from C:). Use this flag to modify the default OLF_OPEN behavior.
OLF_CHECKID
finds file(s) whose ID matches the specified one. Use this flag to override the default OLF_OPEN behavior.
OLF_CREATE
creates the specified file in the current directory. A valid pszName and ID should be provided.

ulWhat - input

The following flags can be used when querying (or writing) a SYSLEVEL file.

QLD_MAJORVERSION
Query (or update) the major version field. It's a one-character field. It should be in the range '0'-'9'. The value is placed in (or taken from) the first character of pBuffer. (Buffer size should be at least 1.)
QLD_MINORVERSION
Query (or update) the minor version field. It's a two-character field. It should be in range '00'-'99'. The value is placed in (or taken from) the first two chars of pBuffer. (Buffer size should be at least 2.)
QLD_REVISION
Query (or update) the revision field. It's should fit in a character. If it's '0', there's no revision available. It can be a letter as well as a digit. The value is placed in (or taken from) the first character of pBuffer. (Buffer size should be at least 1.)
QLD_KIND
Query (or update) the kind field. The value is placed in (or taken from) the first character of *pBuffer. (Buffer size should be at least 1.)
QLD_CURRENTCSD
Query (or update) the current CSD level (when you update this field, its old value is copied to the old CSD level field). It's a seven-character field, and it does not have to be null-terminated. The value is placed in (or taken from) the first seven characters of pBuffer. (Buffer size should be at least 7.)
QLD_PREVIOUSCSD
Query the previous CSD level. You can't update this field. The value is placed in the first seven chars of pBuffer. (Buffer size should be at least 7.)
Note: CSD levels are not null-terminated. Be careful when using such a returned value.
QLD_TITLE
Query (or update) the component title field. It's an eighty-character string (required ending null included). The value is placed in (or taken from) the first eighty characters of pBuffer. On input, the buffer size should be at least 80. On output, the buffer size can exceed 80, but the written string is truncated (and null-terminated) to eighty characters.
QLD_ID
Query (or update) the component ID field. It's a nine-character field. It does not have to be null-terminated. The value is placed in (or taken from) the first nine characters of pBuffer. (Buffer size should be at least 9.)
Note: IDs are not null-terminated. Be careful when using such a returned value.
QLD_TYPE
Query (or update) the component type field.

LvlOpenLevelFile

APIRET LvlOpenLevelFile(PSZ pszName, PHFILE phFile, ULONG ulOpenMode, PSZ pszCID)

Use this to find, open or create a SYSLEVEL file.

It returns the following values:

0     NO_ERROR
2     ERROR_FILE_NOT_FOUND
3     ERROR_PATH_NOT_FOUND
4     ERROR_TOO_MANY_OPEN_FILES
5     ERROR_ACCESS_DENIED
12    ERROR_INVALID_ACCESS
26    ERROR_NOT_DOS_DISK
32    ERROR_SHARING_VIOLATION
36    ERROR_SHARING_BUFFER_EXCEEDED
82    ERROR_CANNOT_MAKE
87    ERROR_INVALID_PARAMETER
99    ERROR_DEVICE_IN_USE
108   ERROR_DRIVE_LOCKED
110   ERROR_OPEN_FAILED
112   ERROR_DISK_FULL
206   ERROR_FILENAME_EXCEED_RANGE
231   ERROR_PIPE_BUSY

pszName - input

The syslevel file extension. It's a three-letter, null-terminated string. That is, LvlOpenLevelFile deals with syslevel files named syslevel.xxx, where xxx is pszName's value.

LvlCloseLevelFile

APIRET LvlCloseLevelFile(HFILE hFile)

Use this to close a SYSLEVEL file. It's just a DosClose alias, for API consistence.

It returns the following values:

0   NO_ERROR
2   ERROR_FILE_NOT_FOUND
5   ERROR_ACCESS_DENIED
6   ERROR_INVALID_HANDLE

LvlQueryLevelData

APIRET LvlQueryLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)

Use this to query an already opened SYSLEVEL file.

It returns the following values:

0     NO_ERROR
6     ERROR_INVALID_HANDLE
87    ERROR_INVALID_PARAMETER
122   ERROR_INSUFFICIENT_BUFFER

When it returns ERROR_INSUFFICIENT_BUFFER, *pulSize contains the minimum requested size.

pBuffer - input/output

Address of the buffer that contains the data to write/read.

pulSize - output

Address of the variable to receive the number of bytes actually read or written.

LvlWriteLevelData

APIRET LvlWriteLevelData(HFILE hFile, ULONG ulWhat, PVOID pBuffer, ULONG ulBufSize, PULONG pulSize)

Use this to update an already opened SYSLEVEL file.

It returns the following values:

0     NO_ERROR
6     ERROR_INVALID_HANDLE
19    ERROR_WRITE_PROTECT
29    ERROR_WRITE_FAULT
87    ERROR_INVALID_PARAMETER
122   ERROR_INSUFFICIENT_BUFFER

When it returns ERROR_INSUFFICIENT_BUFFER, *pulSize contains the minimum requested size.

ulBufSize - input

The length, in bytes, of pBuffer. This is the number of bytes to be read or write.

phFile - output

Address of the handle for the file.

pszCID - input

It's the component ID string. It's required when you specify the OLF_CHECKID flag when opening a syslevel flag. It's a nine-digit string, not null-terminated, which uniquely identifies the component in conjunction with pszName. That is, you can have multiple SYSLEVEL files with the same name, but you can't have multiple syslevel files with the same name and ID.

If you are not using the OLF_CHECKID flag, set pszCID to NULL.

RexxLvl()

[Images:rexx-lvl.gif]

RexxLvl returns a string describing the state of, or the result of an operation upon, a SYSLEVEL file. This function is used to request information on the state of a SYSLEVEL file, or to carry out some specific operation on the SYSLEVEL file.

The first argument can be one of the following strings (of which only the first letter is needed) which describes the action to be carried out:

"c[lose]"
closes the specified syslevel file. File is a filename returned by a previous RexxLvl OPEN or NEW call.
"e[num]"
enumerates the matching SYSLEVEL files. Extension is (an optional) SYSLEVEL file extension. Id is (an optional) component ID. It returns the number of matching files, or 'ERROR'. The corresponding filenames are pushed on the current queue. Use PULL or PARSE PULL to get them back.
"n[ew]"
creates a new SYSLEVEL file, in the current directory. Extension is the SYSLEVEL file extension, and id is the component ID. It returns a file name to be used by subsequent RexxLvl calls.
"o[pen]"
opens the specified SYSLEVEL file. Extension is the SYSLEVEL file extension, and id is the component ID. It returns a file name to be used by subsequent RexxLvl calls, 'NOTFOUND' if the required file was not found or 'ERROR' if an error occurred while searching.
Note: It returns the first matching SYSLEVEL file.
"q[uery]"
queries a SYSLEVEL file field. File is a filename returned by a previous RexxLvl OPEN or NEW call. Field is the field to be queried. It can be any of the following (of which only the first letter is needed):
"n[ame]"
component name. Its length cannot exceed 79 characters.
"i[d]"
component ID. It's a nine-digit string.
"k[ind]"
component kind. It can be 0 (if the component is a base one), 1 or 2 (if the component is a system extension) or 15 otherwise.
"v[ersion]"
component version. It should follows the 'x.yy[.z]' format.
"t[ype]"
component type. It's a string.
"c[csd]"
current CSD level. It's a seven-byte string.
"p[csd]"
previous CSD level. It's a seven-byte string.
It returns the field value or 'ERROR'.
"s[et]"
sets the specified SYSLEVEL file field. File is a filename returned by a previous RexxLvl OPEN or NEW call. Field is the field to be set. It can be any of the values taken by the field parameter of the RexxLvl "query" call, except "p[csd]" (you can't set the "p[csd]" field's value -- it is automatically sets when you update the c[csd] field). Value is the field's new value. It returns an empty string, or 'ERROR'.

LvlQueryLevelFile

APIRET LvlQueryLevelFile(PSZ pszName, PSZ pszCID, PVOID pBuffer, ULONG ulBufSize)


Use this to query/find existing SYSLEVEL files. This function enumerates all the SYSLEVEL files present in the system and returns the names as a list in the pBuffer parameter. Each SYSLEVEL file name is terminated with a NULL character and the last name is terminated with two successive NULL characters. Specifying pszName or pszCID (or both) restricts the enumeration to the corresponding SYSLEVEL files.

It returns the following values:

0     NO_ERROR
87    ERROR_INVALID_PARAMETER
122   ERROR_INSUFFICIENT_BUFFER

When it returns ERROR_INSUFFICIENT_BUFFER, the first ulBufSize bytes of pBuffer are correctly filled.