The Programming components needed by the gopher server delivery system are listed below:
Conventions for COBOL gopher "state-less" programming and testing is described within this paper using COBOL subroutines. "State-less" programming will fail if "logon" terminal programming techniques are used. The general strategy of gopher "state-less" programming consists of these points:
Gopher Report Transaction is defined as:
COBOL subprogram:
Always keep in mind that the response of YOUR program affects the ENTIRE performance of the gopher server!
+Passes socket actually is a disconnect and connect performed between the Gopher Unix gateway and HP server.
The "state-less" gopher design means that gopher requests are shared among many other concurrent requests. There is a limit to the number of concurrent gopher requests, so that the length of time any request takes directly affects the number of clients that get serviced. Target response times on the HPDEV machine should be under 6 seconds.
2. GOPHER Logon environment is shared by a collective library of report programs. Your new program affects every previous gopher program in this collective library in some way.
Global MPE logon resources must be used with discretion because there is only one logon server environment. "Global" resources are Inter-Process Communication (IPC) devices, such as Job Control Word (JCW's), MPE variables, temporary files, even permanent files if used as a temporary storage of information. If such files are needed, then the file names must be "unique" among all other concurrent gopher processes. This is accomplished using the Process Identification Number (PIN) of the executing program. PIN is obtained through the "PROCINFO()" MPE intrinsic call, then converted to ASCII and used as part of a file name.
Another option is to open a file as ",NEW" and close it with ";DEL". Files allocated in this manner are connected only to the open process since the close disposition of ;DEL doesn't catalog the file within any file domain. For example, a file EQuation could be setup within the server job as:
!FILE TEMPWORK,NEW;DEL;REC=-172,12,F,ASCII;DISC=8000;ACC=UPDATE
Multiple concurrent processes can OPEN this file equation and use their own unique version of it, even though the file name is the same for every process. This is sometimes tricky, because any FCLOSE on the file will DELETE it! Since COBOL "CLOSE" cannot be used to rewind this file without deleting it, you must use intrinsic "FCONTROL" with a code value of five (5 - rewind file) so that the file pointer can be re-positioned at the beginning of file (BOF) without using "CLOSE" (faster, also).
Don't use XL libraries that you know nothing about until the interactions between them are predictable. Try to isolate the particular routine needed and copy the routine within GOPHERXL.PUB, if possible. Otherwise, the server program will need to be re-linked with the additional library.
This strategy is needed to keep "state-less" load times short and interactions between routines predictable. SPT (System Performance Tool) software might be needed to determine what is being called within "XL" routines to determine these interactions.
3. You can't discount program "startup" time as a throwaway like you can on a terminal Logon. Startup time is included as part of the total GOPHER transaction time.
This rule generally applies to "XL" library software. Any kind of terminal based checking must be removed. Try to accomplish reporting goals within the constraints of the COBOL language, if possible.
Write the application program as subprogram: $DYNAMIC,POST85. The parameter list will consist of:
$CONTROL DYNAMIC ... You may declare any WORKING STORAGE needed within the program. ... 010500 LINKAGE SECTION. 010600 010700 01 SOCKET-FILE PIC S9(9) COMP. 011100 01 ND-SOC-SEC PIC X(10). 011400 01 GOPHER-PLUS-FORM. 011500 05 FILLER PIC X(04). 010700 01 RETURN-STATUS PIC S9(9) COMP. 012500 012600 PROCEDURE DIVISION USING SOCKET-FILE 012700 ND-SOC-SEC 012800 GOPHER-PLUS-FORM 013000 RETURN-STATUS. USING INTRINSIC "FCONTROL" TO REWIND A COBOL FILE 010700 01 DUMMY PIC X(04). 012500 012600 CALL INTRINSIC "FCONTROL" 012700 USING \SELECT-FILE-NAME\ * 012800 \5\ 013000 DUMMY.
*By using the name contained within the COBOL "SELECT" statement, this name will translate into the required file number required by this intrinsic.
There are two lines of communication going on here. The first line is the network socket communication to the gopher client. The second line is the $STDLIST of the executing program.
When the "DISPLAY" verb is used, these messages are written to the $STDLIST of the GOPHER server job, which acts as a log file of exceptional conditions. So, when you encounter serious errors within your COBOL subroutine, details of the errors, such as IMAGE status codes, etc. should be written using the DISPLAY verb. The Gopher server job listing can be checked for these errors.
A freindly message lacking of techno-talk should be sent to the gopher client, such as: "Sorry, can't do the report right now, try later."
This socket I/O function is used to send ASCII text to gopher client. This function is an inhouse routine that can be modified on demand and behaves much like the COBOL "WRITE" statement.
012600 CALL "SENDLINE" USING \SOCKET-FILE\, 012700 OUTPUT-LINE, 012701 \LINE-SIZE\, 012800 \ADVANCE-LINES\, 013000 RETURN-STATUS.
Where: SOCKET-FILE: PIC S9(9) COMP. Socket passed by gopher server or zero. If zero (0) is passed, the socket output is printed to $STDLIST so that testing can be performed using a driver program (see below).
OUTPUT-LINE: Working storage buffer size of text sent to gopher client. Typically PIC X(80), can be PIC x(1) through X(256). You must pass the corresponding parameter "LINE-SIZE" indicating the buffer size of the output-line (see next parm).
NOTE: 99% of Gopher clients can display line sizes up to 80 characters. Line sizes greater than 80 may cause problems or truncation with some clients. The "official" gopher protocol supports line sizes no greater than 70 but the defacto standard is 80.
LINE-SIZE: PIC S9(9) COMP. Varies as the size of OUTPUT-LINE varies (for example, 80 for a line, or maybe 12 for a single field).
Allowed values: (0 thru 256). Individual lines are buffered within an internal buffer until the buffer fills up, then the entire buffer is sent to client as a single block. The proper method to send blank lines is to set LINE-SIZE to zero (0) and ADVANCE-LINES to the desired number of lines, so that trailing blanks need not be stripped and line feeds are generated on the output file. Otherwise, if a blank line of 80 is sent, the routine must burn CPU stripping 80 blanks.
This number cannot be larger than the declared Working Storage field size or a bounds violation could occur. It is best to compute the buffer size using the COBOL function "LENGTH" to be accurate.
COMPUTE LINE-SIZE = FUNCTION LENGTH(OUTPUT-LINE). CALL "SENDLINE" USING \SOCKET-FILE\, OUTPUT-LINE, \LINE-SIZE\, \ADVANCE-LINES\, RETURN-STATUS.
ADVANCE-LINES: PIC S9(9) COMP. If positive number, indicates number of lines to advance after trailing blanks are stripped from OUTPUT-LINE. If zero or a negative number, no lines are advanced and indicates number of trailing blanks remaining on OUTPUT-LINE, limited by LINE-SIZE.
RETURN-STATUS: PIC S9(9) COMP. Zero (0) if successful in writing. Otherwise, non-zero indicates an error.
If (0) is passed for SOCKET-FILE, output is printed to $STDLIST. This allows testing of the routine on a normal terminal or allows user terminal access using same gopher subprogram code. Otherwise, output is routed through Berkeley Socket call "SEND()" to gopher client.
This routine will strip trailing blanks from "OUTPUT-LINE"
and append
However, if "ADVANCE-LINES" is equal to zero (0) or less than
zero (-1), no
This function formats a line of text using up to five (5) parameters using HP/C format routines. See HP/C "sprintf" library function for details about how to specify format strings. The only format supported is the string format (%s and %000s, where 000 can be field length) wildcard.
Sample call showing five fields, returning a COBOL buffer:
014314 CALL "FORMATLINE" 014601 USING \COL-POS\, A-BUFFER, 014620 C-FORMAT, \CFMT-SIZE\, 014820 FIELD-1, \FIELD-1-SIZE\, 014820 FIELD-2, \FIELD-2-SIZE\, 014820 FIELD-3, \FIELD-3-SIZE\, 014820 FIELD-4, \FIELD-4-SIZE\, 014820 FIELD-5, \FIELD-5-SIZE\, 015020 GIVING COL-POS.
Sample call showing two fields, internal buffering used:
014314 CALL "FORMATLINE" 014601 USING \COL-POS\, \0\, 014620 C-FORMAT, \CFMT-SIZE\, 014820 FIELD-1, \FIELD-1-SIZE\, 014820 FIELD-2, \FIELD-2-SIZE\, 014820 \0\, 015020 GIVING COL-POS.
Where:
COL-POS: PIC S9(9) COMP. Starting Column position on the output line of
the entire string format, defined by "C-FORMAT".
A-BUFFER or \0\ PIC X(80) - PIC X(256). COBOL Buffer to hold formatted fields or \0\ to format the line directly into the internal "SENDLINE" buffer. However, to advance to the next line, you must call "SENDLINE" using a zero buffer size unless you want to append more text to the previous line.
For example:
CALL "SENDLINE" USING \SOCKET-FILE\, OUTPUT-LINE, \0\, \ADVANCE-2-LINES\, RETURN-STATUS.
Advances the current formatted buffer by two lines (resulting in one formatted line, followed by one blank line).
C-FORMAT, \C-FMT-SIZE\: C-FORMAT contains free text with string substitution markers. The parameters are formatted within the specified order. For example:
MOVE "Date: %s" TO C-FORMAT. MOVE 60 to COL-POS. COMPUTE CFMT-SIZE = FUNCTION LENGTH(C-FORMAT). COMPUTE DATE-SIZE = FUNCTION LENGTH(A-DATE). CALL "FORMATLINE" USING \COL-POS\,\0\, C-FORMAT, \CFMT-SIZE\, A-DATE, \DATE-SIZE\ ....
Will output the literal:
"Date: 06/30/94"
starting at column 60 on the internal buffer output line. However, if the format is specified with a field length indicator (such as length 5):
"Date: %5s",
the output text would look like:
"Date: 06/30"
the "/94" would be truncated.
FIELD-1..n contains a COBOL EDIT type or PIC X type text.
FIELD- 1..n-SIZE contains the length of the field, computed with the COBOL "FUNCTION LENGTH(FIELD-1..n)" statement.
When \0\ is used, this indicates the end of the field list when less than five fields are specified.
Sample program using "FORMATLINE" procedure:
000100$CONTROL DYNAMIC,POST85 002000 IDENTIFICATION DIVISION. 002100 002200 PROGRAM-ID. TESTCOB. 002300 AUTHOR. 002700 DATE-WRITTEN. 002800 DATE-COMPILED. 003600 ENVIRONMENT DIVISION. 003800 CONFIGURATION SECTION. 004000 SOURCE-COMPUTER. HP3000. 004100 OBJECT-COMPUTER. HP3000. 005000 DATA DIVISION. 005800 WORKING-STORAGE SECTION. 006200 01 FIRSTNM PIC X(14) VALUE "Eric". 006202 01 MID PIC X(14) VALUE "James". 006204 01 LASTNM PIC X(20) VALUE "Schubert". 006206 01 THE-DATE PIC X(30) VALUE "6/28/94". 006210 01 C-FORMAT PIC X(80). 006400 01 LINE3 PIC X(80) VALUE "line number 3". 006220 01 A-NUMBER PIC 9999V99 VALUE 12.56. 006221 01 A-BUFFER PIC X(120). 006230 01 A-EDIT PIC ZZZZ.99. 000660 01 DUMMY PIC X(4). 008510 01 FIRSTNM-SIZE PIC S9(9) COMP VALUE 0. 008511 01 MID-SIZE PIC S9(9) COMP VALUE 0. 008512 01 LASTNM-SIZE PIC S9(9) COMP VALUE 0. 008513 01 CFMT-SIZE PIC S9(9) COMP VALUE 0. 008514 01 DATE-SIZE PIC S9(9) COMP VALUE 0. 008514 01 A-BUF-SIZE PIC S9(9) COMP VALUE 0. 008515 01 COL-POS PIC S9(9) COMP VALUE 1. 008516 01 ADV1-LINES PIC S9(9) COMP VALUE 1. 008520 01 ADV2-LINES PIC S9(9) COMP VALUE 2. 008600 010500 LINKAGE SECTION. 010700 01 SOCKET-FILE PIC S9(9) COMP. 010800 01 ND-SS PIC X(10). 010900 01 GOPHER-PLUS PIC X(4). 011300 01 RETURN-CODE PIC S9(9) COMP. 011700 012600 PROCEDURE DIVISION USING SOCKET-FILE 012700 ND-SS 012800 GOPHER-PLUS 013000 RETURN-CODE. 014200 A100-MAINLINE. 014300 COMPUTE FIRSTNM-SIZE = FUNCTION LENGTH (FIRSTNM). 014302 COMPUTE MID-SIZE = FUNCTION LENGTH (MID). 014303 COMPUTE LASTNM-SIZE = FUNCTION LENGTH (LASTNM). 014304 COMPUTE DATE-SIZE = FUNCTION LENGTH (THE-DATE). 014305 COMPUTE CFMT-SIZE = FUNCTION LENGTH (C-FORMAT). 014305 COMPUTE A-BUF-SIZE = FUNCTION LENGTH (A-BUFFER). This section of code formats the line into "A-BUFFER" COBOL variable instead of the "sendline" internal buffer. Notice the returned COL-POS is used to start appending the next string to the line. 014310 MOVE "My name is: '%s %s %s' the date is %s" TO C-FORMAT. 014311 MOVE 1 TO COL-POS. 014312 MOVE SPACES TO A-BUFFER. 014314 CALL "FORMATLINE" 014601 USING \COL-POS\, 014610 A-BUFFER, 014620 C-FORMAT, \CFMT-SIZE\, 014820 FIRSTNM, \FIRSTNM-SIZE\, 014900 MID, \MID-SIZE\, 015000 LASTNM, \LASTNM-SIZE\, 015001 THE-DATE, \DATE-SIZE\, 015010 \0\, 015020 GIVING COL-POS. 015030 015031 MOVE " this gets appended" TO C-FORMAT. 015040 CALL "FORMATLINE" 015050 USING \COL-POS\, 015051 A-BUFFER, 015060 C-FORMAT,\CFMT-SIZE\, 015090 \0\, 015095 GIVING COL-POS. 015100 015110 CALL "SENDLINE" 015120 USING \SOCKET-FILE\, 015141 A-BUFFER, \A-BUF-SIZE\, 015150 \ADV2-LINES\, 015160 RETURN-CODE. 015195
This section of code performs almost the same function but writes the
lines into the internal "sendline" buffer. A zero must be passed
within the "buffer" parm (second parm) to indicate not to return
buffer to this COBOL program. However, "sendline" must be called
to write the proper
015198 015199 MOVE "2My name is: '%s %s %s' the date is %s" TO C-FORMAT. 015201 CALL "FORMATLINE" 015202 USING \1\, 015203 \0\, 015204 C-FORMAT, \CFMT-SIZE\, 015205 FIRSTNM, \FIRSTNM-SIZE\, 015206 MID, \MID-SIZE\, 015207 LASTNM, \LASTNM-SIZE\, 015208 THE-DATE, \DATE-SIZE\, 015209 \0\, 015210 GIVING COL-POS. 015211 015212 MOVE " this gets appended" TO C-FORMAT. 015213 CALL "FORMATLINE" 015214 USING \COL-POS\, 015215 \0\, 015216 C-FORMAT,\CFMT-SIZE\, 015217 \0\, 015218 GIVING COL-POS. 015219 015220 CALL "SENDLINE" 015221 USING \SOCKET-FILE\, 015222 DUMMY, \0\, 015223 \ADV2-LINES\, 015224 RETURN-CODE. 015225 015226 GOBACK.
Sample program produces this output:
Machine Origin: schubert.adminis.nd.edu wed, jun 29, 1994, 2:58 pm
My name is: 'Eric James Schubert' the date is 6/28/94 this gets appended
2My name is: 'Eric James Schubert' the date is 6/28/94 this gets appended
'C' program, name of driver source: COBOLCC Compile:
:CCXLLK COBOLCC,$newpass;info="-Aa -O"
:SAVE $OLDPASS,XCOBOLCC
Run test program. Library names contained within GOPHERXL.PUB must be entered in lower case. Otherwise, load errors will occur. Also see notice at end of this page on MPE caching.
:RUN XCOBOLCC;XL="GOPHERXL.PUB"
Enter Proc:testcob
-Load time is 757 Millisec
Enter SSN: 12345678
My name is: 'Eric James Schubert' the date is 6/28/94 this gets appended
2My name is: 'Eric James Schubert' the date is 6/28/94 this gets appended
-Response for testcob is 62 Millisec
-Total time (load & report) is 819 Millisec
:do
:xcobolcc
Enter Proc:testcob
-Load time is 141 Millisec
Enter SSN: 12345678
My name is: 'Eric James Schubert' the date is 6/28/94 this gets appended
2My name is: 'Eric James Schubert' the date is 6/28/94 this gets appended
-Response for testcob is 3 Millisec
-Total time (load & report) is 144 Millisec
NOTICE the difference between load times, the difference being MPE caching of the program module. The second time the program is run, MPE has a copy of the program within memory and takes little load time. So, when doing performance timings, always run the program twice and take the second time.
If you encounter load errors other than -103 (module not found), use the following utility to decode the loader error message.
:RUN msgutil.pub.sys ************************************************************************ ****** MSGUTIL -- SYSTEM CATALOG MESSAGE DISPLAY UTILITY ************************************************************************ ---------------- INTERACTIVE MODE ---------------- M - Message display T - Time display E - Exit Menu Selection > M Enter SUBSYSTEM # [= quit] > 44 NM LOADER ERROR MESSAGES Enter MESSAGE # [ = quit] > 103 ------------------------------------------------------------------------ DYNAMIC LOADING UNRESOLVED EXTERNAL: ( LDRERR 103) ------------------------------------------------------------------------