Serving the MPE Shell on a Platter


Ronald Dupree Moore

 

Hewlett Packard Company
20 Perimeter Summit Blvd
Atlanta, GA 30319
(404) 648-5441
(404) 648-5450 FAX
ronald_d_moore@hp.com

 


 

What is a command file?

 

MPE has come a long way.  In the beginning we had JCWs and UDC to help us automate common system administration task.  As computers in general improved over the last thirty years, so has MPE.  In the beginning it was like cooking with a wood burning stove.  Today we are not only cooking with gas, we're having our meals served to us on a silver platter.  This paper will explore serving MPE commands on a platter, the main focus of this paper is to introduce the reader to commands files, scripts, or shell programs.  A rose by any other name still smells as sweet.  It does not matter which name you use command file, script, or shell program, we are going to learn some fundamental techniques for writing command files.  Topics that will be discussed are:

 

1.                   Variables

2.                   Parameters

3.                   Input

4.                   Expressions

5.                   Branching

6.                   Loops

 

 

We'll start with a definition and purpose of command files.  A command file is a mechanism for executing multiple Command Interpreter (CI) commands in a prescribed sequence, similar to a procedure.  It provides for the use of variables, expression evaluators, and has a structure that simulate the branching, looping, input, and output structures needed for programmatic control, with an interface mechanism providing programmatic access to the CI from application programs.  Command files can be used to simplify tasks.  Complicated routines requiring multiple commands can be made transparent to users through command files.  Some programming tasks can be coded more simply and efficiently with CI commands than with a standard application language.  Routines can be written with CI commands and accessed from application programs through the intrinsic facility.  Variables can be used by both CI routines and application programs to pass information between routines.

 

A command file is a regular MPE flat file.  All the user needs is read access to execute the command file.  As of 6.0 you can execute a command file by having only execute access.  That way the user can not read the lines in the file.  This is an excellent way to hide the code from the user.

 

Command files and UDC can both accomplish the same task.  Command files do not have the overhead that is associated with UDCs.  With UDCs you have to put all the UDC in the same file, identifying a head, body, and tail.  After saving the file, you then must issue the SETCATALOG command to turn on the UDC.  The overhead comes at logon time when the system is required to scan all UDC that are set for a given user.  This adds to the time that it takes during the logon process.

 

With command files there is no catalog to set, you create a standard text file and type the name of the text file to execute the CI commands stored in the file.  There are some down sides to command files.  How do you isolate all of your commands into one group, let's say a group named CMD and want everyone in the account to have access to the command files.  First you need to store all the command files in the group and modify the access to the group using the following command while logged on as the account manager:

 

:ALTGROUP CMD;ACCESS=(R:AC,GU;W,A,L,X,S:GU)

 

This will allow all users of the account to have read access to the files.  The other problem is that you have to qualify the filename by supplying the group name.  For example, if a user wants to execute a command file named PWD from a group other than CMD to determine the current GROUP that he is logged into, the user would have to type:

 

PWD.CMD

 

 

 

The way around this is to set the path using the following command.

 

:SETVAR HPPATH, "!HPGROUP,PUB,PUB.SYS,ARPA.SYS,CMD"

 

As you have probably guessed this solution is only good during your current session.  What happens when you log off?  Yes, you're right.  You have to type the above command ever time you logon.

 

So, how do you get over this new hurdle?  We can accomplish this with a logon UDC.  Here is a sample logon UDC to solve the above problem.

 

:Editor

 

/ADD

1                     STARTIT

2                     OPTION LOGON

3                     SETVAR     HPPATH,  "!!HPGROUP, PUB,PUB.SYS,ARPA.SYS,CMD"

4                     **

5                     //

/KEEP    ACCTUDC,UNN

/EXIT

 

:SETCATALOG   ACCTUDC;APPEND;ACCOUNT

 

 

Now, whenever an account member logs on the above PATH will be set.  This path points to the CMD group, eliminating the need of qualifying the filename to include the group name CMD.

 

There are the two tasks that must be done in order to store all of your command files into a group name CMD.  First you must modify the group to give everyone in the account read access to the group CMD.  Second, you need to create a logon UDC at the account level that will set the PATH to point to the CMD group.  If you make an error in keying in the logon UDC and need to go back into the UDC file to make a correction, you must deactivate the UDC by typing the following:

 

:SETCATALOG    ACCTUDC;DELETE;ACCOUNT

 

Go into the editor to make your corrections.  Don't forget to turn the UDC back on using the APPEND option.

 

What if you want to setup a group to store command files that can be accessed system wide.  Create the CMD group in the SYS account, set the access R:ANY, and set the catalog with :SYSTEM instead of :ACCOUNT.

 

Now that we know what is a command file and how to store them into a common group for all account members, let's learn how to create some useful command files.


 

Creating and using Variables in Command Files

 

I like to think of a variable as a mailbox or storage location in my computer's memory.  When we create a variable, we are telling to computer to reserve us a spot in memory and name that spot with the variable name.  The command:

 

:SETVAR   SIZE, 80

 

will create a spot (a mailbox) in memory called SIZE and place the value 80 into the mailbox named SIZE.

 

Variables can contain textual, numeric, or Boolean data (true or false).  The SETVAR command is used to assign a value to the variable.  Variable names can be any combination of letters and numbers plus the underscore character, up to a total of 255 characters.  Variables must start with a letter or the underscore character.

 

There are 94 predefined HP variables in 6.0, these variables are session or job temporary.  These predefined HP variables are also referred to as system or global variables.  You can gain some valuable information about system variables by typing:

 

:HELP   VARIABLES

 

Some of the system variables are configurable, you can assign values to them for example HPPATH.   But remember that system variables are session dependent, when you log off and back on they will have their original value.   The following is an example of setting HPPROMPT to display the group that you're currently logged into:

 

:SETVAR HPPROMPT, "!!HPGROUP: "

 

When you execute the :CHGROUP command your prompt will reflect the name of the group that you're in.  SETVAR is how you create a variable, but when you use the variable (a system variable or a user defined variable), you must proceed the variable name with an exclamation point "!".

 

Let's explore using variables.  Let's create a variable called "NAME".

 

:SETVAR  NAME,  "RONALD"

 

This will create a mailbox called NAME and place the value RONALD into the mailbox .  If you want to display the value in a variable (mailbox), you can use the ECHO command.  For example to display the value of the variable NAME, you would type the following:

 

:ECHO   !NAME

 

The word RONALD will be displayed to your screen.  The ECHO command is like the PRINT command in the BASIC programming language.  ECHO will display whatever follows the word ECHO to the screen, including the content of a variable when the variable is proceeded with the "!".

 

You can also include the name of a variable inside the string that you store in a variable.  Let's create a variable called STR and place the variable name inside the string.

 

:SETVAR  STR,  "!NAME IS MY HERO"

 

What do you think will get displayed if you ECHO !STR to the screen?  You're right.

 

RONALD IS MY HERO will be echoed to the screen.  What do you think will happen if we change the value of NAME and redisplay STR to the screen.  Well, let's do it and see.

 

:SETVAR   NAME,  "NATALIE"

 

:ECHO !STR

 

RONALD IS MY HERO  will be displayed.  Why?  When we set the variable STR

 

SETVAR  STR,  "!NAME IS MY HERO"         the system replaced !NAME with RONALD and the string that got stored into STR was "RONALD IS MY HERO".  The system substituted the value of RONALD for !NAME.

 

What would you have to do to make the value of the variable NAME change in STR when the value of NAME is changed?  The answer is in what is "defeferenceing  variables".  One bang (!) proceeding the variable name means replace the variable with what is stored in the variable.  So in the example:

 

SETVAR   STR,  "!NAME IS MY HERO"

 

The value in NAME is substituted in to the string, so before the string is stored into STR, the value in NAME is substituted into the string.  You get, "RONALD IS MY HERO", this string is then stored into the variable STR.

 

Two bangs (!!) will not substitute the value of the variable, it will place the variable name with one bang in front of the variable in the string.  For example:

 

:SETVAR   STR,   "!!NAME IS MY HERO"

 

Will evaluate to "!NAME IS MY HERO" and this is what get stored inside of the variable STR.  Now that "!NAME" is stored inside of the variable STR instead of RONALD, every time the variable STR is displayed to the screen the value inside of NAME is displayed.  Let's review.  Type in the following:

 

:SETVAR   NAME,  "RONALD"

:ECHO !NAME                                                                                     (RONALD is displayed)

:SETVAR   STR,   "!NAME IS MY HERO"

:ECHO  !STR                                                                                         (RONALD IS MY HERO is displayed)

:SETVAR   NAME,   "NATALIE"

:ECHO   !STR                                                                                        (RONALD IS MY HERO is displayed)

:SETVAR   STR,   "!!NAME IS MY HERO"

:ECHO   !STR                                                                                        (NATALIE IS MY HERO is displayed)

 

This is why we use two bangs when we set the system variable HPPROMPT.  One bang would have hard coded the group name of the group that you were in when you typed the SETVAR command.  Two bangs will always display what is in the variable at the time of execution.  The value of HPPROMPT is check every time you hit the return key to find the value of what is stored in the variable HPGROUP.  So, remember one bang checks once where two bangs checks every time.

 


Using Parameters with Command Files

 

 

Think of a command file as a program (shell program) and think of parameters as values that are passed to variables inside the program.  The order of PARMs inside the program must match the order of the parameters that are passed to the program.  When using parameters, I recommend that you don't have more than three parameters.  If you are using more than three parameters, I recommend that you use the INPUT statement and prompt the user for the data.  We'll talk mare about the INPUT statement later.

 

Here is a simple command file that I use to send output to a printer:

 

PARM   FILENAME

FILE   L;DEV=205                                 (205 is the logical device number to a printer)

PRINT   !FILENAME  >  *L

 

The PARM statement creates the variable (mailbox) called FILENAME.  The second line of the command file sets up a file equation for a printer.  The third line uses Output redirection to send the output of the print command which normally goes to the screen to the printer 205.  You can key in the above three lines and save the file as "LP".  You might have to ask you system administrator for the ldev number of you printer.

 

To execute this command file you would type in the following:

 

:LP   filename

 

If you're using PARMs in your command files the PARM statement must be the first line of the command file.  Multiple PARMs can be list on the same line, but I recommend that you limit the number of PARMs to three.

 

My intent here is to show you some command files that I share with my students when teaching H3217S (MPE/iX Fundamentals) and H3219S (MPE/iX System Administration).  I'll give you the command and explain what it is doing.

 

Let's look at what a COBOL programmer would have to key in to compile his program.

 

:COB85XL   PROG1,PROG1.OBJ

 

Assuming that the source code lives in the SRC group and this is where the programmer is compiling the program.  We could write a command file to compile the source code and store the object code in the OBJ group.

 

PARM FILENAME

COB85XL !FILENAME,!FILENAME.OBJ

 

You can type in the above and save the file as COB.  To execute the command file you would type:

 

:COB   PROG1

 

We can also create a command to link the object and store the executable code in the EXEC group.

 

PARM FILENAME

LINK   !FILENAME.OBJ,!FILENAME.EXEC

 

You can type in the above and save the file as LK.  To execute the command file you would type:

 

LK   PROG1

How about a command file to allow you to change groups.  Yes, we can do that.  Here is the code:

 

PARM    GROUPNAME=!HPHGROUP

CHGROUP   !GROUPNAME

 

There is something new on line one.  What is happening with the "=!HPHGROUP"?  First of all what is stored in the variable HPHGROUP.  This is where the system stores the name of your home group.  To more efficiently create command files you need an understanding of how the real command works.  CHGROUP will make your present working group, the name of the group following the command.  But what happens if you type CHGROUP without specifying a group name.  CHGROUP will place you in your home group.  Placing an equal sign after a PARM is creating a default for the PARM.  In other words if the user type CD DATA, the system will place the user in the DATA group.  Let's assume that SRC is the user's home group.  If the user type CD (note you did not specify a group after the command file name), the system will use what is after the equal sign (the default value).  In this case the system will place the user in his home group, because HPHGROUP contains the value of the users home group.

 

Can you create a command file called "L" that will perform a LISTFILE?  Sure you can.  First how does LISTFILE work.

 

:LISTFILE  file-reference, option

 

The file-reference could be a single filename or it could be a wild card that would display the names of all the files that match the wild card pattern.  But what happens if you don't specify a file-reference.  The system will assign the "@" wild card for you.  Therefore, the "@" is the default file-reference for LISTFILE.  The options range from -3 to 9.  But what happens if  you don't specify an option.  The system will assign the value "0"; therefore "0" is the default for LISTFILE.  Knowing the above, now you can write the command file for  LISTFILE.

 

PARM   FILENAME=@,  OPT=0

LISTFILE   !FILENAME,!OPTION

 

On line one we have assigned two PARMs to pass to the command file "L".  The first PARM "FILENAME" has a default of "@".  The second PARM "OPT" has a default of "0".  You could execute the command file by typing:

 

:L                             (list all files in the present working group using the defaults "@" and "0")

:L  B@                    (list all files that start with a B and use the default "0" for the option parm)

:L ,2                         (list all files using the default for file-reference "@" and option 2)

:L  xfile,3 (list the information in the file label for xfile)

 

It is important to arrange the PARMs in the correct order, with the file-reference coming first, followed by the file option.

 

The syntax for aborting a job is tedious to type.  A command file would be of great assistance.  Can you write a command file to abort a job?  Sure you can.  Let's look at the code.

 

PARM   JOBID

ABORTJOB   #J!JOBID

 

How about a command file to alter the priority of a job if the JOBFENCE for the system is set to 10.  We can do that.

 

PARM   JOBID,  JOBPRI=11

ALTJOB   #J!JOBID;INPRI=!JOBPRI

 

Had enough of parameters.  Let's look at the INPUT statement.

Using the INPUT Statement with Command Files

 

The INPUT statement is used to solicit a response from the user.   Let's say you want to create a command file to create users.  You could pass the value of the user's name as a parameter or you could ask the user to enter the value using the INPUT statement.  For example:

 

CLS

ECHO This Command File will prompt the user for a user's name and create a user and home group

ECHO for that user, using the value entered.

ECHO

ECHO

INPUT USERNAME, "Please enter the user name:    "

NEWUSER !USERNAME;HOME=!USERNAME

NEWGROUP !USERNAME

ECHO

ECHO

LISTUSER !USERNAME

LISTGROUP !USERNAME

ECHO

ECHO

ECHO End of Program

 

The syntax is:

 

INPUT  variable, "Input string    "

 

Here is another example:

 

CLS

ECHO

ECHO

ECHO This Command File will prompt the user for the number of the job to delete

ECHO

ECHO

INPUT JOBNUM,   "Please enter the job number:  "

ABORTJOB   #J!JOBNUMBER

ECHO

ECHO

ECHO End of Program

 

We could have accomplished the same task by using a parameter.  For example you could go into the editor and type the follow lines then save the file as "KILL"

 

PARM  JOBNUM

ABORTJOB  #J!JOBNUM

 

To execute the above command file you would type:  :KILL   49

 

This would abort the job with the id "#J49"

 

Both the INPUT statement and the use of parameters in have their advantages and disadvantages.  If you're only passing one or two values to the command file parameters are fine, but if you're passing more than two values I like the INPUT statement.  With the INPUT statement, I don't have to worry about the order of the parameters.

 

Using Expression Substitution with Command Files

 

 

MPE provides an expression evaluator that supports a large selection of arithmetic operations, number conversions, file information, and string manipulation operations.  You can type :HELP FUNCTIONS to see a list of the available expression evaluators.  These expression evaluators form the basis of comparison structures in establishing branching and looping.

 

The result of an expression must be dereferenced explicitly.  This is accomplished by placing the expression in square brackets and preceding it with a bang (!).  For example:

 

CLS

ECHO

ECHO

ECHO This command file will prompt the user for a filename, record size, and the file size

ECHO (the number of records).  It will check to see if the file already exist.  If the file does not

ECHO exist it will build the file and automatically increase the file size by 20%.

ECHO

ECHO

INPUT FILENAME, "Please enter the filename:  "

ECHO

ECHO RECSIZE, "Please enter the record size:   "

ECHO

ECHO FILESIZE, "Please enter the file size:       "

IF  (FINFO("!FILENAME", "EXISTS")) THEN

     BUILD !FILENAME;REC=!RECSIZE;DISC=![FILESIZE * 1.2]

ELSE

     ECHO !FILENAME already exist

ENDIF

ECHO

ECHO

ECHO

ECHO End of Program

 

Note the !RECSIZE is explicitly dereferenced outside of square brackets, while FILESIZE is implicitly dereferenced inside of the square brackets.

 

Here is another command file.  This one will tell you the creator of a file.

 

CLS

ECHO

ECHO

ECHO This command file will prompt the user for a filename.

ECHO It will then display the name of the Creator.

ECHO

ECHO

INPUT FILENAME, 'Please enter the filename:  "

SETVAR CREATOR, ![FINFO(FILENAME,"CREATOR")]

ECHO

ECHO

ECHO !CREATOR is the file creator

ECHO

ECHO

ECHO End of Program

 

Using Branches with Command Files

 

 

The IF statement is how we perform branching within command files.  Let's look at the three different forms of the IF statement:

 

Example 1:              Simple IF-THEN-ENDIF statement:

 

IF expression THEN

     Some CI command (when true)

ENDIF

 

Example 2:              IF-THEN-ELSE-ENDIF statement:

 

IF expression THEN

     Some CI command (when true)

ELSE

     Some CI command (when false)

ENDIF

 

Example 3:              IF-THEN-ELSEIF-ELSE-ENDIF statement:

 

IF expression THEN

     Some CI command (when true)

ELSEIF expression THEN

     Some CI command (when true)

     Some CI command (when true)

ELSEIF expression THEN

     Some CI command (when true)

ELSE

     Some CI command (when false)

ENDIF

    

Example 1 is used when there is only one choice, example 2 is used when there are two choices, and example 3 is used when there is more than two choices.

 

Note following the first ELSEIF in example 3 that there are two CI command executed.  You can execute one or more command when a condition is true or false.  I used only one to keep the syntax simple.

 

Can we write a command file to check to see if a session name was entered at logon time?  Sure we can check to see if a user has entered a session name at logon, but we have to do it as a logon UDC rather than as a command file.  I use command files when ever possible, but there are two times when you have to use UDCs.  One such time is to execute some commands at logon and the other is to mask or hide a CI command.  Here is the logon UDC to check for a session name.  Inside the EDITOR you would type the following:

 

STARTIT

OPTION LOGON

IF HPJOBNAME = "" THEN

     ECHO You must enter a session name

     INPUT JNAME, "Please enter a session name:  "

     HELLO !HPJOBNAME, !HPUSER.!HPACCOUNT;HIPRI

ENDIF

**

 

After entering the above, you could keep the file as, ACCTUDC,UNN and exit the EDITOR.  Now you have to set the catalog:

 

:SETCATALOG   ACCTUDC;APPEND;ACCOUNT

 

If the user logs on without a session name the system will notify the user that he/she did not enter a session name.  It will prompt the user to enter a session name and then execute the HELLO command to re-logon the user.  This is a simple version of forcing the user to enter a session name.  We could do additional checking of the session name.  But our purpose here was to illustrate how to use the IF-THEN-ENDIF statement.  Also note that in the above example the system will never execute the ENDIF statement in the code.

 

In the next example we'll check to see if a file exist.  We will use the IF-THEN-ELSE-ENDIF.  Here is the code:

 

CLS

ECHO

ECHO

ECHO  This command file will check to see if a file exists.

ECHO

INPUT   FNAME,   "Please enter the file name:   "

IF (FINFO("!FNAME", "EXITS"))   THEN

     ECHO !FNAME EXISTS

ELSE

     ECHO !FNAME DOES NOT EXIST

ENDIF

ECHO

ECHO

ECHO End of Program

 

The next example we'll use the IF-THEN-ELSEIF-ELSE-ENDIF to solicit the user's input from a menu:

 

CLS

ECHO

ECHO

ECHO

ECHO "                           Main Menu for File Manipulation"

ECHO

ECHO

ECHO "            1.       Create a File

ECHO

ECHO "            2.       Delete a File

ECHO

ECHO "            3.       Display a Files content

ECHO

ECHO "            4.       Exit

ECHO

ECHO

INPUT  CHOICE, "            Enter a number from 1 to 4       "

IF  !CHOICE = 1 THEN

     CLS

     ECHO

     INPUT  FNAME,   "Enter the name of the file to create:  "

     LISTFILE !FNAME,2

ELSEIF  !CHOICE  =  2   THEN

     CLS

     ECHO

     INPUT FNAME,  "What is the name of the file that you want to delete:   "

     PURGE !FNAME

     ECHO !FNAME has been deleted

ELSEIF  !CHOICE  =  3   THEN

     CLS

     ECHO

      INPUT !FNAME,  "What is the name of the file you to read:  "

      PRINT  !FNAME

ELSEIF   !CHOICE   =  4   THEN

     RETURN

ELSE

     CLS

     ECHO 

     ECHO

     ECHO   You have entered an invalid response, please enter a number from 1 to 4

ENDIF

 

The above will execute only one choice and then the program will quit.  The next section will show you how to put the above code in a loop, that will return to the main menu after every choice.  It will only quit when the choice is equal to "4".


Using Loops with Command Files

 

 

MPE provides a looping structure through the WHILE and ENDWHILE statements.  With expression evaluator functions a series of statements can be repeated as long as a particular condition is true.

 

First let's use the above menu code and add a loop structure around it:

 

SETVAR CHOICE   " "

WHILE CHOICE <> "4"   DO

CLS

ECHO

ECHO

ECHO

ECHO "                           Main Menu for File Manipulation"

ECHO

ECHO

ECHO "            1.       Create a File

ECHO

ECHO "            2.       Delete a File

ECHO

ECHO "            3.       Display a Files content

ECHO

ECHO "            4.       Exit

ECHO

ECHO

INPUT  CHOICE, "            Enter a number from 1 to 4       "

IF  !CHOICE = 1 THEN

     CLS

     ECHO

     INPUT  FNAME,   "Enter the name of the file to create:  "

     LISTFILE !FNAME,2

ELSEIF  !CHOICE  =  2   THEN

     CLS

     ECHO

     INPUT FNAME,  "What is the name of the file that you want to delete:   "

     PURGE !FNAME

     ECHO !FNAME has been deleted

ELSEIF  !CHOICE  =  3   THEN

     CLS

     ECHO

      INPUT !FNAME,  "What is the name of the file you to read:  "

      PRINT  !FNAME

ELSEIF   !CHOICE   =  4   THEN

     RETURN

ELSE

     CLS

     ECHO 

     ECHO

     ECHO   You have entered an invalid response, please enter a number from 1 to 4

ENDIF

ENDWHILE

 

Next we'll look at a LOOP statement that requires an index.  What if you had to create accounts every week for training a fresh group of students.  If the students logged on as :HELLO MGR.CLASSnn, where nn could be any number from 1 to 50.  Here is the code:

 

CLS

ECHO

ECHO

ECHO This command file will create accounts for student logon.

ECHO It will prompt the user for the number of accounts to create.

ECHO

ECHO

INPUT MAX,  "How many accounts do you want to create:   "

SETVAR COUNT, 1

WHILE !COUNT  <=  !MAX

     SETVAR ACCT, "CLASS!COUNT"

     ECHO Creating !ACCT

     NEWACCT !ACCT,MGR

     SETVAR COUNT, !COUNT  +  1

ENDWHILE

ECHO

ECHO

ECHO  !MAX account were created

ECHO

ECHO

ECHO

ECHO   End of program

 

One thing to remember is that you must increment your counter.  This is done just before the ENDWHILE statement.

 

This is just the tip of the iceberg as far as creating command files, there is a manual "Command Interpreter Access and Variables Programmer's Guide", customer order number 32650-60021 that goes into more details about how to create command files, UDCs, and JOBs.  We are going to end this paper with and example of a job that will perform your nightly back at 10:00pm every night except Saturday and Sunday.

 

!JOB BACKUP,MANAGER.SYS;HIPRI

!TELL MANAGER.SYS;The nightly backup job has started

!IF !HPDAY >= 1 AND !HPDAY <= 5 THEN

     FILE T;DEV=TAPE

     FILE SYSLIST;DEV=LP

     STORE @.@.@;*T;DIRECTORY;SHOW=OFFLINE;PROGRESS=5

     STREAM BACKUP.PUB.SYS;AT=20:00

ELSE

     STREAM BACKUP.PUB.SYS;DAY=1;AT=20:00

ENDIF

 

To start the process going you would type:    :STREAM   BACKUP;AT=10:00.

 

Programming the MPE shell can be fun and rewarding.  MPE has provided programming tools to make your life easy as a shell programmer.  Return to you system and have fun writing code to automate routine and complex task. 

 

HAPPY SHELL PROGRAMMING  -  SERVED TO YOU ON THE MPE SHELL.

 

Author | Title | Track | Home

Send email to Interex or to the Webmaster
©Copyright 1999 Interex. All rights reserved.