Guardian Programmer’s Guide Abstract This guide describes how to access operating-system services from an application program using Guardian™ procedure calls. Product Version N/A Supported Release Version Updates This manual supports J06.03 and all subsequent J-series RVUs, H06.03 and all subsequent H-series RVUs, and G06.25 and all subsequent G-series RVUs, until otherwise indicated by its replacement publications.
Document History Part Number Product Version Published 421922-009 N/A August 2010 421922-010 N/A January 2011 421922-011 N/A August 2011 421922-013 N/A February 2012 421922-014 N/A August 2012
Guardian Programmer’s Guide Glossary Index What’s New in This Manual xxv Manual Information xxv New and Changed Information Figures Tables xxv About This Manual xxix Contents xxix Related Reading xxx Notation Conventions xxxv Legal Notices xl HP Encourages Your Comments xli 1.
1. Introduction to Guardian Programming (continued) Contents 1.
3. Coordinating Concurrent File Access (continued) Contents 3. Coordinating Concurrent File Access (continued) Setting the Access Mode 3-1 Setting the Exclusion Mode 3-3 Locking a File 3-6 Avoiding Deadlocks 3-7 Avoiding Multiple-Process Deadlocks 3-7 Avoiding Single-Process Deadlocks 3-10 4.
5. Communicating With Disk Files (continued) Contents 5.
Contents 6. Communicating With Processes (continued) 6.
7. Using DEFINEs (continued) Contents 7.
. Communicating With Devices (continued) Contents 9. Communicating With Devices (continued) Getting Device Information 9-5 Additional Device Information (G-series Only) 9-8 10.
Contents 11. Communicating With Printers (continued) 11.
Contents 12. Communicating With Magnetic Tape (continued) 12.
13. Manipulating File Names Contents 13.
Contents 14. Using the IOEdit Procedures (continued) 14.
Contents 15. Using the Sequential Input/Output Procedures (continued) 15.
16. Creating and Managing Processes Contents 16.
17. Managing Memory Contents 17.
18. Managing Time (continued) Contents 18.
20. Interfacing With the ERROR Program Contents 20. Interfacing With the ERROR Program Creating an ERROR Process 20-2 Opening an ERROR Process 20-3 Sending an ERROR Process a Startup Message 20-4 Reading and Processing Error-Message Text 20-5 Closing and Deleting an ERROR Process 20-5 Using the ERROR Process: An Example 20-6 21.
23. Writing a Command-Interpreter Monitor ($CMON) Contents 23.
Contents 24. Writing a Terminal Simulator (continued) 24.
26. Synchronizing Processes Contents 26.
27. Fault-Tolerant Programming in C (continued) Contents 27.
28. Using Floating-Point Formats (continued) Contents 28. Using Floating-Point Formats (continued) Conversion Routines 28-5 Floating-Point Operating Mode Routines 28-5 A.
Figures Contents Figure 5-3. Figure 5-4. Figure 5-5. Figure 5-6. Figure 5-7. Figure 5-8. Figure 5-9. Figure 6-1. Figure 6-2. Figure 6-3. Figure 6-4. Figure 6-5. Figure 6-6. Figure 6-7. Figure 9-1. Figure 10-1. Figure 10-2. Figure 10-3. Figure 10-4. Figure 10-5. Figure 10-6. Figure 10-7. Figure 10-8. Figure 10-9. Figure 11-1. Figure 12-1. Figure 12-2. Figure 12-3. Figure 12-4. Figure 14-1. Figure 15-1. Figure 15-2. Figure 15-3. Figure 15-4. Figure 15-5. Figure 15-6.
Figures Contents Figure 15-7. Requester and Server Processes in One-Way Communication (TAL Version) 15-51 Figure 16-1. TNS and Native Process Code Spaces 16-7 Figure 16-2. Mom and Ancestor Processes 16-12 Figure 16-3. Job Ancestor Relationships 16-14 Figure 16-4. Execution Priority Example 16-17 Figure 16-5. Process States 16-18 Figure 16-6. Runnable Processes 16-19 Figure 16-7. Running a Process at a High PIN or a Low PIN 16-30 Figure 17-1. The User Data Segment 17-2 Figure 17-2.
Tables Contents Figure 22-4. Figure 22-5. Figure 23-1. Figure 23-2. Figure 25-1. Figure 25-2. Figure 26-1. Figure 27-1. Figure 27-2. Figure 27-3. Figure 27-4. Figure 27-5. Figure 27-6.
What’s New in This Manual Manual Information Guardian Programmer’s Guide Abstract This guide describes how to access operating-system services from an application program using Guardian™ procedure calls. Product Version N/A Supported Release Version Updates This manual supports J06.03 and all subsequent J-series RVUs, H06.03 and all subsequent H-series RVUs, and G06.25 and all subsequent G-series RVUs, until otherwise indicated by its replacement publications.
Changes to the 421922-013 manual: What’s New in This Manual Changes to the 421922-013 manual: Added an appendix to describe Mixed Data Model Programming. Changes to the 421922-011 manual: • • • • Rephrased the words Processor and Processor Module to CPU and to IPU at few instances throughout the document. Updated the first sentence in Spreading the Workload Among Multiple CPUs.
Changes to the 421922-009 Manual What’s New in This Manual • ° ° ° Figure 27-1, Active Backup Program Structure, on page 27-8. Figure 27-5, Primary Process Functional Flow, on page 27-33. Figure 27-6, Backup Process Functional Flow, on page 27-34. Added the following tables: ° ° Table 11-4, Default VFC Table, on page 11-22. Table 11-5, Modified VFC Table, on page 11-24. Changes to the 421922-009 Manual • • • • Updated the description of Using DEFINEs on page 7-1.
Changes to the 421922-004 Manual What’s New in This Manual • • • • • • Overview of Flat Segments Allocating a Selectable Segment Allocating a Flat Segment Using Memory Pools Section 25, Debugging, Trap Handling, and Signal Handling includes information about using the H-series debuggers. Section 28, Using Floating-Point Formats adds that the IEEE floating-point format is the default floating-point format on H-series NonStop operating system and later product versions.
About This Manual This guide describes how to call Guardian procedures from your application program to obtain services from the operating system. Except where noted, the Guardian programming environment is assumed. This guide is intended for intermediate and advanced application programmers who are not familiar with the operating system. You should already be familiar with the high-level programming language in which you are implementing your application.
Related Reading About This Manual • • • • • • Sections Section 21, Writing a Requester Program and Section 22, Writing a Server Program provide working examples of requester and server programs. Sections Section 23, Writing a Command-Interpreter Monitor ($CMON) and Section 24, Writing a Terminal Simulator show how to write a Tandem Advanced Command Language (TACL) monitor program ($CMON) and how to write a terminal simulator.
Manual Containing Application Availability Information About This Manual Manual Containing Application Availability Information The Availability Guide for Application Design provides an overview of application availability options available to software designers and developers.
Manuals About the Command Interface About This Manual Figure i. Related Manuals VST 001.
Manuals Describing Programming Languages and Tools About This Manual Manuals Describing Programming Languages and Tools The following manuals contain reference information for writing programs in high-level languages.
Database-Related Manuals About This Manual Database-Related Manuals The following manuals contain programming material for writing programs that access either Enscribe data files or a NonStop SQL/MP database: • • • • • • • • • • Enscribe Programmer’s Guide NonStop SQL/MP Reference Manual NonStop SQL Programming Manual for TAL NonStop SQL/MP Programming Manual for COBOL85 NonStop SQL/MP Programming Manual for C NonStop SQL Programming Manual for Pascal Introduction to NonStop Transaction Manager/MP (TM/M
System and Internal Design Manuals About This Manual The following manuals contain configuration information for G-series releases.
General Syntax Notation About This Manual italic computer type. Italic computer type letters within text indicate C and Open System Services (OSS) variable items that you supply. Items not enclosed in brackets are required. For example: pathname [ ] Brackets. Brackets enclose optional syntax items. For example: TERM [\system-name.]$terminal-name INT[ERRUPTS] A group of items enclosed in brackets is a list from which you can choose one item or none.
General Syntax Notation About This Manual Quotation marks around a symbol such as a bracket or brace indicate the symbol is a required character that you must enter as shown. For example: "[" repetition-constant-list "]" Item Spacing. Spaces shown between items are required unless one of the items is a punctuation symbol such as a parenthesis or a comma. For example: CALL STEPMOM ( process-id ) ; If there is no space between two items, spaces are not permitted.
Notation for Messages About This Manual Notation for Messages The following list summarizes the notation conventions for the presentation of displayed messages in this manual. Nonitalic text. Nonitalic letters, numbers, and punctuation indicate text that is displayed or returned exactly as shown. For example: Backup Up. lowercase italic letters. Lowercase italic letters indicate variable items whose values are displayed or returned. For example: p-register process-name [ ] Brackets.
Notation for Management Programming Interfaces About This Manual % Percent Sign. A percent sign precedes a number that is not in decimal notation. The % notation precedes an octal number. The %B notation precedes a binary number. The %H notation precedes a hexadecimal number. For example: %005400 %B101111 %H2F P=%p-register E=%e-register Notation for Management Programming Interfaces UPPERCASE LETTERS. Uppercase letters indicate names from definition files; enter these names exactly as shown.
Change Bar Notation Legal Notices Legal Notices © Copyright 2012 Hewlett-Packard Development Company L.P. Confidential computer software. Valid license from HP required for possession, use or copying. Consistent with FAR 12.211 and 12.212, Commercial Computer Software, Computer Software Documentation, and Technical Data for Commercial Items are licensed to the U.S. Government under vendor's standard commercial license. The information contained herein is subject to change without notice.
HP Encourages Your Comments Legal Notices HP Encourages Your Comments HP encourages your comments concerning this document. We are committed to providing documentation that meets your needs. Send any errors found, suggestions for improvement, or compliments to docsfeedback@hp.com. Include the document title, part number, and any comment, error found, or suggestion for improvement that you have concerning this document.
1 Introduction to Guardian Programming Writing an application program requires an understanding of the environment and services provided by the operating system. This guide describes how to use Guardian procedures in your application program to obtain services from the HP NonStop operating system and the file system (that is, from the operating system). This section introduces some of the key topics covered in this guide and provides references to other sections that contain more detailed information.
Introduction to Guardian Programming Application-Level Fault Tolerance Application-Level Fault Tolerance There are several ways in which an application can be designed to withstand operating-system failures. Three such methods are introduced below: • • • Transaction protection using the NonStop Transaction Manager/MP (TM/MP) Process pairs Persistent processes Any combination of these techniques could be appropriate for providing fault tolerance, depending on the needs of the application.
Introduction to Guardian Programming Mirrored Disks Mirrored Disks One effective protection against loss of data is the use of mirrored disk volumes. Mirrored disk volumes maintain copies of data on two physically independent disk drives that are accessed as a single device and managed by the same I/O process. All data written to one disk is also written to the other disk. All data read from one disk could be read from the other, because the data is identical.
Introduction to Guardian Programming • The File System By interacting with the Tandem Advanced Command Language (TACL) command interpreter This guide describes how to use Guardian procedures. For information about entering commands at the command-interpreter prompt, see the TACL Reference Manual.
Introduction to Guardian Programming The File System Operations that you can perform on file names are described in Section 13, Manipulating File Names. For example, you can scan a string of characters to see whether it contains a valid file name, or you can modify portions of a file name. Concurrent File Access Because the operating system provides a multiprocessing environment, it is possible that more than one process may try to access the same data concurrently.
Introduction to Guardian Programming The File System Interprocess Communication The message system allows processes to pass messages to each other. This subsystem not only provides critical links between various process modules within the operating system itself but also provides the mechanism for user processes to send messages to each other and for user processes to receive messages from the operating system. Interprocess communication is done using request-response interactions.
Introduction to Guardian Programming The Startup Sequence For printers, the operating system allows the application not only to write data to the printer file but also to provide control operations such as advancing the paper to the top of the page or changing the character font of printers that have that capability. The printer control language (PCL) provides application control capability. Section 11, Communicating With Printers, provides details.
Introduction to Guardian Programming Process Management Process Management The Guardian procedures provide you with the ability to create and manage processes, including the ability to allocate process resources such as the CPU in which the process will run. One of the distinguishing features about the operating system is the ability of the system to withstand failures without stopping the application.
Introduction to Guardian Programming Time Management For both TNS/R native processes and TNS processes, if you need more space than what is available in the data areas normally provided by the system, you must use extended data segments. There are two types of extended data segments: Flat segments, which can provide up to 128 megabytes of extra storage, and selectable segments, which can provide up to 127.5 megabytes of extra storage.
The Requester/Server Application Model Introduction to Guardian Programming symbolic command-line debugger used for debugging TNS/E native process and snapshots on HP TNS/E systems. Certain critical error conditions occurring during process execution prevent normal process execution. They are mostly unrecoverable. In TNS processes, these errors cause traps. In native processes, these errors cause the process to receive a signal.
Introduction to Guardian Programming Advantages of the Requester/Server Model The fact that the file system treats processes as files allows you to send user messages to them as if writing to a file. Remember that processes read interprocess messages by reading from a special input file opened with the name $RECEIVE. Each process has its own $RECEIVE input file. Much of this guide assumes a requester/server model.
Advantages of the Requester/Server Model Introduction to Guardian Programming Figure 1-2. Multiple Users in a Requester/Server Application VST003.VSD Figure 1-2 shows one terminal for each requester process. Requesters can also provide support for several terminals in each process. Adding New Functions If you need to add a new function to your application, you can add another server process that performs the new function. The existing server processes need no modification.
Advantages of the Requester/Server Model Introduction to Guardian Programming Figure 1-3. Multiple Functions in a Requester/Server Application VST004.
Introduction to Guardian Programming 1 Introduction to Guardian Programming Spreading the Workload Among Multiple CPUs One reason why the requestor/server model works well with the operating system is the fact that you can take advantage of the NonStop multi-CPU architecture, which allows different parts of the application to run in parallel on different CPUs.
Introduction to Guardian Programming Monitoring Server Processes Monitoring Server Processes A monitor is a separate process that, along with other functions it might be performing, monitors and controls the execution of other processes. Because server processes must continue to run to provide needed services, one common use of a monitor is to check that each server continues running and to restart any server that stops.
Client/Server Application Model Introduction to Guardian Programming Figure 1-5. Client/Server Architecture VST148.
Introduction to Guardian Programming 1 Introduction to Guardian Programming Distributed Client/Server Distributed applications place parts of the business logic on various servers, which can be the same or different platforms (see Figure 1-6 below). For example, the web server might provide the user interface while the two servers provide the application and database logics. Figure 1-6. Distributed Client/Server VST149.
Introduction to Guardian Programming Accessing Guardian Procedures NonStop Server Object Gateway links popular desktop tools and critical business services using ActiveX controls. It enables any application that supports ActiveX controls to access Pathway services. SOG simplifies the development and deployment of GUI clients by shielding developers from the complexities of the transaction processing server. Pathway services appear as ActiveX objects within the client application.
Introduction to Guardian Programming Calling Guardian Procedures From C or C++ The above ?SOURCE directive copies into your program for compilation the external declarations for only the FILE_OPEN_, FILE_CLOSE_, READX, WRITEX, and WRITEREADX procedures. Multiple versions of the external declarations file are available in case you might need to run your program on older versions of the operating system as well as the current version.
Introduction to Guardian Programming Calling Guardian Procedures From Pascal Calling Guardian Procedures From Pascal The PEXTDECS file makes it easy to import Guardian procedures into a Pascal program. This file contains Pascal-coded definitions for Guardian procedure calls, enabling you to invoke Guardian procedures as Pascal functions. To pass parameter values to the Guardian procedure call, you simply pass the equivalent parameters to the corresponding Pascal function.
Introduction to Guardian Programming TNS/E Program Execution Modes TNS/E Program Execution Modes Like the G-series environment, the H-series and J-series environment support a native execution mode for TNS/E native processes. TNS/E native compilers and tools are used to generate native code that uses the process, memory, and instruction set architectures that are native to Itanium CPUs. The H-series and J-series RVUs support native versions of C, C++, and COBOL as well as a pTAL compiler.
Introduction to Guardian Programming • • • Similarities and Differences Between H-series RVUs on the TNS/E Platform and G-Series RVUs on the Full support for native-mode cross-compilation on the PC. Full support for TNS/R native compilers and linkers. You can compile and link, but not execute, TNS/R native applications on an H-series system. Support for FORTRAN and Pascal languages.
Introduction to Guardian Programming TNS/R Program Execution Modes TNS/E native and TNS/R native object files differ in other respects as well. See the eld Manual and the enoft Manual for details on the structure of TNS/E native object files. TNS/R Program Execution Modes TNS/R systems can execute TNS/R native code, TNS code, and accelerated code. User processes can run in all three of these modes.
Introduction to Guardian Programming • • • • Synchronizing Processes ZSYSTAL contains literals and data structure declarations for TAL programs. ZSYSC contains literals and data structure declarations for C programs. ZSYSCOB contains literals and data structure declarations for COBOL85 programs. ZSYSPAS contains literals and data structure declarations for Pascal programs. To use the DDL declarations in your application, include the appropriate ZSYS file in your program.
2 Using the File System This section reviews the concept of a file and describes some of the common operations that you can use on a file. This section discusses the different types of files and describes file-name syntax.
Disk Files Using the File System product. This guide discusses access to Enscribe files. For details on SQL files, see the SQL programming manuals. Types of Enscribe Files The Enscribe database record manager provides access to and operations on Enscribe disk files. The Enscribe software is an integral part of the operating system. It supports the following file types: • • • • Key-sequenced files, in which records are placed in ascending sequence based on a key field.
Device Files Using the File System Figure 2-1. Disk Files VST006.VSD Device Files In addition to program and data files stored on disk, every terminal, printer, and magnetic tape is a file. Treating devices in this way makes device I/O as easy as accessing disk files. This approach allows disk files and devices to be handled uniformly where appropriate and allows programs to be as device-independent as possible.
Process Files and $RECEIVE Using the File System Process Files and $RECEIVE The file system allows you to open and access processes as files. A process can open another process using a process file name and then send data to the process by writing to the open file. A process can receive data from other processes by opening a file using the special file name “$RECEIVE.” Through $RECEIVE, you can read not only messages from other processes but also operating-system messages.
Permanent Disk-File Names Using the File System The syntax definition for a permanent disk file is shown below. (Temporary disk files are described later.) Permanent disk-file name: [node-name.][[volume-name.]subvolume-name.
Temporary Disk-File Names Using the File System ARCHIVE Temporary Disk-File Names Sometimes a file is required only as temporary work space for a program and is no longer useful once the process has terminated. Such a file is known as a temporary file. A temporary file must be created programmatically, and it exists only until the file is closed. The name of such a file has the following syntax: Temporary disk-file name: [node-name.][volume-name.
Process File Names Using the File System The device-name part of the name can be up to 8 characters long and must start with a dollar sign ($). Again, all characters must be alphanumeric, and the second character of the device-name part must be a letter. The qualifier is an optional alphanumeric string that always begins with the pound sign (#) character followed by an alphabetic character. The meaning of a qualifier depends on the device type. We recommend using device names to identify devices.
Process File Names Using the File System operating system must therefore be able to distinguish different instances of the same named server. The named form of the process also permits qualifiers (q1 and q2) to be passed to the process. These are alphanumeric values. q1 must start with a pound sign (#). (q2 must not include a pound sign.) Although they are checked for correct format, these qualifiers have no meaning to the file system. Their meaning is applicationdependent.
Location Independent Disk-File Names Using the File System Note that a process descriptor always contains a node name and a sequence number. It never contains qualifiers. Location Independent Disk-File Names Location independent disk-file names are supported by the NonStop Storage Management Foundation (SMF) product, which is designed to help automate system storage-management tasks.
Using CLASS MAP DEFINEs Using the File System Using CLASS MAP DEFINEs A DEFINE is a collection of attributes to which a common name has been assigned. These attributes can be passed to a process simply by referring to the DEFINE name from within the process. The =_DEFAULTS DEFINE is an example of such a DEFINE; this DEFINE passes the default node name, volume, and subvolume to a process. The DEFINE mechanism can be used for passing file names to processes; this kind of DEFINE is called a CLASS MAP DEFINE.
Creating Files Using the File System Disk files, for example, can be created programmatically using the FILE_CREATE[LIST]_ procedure or interactively using the TACL CREATE or File Utility Program (FUP) CREATE command. Device files are created either at operatingsystem generation time or by COUP (D-series releases) and SCF (G-series releases); they are not created programmatically.
Creating Files Using the File System The second parameter designates the actual length of the supplied file name. File names are variable length, so it is necessary to tell the operating system how many bytes to expect. In this case, pointers have been used to identify each end of the filename string before computing the string length. To create a temporary file, use the FILE_CREATE_ procedure without specifying the subvolume or file ID of the name.
Creating Files Using the File System secondary extents are allocated. The secondary extents are all the same size, but the primary extent may be a different size than the secondary extents. Extents are automatically allocated to the file by the disk process as the need arises up to a filedependent maximum value. Parameters of the FILE_CREATE[LIST]_ procedure allow you to specify the extent sizes. One parameter specifies the length of the primary extent in pages (2048-byte units).
Creating Files Using the File System named. (See the discussion earlier in this section about named and unnamed processes.) If the process is created programmatically using the PROCESS_CREATE_ procedure, the process is named or unnamed depending on the information supplied with the call. One parameter of the PROCESS_CREATE_ procedure is known as the name-option parameter. If it is 1, then the process is named using the name supplied in the name:length parameter. When creating processes from $SYSTEM.
Opening Files Using the File System If the name-option parameter is set to 2, then the operating system provides a name. To set the name-option parameter to 2, we recommend using the ZSYS^VAL^PCREATOPT^NAMEDBYSYS literal from the ZSYSTAL file. In this case, the name:length parameter is omitted.
Opening Files Using the File System Examples of opening disk files, device files, and process files follow. Opening Disk Files To open a disk file, use a call like the following: FILE^NAME ':=' "$OURVOL.MYSUBVOL.DATAFILE" -> @S^PTR; LENGTH := @S^PTR '-' @FILE^NAME; ERROR := FILE_OPEN_(FILE^NAME:LENGTH, FILENUM); The first parameter (FILE^NAME:LENGTH) is the file name created by the FILE_CREATE[LIST]_ procedure (or the TACL CREATE command or the FUP CREATE command).
Opening Files Using the File System naming is a system-management function, therefore you need to know some systemconfiguration information before attempting to open a device file.
Reading and Writing Data Using the File System !nowait^depth!, RECEIVE^DEPTH); The requester process can now pass messages to the server process. Reading and Writing Data The operating system supports several procedure calls that enable reading and writing of files: • • READX and READ each read a record from an open file into an application buffer.
Reading and Writing Data Using the File System When you open a file, the current-record and next-record pointers point to the first byte in the file: VST008.VSD A READ[X] or WRITE[X] procedure call always begins at the byte pointed to by the next-record pointer. The next-record pointer is advanced on each READ[X] or WRITE[X] call to provide automatic sequential access to the file. Normally, the next-record pointer is rounded up to an even value.
Reading and Writing Data Using the File System The actual number of bytes transferred is returned in NUMXFERRED. The positions of the pointers are as follows. The next-record pointer is increased by 512 bytes; the current-record pointer still addresses relative byte 0: VST009.VSD VST009.VSD If you reissue an identical READX call, the next 512 bytes are read into SBUFFER (starting at byte 512).
Using the File System 2 Using the File System If you now issue the following WRITEX call, 512 bytes are written into the disk file, starting at the byte addressed by the next-record pointer. The effect on the pointers is the same as if you had issued a READ call: WCOUNT := 512; CALL WRITEX(FILENUM, SBUFFER, WCOUNT); VST011.VSD Random access to a disk file is provided by the POSITION procedure. This procedure sets the current-record pointer and next-record pointer.
Using the File System sequential access is automatic. The current-record pointer still points at relative byte 4096: VST013.VSD I/O operations can also be performed starting at the relative byte pointed at by the current-record pointer. To read from the current-record pointer, you use the READUPDATE[X] procedure; to write starting at the current-record pointer, you use the WRITEUPDATE[X] procedure.
Using the File System Following the above call, the current-record and next-record pointers are positioned as follows: VST015.VSD Successive write operations then append records to the file. I/O With Devices Sections 9 through 12 describe I/O operations to device files in detail. This subsection briefly presents the procedures used and gives one specific example of the WRITEREADX procedure that is particularly useful for communicating with terminals.
Using the File System SBUFFER ':=' "PLEASE ENTER ACCOUNT NUMBER: WCOUNT := @S^PTR '-' @SBUFFER; RCOUNT := 72; CALL WRITEREADX(FILENUM, SBUFFER, WCOUNT, RCOUNT, NUMXFERRED); " -> @S^PTR; The call writes 30 bytes from the memory buffer SBUFFER, then prepares for reading up to 72 bytes of information back into the same buffer. A count of the number of bytes entered is given in NUMXFERRED. I/O With Processes A process writes messages to another process by writing to the open process file.
Using the File System . . CALL REPLYX(BUFFER,COUNT); The call to REPLYX by the server satisfies the WRITEREADX call. That is, whatever REPLYX returns in its BUFFER is what WRITEREADX reads. Note that the sixth parameter, the receive depth, is specified in the FILE_OPEN_ call in the server. Here, the receive depth is specified as 1 to enable the READUPDATEX procedure to process one message at a time.
Getting File Information Using the File System Getting File Information The following related procedures provide information on all files: disk files, device files, and process files: FILE_GETINFO_ Returns brief information about an open file identified by file number. FILE_GETINFOBYNAME_ Returns brief information about a file identified by file name. The file need not be open to get information using this procedure.
Handling File-System Errors Using the File System • • Error numbers in the range 10 and up indicate an error encountered in a standard operation, such as an attempt to access a file before it is open, or that a system component failed while the procedure was executing. Error numbers 300 through 511 are reserved for application-dependent use. Returned Error Numbers and Condition Codes Some file-system procedures return the error number directly to the calling program.
Handling File-System Errors Using the File System Retrying After an Error In some cases, the error condition may be temporary. Your program can try the operation again after a period of time or following some operator intervention.
Closing Files Using the File System Closing Files You can close files explicitly using the FILE_CLOSE_ procedure. ERROR := FILE_CLOSE_(FILENUM); If you do not explicitly close a file, the file remains open until the process stops. When a process stops, all files that the process has open are automatically closed. Once you have closed a file, the file number can no longer access that file. The file number is now available to be reassigned to another file.
Accessing Files: An Example Using the File System • • • • • • • The INIT procedure reads and discards the Startup messages before opening the terminal file and the disk file containing the daily log. The GET^COMMAND procedure displays a menu of options on the user’s terminal and returns the selected option (“r,” “a,” or “x”) to the main procedure. The READ^RECORD procedure reads records from the log file.
Accessing Files: An Example Using the File System ?INSPECT,SYMBOLS,NOMAP,NOCODE ?NOLIST, SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?LIST LITERAL MAXFLEN = ZSYS^VAL^LEN^FILENAME; !max-file name ! length LITERAL BUFSIZE = 512; STRING .SBUFFER[0:BUFSIZE]; STRING .S^PTR; INT LOGNUM; INT TERMNUM; !I/O buffer (one extra char) !pointer to end of string !log file number !terminal file number ?NOLIST, SOURCE $SYSTEM.SYSTEM.
Accessing Files: An Example Using the File System !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name and its length ! and the error number. This procedure is used when the ! file is not open, so there is no file number for it. ! FILE^ERRORS is to be used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Accessing Files: An Example Using the File System !----------------------------------------------------------! Procedure to write a message on the terminal and check ! for any error. If there is an error, this procedure ! attempts to write a message about the error and then ! stops the program. !----------------------------------------------------------PROC WRITE^LINE(BUF,LEN); STRING .
Accessing Files: An Example Using the File System !-----------------------------------------------------------! Procedure for reading records. The user selected function ! "r." The start of the read is selected randomly by record ! number. The user has the option of sequentially reading ! subsequent messages.
Accessing Files: An Example Using the File System ! ! ! Prompt the user to read the next record.
Accessing Files: An Example Using the File System !-----------------------------------------------------------! Procedure to exit the program. !-----------------------------------------------------------PROC EXIT^PROGRAM; BEGIN CALL PROCESS_STOP_; END; !-----------------------------------------------------------! Procedure to process an invalid command. The procedure ! informs the user that the selection was other than "r," ! "a," or "x.
Accessing Files: An Example Using the File System !-----------------------------------------------------------! Procedure to initialize the program. It calls ! INITIALIZER to dispose of the startup sequence of messages. ! It opens the home terminal and the data file used by the ! program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .LOGNAME[0:MAXFLEN - 1]; INT LOGLEN; STRING .
Accessing Files: An Example Using the File System !-----------------------------------------------------------! This is the main procedure. It calls the INIT procedure to ! initialize and then goes into a loop calling GET^COMMAND ! to get the next user request and then calling a procedure ! to carry out the selected request.
3 Coordinating Concurrent File Access Several processes can access the same file at the same time. This section describes the procedures that allow you to coordinate such concurrent access. Each process indicates (when opening the file) how it intends to use the file, either by specifying the access mode and the exclusion mode to the file or by accepting default values.
Coordinating Concurrent File Access Setting the Access Mode The following example opens three files, one for reading and writing, one for read-only access, and one for write-only access: LITERAL READ^WRITE = 0; LITERAL READ^ONLY = 1; LITERAL WRITE^ONLY = 2; . . . ERROR := FILE_OPEN_(FILENAME1:LENGTH1, FILENUM1, READ^WRITE); IF ERROR <> 0 THEN ... ERROR := FILE_OPEN_(FILENAME2:LENGTH2, FILENUM2, READ^ONLY); IF ERROR <> 0 THEN ...
Coordinating Concurrent File Access Setting the Exclusion Mode Refer to the TACL Reference Manual for more details on the TACL FILEINFO command. Consider a file owned by user 24,48 (where 24 is the user’s group number and 48 is the user number within that group) with security permissions “GOA-”: G In the read column indicates that anyone in the user’s group on the local node can read the file, but no one outside the group can read it.
Coordinating Concurrent File Access Setting the Exclusion Mode a shared database. Any transaction-processing application would be typical. Here, data integrity can be provided at a lower level (for example, the record level). • Protected The opening process tolerates only other openers with read-only access mode. In addition, processes attempting to open the file for exclusive access are barred from the file.
Setting the Exclusion Mode Coordinating Concurrent File Access Figure 3-1. Exclusion and Access Mode Compatibility VST121.VSD The following example opens three files: LITERAL READ^WRITE = 0; LITERAL READ^ONLY = 1; LITERAL WRITE^ONLY = 2; LITERAL SHARED^ACCESS = 0; LITERAL EXCLUSIVE^ACCESS = 1; LITERAL PROTECTED^ACCESS = 3; . . . ERROR := FILE_OPEN_(FILENAME1:LENGTH1,FILENUM1); IF ERROR <> 0 THEN ...
Coordinating Concurrent File Access Locking a File ERROR := FILE_OPEN_(FILENAME2:LENGTH2,FILENUM2, READ^ONLY, PROTECTED^ACCESS); IF ERROR <> 0 THEN ... ERROR := FILE_OPEN_(FILENAME3:LENGTH3, FILENUM3, !access!, EXCLUSIVE^ACCESS); IF ERROR <> 0 THEN ... The first FILE_OPEN_ call uses the default values to open FILENAME1 for reading and writing with shared exclusion mode. The second call opens FILENAME2 for readonly access with protected exclusion mode.
Coordinating Concurrent File Access • Avoiding Deadlocks You can specify that read operations should finish normally and that data should be returned in the buffer even though another process has the file locked. You should be aware, of course, that the process that has locked the file might change the record after the process reads the record: LITERAL READ^THROUGH^MODE = 2; . .
Coordinating Concurrent File Access Avoiding Multiple-Process Deadlocks Figure 3-2. Two Processes in Deadlock VST016.VSD Process A acquires the lock on file 1. Process B acquires the lock on file 2. Now process A would like to lock file 2 but cannot because process B has it locked. Process B can never release the lock it has on file 2 because it is waiting for a lock on file 1, which process A can never release. You can avoid this kind of deadlock by careful programming practice, as shown in Figure 3-3.
Avoiding Multiple-Process Deadlocks Coordinating Concurrent File Access Figure 3-3.
Avoiding Single-Process Deadlocks Coordinating Concurrent File Access 3 Coordinating Concurrent File Access Avoiding Single-Process Deadlocks A process can also cause itself to deadlock, as shown in Figure 3-4. Figure 3-4. Single-Process Deadlock VST018.VSD Here, process C has opened the same file twice, returning two file numbers. The process acquires a lock using one of the file numbers, then tries to read the file using the other file number. Process C waits forever for itself to unlock the file.
Avoiding Single-Process Deadlocks Coordinating Concurrent File Access Figure 3-5. Avoiding the Single-Process Deadlock VST019.VSD By issuing a call to SETMODE function 4 on each file number, subsequent read operations return immediately with an error code if the read could not proceed. Deadlock is thus avoided.
4 Using Nowait Input/Output This section discusses how to do I/O operations without having the process wait for completion of the operation. A process that does not wait for I/O operations to finish is said to be using nowait I/O. This section uses examples to show the different ways in which you can use nowait I/O.
Overview of Nowait Input/Output Using Nowait Input/Output Figure 4-1. Waited and Nowait I/O VST020.VSD When you use nowait I/O, however, you typically do so for one of the following reasons: • • To apply a time limit to an operation To support multiple independent logical processing threads in a single program The ability to overlap application processing with I/O operations is often secondary.
Applying a Nowait Operation on a Single File Using Nowait Input/Output I/O operations initiated by the procedures listed below execute in parallel with your process when invoked on a file opened for nowait I/O. You must complete each of these calls by a separate call to the AWAITIO[X] procedure if the I/O operation is against a file opened for nowait I/O.
Applying a Nowait Operation on a Single File Using Nowait Input/Output In this example, if the WRITEX call initiates just one I/O operation (as, for example, when writing to an unstructured file), it returns immediately to the program. If the WRITEX call initiates several I/O operations (as, for example, when writing to a keysequenced file that has alternate keys), then the program waits until the last I/O operation starts.
Using Nowait Input/Output Applying Multiple Nowait Operations on a Single File Applying Multiple Nowait Operations on a Single File Files other than disk files allow multiple I/O operations to run concurrently. Figure 4-2 shows several I/O operations executing concurrently against the same file. This file must be opened specifically to allow concurrent I/O operations. Figure 4-2. Multiple Concurrent Operations on One File VST021.
Completing I/Os in the Order Initiated Using Nowait Input/Output This subsection discusses both of these approaches. In both of these approaches, the AWAITIO[X] call can wait indefinitely, time out, or check for completion and return immediately, exactly as in the single-I/O single-file model discussed in the previous subsection. (When a timeout expires, only the oldest I/O operation is canceled.
Completing I/Os in Any Order Using Nowait Input/Output Completing I/Os in Any Order Now consider what happens when I/O operations are allowed to finish in any order. Two different uses of the SETMODE procedure produce slightly different results: • • Setting parameter-1 to 1 causes I/O operations to finish in any order, except if more than one operation is ready to finish at the time of the AWAITIO[X] call. In this case, the operations that are ready finish in their issued order.
Using File-System Buffering Using Nowait Input/Output BYTES := 25; LENGTH := 512; CALL WRITEREADX(F4, BUFFER2, BYTES, LENGTH, TAG2); IF <> THEN ... BYTES := 12; LENGTH := 512; CALL WRITEREADX(F4, BUFFER3, BYTES, LENGTH, TAG3); IF <> THEN ... . . . CALL AWAITIOX(F4, !buffer^address!, BYTE^COUNT, TAG); IF <> THEN ... The sixth parameter (tag) of each WRITEREADX call assigns a unique tag. The AWAITIOX procedure retrieves the tag value in its own tag parameter and thereby identifies the completing operation.
Using File-System Buffering Using Nowait Input/Output By issuing SETMODE function 72, you cause the operating system to use an intermediate file-system buffer in the process file segment (PFS): LITERAL USE^PFS = 72, ON = 1; . . CALL SETMODE(FILE^NUM, USE^PFS, ON); Consider two concurrent read operations that use the same buffer without using filesystem buffering: 1. The first read operation starts and reads data into the application buffer. 2.
Using Nowait Input/Output Applying Nowait Operations to Multiple Files Applying Nowait Operations to Multiple Files Your program may open several files for nowait I/O and issue one or more I/O operations against each file. (Some kinds of opens, for example those of disk files, allow only one operation at time.) Figure 4-3 shows how nowait operations can occur concurrently on multiple files. Figure 4-3. Nowait Operations on Multiple Files VST022.
Applying Nowait Operations to Multiple Files Using Nowait Input/Output parameter of the AWAITIO[X] procedure to -1. When AWAITIO[X] returns, it sets the file-number parameter to the number of the file whose I/O operation finished. If you issue just one I/O operation at a time to each file, then the file number returned by the AWAITIO[X] procedure is enough to identify the completed operation. However, it is often easier to use tags as described in the previous subsection.
Nowait I/O: An Example Using Nowait Input/Output Nowait I/O: An Example The following example shows a complete working program that uses nowait I/O to time out terminal response. This example enhances the example given at the end of Section 2, Using the File System. Whenever the program prompts the user to enter a value, a timer is started. If the user does not respond to the prompt within five minutes, the user is logged off and the program terminates.
Nowait I/O: An Example Using Nowait Input/Output !-----------------------------------------------------------! Here are some DEFINEs to make it easier to format and ! print messages.
Nowait I/O: An Example Using Nowait Input/Output !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is used when the ! file is not open, so there is no file number for it. ! FILE^ERRORS is to be used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Nowait I/O: An Example Using Nowait Input/Output !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and error number are determined from the file number ! and FILE^ERRORS^NAME is then called to do the display. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
Nowait I/O: An Example Using Nowait Input/Output !-----------------------------------------------------------! Procedure to write messages on the terminal and read the ! user's reply. If there is an error, this procedure ! attempts to write a message about the error and the program ! is stopped. If the read takes longer than the defined wait ! time, the procedure returns 1 as its result to signal the ! caller of the timeout. Otherwise it returns 0.
Nowait I/O: An Example Using Nowait Input/Output !-----------------------------------------------------------! Procedure to prompt the user to log on. If logon is ! successful, the global variable LOGGED^ON is set to 1. !-----------------------------------------------------------PROC LOGON; BEGIN LITERAL NAMESIZE = 20; LITERAL PWSIZE = 10; STRING .USER^NAME[0:NAMESIZE - 1]; INT NAMELEN; STRING .
Nowait I/O: An Example Using Nowait Input/Output ! ! ! Perform application-dependent check to verify the user name and password and set LOGGED^ON to true if successful. This example does no checking.
Nowait I/O: An Example Using Nowait Input/Output !-----------------------------------------------------------! Procedure for reading records. The user selected function ! "r." The start of the read is selected randomly by record ! number. The user has the option of sequentially reading ! subsequent messages.
Nowait I/O: An Example Using Nowait Input/Output IF WRITEREADTERM(SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ) THEN BEGIN LOGGED^ON := 0; RETURN; END; END UNTIL NOT (SBUFFER[0] = "y" OR SBUFFER[0] = "Y"); END; !-----------------------------------------------------------! Procedure for appending a record. The user selected ! function "a." The user is prompted to enter comments. The ! procedure puts the comments in a new record at the end of ! the file.
Nowait I/O: An Example Using Nowait Input/Output !-----------------------------------------------------------! Procedure to process an invalid command. The procedure ! informs the user that the selection was other than "r," ! "a," or "x.
Nowait I/O: An Example Using Nowait Input/Output IF ERROR <> 0 THEN CALL PROCESS_STOP_; ! Open the log file with a sync depth of 1: LOGNAME ':=' "$ADMIN.OPERATOR.
Nowait I/O: An Example Using Nowait Input/Output !-----------------------------------------------------------! This is the main procedure. It calls the INIT procedure to ! initialize and then goes into a loop calling GET^COMMAND ! to get the next user request and calling the procedure ! to carry out that request.
Using FILE_COMPLETE_ and its Companion Procedures Using Nowait Input/Output Using FILE_COMPLETE_ and its Companion Procedures The FILE_COMPLETE_ procedure and its companion procedures, FILE_COMPLETE_SET_ and FILE_COMPLETE_GETINFO_, provide additional capabilities for programs that use nowait I/O. They combine and enhance the function of the AWAITIO[X] procedures and the Open System Services (OSS) select() function.
Using the FILE_COMPLETE_SET_ Procedure Using Nowait Input/Output The COMPLETE^ELEMENT structure is defined in the ZSYS* files.
Using Nowait Input/Output Using the FILE_COMPLETE_SET_ Procedure -- Describe Guardian file to be added to the enabled set COMPLETE_LIST[0].Z^FNUM^FD := FILENUM; -- Guardian file -- number COMPLETE_LIST[0].Z^OPTIONS.Z^SET^FILE := 0;-- Add this file to -- the enabled set COMPLETE_LIST[0].Z^OPTIONS.Z^FILETYPE := 0;-- This is a -- Guardian file -- Describe OSS file to be added to the enabled set COMPLETE_LIST[1].Z^FNUM^FD := FILEDESC1; -- OSS file -- descriptor COMPLETE_LIST[1].Z^OPTIONS.
Using Nowait Input/Output Using the FILE_COMPLETE_GETINFO_ Procedure Using the FILE_COMPLETE_GETINFO_ Procedure You can use the FILE_COMPLETE_GETINFO_ procedure to obtain information about the set of files that are currently enabled for completion. Through an output parameter, the FILE_COMPLETE_GETINFO_ procedure returns an array of COMPLETE^ELEMENT structures that describe the files that are enabled for completion.
Using the FILE_COMPLETE_ Procedure Using Nowait Input/Output Completion on Guardian Files You can use the FILE_COMPLETE_ procedure to complete I/O operations on the same Guardian files as the AWAITIO[X] procedures. (Refer to the discussion of the AWAITIO[X] procedures earlier in this section.) It is possible to use the FILE_COMPLETE_ procedure in parallel with the AWAITIO[X] procedures in your program.
Using Nowait Input/Output Using the FILE_COMPLETE_ Procedure file that was completed or the OSS file that is ready. This structure is defined in the ZSYS* files.
Nowait-Depth Using Nowait Input/Output In the following example, the call to the FILE_COMPLETE_ procedure waits for ten seconds for a completion to occur on one of the files in the enabled set; the temporary override set is not used. ?SOURCE ZSYSTAL( ZSYS^DDL^COMPLETION^INFO ) STRUCT .COMPLETE_INFO (ZSYS^DDL^COMPLETION^INFO^DEF); INT(32) TIME_LIMIT; INT ERROR; . .
Nowait-Depth Using Nowait Input/Output Note that FILE_OPEN_ can be performed no-waited. In this case FILE_OPEN_ returns a file number immediately, which must be used in a later call to AWAITIO (the open does not complete until AWAITIO returns). Use this when opening a process file to ensure that the opened process reads its $RECEIVE messages. If you call FILE_OPEN_ waited, the requester might hang indefinitely if the server misbehaves.
5 Communicating With Disk Files Data files are typically either NonStop SQL files or files managed by the Enscribe database record manager. This section discusses Enscribe files. For details of NonStop SQL file management, see the NonStop SQL manuals. In this section, you will learn about each file type supported by the Enscribe software and how to access such files using Guardian procedure calls.
Structured Files Communicating With Disk Files Unstructured files are suitable for applications that are sequential in nature or where the application itself provides the structure. Files created by the EDIT and TEDIT programs are typical examples of unstructured files.
Structured Files Communicating With Disk Files Figure 5-1. Relative-File Structure VST023.
Structured Files Communicating With Disk Files Entry-Sequenced Files Records in an entry-sequenced file are variable length. Writing to the file involves appending records to the end of the file. Records therefore appear in the file in the order in which they were written. Once the record is written, its size cannot be changed. Figure 5-2 shows the structure of an entry-sequenced file. Figure 5-2. Entry-Sequenced File Structure VST024.
Structured Files Communicating With Disk Files Key-Sequenced Files In key-sequenced files, each record is identified by a unique key that is stored in the record itself. With key-sequenced files, you can read records, insert new records, delete records, and update records. When updating records, you also have the possibility of changing the length of the record. Tree-structured index records provide random access to the data records. Data records can also be accessed sequentially in key sequence.
Structured Files Communicating With Disk Files Figure 5-3. Key-Sequenced File Structure VST025.
Alternate-Key Files Communicating With Disk Files Alternate-Key Files An alternate-key file is a key-sequenced file that has a special association with a primary file. The primary file can be a relative file, an entry-sequenced file, or another key-sequenced file. This association allows the file system to use the records in the alternate-key file to keep track of records in the primary file. An alternate-key file can be opened and read or updated by itself, just like any keysequenced file.
Queue Files Communicating With Disk Files Figure 5-4. An Alternate-Key File VST026.VSD When a new key is added to the data file, new alternate keys are automatically added to the alternate-key file (unless the alternate key is defined as not automatically updated). Unlike primary keys, alternate keys can be duplicated if the file is designated to accept duplicate keys. Duplicate keys are added to the alternate-key file in the same order as the corresponding key in the primary file.
Using Unstructured Files Communicating With Disk Files For more information about queue files and how to use them, refer to the Enscribe Programmer’s Guide. Using Unstructured Files You can access unstructured files using system procedures such as FILE_OPEN_, READ[X], READUPDATE[X], WRITE[X], WRITEUPDATE[X], and so on. Unstructured files are suitable for sequential I/O; successive calls to the READX procedure, for example, read successive records from the file.
Creating Unstructured Files Communicating With Disk Files The following example interactively creates an unstructured file with a buffer size of 512 bytes using the FUP CREATE command: 1> FUP -SET TYPE U -SET BUFFERSIZE 512 -SHOW TYPE U EXT ( 1 PAGES, 1 PAGES ) MAXEXTENTS 16 BUFFERSIZE 512 -CREATE $ADMIN.OPERATOR.LOGFILE CREATED - $ADMIN.OPERATOR.LOGFILE -EXIT 2> Refer to the File Utility Program (FUP) Reference Manual for more details on how to create files using the FUP CREATE command.
Opening Unstructured Files Communicating With Disk Files Opening Unstructured Files You open an unstructured file as you would any other file, by using the FILE_OPEN_ procedure. INT FILE^NUM; . . CALL FILE_OPEN_(FILE^NAME:LENGTH, FILE^NUM, !access!, !exclusion!, !nowait^depth!, SYNC^DEPTH); This example opens the file for reading and writing with waited I/O.
Renaming Unstructured Files Communicating With Disk Files Section 3, Coordinating Concurrent File Access, describes how to use the LOCKFILE procedure to acquire a file lock, and how to remove a file lock using the UNLOCKFILE procedure. LOCKREC and UNLOCKREC work in a similar way, as follows: CALL POSITION(FILE^NUM, RECORD^ADDRESS); CALL LOCKREC(FILE^NUM); IF <> THEN ... !could not get the lock . . !protected I/O operations . .
Avoiding Unnecessary Cache Flushes to Unstructured Files Communicating With Disk Files Avoiding Unnecessary Cache Flushes to Unstructured Files You can avoid unnecessary cache flushes to unstructured files in the same way as for any other file by using function 152 of the SETMODE procedure as shown in the following code fragment: LITERAL AVOID^FLUSH = 152, DONT^FLUSH = 1; . . CALL SETMODE(FILE^NUM, AVOID^FLUSH, DONT^FLUSH); IF <> THEN ...
Purging Unstructured Files Communicating With Disk Files Purging Unstructured Files You purge an unstructured file the same way you would purge any file; either interactively using the FUP PURGE command or TACL PURGE command, or programmatically by calling the FILE_PURGE_ procedure. Purging does not normally delete the data, but it changes pointers to show the file to be absent and its extent space deallocated. The file should be closed before you attempt to purge it.
Altering Unstructured-File Attributes Communicating With Disk Files Altering Unstructured-File Attributes As for any other file type, file attributes for an unstructured file are normally set when the file is created. These attributes include, for example, an application-supplied file code or an expiration time before which the file cannot be purged. You can, however, change some attributes of an existing file by calling the FILE_ALTERLIST_ procedure. Note.
Creating Relative Files Communicating With Disk Files Creating Relative Files You can create a relative file either interactively using the FUP CREATE command or programmatically using the FILE_CREATE[LIST]_ procedure. In either case, you need to supply information about how to build the file, including the appropriate file type, block size, and maximum record length.
Opening Relative Files Communicating With Disk Files The record length is set to 128 bytes, which sets the limit on the size of a logical record. (Recall that the maximum size of a logical record for a relative file is the record length.) A block size of 4096 limits the maximum size of a record that could be specified. The block size is the number of bytes that are transferred between the disk and the disk process. The block size can be 512, 1024, 2048, or 4096 bytes.
Communicating With Disk Files Locking, Renaming, Caching, Closing, Purging, and Altering Relative Files Locking, Renaming, Caching, Closing, Purging, and Altering Relative Files The operations of locking, renaming, closing, and purging relative files, altering relative-file attributes, and avoiding unnecessary cache flushes of relative files are the same as for any disk file. See Using Unstructured Filesearlier in this section.
Relative-File Programming Example Communicating With Disk Files • • The READ^RECORD procedure prompts the user for the record number of the first record to be read instead of always starting with the first record in the file. The LOGGER and GET^COMMAND procedures support the “u” option for updating a record. The code for this program appears on the following pages. ?INSPECT,SYMBOLS,NOMAP,NOCODE ?NOLIST, SOURCE $SYSTEM.ZSYSDEFS.
Relative-File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Here are some DEFINEs to make it easier to format and print ! messages.
Relative-File Programming Example Communicating With Disk Files !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when ! the file is not open, so there isn't a file number for it. ! FILE^ERRORS is to be used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Relative-File Programming Example Communicating With Disk Files !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and error number are determined from the file number ! and FILE^ERRORS^NAME is then called to do the display. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
Relative-File Programming Example Communicating With Disk Files !-----------------------------------------------------------! This procedure asks the user for the next function to do: ! ! "r" to read records ! "u" to update a record ! "a" to append a record ! "x" to exit the program ! ! The selection made is returned as the result of the call.
Relative-File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure for reading records. The user selected function ! "r." The start of the read is selected randomly by record ! number. The user has the option of sequentially reading ! subsequent messages. !-----------------------------------------------------------PROC READ^RECORD; BEGIN INT COUNT^READ; INT(32) RECORD^NUM; STRING .
Relative-File Programming Example Communicating With Disk Files ! ! Loop reading and displaying records until user declines to read next record (any response other than "y"): DO BEGIN PRINT^BLANK; ! ! ! Read a record from the log file and display it on the terminal.
Relative-File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure for updating a record. The user selected ! function "u." The user is prompted for the record number ! to update. The procedure displays the current contents and ! prompts for the new. After the user enters the new ! contents, the procedure updates the log file.
Relative-File Programming Example Communicating With Disk Files CALL FILE^ERRORS(LOGNUM); END; ! Write the record to the terminal screen: PRINT^BLANK; CALL WRITE^LINE(SBUFFER,COUNT^READ); ! Prompt the user for the updated record: PRINT^BLANK; SBUFFER ':=' "Enter New Contents of Record: " -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); ! Write new record to log file: CALL WRITEUPDATEX(LOGNUM,SBUFFER,COUNT^READ); IF <> THEN CALL
Relative-File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure to exit the program. !-----------------------------------------------------------PROC EXIT^PROGRAM; BEGIN CALL PROCESS_STOP_; END; !-----------------------------------------------------------! Procedure to process an invalid command. The procedure ! informs the user that the selection was other than "r," ! "u", "a," or "x.
Relative-File Programming Example Communicating With Disk Files !-----------------------------------------------------------! This procedure does the initialization for the program. ! It calls INITIALIZER to dispose of the startup messages. ! It opens the home terminal and the data file used by the ! program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .LOGNAME[0:MAXFLEN - 1]; INT LOGLEN; STRING .
Relative-File Programming Example Communicating With Disk Files !-----------------------------------------------------------! This is the main procedure. It calls the INIT procedure to ! initialize and then it goes into a loop calling GET^COMMAND ! to get the next user request and calling the procedure ! to carry out that request.
Using Entry-Sequenced Files Communicating With Disk Files 5 Communicating With Disk Files Using Entry-Sequenced Files Entry-sequenced files have a different structure than relative files, therefore file creation is different. File access is also different; random file access, for example, is done by record address instead of record number.
Opening Entry-Sequenced Files Communicating With Disk Files FILE^NAME ':=' "\SYS.$HR.RECORDS.ESFILE" -> @S^PTR; LENGTH := @S^PTR '-' @FILE^NAME; CALL FILE_CREATE_(FILE^NAME:ZSYS^VAL^LEN^FILENAME, LENGTH, !file^code!, !primary^extent^size!, !secondary^extent^size!, !max^extents!, FILE^TYPE, !options!, RECORD^LENGTH, BLOCK^LENGTH); A file type of 2 specifies an entry-sequenced file. The maximum record length has been set to 4072 bytes, almost equal to the block size.
Locking, Renaming, Caching, Closing, Purging, and Altering Entry-Sequenced Files Communicating With Disk Files To allow the appended record to be randomly accessed, you can acquire the record address (combination of block number and record number) by issuing a FILE_GETINFOLIST_ call as follows: LITERAL MAX^RESULT^LENGTH = 34; INT(32) RECORD^ADDRESS := 0D; . .
Monitoring Writes to a Disk File Communicating With Disk Files Using the CONTROL 27 Operation To find out whether a write operation might have taken place, you issue the CONTROL 27 operation against a file number that is open for nowait I/O. When a write occurs against any open on this file, the corresponding call to the AWAITIO procedure returns.
Monitoring Writes to a Disk File Communicating With Disk Files 4. The DO-UNTIL loop issues read operations against the file number returned by the waited open. The loop exits when the READX procedure returns an end-of-file error. This operation serves the following purposes: • • • The first time through the loop, the calls to READX read any records that were already written to the file.
Entry-Sequenced File Programming Example Communicating With Disk Files Entry-Sequenced File Programming Example This example again uses the log-file program. Here, the program is shown modified to use an entry-sequenced file. The entry-sequenced file is suitable for this kind of application because: • • • File entries are chronological. Variable-length entries are permitted.
Entry-Sequenced File Programming Example Communicating With Disk Files • The APPEND^RECORD procedure contains additional code to return the record address and display it on the terminal. The user can use this address to randomly access the record. The code for this program follows. ?INSPECT,SYMBOLS,NOMAP,NOCODE ?NOLIST,SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?LIST LITERAL MAXFLEN = ZSYS^VAL^LEN^FILENAME; !maximum file-name ! length LITERAL BUFSIZE = 512; STRING STRING STRING INT INT STRING INT INT .
Entry-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Here are some DEFINEs to make it easier to format and print ! messages.
Entry-Sequenced File Programming Example Communicating With Disk Files !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when ! the file is not open, so there isn't a file number for it. ! FILE^ERRORS is to be used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Entry-Sequenced File Programming Example Communicating With Disk Files !----------------------------------------------------------! This procedure writes a message on the terminal and checks ! for any error. If there is an error, it attempts to write ! a message about the error and the program is stopped. !----------------------------------------------------------PROC WRITE^LINE (BUF, LEN); STRING .
Entry-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure for reading records. The user selected function ! "r." The start of the read is selected randomly by record ! number. The user has the option of sequentially reading ! subsequent messages. !-----------------------------------------------------------PROC READ^RECORD; BEGIN INT COUNT^READ; INT(32) RECORD^NUM; STRING .
Entry-Sequenced File Programming Example Communicating With Disk Files ! Loop reading and displaying records until user declines ! to read next record (any response other than "y"): DO BEGIN PRINT^BLANK; ! Read a record from the log file and display it on the ! terminal. If end-of-file is reached, return control ! to LOGGER procedure.
Entry-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure for appending a record. The user selected ! function "a." The user is prompted to enter comments. The ! procedure puts the comments in a new record at the end of ! the file.
Entry-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure to exit the program. !-----------------------------------------------------------PROC EXIT^PROGRAM; BEGIN CALL PROCESS_STOP_; END; !-----------------------------------------------------------! Procedure to process an invalid command. The procedure ! informs the user that the selection was other than "r," ! "u," "a," or "x.
Entry-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! This procedure does the initialization for the program. ! It calls INITIALIZER to dispose of the startup messages. ! It opens the home terminal and the data file used by the ! program. !-----------------------------------------------------------PROC INIT; BEGIN INT ERROR; ! Read and discard startup messages. CALL INITIALIZER; ! ! ! ! Open the terminal file.
Entry-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! This is the main procedure. It calls the INIT procedure to ! initialize and then it goes into a loop calling GET^COMMAND ! to get the next user request and calling the procedure ! to carry out that request.
Using Key-Sequenced Files Communicating With Disk Files Using Key-Sequenced Files This subsection discusses the way the programmatic interface to key-sequenced files differs from that of the other structured file types. Specifically, it discusses how to create and access a key-sequenced file. At the end of the subsection is a working example of a program that uses a key-sequenced file. The discussion here is limited to primary-key access.
Opening Key-Sequenced Files Communicating With Disk Files The following example creates a key-sequenced file interactively using the FUP CREATE command. The new file has a block size of 4096 bytes, maximum record length of 128 bytes, and key length of 16 bytes, and the key offset is zero. 1> FUP -SET TYPE K -SET BLOCK 4096 -SET REC 128 -SET IBLOCK 2048 -SET KEYLEN 16 -SHOW TYPE K EXT ( 1 PAGES, 1 PAGES ) REC 128 BLOCK 4096 IBLOCK 2048 KEYLEN 16 KEYOFF 0 MAXEXTENTS 16 -CREATE \SYS.$MANUF.RECORDS.
Positioning, Reading, and Writing With KeySequenced Files Communicating With Disk Files Positioning, Reading, and Writing With Key-Sequenced Files Read and write operations on key-sequenced files are done using READ[X], READUPDATE[X], WRITE[X], and WRITEUPDATE[X] procedure calls, as for any file. However, what is unique about key-sequenced files is the way you position the currentrecord and next-record pointers before reading, writing, or updating the file.
Positioning, Reading, and Writing With KeySequenced Files Communicating With Disk Files one record each time it goes through the loop. The READX procedure returns an error when the end-of-file is reached; you can use this condition to exit the loop. LITERAL APPROX = 0; . . KEY^VALUE := "CAAAAAAAAAAAAAAA"; CALL KEYPOSITION(NAME^FILE^NUM, KEY^VALUE, !key^specifier!, !length^word!, APPROX); WHILE 1 DO BEGIN CALL READX(NAME^FILE^NUM, BUFFER, $LEN(RECORD), BYTES^READ); IF <> THEN ... END; . .
Locking, Renaming, Caching, Closing, Purging, and Altering Key-Sequenced Files Communicating With Disk Files So far, sequential reading of a key-sequenced file has been assumed to mean reading records in ascending key sequence. By setting bit 1 of the positioning-mode parameter, however, you can read sequentially in descending key sequence. Positioning is unnecessary when writing a new record to a key-sequenced file.
Communicating With Disk Files Key-Sequenced File Programming Example -EXIT 2> The program is similar to the relative-file example given earlier in this section in that it enables the user to read records, add records, and update records. Because access to the file is by key value, however, the mechanism is different.
Key-Sequenced File Programming Example Communicating With Disk Files the part number, which is already known. The parameter to the procedure specifies whether an update or a new record is required. The code for this program appears on the following pages. ?INSPECT,SYMBOLS,NOMAP,NOCODE ?NOLIST,SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?LIST LITERAL MAXFLEN = ZSYS^VAL^LEN^FILENAME; LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL OLD = NEW = BUFSIZE = PARTSIZE= DESCSIZE= SUPPSIZE= 0; 1; 132; 6; 60; 60; STRING .
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Here are a few DEFINEs to make it a little easier to ! format and print messages.
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when ! the file is not open, when there is no file number for it. ! FILE^ERRORS is used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! This procedure writes a message on the terminal and checks ! for any error. If there is an error, it attempts to write ! a message about the error and the program is stopped. !-----------------------------------------------------------PROC WRITE^LINE(BUF,LEN); STRING .
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure to display a part record on the terminal !-----------------------------------------------------------PROC DISPLAY^RECORD; BEGIN PRINT^BLANK; ! Display part number: PRINT^STR("Part Number Is: ! Display part description: PRINT^STR("Part Description: ! " & PART^RECORD.DESCRIPTION FOR PART^RECORD.
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure to prompt user for input to build a new record or ! update an existing record. When updating, an empty ! response (COUNT^READ=0) means to leave the existing value ! unchanged. !-----------------------------------------------------------PROC ENTER^RECORD(TYPE); INT TYPE; BEGIN INT COUNT^READ; INT STATUS; STRING .
Communicating With Disk Files ! Key-Sequenced File Programming Example Prompt for the name of the supplier: SBUFFER ':=' "Enter Supplier Name: " -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); IF TYPE = NEW OR COUNT^READ > 0 THEN BEGIN COUNT^READ := $MIN(COUNT^READ,SUPPSIZE); BLANK^FILL(PART^RECORD.SUPPLIER); PART^RECORD.SUPPLIER ':=' SBUFFER FOR COUNT^READ; PART^RECORD.
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure for reading records. The user selected function ! "r." The start of the read is selected by approximate key ! positioning. The user has the option of sequentially ! reading subsequent records.
Key-Sequenced File Programming Example Communicating With Disk Files PRINT^BLANK; ! ! ! Prompt the user to read the next record (user must respond "y" to accept, otherwise return to select next function): SBUFFER ':=' ["Do you want to read another ", "record (y/n)? "] -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); SBUFFER[COUNT^READ] := 0; END UNTIL NOT (SBUFFER[0] = "y" OR SBUFFER[0] = "Y"); END; Guardian Programmer’s Guide —
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure for updating a record. The user selected ! function "u." The user is prompted to enter the part ! number of the record to be updated, then the old contents ! are displayed on the user's terminal before the user ! is prompted to enter the updated record.
Key-Sequenced File Programming Example Communicating With Disk Files ! Save the record for later comparison SAVE^REC ':=' PART^RECORD FOR $LEN(PART^RECORD) BYTES; ! Display the record on the terminal: CALL DISPLAY^RECORD; ! Prompt the user for the updated record: CALL ENTER^RECORD(OLD); ! ! ! Now that we have the user's changes, reread the record and check to see whether someone else changed it while the user was responding.
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure for inserting a record. The user selected ! function "i." The user is prompted to enter the new record. ! The procedure inserts the new record in the appropriate ! place in the file.
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure to process an invalid command. The procedure ! informs the user that the selection was other than "r," ! "u", "a," or "x.
Key-Sequenced File Programming Example Communicating With Disk Files ! Open the part file with a sync depth of 1: PARTFILE^NAME ':=' "$APPL.SUBAPPL.PARTFILE" -> @S^PTR; PARTFILE^LEN := @S^PTR '-' @PARTFILE^NAME; ERROR := FILE_OPEN_(PARTFILE^NAME:PARTFILE^LEN, PARTFILE^NUM, !access!, !exclusion!, !nowait^depth!, 1); IF ERROR <> 0 THEN CALL FILE^ERRORS^NAME(PARTFILE^NAME:PARTFILE^LEN, ERROR); END; !-----------------------------------------------------------! This is the main procedure.
Communicating With Disk Files Key-Sequenced File Programming Example END; END; Guardian Programmer’s Guide — 421922-014 5 - 67
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure for inserting a record. The user selected ! function "i." The user is prompted to enter comments. The ! procedure puts the comments in a new record at the end of ! the file.
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! Procedure to exit the program. !-----------------------------------------------------------PROC EXIT^PROGRAM; BEGIN CALL PROCESS_STOP_; END; !-----------------------------------------------------------! Procedure to process an invalid command. The procedure ! informs the user that the selection was other than "r," ! "u," "i," or "x.
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! This procedure does the initialization for the program. ! It calls INITIALIZER to dispose of the startup messages. ! It opens the home terminal and the data file used by the ! program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .LOGNAME[0:MAXFLEN - 1]; INT LOGLEN; STRING .
Key-Sequenced File Programming Example Communicating With Disk Files !-----------------------------------------------------------! This is the main procedure. It calls the INIT procedure to ! initialize and then it goes into a loop calling GET^COMMAND ! to get the next user request and calling the procedure ! to carry out that request.
Using Alternate Keys With an Entry-Sequenced File Communicating With Disk Files Using Alternate Keys With an Entry-Sequenced File The application of alternate keys to an entry-sequenced file is similar to applying alternate keys to relative files. Instead of a record number, the alternate-key file crossreferences the alternate-key value to a record address. Applying alternate keys to the example given in the subsection Using Entry-Sequenced Files produces the structure shown in Figure 5-6: Figure 5-6.
Communicating With Disk Files Using Alternate Keys With a Key-Sequenced File Figure 5-7. Example of Alternate-Key File for Use With a Key-Sequenced File VST031.
Using Alternate Keys With a Key-Sequenced File Communicating With Disk Files description is variable in length, the key length is specified in the call to KEYPOSITION. You can add further options to the following code to access records by supplier name, inventory level, or price. !-----------------------------------------------------------! Procedure for reading records. The user selected function ! "r." The start of the read is selected by approximate key ! positioning.
Using Alternate Keys With a Key-Sequenced File Communicating With Disk Files ! ! ! Set the compare length to the length of the primary key and pad the value with blanks, in case the full length was not entered: COMPARE^LEN := PARTSIZE; SBUFFER[COUNT^READ] ':=' [PARTSIZE*[" "]]; ! Set the positioning mode to approximate: POSITIONING^MODE := APPROX; "d", "D" -> ! Prompt for part description: PRINT^BLANK; SBUFFER ':=' "Enter Part Description: " -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUF
Using Alternate Keys With a Key-Sequenced File Communicating With Disk Files ! ! Loop reading and displaying records until user declines to read the next record (any response other than "y"): DO BEGIN PRINT^BLANK; ! ! ! Read a record from the part file. If the end of file is reached, return control to the main procedure.
Using Partitioned Files Communicating With Disk Files Using Partitioned Files When you create a file, you can choose to have the file reside on multiple volumes. A file can span up to 16 volumes in this way. Moreover, the disk volumes can be connected to the same or different controllers, on the same or different processing modules, or can even span multiple systems. Once the file is created, the locations of file partitions are transparent to the application.
Creating Partitioned Files Communicating With Disk Files The simplest way to create a partitioned file is by using the FUP CREATE command as shown below. This example creates a partitioned file that could be used by the inventory application described in the subsection Using Key-Sequenced Files, earlier in this section.
Creating Partitioned Files Communicating With Disk Files PROC CREATE^PARTS MAIN; BEGIN STRING .KEYFILE[0:ZSYS^VAL^LEN^FILENAME]; INT INT LENGTH; .ITEM^LIST[0:63]; INT INT INT INT .VALUES[0:512]; NUMBER^ITEMS; VALUES^LEN; ERROR; !primary-key ! file name !length of primary file name !list of items to pass to ! FILE_CREATELIST_ !values of those items !number of items !total length of items !system procedure call error KEYFILE ':=' "$ADMIN.OPERATOR.
Accessing Partitioned Files Communicating With Disk Files ! Create the file: ERROR := FILE_CREATELIST_(KEYFILE:ZSYS^VAL^LEN^FILENAME, LENGTH, ITEM^LIST, NUMBER^ITEMS, VALUES, VALUES^LEN); END; Either of the above examples creates a file with four partitions on volumes $ADMIN, $PART1, $PART2, and $PART3. The records are segregated by key value as shown in Figure 5-8: Figure 5-8. Sample Partitioned File VST032.
Using Alternate Keys Communicating With Disk Files 5 Communicating With Disk Files Using Alternate Keys This subsection examines how you can access a record in a structured file using a key value other than the primary key. To do this, you need an alternate-key file to provide the link between the alternate and primary keys.
Creating Alternate-Key Files Communicating With Disk Files The next example creates the same files programmatically using the FILE_CREATELIST_ procedure. You supply this procedure with an item-list and a list of corresponding values. The item-list parameter is an array of numbers that identify the values given in the values parameter.
Creating Alternate-Key Files Communicating With Disk Files ITEM^LIST ':=' [ZSYS^VAL^FCREAT^FILETYPE, ZSYS^VAL^FCREAT^LOGICALRECLEN, ZSYS^VAL^FCREAT^BLOCKLEN, ZSYS^VAL^FCREAT^KEYOFFSET, ZSYS^VAL^FCREAT^KEYLEN, ZSYS^VAL^FCREAT^NUMALTKEYS, ZSYS^VAL^FCREAT^ALTKEYDESC, ZSYS^VAL^FCREAT^NUMALTKEYFILES, ZSYS^VAL^FCREAT^ALTFILELEN, ZSYS^VAL^FCREAT^ALTFILENAMES]; NUMBER^ITEMS := 10; VALUES ':=' [3, 130, 4096, 0, 6, 2, ! ! Alternate key descriptor for description field: "DE", 60, 6, 0, 0, 0, ! ! !key specifier f
Creating Alternate-Key Files Communicating With Disk Files ! Concatenated alternate-key file names: "$ADMIN.OPERATOR.ALT2 $ADMIN.OPERATOR.ALT3"] -> @S^PTR; VALUES^LEN := (@S^PTR '-' @VALUES) '<<' 1; ! !length in ! bytes ! of VALUES ! parameter Create the primary file: ERROR := FILE_CREATELIST_(KEYSFILE:ZSYS^VAL^LEN^FILENAME, LENGTH, ITEM^LIST, NUMBER^ITEMS, VALUES, VALUES^LEN); ! Create the alternate-key file ALT2: KEYSFILE ':=' "$ADMIN.OPERATOR.
Adding Keys to an Alternate-Key File Communicating With Disk Files ERROR := FILE_CREATE_(KEYSFILE:ZSYS^VAL^LEN^FILENAME, LENGTH, !file^code!, !primary^extent^size!, !secondary^extent^size!, !max^extents!, !file^type!, !options!, REC^LEN, BLOCK^LEN, KEY^LEN, KEY^OFFSET); END; Adding Keys to an Alternate-Key File Usually, you do not add keys to an alternate-key file directly. The file system inserts the alternate keys automatically whenever a new key is added to the primary-key file.
Communicating With Disk Files Using Alternate Keys With a Relative File Using Alternate Keys With a Relative File Alternate keys used with a relative file reference a record number. As with any alternate-key mechanism, each occurrence of an alternate key references a primary key. Recall that for relative files, the primary key is the record number. The log-file programming example from the subsection Using Relative Files will be enhanced to show how alternate keys can be used with relative files.
Using Alternate Keys With a Relative File Communicating With Disk Files Alternate keys suit this application because: • • • Using a key value such as the date is a convenient way of accessing data. (The relative-file example shown earlier in this section expects the user to know the record number.) The user can make more than one entry per day in the log, because alternate keys can be duplicated.
Communicating With Disk Files • • The GET^DATE procedure has been added to prompt the user for the date and check its length. The READ^RECORD procedure is modified as follows: • • • • • • Using Alternate Keys With a Relative File The procedure prompts the user for a date instead of a record number. The procedure positions the pointers using the KEYPOSITION procedure (instead of POSITION). KEYPOSITION uses the alternate key to position the pointers.
Using Alternate Keys With a Relative File Communicating With Disk Files ?INSPECT,SYMBOLS,NOMAP,NOCODE ?NOLIST, SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?LIST LITERAL MAXFLEN = ZSYS^VAL^LEN^FILENAME; !maximum file-name ! length LITERAL DATESIZE = 8; !size of date field LITERAL COMMENTSIZE = 502; !size of comment field STRING .S^PTR; INT LOGNUM; INT TERMNUM; !pointer to end of string !log file number !terminal file number STRUCT .
Using Alternate Keys With a Relative File Communicating With Disk Files !-----------------------------------------------------------! Here are some DEFINEs to make it easier to format and print ! messages.
Using Alternate Keys With a Relative File Communicating With Disk Files !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is used when the ! file is not open, when there is no file number for it. ! FILE^ERRORS is used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Using Alternate Keys With a Relative File Communicating With Disk Files !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and error number are determined from the file number ! and FILE^ERRORS^NAME is then called to display the ! information. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
Using Alternate Keys With a Relative File Communicating With Disk Files !-----------------------------------------------------------! Procedure to ask the user for the next function to do: ! ! "r" to read records ! "u" to update a record ! "i" to insert a record ! "x" to exit the program ! ! The selection made is returned as the result of the call.
Using Alternate Keys With a Relative File Communicating With Disk Files !-----------------------------------------------------------! Procedure for getting a date from the user. The date ! entered is returned in SBUFFER.
Using Alternate Keys With a Relative File Communicating With Disk Files !-----------------------------------------------------------! Procedure for reading records. The user selected function ! "r." The start of the read is selected randomly by record ! number. The user has the option of sequentially reading ! subsequent messages.
Using Alternate Keys With a Relative File Communicating With Disk Files ! Print the record on the terminal: PRINT^STR("Date: PRINT^STR("Comments: " & RECORD.DATE FOR DATESIZE); " & RECORD.DATA FOR RECORD.DATA^LEN); PRINT^BLANK; ! ! ! Prompt the user to read the next record.
Using Alternate Keys With a Relative File Communicating With Disk Files !----------------------------------------------------------! Procedure for updating a record. The user selected ! function "u." The user is prompted for the key of the ! record to update. The procedure displays the current ! contents and prompts for the new. After the user enters ! the new contents, the procedure updates the log file.
Using Alternate Keys With a Relative File Communicating With Disk Files CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, COMMENTSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); RECORD.DATA ':=' SBUFFER FOR COUNT^READ; RECORD.
6 Communicating With Processes This section describes how to use file-system procedures to communicate with other processes. Specifically, this section covers the following topics: • • • • How processes engage in two-way communication. Here, a process sends a message to another process. After processing the message, the recipient replies to the message. How processes engage in one-way communication. In one-way communication, the sender receives no meaningful information from the recipient.
Communicating With Processes Sending and Receiving Messages: An Introduction information to child processes by way of a Startup message (see Section 8, Communicating With a TACL Process). • Permits user processes to receive system messages. A process sends a message to another process by opening the recipient process file and writing a message to it.
Sending and Receiving Messages: An Introduction Communicating With Processes FILE_GETINFO_ Provides error information and characteristics about the open process file or $RECEIVE file. FILE_GETRECEIVEINFO_ Returns information about the last message read from the $RECEIVE file. The information includes a tag that identifies the message. FILE_OPEN_ Establishes communication with a process file for sending messages or with the $RECEIVE file for receiving messages.
Sending Messages to Other Processes Communicating With Processes WRITE[X] Sends a message to another process and waits for a reply (assuming waited I/O). WRITE[X] ignores the reply data and is often used to send a server request for which the server does not send any reply data. The WRITE procedure sends data from a buffer in the user data segment of the sending process. The WRITEX procedure sends data from either the user data segment or an extended data segment of the sending process.
Opening a Process Communicating With Processes Examples of Opening a Process The following example opens process $SER1 for waited I/O. FILE^NAME := "$SER1" -> @S^PTR; LENGTH := @S^PTR '-' @FILE^NAME; ERROR := FILE_OPEN_(FILE^NAME:LENGTH, PROC^NUM); IF ERROR <> 0 THEN ...
Writing Messages to Another Process Communicating With Processes Writing Messages to Another Process Once the process file is open, you can communicate with the process by writing a message to the file number returned by the FILE_OPEN_ call. To send a message, you use either the WRITE[X] or WRITEREAD[X] procedure. For two-way communication (reply data expected), you should use WRITEREAD[X].
Writing Messages to Another Process Communicating With Processes . . CALL AWAITIOX(PROC^NUM); IF <> THEN ... Writing a message for one-way communication with error return is no different from that given in the above examples. The only difference is in the server process, which must use READUPDATE[X] and REPLY[X]. Writing a Message: Reply Data Expected Two-way communication expects reply data in reply to a written message.
Queuing Messages on $RECEIVE Communicating With Processes Queuing Messages on $RECEIVE Messages destined for a given server process are placed by the file-system software in a queue in the $RECEIVE file for the process in question. Note that this queue exists in main memory, not on disk. Normally, the queue is organized so that the server process reads messages from $RECEIVE in the order in which they arrive.
Queuing Messages on $RECEIVE Communicating With Processes To reorder the message queue according to message priority, the server process must issue SETMODE function 36 as follows: LITERAL PRIORITY^QUEUING = 36, ON = 1, OFF = 0; . . CALL SETMODE(RECV^NUM, PRIORITY^QUEUING, ON); IF <> THEN ... The priority of every process is set when the process is created. Refer to Section 16, Creating and Managing Processes, for details of how to do this using the PROCESS_CREATE_ procedure.
Receiving and Replying to Messages From Other Processes Communicating With Processes Receiving and Replying to Messages From Other Processes First look at how two-way communication works. This subsection is concerned with processes that read a message, process the message, and then reply to the sender before reading the next message. The file system keeps track of where to send replies. The following paragraphs describe how to perform two-way communication.
Reading Messages for Two-Way Communication Communicating With Processes Reading Messages for Two-Way Communication Use the READUPDATE[X] procedure to read a message from $RECEIVE if you want to reply to the message with data. READUPDATE[X] reads the message without terminating the WRITEREAD[X] procedure issued by the sender of the message. The WRITEREAD[X] procedure instead waits for a reply. An example of a READUPDATEX call follows: CALL READUPDATEX(RECV^NUM, SBUFFER, RCOUNT); IF <> THEN ...
Replying to Messages Communicating With Processes Error numbers outside the range 256 through 511 are reserved for the operating system and should not be used arbitrarily, because this could interfere with correct error handling inside the operating system and file system. If there is no problem, a value of zero (the default value) will be returned.
Sending, Receiving, and Replying to Messages: An Example Communicating With Processes 6 Communicating With Processes Sending, Receiving, and Replying to Messages: An Example In the example shown in Figure 6-4, the server process sends a reply back to the requester. This example allows the user of the terminal running the requester process to query the user of the terminal running the server process. The purpose of the example is to show the concept.
Closing $RECEIVE Communicating With Processes Figure 6-4. Two-Way Interprocess Communication VST036.
Receiving Messages From Other Processes: OneWay Communication Communicating With Processes Receiving Messages From Other Processes: One-Way Communication Now look at how one-way communication works. When receiving messages in oneway communication, all the server has to do is read the message and process it. No reply data is necessary in the reply. To receive a message from another process, the server process must open the $RECEIVE file and read from it.
Communicating With Processes Sending and Receiving One-Way Messages: An Example process file as typed by the user at a terminal. The server reads each message from $RECEIVE and displays the message on the home terminal of the process. Both processes stop when the user at the terminal of the requester process types “EXIT.” Again, the purpose of this example is to show the concept. The example does not necessarily perform a useful function. Figure 6-5. One-Way Interprocess Communication VST037.VSD VST037.
Handling Multiple Messages Concurrently Communicating With Processes Handling Multiple Messages Concurrently So far, you have seen how a server processes requests one at a time as it reads them from the $RECEIVE file. However, in some applications, it could happen that for the server to complete a request, it must wait for events outside the server process to finish. Other requests might have to wait a long time for the server to become available.
Reading Messages for Concurrent Processing Communicating With Processes Reading Messages for Concurrent Processing You use the READUPDATE[X] procedure to read each message without terminating the corresponding WRITEREAD[X] call. The WRITEREAD[X] procedure finishes when you reply to the message using the REPLY[X] procedure. With queued messages, however, you should use message tags to make sure that each reply goes to the process that sent the message you are replying to.
Getting Information About Messages Read From $RECEIVE Communicating With Processes Getting Information About Messages Read From $RECEIVE In addition to the message tag, the FILE_GETRECEIVEINFO_ procedure returns additional information about the last message read from the $RECEIVE file. The FILE_GETRECEIVEINFO_ procedure returns information as follows: STRUCT .INFORMATION(ZSYS^DDL^RECEIVEINFORMATION^DEF); . . ERROR := FILE_GETRECEIVEINFO_(INFORMATION); IF ERROR <> 0 THEN ...
Getting Information About Messages Read From $RECEIVE Communicating With Processes The following example extracts the type of operation requested in the message: STRUCT .INFORMATION(ZSYS^DDL^RECEIVEINFORMATION^DEF); . . ERROR := FILE_GETRECEIVEINFO_(INFORMATION); IF ERROR <> 0 THEN ...; CASE INFORMATION.
Replying to Messages Communicating With Processes ERROR); IF <> THEN ... Getting the Message Tag The message tag identifies a message and is used when the recipient process may have to process multiple messages. The tag enables the recipient process to send the reply to the correct process, as described earlier in this section. The message tag is returned by the FILE_GETRECEIVEINFO_ procedure in word 2: STRUCT .INFORMATION(ZSYS^DDL^RECEIVEINFORMATION^DEF); . .
Communicating With Processes Handling Multiple Messages Concurrently: An Example Handling Multiple Messages Concurrently: An Example Figure 6-6 shows an example of message queuing. It is similar to the example given in Figure 6-4, where concurrent message processing was not done. Here, however, the server process accepts input from two requesters, queues one message from each, and then processes and replies to both messages. Figure 6-6. Example of Handling Multiple Messages Concurrently VST038.
Checking for Canceled Messages Communicating With Processes Checking for Canceled Messages Typically, a server processes messages from other processes as follows: 1. The server reads a message from its $RECEIVE file. 2. The server performs some processing in response to the message. 3. The server replies to the process that sent the message.
Checking for Cancellation Messages Communicating With Processes These methods are described in the following paragraphs. Both methods involve use of the message tag. The message tag is generally used to distinguish among multiple messages when the server chooses to concurrently process several messages (see Handling Multiple Messages Concurrently earlier in this section). Here, the message tag is used to identify the canceled message.
Using the MESSAGESTATUS Procedure Communicating With Processes To enable receipt of cancellation messages in its $RECEIVE file, the server process must call SETMODE function 80. SETMODE function 80 controls several functions related to the $RECEIVE file, one of which is receipt of cancellation messages. For a list of the functions performed by SETMODE function 80, see the Guardian Procedure Calls Reference Manual. The following example enables the receipt of cancellation messages.
Receiving and Processing System Messages Communicating With Processes Receiving and Processing System Messages Recall that in addition to receiving messages from other processes, a process may receive system messages from the operating system on the $RECEIVE file. Of the many system messages that the operating system can send, the writer of an application usually need be aware of only a subset. Of the system messages that a process typically processes, some are implicit and others are explicit.
Receiving System Messages Communicating With Processes Receiving System Messages To receive system messages, your program needs to perform the following operations: • • • Open $RECEIVE so that your program is able to receive system messages. Choose to read D-series system messages (the default) or C-series system messages. Read system messages. The following paragraphs describe how to perform these operations.
Receiving System Messages Communicating With Processes Whether you choose to receive system messages affects the time at which the corresponding open in the requester finishes: • • • If the server opens $RECEIVE without requesting system messages, the requester’s open finishes as soon as the server has opened $RECEIVE.
Processing Open and Close System Messages Communicating With Processes Processing Open and Close System Messages Message number -103 (the Open message) is delivered to a process when another process tries to open the process (using the FILE_OPEN_, OPEN, or OPEN^FILE procedure).
Processing Control, Setmode, Setparam, and Controlbuf Messages Communicating With Processes Processing Control, Setmode, Setparam, and Controlbuf Messages Your process should accept the -32 (Control), -33 (Setmode), -37 (Setparam), -34 (Resetsync), and -35 (Controlbuf) messages only if the process is simulating an I/O device. In other words, some other process will issue CONTROL, SETMODE, SETPARAM, or CONTROLBUF procedure calls against this process as if the process were an I/O device.
Handling Errors Communicating With Processes Handling Errors For the $RECEIVE file, there are no error conditions for which error recovery should be attempted, except error 40 (operation timed out). For a process file opened with a sync depth greater than zero, there are no error conditions for which error recovery should be retried, except error 40.
Programming the Requester Communicating With Processes Create the database file using FUP commands as follows: 1> FUP -SET TYPE K -SET BLOCK 2048 -SET REC 135 -SET IBLOCK 2048 -SET KEYLEN 6 -SHOW TYPE K EXT ( 1 PAGES, 1 PAGES) REC 130 BLOCK 2048 IBLOCK 2048 KEYLEN 6 KEYOFF 0 MAXEXTENTS 16 -CREATE $APPL.SUBAPPL.RECFILE CREATED - $APPL.SUBAPPL.RECFILE -EXIT 2> Because the requester process opens the server by the name $SER1, you need to run the server process by this name.
Programming the Requester Communicating With Processes The MAIN procedure responds to the user’s selection by calling the appropriate procedure: • The READ^RECORD procedure allows the user to read one record followed optionally by subsequent sequential reads. It prompts the user for a part number and then sends the part number, along with a function code for an approximate read, to the server process.
Programming the Requester Communicating With Processes ?INSPECT, SYMBOLS, NOCODE ?NOLIST,SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?LIST LITERAL MAXFLEN = ZSYS^VAL^LEN^FILENAME; LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL OLD = NEW = BUFSIZE = PARTSIZE = DESCSIZE = SUPPSIZE = READ^APPROX= READ^EXACT = WRITE^ONE = UPDATE^ONE = READ^NEXT = 0; 1; 132; 6; 60; 60; 1; 2; 3; 4; 5; STRING .SBUFFER[0:BUFSIZE]; STRING .
Programming the Requester Communicating With Processes ?NOLIST, SOURCE $SYSTEM.SYSTEM.EXTDECS0 (INITIALIZER, ? PROCESS_GETINFO_,FILE_OPEN_,WRITEREADX,WRITEX,NUMIN, ? PROCESS_STOP_,READX,DNUMOUT,FILE_GETINFO_,DNUMIN) ?LIST !-----------------------------------------------------------! Here are a few DEFINEs to make it a little easier to format ! and print messages.
Programming the Requester Communicating With Processes !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when ! the file is not open, when there is no file number for it. ! FILE^ERRORS is used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Programming the Requester Communicating With Processes !-----------------------------------------------------------! This procedure writes a message on the terminal and checks ! for any error. If there is an error, it attempts to write ! a message about the error and the program is stopped. !-----------------------------------------------------------PROC WRITE^LINE(BUF,LEN); STRING .
Programming the Requester Communicating With Processes !-----------------------------------------------------------! Procedure to display a part record on the terminal !-----------------------------------------------------------PROC DISPLAY^RECORD; BEGIN PRINT^BLANK; ! Display part number: PRINT^STR("Part Number Is: " & PART^RECORD.PART^NUMBER FOR PARTSIZE); ! Display part description: PRINT^STR("Part Description: " & PART^RECORD.DESCRIPTION FOR PART^RECORD.
Programming the Requester Communicating With Processes !-----------------------------------------------------------! Procedure to prompt user for input to build a new record or ! update an existing record. When updating, an empty ! response (COUNT^READ=0) means to leave the existing value ! unchanged. !-----------------------------------------------------------PROC ENTER^RECORD(TYPE); INT TYPE; BEGIN INT COUNT^READ; INT STATUS; STRING .
Programming the Requester Communicating With Processes ! Prompt for a part description: SBUFFER ':=' "Enter Part Description: " -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); IF TYPE = NEW OR COUNT^READ > 0 THEN BEGIN COUNT^READ := $MIN(COUNT^READ,DESCSIZE); BLANK^FILL(REQUEST.PART.DESCRIPTION); REQUEST.PART.DESCRIPTION ':=' SBUFFER FOR COUNT^READ; REQUEST.PART.
Programming the Requester Communicating With Processes ! Prompt for the unit price: PROMPT^AGAIN1: SBUFFER ':=' "Enter Unit Price: $" -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); IF TYPE = NEW OR COUNT^READ > 0 THEN BEGIN SBUFFER[COUNT^READ] := 0; @NEXT^ADDR := NUMIN(SBUFFER,REQUEST.PART.
Programming the Requester Communicating With Processes !-----------------------------------------------------------! Procedure for reading records. The user selected function ! "r." The start of the read is selected by approximate key ! positioning. The user has the option of sequentially ! reading subsequent records.
Programming the Requester Communicating With Processes ! ! Prompt user to read more records.
Programming the Requester Communicating With Processes ! ! ! Prompt the user to read the next record (user must respond "y" to accept, otherwise return to select next function): SBUFFER ':=' ["Do you want to read another ", "record (y/n)? "] -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); SBUFFER[COUNT^READ] := 0; END UNTIL NOT (SBUFFER[0] = "y" OR SBUFFER[0] = "Y"); END; !--------------------------------------------------------
Programming the Requester Communicating With Processes ! ! ! Send the request to the server.
Programming the Requester Communicating With Processes ! ! ! ! Now that we have the user's changes, send a request to the server to have the file updated. The server uses the REQUEST.OLD information to determine if the record has been updated while the user was responding: REQUEST.
Programming the Requester Communicating With Processes !-----------------------------------------------------------! Procedure for inserting a record. The user selected ! function "i." The user is prompted to enter the new record. ! The procedure inserts the new record in the appropriate ! place in the file. !-----------------------------------------------------------PROC INSERT^RECORD; BEGIN INT ERROR; PRINT^BLANK; ! Set the REQUEST^FUNCTION: REQUEST.
Programming the Requester Communicating With Processes !-----------------------------------------------------------! Procedure to exit the program. !-----------------------------------------------------------PROC EXIT^PROGRAM; BEGIN CALL PROCESS_STOP_; END; !-----------------------------------------------------------! Procedure to process an invalid command. The procedure ! informs the user that the selection was other than "r," ! "u", "a," or "x.
Programming the Requester Communicating With Processes !-----------------------------------------------------------! This procedure does the initialization for the program. ! It calls INITIALIZER to dispose of the startup messages. ! It opens the home terminal and the data file used by the ! program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .SERVER^NAME[0:MAXFLEN - 1]; INT SERVERLEN; STRING .
Programming the Requester Communicating With Processes !-----------------------------------------------------------! This is the main procedure. It calls the INIT procedure to ! initialize, then it goes into a loop calling GET^COMMAND ! to get the next user request and calling the procedure ! to carry out that request.
Programming the Server Communicating With Processes Programming the Server The server program reads and processes messages that arrive in the $RECEIVE file. Each message contains a function code that the server process uses to determine what action to take. If the action involves writing a record to the database, then the message also contains the database record to be written.
Programming the Server Communicating With Processes it with the current record value. It does this to ensure that the record was not changed while the user was entering the new data. • The WRITE^RECORD procedure is called when the requester process sets the function code to insert a new record. The procedure extracts the database record from the incoming message and writes it to the database file.
Programming the Server Communicating With Processes ?INSPECT,SYMBOLS,NOMAP,NOCODE ?NOLIST,SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?LIST LITERAL MAXFLEN = ZSYS^VAL^LEN^FILENAME; LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL LITERAL OLD = 0; NEW = 1; BUFSIZE = 132; PARTSIZE= 6; DESCSIZE= 60; SUPPSIZE= 60; READ^APPROX= 1; READ^EXACT = 2; WRITE^ONE = 3; UPDATE^ONE = 4; READ^NEXT = 5; STRING .SBUFFER[0:BUFSIZE]; STRING .
Programming the Server Communicating With Processes ?NOLIST, SOURCE $SYSTEM.SYSTEM.EXTDECS0 (INITIALIZER, ? PROCESS_GETINFO_,FILE_OPEN_,WRITEREADX,WRITEX,REPLYX, ? KEYPOSITION,PROCESS_STOP_,READX,FILE_GETINFO_, ? READUPDATEX,READUPDATELOCKX,WRITEUPDATEUNLOCKX, ? FILE_GETINFOLIST_,UNLOCKREC,DNUMOUT) ?LIST !-----------------------------------------------------------! Here are a few DEFINEs to make it a little easier to format ! and print messages.
Programming the Server Communicating With Processes !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when ! the file is not open, when there is no file number for it. ! FILE^ERRORS is used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Programming the Server Communicating With Processes !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and error number are determined from the file number ! and FILE^ERRORS^NAME is then called to display the ! information. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
Programming the Server Communicating With Processes !-----------------------------------------------------------! This procedure reads one record from the data file. ! It is invoked in response to a READ^APPROX request issued ! by a requester process. !-----------------------------------------------------------PROC READ^APPROX^RECORD; BEGIN INT COUNT^READ; INT ERROR; ! Position approximately to the selected record: CALL KEYPOSITION(PARTFILE^NUM, REQUEST.PART.
Programming the Server Communicating With Processes !-----------------------------------------------------------! This procedure reads one record from the data file. ! It is invoked in response to a READ^EXACT request issued by ! a requester process, as the first phase of a record update. !-----------------------------------------------------------PROC READ^EXACT^RECORD; BEGIN INT COUNT^READ; INT ERROR; ! Position exactly to the selected record: CALL KEYPOSITION(PARTFILE^NUM, REQUEST.PART.
Programming the Server Communicating With Processes !-----------------------------------------------------------! Procedure for updating a record. The procedure first ! reads the record from the data file and checks it against ! the original value received by the requester to make sure ! it has not been updated by another user. ! The procedure then writes the updated record to the file.
Programming the Server Communicating With Processes ! Write the new record to the file: CALL WRITEUPDATEUNLOCKX(PARTFILE^NUM,REQUEST.PART, $LEN(PART^RECORD)); IF <> THEN BEGIN CALL FILE_GETINFO_(PARTFILE^NUM,REPLY^ERROR); RETURN; END; REPLY^ERROR := 0; END; !-----------------------------------------------------------! Procedure to read the next record from the data file. ! The requester supplied the part number of the last ! record read.
Programming the Server Communicating With Processes !-----------------------------------------------------------! This procedure does the initialization for the program. ! It calls INITIALIZER to dispose of the startup messages. ! It opens the home terminal and the data file used by the ! program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .PARTFILE^NAME[0:MAXFLEN - 1]; !name of part file INT PARTFILE^LEN; !length of part-file ! name STRING .
Programming the Server Communicating With Processes ERROR := FILE_OPEN_(RECV^NAME:RECVLEN,RECV^NUM, !access!, !exclusion!, !nowait^depth!, RECV^DEPTH,OPTIONS); IF ERROR <> 0 THEN CALL FILE^ERRORS^NAME(RECV^NAME:RECV^NUM,ERROR); ! Open the part file with a sync depth of 1: PARTFILE^NAME ':=' "=PARTFILE" -> @S^PTR; PARTFILE^LEN := @S^PTR '-' @PARTFILE^NAME; SYNCH^DEPTH := 1; ERROR := FILE_OPEN_(PARTFILE^NAME:PARTFILE^LEN, PARTFILE^NUM, !access!, !exclusion!, !nowait^depth!, SYNCH^DEPTH); IF ERROR <> 0 THE
Programming the Server Communicating With Processes !-----------------------------------------------------------! This is the main procedure. It calls the INIT procedure to ! initialize, then it goes into a loop calling GET^COMMAND ! to get the next user request and calling the procedure ! to carry out that request.
7 Using DEFINEs DEFINEs are file-system elements that provide a means for passing information to a process. For example, you can use DEFINEs to pass attributes to a process to provide: • • • • An alternate name for accessing a file A list of subvolumes to search for a file name A simple way to set up attributes for labeled-tape processing A simple means of passing attributes to the spooler subsystem You can make use of DEFINEs interactively using TACL, or you can work with DEFINEs programmatically.
Example Uses for DEFINEs Using DEFINEs Section 16, Creating and Managing Processes, also contains additional information about DEFINEs. It describes how to use the PROCESS_LAUNCH_ procedure to control how DEFINEs are passed from one process to another during process creation. For complete details of the syntax of all DEFINE-related procedure calls, see the Guardian Procedure Calls Reference Manual. Example Uses for DEFINEs DEFINEs allow attributes to be grouped and named.
Example of a CLASS MAP DEFINE Using DEFINEs SUBVOL20 RELSUBVOL20 The name of the DEFINE can then be used in a call to the FILENAME_RESOLVE_ procedure to provide a search list for a program file you want to execute, or it can be used to provide a search list for locating component source files when compiling a program. If any attribute is a list, the search order is from left to right within the list. CLASS TAPE DEFINEs contain attribute information for use with labeled magnetic tapes.
Example of a CLASS SEARCH DEFINE Using DEFINEs FILENUM); IF ERROR <> 0 THEN ... Example of a CLASS SEARCH DEFINE The following example sets up attributes for a DEFINE named =_PROGRAM_SEARCH using procedures DEFINESETATTR and DEFINEADD. Later, the program calls the FILENAME_RESOLVE_ procedure to find the name within the search paths set up by the search DEFINE. FILENAME_RESOLVE_ returns the fully qualified file name.
Example of a CLASS TAPE DEFINE Using DEFINEs Example of a CLASS TAPE DEFINE The following example sets up a DEFINE called =ANSITAPE1. When the DEFINE is later passed to the FILE_OPEN_ procedure, the corresponding tape file is opened with the DEFINE attributes automatically set. ATTRIBUTE^NAME ':=' "CLASS "; ATTRIBUTE^VALUE ':=' "TAPE"; ATTRIBUTE^LENGTH := 4; ERROR := DEFINESETATTR(ATTRIBUTE^NAME, ATTRIBUTE^VALUE, ATTRIBUTE^LENGTH, DEFAULT^NAMES); IF ERROR <> 0 THEN ...
CLASS DEFAULTS DEFINEs Using DEFINEs (Although swap space is handled by the Kernel-Managed Swap Facility (KMSF), a swap file name can still be passed in the =_DEFAULTS DEFINE. This feature supports programs that use the swap file name to determine the volume on which to create temporary files.) The following example sets up the =_DEFAULTS DEFINE with the subvolume $OURVOL.ACCOUNTS.
DEFINE Names Using DEFINEs DEFINE Names You have seen DEFINE names like =MYFILE and =ANSITAPE1 in the previous subsection. You specify the name when creating the DEFINE, and you use the name to subsequently identify the DEFINE. (The above examples show DEFINE creation using the DEFINEADD procedure.) A DEFINE name must conform to the following rules: • • • • The name must be 2 through 24 characters long. The first character must be an equal sign (=). The second character must be a letter.
DEFINE Attributes Using DEFINEs the Spooler Plus Utilites Reference Manual for detailed information about the CLASS SPOOL DEFINE and its attributes. • • You can use a CLASS SEARCH DEFINE name as a parameter to the FILENAME_RESOLVE_ procedure described in Section 13, Manipulating File Names. You can use a CLASS SORT DEFINE name to specify sort parameters interactively using the RUN FASTSORT command or programmatically as a parameter to the SORTBUILDPARAM procedure.
Attribute Values Using DEFINEs filename The attribute can contain a file name. The file name can be fully or partially qualified. A partially qualified file name is expanded by the file system using the volume and subvolume that you specify as a parameter to the DEFINESETATTR procedure. subvolnam e The attribute can contain a subvolume name. The subvolume name can be fully or partially qualified.
Working With DEFINEs Using DEFINEs The CLASS attribute determines what other attributes are associated with the DEFINE. The CLASS attribute has a data type of keyword. When assigning values to DEFINE attributes, you must assign one of these values to the CLASS attribute first. Assigning a value to the CLASS attribute causes default values to be assigned to other attributes in that DEFINE CLASS.
Referring to DEFINEs Using DEFINEs By default, DEFINE mode is turned on, so it is normally not necessary to enable DEFINE mode. If DEFINE mode is not enabled, however, you can turn it on as described below. Note. DEFINE mode, like DEFINEs themselves, is propagated from the parent process. If DEFINE mode is turned on in the creator process, then it is also turned on in the child process, unless the parent process specifically requested something else.
Setting Attributes in the Working Set Using DEFINEs Setting Attributes in the Working Set The primary method for setting attributes in the working set is the DEFINESETATTR procedure. The DEFINESETLIKE and DEFINERESTORE procedures, however, also have the effect of setting working set attributes. The following paragraphs describe each of these procedures. Setting Attributes Using the DEFINESETATTR Procedure The DEFINESETATTR procedure assigns a value to an individual attribute in the working set.
Checking the Working Set for Errors Using DEFINEs When you have assigned a value to an attribute, the system verifies that the value you specified matches the data type of the attribute. The procedure returns an error if the value does not match the data type. Setting Attributes Using the DEFINESETLIKE Procedure For any DEFINE that already exists in the context of the current process, you can initialize the attributes of the working set to the values of the attributes of that DEFINE.
Adding a DEFINE to the Context of Your Process Using DEFINEs • • • 2057 indicates that the working set is incomplete: that is, a required parameter for the DEFINE CLASS is missing. 2058 indicates that the working set is inconsistent; CHECK^NUM qualifies the inconsistency. For more information about CHECK^NUM, refer to the Guardian Procedure Errors and Messages Manual. 2059 indicates that the working set is invalid.
Deleting DEFINEs From the Process Context Using DEFINEs ERROR := DEFINEADD(DEFINE^NAME,REPLACE,CHECK^NUM); CASE ERROR OF BEGIN . . Deleting DEFINEs From the Process Context You can delete DEFINEs from the context of a process using the DEFINEDELETE or DEFINEDELETEALL procedure. The DEFINEDELETE procedure deletes a specific DEFINE, for example: DEFINE^NAME ':=' "=TAPE1 ERROR := DEFINEDELETE(DEFINE^NAME); IF ERROR <> 0 THEN ...
Saving and Restoring the Working Set Using DEFINEs LITERAL ADD^DEFINE = 0, CHG^DEFINE = 1; . . DEFINE^NAME ':=' "=TAPE1 ERROR := DEFINERESTORE(TAPE^BUFFER, ADD^DEFINE, DEFINE^NAME, CHECK^NUM); IF ERROR = 2058 THEN ... ELSE IF ERROR <> 0 THEN ... DEFINE^NAME ':=' "=TAPE2 ERROR := DEFINERESTORE(TAPE^BUFFER2, CHG^DEFINE, DEFINE^NAME, CHECK^NUM); IF ERROR = 2058 THEN ... ELSE IF ERROR <> 0 THEN ... "; "; The first of the two calls shown above adds the DEFINE to the context of the current process.
Using DEFINEs: An Example Using DEFINEs The following example uses DEFINESAVEWORK2 to save the attribute values stored in the current working set. This procedure copies the attributes in the working set to the background set: ERROR := DEFINESAVEWORK2; IF ERROR <> 0 THEN ... Any attributes that were stored in the background set are destroyed. The next example restores the background set into the working set using the DEFINERESTOREWORK2 procedure: ERROR := DEFINERESTOREWORK2; IF ERROR <> 0 THEN ...
Using DEFINEs: An Example Using DEFINEs To add another class of DEFINE, you need to add a procedure, similar to the SET^TAPE procedure, that prompts the user to enter attribute values for the new DEFINE CLASS. You will also need to make changes to the GET^DEFINE^CLASS and CREATE^DEFINES procedures as indicated in the following pages. ?INSPECT,SYMBOLS,NOMAP,NOCODE ?NOLIST, SOURCE $SYSTEM.ZSYSDEFS.
Using DEFINEs: An Example Using DEFINEs !-------------------------------------------------------! Here are some DEFINEs to make it easier to format and ! print messages.
Using DEFINEs: An Example Using DEFINEs !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when ! the file is not open, so there isn't a file number for it. ! FILE^ERRORS is to be used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Using DEFINEs: An Example Using DEFINEs !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and error number are determined from the file number ! and FILE^ERRORS^NAME is then called to do the display. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
Using DEFINEs: An Example Using DEFINEs !---------------------------------------------------------! Procedure to print DEFINE errors on the terminal.
Using DEFINEs: An Example Using DEFINEs ! Display the error description on the terminal: CALL WRITEX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER); IF <> THEN CALL FILE^ERRORS(TERMNUM); END; !----------------------------------------------------------! This procedure writes a message on the terminal and checks ! for any error. If there is an error, it attempts to write ! a message about the error and the program is stopped.
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! This procedure asks the user for the CLASS of the next ! DEFINE: ! ! "1" for a CLASS MAP DEFINE ! "2" for a CLASS SEARCH DEFINE ! "3" for a CLASS TAPE DEFINE ! "4" for a CLASS SPOOL DEFINE ! "5" for a CLASS SORT DEFINE ! "6" for a CLASS SUBSORT DEFINE ! "7" for a CLASS CARALOG DEFINE ! ! The selection made is returned as the result of the call.
Using DEFINEs: An Example Using DEFINEs !----------------------------------------------------------! Procedure to prompt the user for a DEFINE attribute and ! call DEFINESETATTR if the user provides the attribute. !----------------------------------------------------------PROC DEFINE^ATTR(ATTR^NAME,VALUES^LIST,VALUES^LEN); STRING .ATTR^NAME; STRING .VALUES^LIST; INT VALUES^LEN; BEGIN INT INT INT ! .
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! Procedure to prompt the user for all attributes for a CLASS ! MAP DEFINE. !-----------------------------------------------------------PROC SET^MAP; BEGIN STRING .VALUES^LIST[0:BUFSIZE - 1]; STRING .
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! Procedure to prompt the user for all attributes for a CLASS ! SEARCH DEFINE. !-----------------------------------------------------------PROC SET^SEARCH; BEGIN STRING .VALUES^LIST[0:BUFSIZE - 1]; STRING .
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! Procedure to prompt the user for all attributes for a CLASS ! TAPE DEFINE. !-----------------------------------------------------------PROC SET^TAPE; BEGIN STRING .VALUES^LIST[0:BUFSIZE - 1]; STRING .
Using DEFINEs: An Example Using DEFINEs NAME ':=' "FILESEQ "; VALUES^LIST ':=' "A number in the range 0001 through 9999" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "GEN "; VALUES^LIST ':=' "A number in the range 0001 through 9999" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "LABELS "; VALUES^LIST ':=' "ANSI, IBM, OMITTED, or BYPASS" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "MOUNTMSG "; VAL
Using DEFINEs: An Example Using DEFINEs NAME ':=' "USE "; VALUES^LIST ':=' "IN, OUT, EXTEND, or OPENFLAG" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "VERSION "; VALUES^LIST ':=' "A number in the range 00 through 99" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "VOLUME "; VALUES^LIST ':=' "A six-byte volume ID or SCRATCH" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); END; Guardian Programmer’s Guide — 421
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! Procedure to prompt the user for all attributes for a CLASS ! SPOOL DEFINE. !-----------------------------------------------------------PROC SET^SPOOL; BEGIN STRING .VALUES^LIST[0:BUFSIZE - 1]; STRING .
Using DEFINEs: An Example Using DEFINEs NAME ':=' "MAXPRINTLINES "; VALUES^LIST ':=' "A number in the range 1 through 65534" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "MAXPRINTPAGES "; VALUES^LIST ':=' "A number in the range 1 through 65534" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "OWNER "; VALUES^LIST ':=' "Any valid owner ID" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "PAGESIZE "; VAL
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! Procedure to prompt the user for all attributes for a CLASS ! SORT DEFINE. !-----------------------------------------------------------PROC SET^SORT; BEGIN STRING .VALUES^LIST[0:BUFSIZE - 1]; STRING .
Using DEFINEs: An Example Using DEFINEs NAME ':=' "PROGRAM "; VALUES^LIST ':=' "Any valid program file name" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "SCRATCH "; VALUES^LIST ':=' "Any valid disk volume name" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "SEGMENT "; VALUES^LIST ':=' "A number 64 or greater" -> @S^PTR; CALL DEFINE^ATTR(NAME,VALUES^LIST, @S^PTR '-' @VALUES^LIST); NAME ':=' "SUBSORTS "; VALUES^LIST ':=' "A comma-sepa
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! Procedure to prompt the user for all attributes for a CLASS ! SUBSORT DEFINE. !-----------------------------------------------------------PROC SET^SUBSORT; BEGIN STRING .VALUES^LIST[0:BUFSIZE - 1]; STRING .
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! Procedure to prompt the user for all attributes for a CLASS ! CATALOG DEFINE. !-----------------------------------------------------------PROC SET^CATALOG; BEGIN STRING .VALUES^LIST[0:BUFSIZE - 1]; STRING .
Using DEFINEs: An Example Using DEFINEs !----------------------------------------------------------! Procedure to create DEFINEs. The procedure prompts the ! user for the DEFINE CLASS, then for each attribute that a ! DEFINE of the selected CLASS can have. When the attributes ! have all been entered, the procedure checks their validity ! and creates the DEFINE. !----------------------------------------------------------PROC CREATE^DEFINES; BEGIN STRING .
Using DEFINEs: An Example Using DEFINEs BEGIN CALL DEFINE^ERRORS(ERROR); GOTO DO^AGAIN; END; ! Prompt the user for the DEFINE name: REENTER^NAME: PRINT^BLANK; SBUFFER ':=' "Please Enter a Name for the DEFINE: " ->@S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); IF SBUFFER[0] <> "=" THEN BEGIN PRINT^STR("First Character Must Be '='"); GOTO REENTER^NAME; END; IF COUNT^READ > 24 THEN BEGIN PRINT^STR ("Maximum DEFINE name length 24 chara
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! This procedure does the initialization for the program. ! It calls INITIALIZER to dispose of the startup messages. ! It opens the home terminal and the data file used by the ! program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .
Using DEFINEs: An Example Using DEFINEs !-----------------------------------------------------------! This is the main procedure. It calls the INIT procedure to ! initialize, then it goes into a loop calling GET^COMMAND ! to get the next user request and calling the procedure ! to carry out that request.
8 Communicating With a TACL Process This section discusses how processes communicate with TACL processes, including: • • • • • • How to set up the process environment by using commands at the TACL prompt. How the process environment information is passed to the application using command-interpreter messages. These messages include the Startup, Assign, and Param messages. They are part of a different set of messages than the system messages discussed in Section 6, Communicating With Processes.
Communicating With a TACL Process Setting Up the Process Environment The following TACL commands can affect the parameter information sent to a new process: ASSIGN Makes logical-file assignments. A logical-file assignment equates a file name with a logical file of a program and optionally assigns file characteristics to that file. For each ASSIGN in effect when the program is run, one Assign message is sent to the new process at the option of the new process. CLEAR Clears ASSIGN and PARAM settings.
Communicating With a TACL Process Obtaining Startup Information process (command-interpreter message code -3). This message contains ASCII parameter values set up by the PARAM command. 5. The system sends a system message number -104 (Close message) when the TACL process closes the new process. This message indicates that the TACL process has completed its communication with the new process. The new process can read the above messages from its $RECEIVE file by calling the INITIALIZER procedure.
Obtaining Startup Information Communicating With a TACL Process The structure of the Startup message is shown below: Structure of the Startup message: STRUCT CI^STARTUP; BEGIN INT MSGCODE; STRUCT DEFAULT; BEGIN INT VOLUME[0:3]; name INT SUBVOL[0:3]; END; STRUCT BEGIN INT INT INT END; INFILE; STRUCT BEGIN INT INT INT END; OUTFILE; VOLUME[0:3]; SUBVOL[0:3]; FNAME[0:3]; VOLUME[0:3]; SUBVOL[0:3]; FNAME[0:3]; STRING PARAM[0:n-1]; (if END; !word 0 – value -1 !word 1 – default volume !default subvolum
Obtaining Startup Information Communicating With a TACL Process The input and output file names in the Startup message have the same structure as file names on a C-series system. To convert these names to D-series format, you can use the OLDFILENAME_TO_FILENAME_ procedure. • A parameter string containing any parameters supplied to the RUN command. The parameter string contains exactly the same characters as contained in the parameter string to the RUN command, plus a null byte to terminate the string.
Communicating With a TACL Process Using INITIALIZER to Read the Startup Message Using INITIALIZER to Read the Startup Message It is up to the application to choose what to do with the information contained in the Startup message. To use the startup information, your program must first read the Startup message from its $RECEIVE file. The INITIALIZER procedure provides a convenient way to do this.
Processing the Startup Message Communicating With a TACL Process The main procedure can then access the Startup information in the global data area and open the IN and OUT files identified in the Startup message. Recall, however, that theses file names are in C-series format and must be converted to D-series format using the OLDFILENAME_TO_FILENAME_ procedure before you can pass these names to the FILE_OPEN_ procedure. ? INSPECT, SYMBOLS !Global variables: STRUCT .
Using ASSIGNs and PARAMs Communicating With a TACL Process START^IT); END; Using ASSIGNs and PARAMs To use the values provided with ASSIGN or PARAM commands, a program must specifically request to receive the Assign and Param messages. You can use the INITIALIZER procedure to request this information.
Using ASSIGNs and PARAMs Communicating With a TACL Process The structure of the Assign message follows.
Using ASSIGNs and PARAMs Communicating With a TACL Process Suppose a program logically refers to a process as SERVER1, then the following ASSIGN command associates this logical name with the actual server name $SER1: 1> ASSIGN SERVER1,$SER1 The Assign message received by the program is as follows: VST041.VSD Note. In the figure above, the empty boxes all represent blank characters.
Using ASSIGNs and PARAMs Communicating With a TACL Process All parameter values assigned using PARAM commands are delivered to the starting process in one Param message: Structure of a Param message: STRUCT CI^PARAM; BEGIN INT MSG^CODE; INT NUMPARAMS; !word 0 – value -3 !word 1 – number of ! parameters in this message STRING PARAMETERS[0:1023]; END; !word 2 – the parameters The structure of each parameter in the PARAMETERS field: param[0] param[1] FOR n param[n+1] param[n+2] FOR v = “n,” length in b
Communicating With a TACL Process Using INITIALIZER to Read Assign and Param Messages Using INITIALIZER to Read Assign and Param Messages To request the TACL process to send all Assign and Param messages, your process can again use the INITIALIZER procedure. In addition to reading the Startup message, the INITIALIZER procedure reads the Assign and Param messages if bit 0 of the flags parameter is set to zero (the default setting).
Processing Assign Messages Communicating With a TACL Process 6. INITIALIZER returns control to the main procedure. ? INSPECT, SYMBOLS !Global variables: INT SERVER1[0:11]; INT SERVER2[0:11]; ? ? ? ? !Names of two server ! processes NOLIST SOURCE $SYSTEM.SYSTEM.EXTDECS0(INITIALIZER) DATAPAGES = 2 LIST PROC ASSIGN^NAME(RUCB, ASSIGN^DATA, MESSAGE, LENGTH, MATCH) VARIABLE; INT .RUCB, .ASSIGN^DATA, .MESSAGE, LENGTH, MATCH; BEGIN STRING .
Processing the Param Message Communicating With a TACL Process Processing the Param Message This example requests, reads, and processes a Param message. Here, the process expects only one PARAM named P1. Again two user procedures perform the processing as follows: 1. The main procedure calls INITIALIZER and supplies it with the name of the procedure that processes Param messages. 2. The INITIALIZER procedure reads the Startup message and any Assign messages. 3.
Communicating With a TACL Process ! Processing the Param Message Point to the first parameter: @PTR := @MESSAGE[2] '<<'1; ! Set global value to false to indicate no P1 found yet: PARAM1^PRESENT := 0; ! Loop for each parameter until P1 found: FOR I := 1 TO NUMPAR DO BEGIN ! If length and name match then P1 found: IF PTR = 2 AND PTR[1] = "P1" THEN BEGIN ! ! Advance the pointer to the value for P1: and save the value for P1 in the global PARAM1: @PTR := @PTR '+' PTR '+' 1; PARAM1 ':=' PTR[1] FOR PTR;
Communicating With a TACL Process Setting a Timeout Value for INITIALIZER Setting a Timeout Value for INITIALIZER Normally, INITIALIZER waits 60 seconds for a startup sequence message to arrive on $RECEIVE. If no message is received in that time, INITIALIZER times out, assuming that the sending process has either terminated or is not going to send a startup sequence.
Communicating With a TACL Process Reading the Startup Sequence Without INITIALIZER To receive Param messages, bit 1 of the first byte must be 1. All other bits must be 0. If you do not reply to the Startup message or if you reply with some other reply code, then your process will receive no further startup sequence messages. 4. Read and process each Assign message if there are any and reply to each Assign message with a reply code of 0. 5.
Communicating With a TACL Process Reading the Startup Sequence Without INITIALIZER CASE RCV^BUF OF BEGIN -1 -> BEGIN ! Process Startup message . . REPLY^CODE := 70; END; -2 -> BEGIN ! Process Assign message . . REPLY^CODE := 0; END; -3 -> BEGIN ! Process Param message . .
Waking the TACL Process Communicating With a TACL Process Waking the TACL Process The Wakeup message can be sent by an application process to the TACL process to cause the TACL process to return to command-input mode. In command-input mode, TACL prompts for commands.
Causing the TACL Process to Display Text Communicating With a TACL Process STRING .CI^NAME[0:MAXLEN - 1]; ! !CI process name Set up the correct message code: WAKEUP^MSG.MSGCODE := -20; ! Open the TACL process: CI^NAME ':=' "$G55"; LENGTH := 4; ERROR := FILE_OPEN_(CI^NAME:LENGTH,CI^NUM); IF ERROR <> 0 THEN CALL DEBUG; ! Write the Wakeup message to the TACL process: CALL WRITE(CI^NUM,WAKEUP^MSG,2); IF <> THEN CALL DEBUG; END; Note.
Causing the TACL Process to Display Text Communicating With a TACL Process To make the TACL process display text, you must first open the TACL process as you would any process, then write the Display message to the open file number. The following example shows how: ?INSPECT, SYMBOLS ?NOLIST ?SOURCE $SYSTEM.SYSTEM.EXTDECS(FILE_OPEN_,WRITE, ? DEBUG) ?LIST PROC EGCI MAIN; BEGIN LITERAL MAXLEN = 256; STRUCT .DISPLAY^MSG; BEGIN INT MSGCODE; STRING TEXT[0:131]; END; INT LENGTH; INT CI^NUM; STRING .
9 Communicating With Devices In addition to introducing some of the major features of the I/O subsystem, this section discusses mechanisms that your program can use to communicate with devices in general.
Addressing Devices Communicating With Devices Figure 9-1. Overview of the I/O Subsystem VST043.VSD Addressing Devices At system-generation time, each device is given a device name and a device number. Refer to the appropriate system generation manual for details on how these names and numbers are assigned. Recall from Section 2, Using the File System, that you can use either the device name or the logical device number to identify a device: [node-name.
Accessing Devices Communicating With Devices So how do you know the name of the device you want your program to communicate with? It depends on the device in question.
Controlling Devices Communicating With Devices Controlling Devices You use the following procedures to control devices: • • • • • CONTROL performs device-specific I/O operations like sending a form feed to a printer or terminal or rewinding a magnetic tape. CONTROLBUF is similar to CONTROL in that it specifies operations to be performed on a device. CONTROLBUF, however, also enables buffered data to be passed to a device.
Getting Device Information Communicating With Devices Getting Device Information On D-series systems, you can obtain detailed information about devices either by supplying a logical device number to the DEVICE_GETINFOBYLDEV_ procedure or by supplying a device name to the DEVICE_GETINFOBYNAME_ procedure. DEVICE_GETINFOBYLDEV_ also has the option of searching for the next logical device number above the one specified.
Getting Device Information Communicating With Devices !Data structure for returned logical device information: STRUCT .
Getting Device Information Communicating With Devices STRUCT BEGIN INT INT INT INT INT END; STRUCT BEGIN INT INT INT INT INT END; END; PATH2; !substructure for path 2 FLAGS; CHANNEL; CONTROLLER; UNIT; STATE; PATH3; !substructure for path 3 FLAGS; CHANNEL; CONTROLLER; UNIT; STATE; !Variables for DEVICE_GETINFOBYLDEV_ procedure: INT ERROR, LOGICAL^DEVICE, !logical device number .
Communicating With Devices Additional Device Information (G-series Only) !Set the search option, starting from logical device !number 0: OPTIONS.
Additional Device Information (G-series Only) Communicating With Devices The following C program shows how the CONFIG_GETINFO_BYNAME2_ procedure can be used to obtain device information for an arbitrary process, in this case "$EXMPL." This program assumes that the device $EXMPL provides a type for its device-specific information in "example.h" called example_specific_info_type.
Additional Device Information (G-series Only) Communicating With Devices To support the interface for CONFIG_GETINFO_BYNAME2_ procedure or CONFIG_GETINFO_BYLDEV2_ procedure, a device must be coded to handle a new system message from $RECEIVE (-147: ZSYS_VAL_SMSG_CONFIGINFO). The format of this system message is defined in zsysc by the zsys_ddl_smsg_configinfo2_def type. The reply from a device that supports these procedures is expected to have a zsys_ddl_smsg_confinf_reply_def format.
Communicating With Devices Additional Device Information (G-series Only) ); if(_status_ne(cc)) { short err; FILE_GETINFO_(g_recv_fnum, &err); printf("Warning! Error %d on reply to configinfo sysmsg\n",err); } return; } break; } } Guardian Programmer’s Guide — 421922-014 9 - 11
10 Communicating With Terminals This section describes how an application process can communicate with a terminal using file-system procedure calls. The file system can communicate with any terminal whose characteristics can be defined to the system through one of the programs that can configure devices, such as the system-generation program, SYSGEN. Refer to the System Generation Manual for Terminals and Printers.
Accessing a Terminal Communicating With Terminals You access a terminal the same way as you would any file, by using procedure calls to the file system. You use the following procedure calls to perform the indicated tasks with terminals: AWAITIO[X] Waits for completion of outstanding I/O operations that are pending on the open terminal. CANCEL Cancels the oldest outstanding operation on an open terminal. CANCELREQ Cancels a specified operation on an open terminal.
Accessing a Terminal Communicating With Terminals Table 10-1 summarizes all CONTROL operations that affect terminal operation. Table 10-1. Terminal CONTROL Operations CONTROL Number Operation 1 Provides forms control 11 Specifies a wait for a modem connection 12 Disconnects a modem On return from one of the calls listed in Table 10-1, the condition code should be CCE if the CONTROL operation was successful. A condition code of CCL indicates an error.
Opening a Terminal Communicating With Terminals On return from a call to SETMODE for one of the calls listed in Table 10-2, the condition code should be CCE if the function was performed successfully. A condition code of CCL indicates an error. A condition code of CCG indicates that the attempted SETMODE function is invalid for the type of device.
Opening a Terminal Communicating With Terminals INT INT END; STRUCT BEGIN INT INT INT END; STRUCT BEGIN INT INT INT END; STRING END; ! VOLUME[0:3]; SUBVOLUME[0:3]; INFILE; VOLUME[0:3]; SUBVOL[0:3]; FNAME[0:3]; OUTFILE; VOLUME[0:3]; SUBVOL[0:3]; FNAME[0:3]; PARAM[0:529]; Convert the C-series file name into a D-series file name: ERROR := OLDFILENAME_TO_FILENAME_(CI^STARTUP.
Transferring Data Between Application and Terminal Communicating With Terminals Opening the Home Terminal To open the home terminal (the terminal that starts the application), you can find out the name of the terminal using the PROCESS_GETINFO_ procedure. The PROCESS_GETINFO_ procedure returns the terminal name and the name length. You pass both of these parameters to the FILE_OPEN_ procedure.
Transferring Data Between Application and Terminal Communicating With Terminals Here, the application process reads up to BUFSIZE (the size of SBUFFER) characters into the buffer and returns the number actually read in the variable COUNT^READ. On issuing the READX procedure call, the process waits for the read operation to finish, which is an indefinite time.
Timing Out Terminal Response Communicating With Terminals SBUFFER ':=' [%33,"a",%21] -> @S^PTR; WCOUNT := @S^PTR '-' @SBUFFER; CALL WRITEREADX(TERMNUM, SBUFFER, WCOUNT, BUFSIZE, COUNT^READ); IF <> THEN ... After the WRITEREADX procedure finishes, SBUFFER contains the seven-character cursor address and 7 is returned in COUNT^READ. Timing Out Terminal Response Operations with terminals require human response and therefore can take an indefinite time.
Echoing Text to the Terminal Communicating With Terminals Echoing Text to the Terminal When a user types text at a terminal, the text usually appears on the screen as it is typed; that is, the text is echoed. Sometimes it is useful, however, for text to be hidden; for example, when typing in a password. For terminals that are operating in conversational mode and have terminal echo mode configured at system-generation time, you can control whether text is echoed.
Terminating Terminal Access Communicating With Terminals MODE, CONVERSATIONAL); IF <> THEN ... To set page mode: LITERAL PAGE = 1; . . CALL SETMODE(TERMNUM, MODE, PAGE); IF <> THEN ... Conversational and page modes of operation are described in detail later in this section. Terminating Terminal Access You terminate access to a terminal as you would for any other file, either by stopping the process or by calling the FILE_CLOSE_ procedure: ERROR := FILE_CLOSE_(TERMNUM); IF ERROR <> 0 THEN ...
Using the Line-Termination Character Communicating With Terminals Using the Line-Termination Character A default line-termination character is configured for each terminal at systemgeneration time. It is usually a carriage return. You can set the line-termination character to any character you like using function 9 of the SETMODE procedure (see Setting the Interrupt Characters for Conversational Mode later in this subsection). An example explains how the line-termination mechanism works.
Setting the Interrupt Characters for Conversational Mode Communicating With Terminals If the user responds to the READX call by entering only a carriage return, then the contents of the application buffer remain unchanged, zero is returned in COUNT^READ, and the file system issues a line feed to the terminal: CR initial cursor position Recall that a read operation also terminates when the specified read-count is satisfied.
Communicating With Terminals Setting the Interrupt Characters for Conversational Mode Figure 10-2. Conversational-Mode Interrupt Characters—Default Values VST118.VSD Using the Backspace Character The backspace character permits the user to back up and then reenter one or more mistyped characters. The specific action involved depends on the type of terminal. Typically, on video terminals the cursor is backspaced one position for each backspace received.
Communicating With Terminals Setting the Interrupt Characters for Conversational Mode Using the End-of-File Character The end-of-file character permits a user to signal an application process that no more data will be entered. When the file system receives the end-of-file character, the current file operation is considered to be complete. No data is transferred into the application program’s buffer area, the count-read parameter returns 0, and the condition code indicator is set to CCG.
Setting the Interrupt Characters for Conversational Mode Communicating With Terminals The following example replaces the configured line-termination character with the line feed character. The other interrupt characters remain unchanged: LITERAL INTERRUPT^CHARACTERS = 9; . . PARAM1 ':=' [%10,%30]; PARAM2 ':=' [%31,%12]; CALL SETMODE(TERM^NUM, INTERRUPT^CHARACTERS, PARAM1, PARAM2); IF <> THEN ... The user now terminates each line with line feed instead of carriage return.
Controlling Forms Movement Communicating With Terminals Controlling Forms Movement The SETMODE and CONTROL procedures explicitly control forms movement in conversational mode. Controlling Spacing Use SETMODE function 6 to change between single spacing and no spacing when writing data to the terminal. In single spacing the system appends a carriage return/line feed sequence to each write operation.
Controlling Forms Movement Communicating With Terminals SBUFFER ':=' ["Denote blanks by b.",%015] -> @S^PTR; CALL WRITEX(TERMNUM, SBUFFER, @S^PTR ‘-’ @SBUFFER); IF <> THEN ... CALL DELAY(TWO^TENTHS^OF^SECOND); CALL SETMODE(TERMNUM, SET^SPACE, SPACE); IF <> THEN ... SBUFFER ':=' " / " -> @S^PTR; CALL WRITEX(TERMNUM, SBUFFER, @S^PTR '-' @SBUFFER); IF <> THEN ... The example prints the text “Denote blanks by b.” and then overstrikes the “b” with the slash character (/).
Communicating in Page Mode Communicating With Terminals However, in this case you must delay the application program to permit the forms movement to finish: DEFINE TWO^SECONDS = 200D; . . CALL WRITEX(TERMNUM,%014,1); IF <> THEN... CALL DELAY(TWO^SECONDS); The application suspends itself for two seconds after sending the form-feed character to the terminal. Communicating in Page Mode Normally, terminals operating in page mode store each character in display memory in the terminal as it is typed.
Using the Page-Termination Character Communicating With Terminals Figure 10-4. Page Transfer Mode VST045.VSD Using the Page-Termination Character A page-termination character is configured at system-generation time for each pagemode terminal. You can set the page-termination character to any character you like using function 9 of the SETMODE procedure (see Setting the Interrupt Characters for Page Mode below).
Setting the Interrupt Characters for Page Mode Communicating With Terminals 10 Communicating With Terminals Setting the Interrupt Characters for Page Mode Initially, the only valid interrupt character is the page-termination character. A shown in Figure 10-5, all four interrupt characters that apply to page mode are set to the configured page-termination character. These interrupt characters apply to each page-mode terminal when the terminal is first opened.
Setting the Interrupt Characters for Page Mode Communicating With Terminals Receipt of any interrupt character other than the configured page-termination character has the following effect: • • • The system considers the operation to be complete. The application program receives the page-termination character in the application buffer along with the page image (if any). The count-read parameter returned by the read operation includes the interrupt character.
Communicating With Pseudopolled Terminals Communicating With Terminals Now call SETMODE again, but to change the mode to page mode: CALL SETMODE(TERM^NUM,CHANGE^MODE,PAGE^MODE); IF <> THEN ... . . The terminal is now operating in page mode with all four interrupt characters set to carriage return (the configured default). Now call SETMODE again, this time to change the interrupt characters for page mode: CALL SETMODE(TERM^NUM, SET^INTCHARS, ETX^EOT,ETX^EOT); IF <> THEN ... . .
Managing the BREAK Key Communicating With Terminals The advantage of having the application program handle triggering is that only one word of buffer space is used while the user enters information. The buffer space is allocated after the user presses the ENTER key. (Terminals operating in normal page mode require that the entire system buffer space be allocated during the wait for a transfer to take place.) Here’s how application triggering works.
Managing the BREAK Key Communicating With Terminals simply checks the $RECEIVE file for system message -105 (Break-on-Device message). You can use BREAK in conversational or page mode.
Taking BREAK Ownership Communicating With Terminals To establish BREAK mode for the terminal, you need to use SETMODE function 12. Once in BREAK mode, only operations that are associated with BREAK are allowed to access the terminal. To interpret BREAK-related errors, check for error 110 (only BREAK access permitted) and error 111 (operation aborted because of BREAK). Any process using the same terminal as the TACL process or other process that handles the BREAK key must check for these errors.
Releasing BREAK Ownership Communicating With Terminals PARAM^ARRAY, PARAM^COUNT, LAST^PARAM^ARRAY, LAST^PARAM^COUNT); The first word of the last-param-array contains an internally defined integer that identifies the current owner of BREAK. The second word contains an indication of the BREAK mode. The last-param-count returns the length of the last-param-array value in bytes, and is always 8 for function 3.
Selecting BREAK Mode Communicating With Terminals 10 Communicating With Terminals Selecting BREAK Mode Although several processes may have access to a terminal, a process can gain exclusive access to that terminal when BREAK is pressed. Such a process is executing in BREAK mode. You establish BREAK mode at the same time you take BREAK ownership. When a process executes in BREAK mode, it can communicate with the terminal using only operations that have BREAK access.
Selecting BREAK Mode Communicating With Terminals Figure 10-8. BREAK Access Established After Pressing the BREAK Key VSD047.
Selecting BREAK Mode Communicating With Terminals If you establish BREAK access before pressing the BREAK key, then the terminal becomes accessible immediately after the BREAK key is pressed, as shown in Figure 10-9. Figure 10-9. BREAK Access Established Before Pressing the BREAK Key VST048.
Selecting BREAK Mode Communicating With Terminals Establishing BREAK Mode To establish BREAK mode, you must specify BREAK mode when enabling BREAK. Doing this tells the system to put the terminal into BREAK mode when the BREAK key is pressed. Once BREAK mode is established, only file operations having BREAK access are allowed access to the terminal. The following example enables BREAK.
Selecting BREAK Mode Communicating With Terminals Reestablishing Normal Access and Normal Mode Another call to SETMODE function 12 relinquishes BREAK access and BREAK mode, returning the terminal status to normal access and normal mode. You achieve this by setting parameter-1 and parameter-2 to zero: LITERAL NORMAL^ACCESS = 0, NORMAL^MODE = 0; . . CALL SETMODE(HOME^TERM^NUM, SET^ACCESS, NORMAL^MODE, NORMAL^ACCESS); All types of access are now permitted to the terminal.
Selecting BREAK Mode Communicating With Terminals !Global variables: STRING .HOME^TERM[0:MAXLEN -1], .RECV^FILE[0:7] := "$RECEIVE"; !terminal file name INT !terminal number !buffer for terminal I/O !max bytes read !count of bytes to write !file-system error number !number of bytes read !input to SETPARAM !length of PARAM^ARRAY !values for last owner !size of last-param-array !buffer for $RECEIVE ! messages !file number for $RECEIVE !computation variables HOME^TERM^NUM, .
Selecting BREAK Mode Communicating With Terminals ! Prompt the user to enter a string of characters: WCOUNT := 2; BUFFER ':=' "? "; RCOUNT := 128; CALL WRITEREAD(HOME^TERM^NUM,BUFFER,WCOUNT, RCOUNT,BYTES^READ); IF <> THEN BEGIN CALL FILE_GETINFO_(HOME^TERM^NUM,ERROR); CALL DEBUG; END; ! If the user enters "exit" then terminate: IF FIRST^BYTE = "exit" THEN BEGIN CALL SETMODE(HOME^TERM^NUM,BREAK^ACCESS,NORMAL^MODE, BREAK^ACCESS^OFF); LAST^PARAM^COUNT := 8; CALL SETPARAM(HOME^TERM^NUM,SET^BREAK^FUNCTION,
Selecting BREAK Mode Communicating With Terminals !-----------------------------------------------------------! Main procedure does computation without terminal ! interaction. The procedure checks $RECEIVE periodically ! for a Break-on-Device message and then calls BREAK^IT to ! process the message.
Selecting BREAK Mode Communicating With Terminals ! Loop indefinitely, checking for Break-on-Device message: WHILE 1 = 1 DO BEGIN ! Issue a nowait read on $RECEIVE: CALL READ(RECV^NUM,RECV^BUF,132); ERROR := 0; LOOP := 40; ! Loop until nowait read finishes: WHILE LOOP = 40 DO BEGIN !Check for completion of read operation. !immediately if incomplete: Return CALL AWAITIO(RECV^NUM, !buffer^address!, BYTES^READ, !tag!, 0D); IF = THEN BEGIN ! Process user message . .
Recovering From Errors Communicating With Terminals ! ! Do some computation -- this code could be any noninteractive task: J := 0; WHILE J < 2000 DO BEGIN I := 0; WHILE I < 2000 DO I := I + 1; J := J + 1; END; END; END; END; Recovering From Errors For terminals, error recovery depends on the specific error.
Recovering From Errors That Indicate a Temporary Lack of Resources Communicating With Terminals The following paragraphs describe the effects of these errors. At the end of this subsection is a sample program for dealing with terminal errors. For all errors, you can get a short description of the error using the TACL ERROR command with the error number as parameter. For more detailed information on the error, refer to the Guardian Procedure Errors and Messages Manual.
Responding to Operator Preemption Communicating With Terminals be retried. If a read operation was being performed, then a message should be sent advising the user to retype the last entry before retrying the read. Keep in mind, however, that if more than one process is accessing a terminal and the BREAK feature is used, only BREAK access should be allowed after BREAK is pressed. Therefore, subsequent retries are rejected with error 110 until normal access is permitted.
Recovering From Errors: A Sample Program Communicating With Terminals If an error 210 through 231 occurs, then the operation failed at some indeterminate point. If reading, you should send a message to the user to reenter the data. Your application should then try the read operation again. Recovering From Errors: A Sample Program The TERM^IO procedure shown in the following example provides a simple way of handling terminal I/O errors.
Recovering From Errors: A Sample Program Communicating With Terminals !--------------------------------------------------------------! Procedure to perform terminal I/O !--------------------------------------------------------------PROC TERM^IO; BEGIN INT WCOUNT; INT COUNT^READ; INT DONE := NO; INT RETRY^COUNT; INT ERROR; ! Loop until file operation successful: WHILE DONE = NO DO BEGIN ! ! Set flag for success, initialize retry count, and prompt for input: DONE := YES; RETRY^COUNT := 0; SBUFFER ':=' "AP
Recovering From Errors: A Sample Program Communicating With Terminals ! Retry up to RETRY^LIMIT: BEGIN RETRY^COUNT := RETRY^COUNT + 1; IF RETRY^COUNT < RETRY^LIMIT THEN ! ! Retry limit not yet reached, so try again after one second delay: BEGIN SBUFFER ':=' "Terminal I/O error: trying again" -> @S^PTR; WCOUNT := @S^PTR '-' @SBUFFER; CALL WRITEX(TERM^NUM,SBUFFER,WCOUNT); CALL DELAY(100D); DONE := NO; END ELSE ! Retry limit reached.
11 Communicating With Printers This section describes how your program gains access to a printer and writes data to a printer. Specifically, this section covers the following topics: • • • How to open a printer, write text to it, and pass control information to it using CONTROL operations and SETMODE functions. Accessing a Printer provides details. How to control laser printers and matrix line printers by sending escape sequences to them. Using the Printer Control Language provides an overview.
Accessing a Printer Communicating With Printers Accessing a Printer This subsection introduces the system procedures that relate to printer control and provides a skeleton program for printer access. Procedures for Working With Printers You access a printer the same way as you would any other file, by using file-system procedure calls. You use the following procedures to perform the indicated tasks with printers: AWAITIO[X] Checks for completion of a pending I/O operation.
Procedures for Working With Printers Communicating With Printers Table 11-1 summarizes all CONTROL operations that affect printer operation. Table 11-1. Printer CONTROL Operations CONTROL Number Operation 1 Provides forms control 11 Specifies a wait for a modem connection 12 Disconnects a modem On return from one of the calls listed in Table 11-1, the condition code should be CCE if the CONTROL operation was successful. A condition code of CCL indicates an error.
A Printer Program Outline Communicating With Printers A Printer Program Outline The general approach to directly accessing a line printer from an application program is: 1. Open the printer by calling the FILE_OPEN_ procedure. Use the printer file name to identify the printer to the FILE_OPEN_ procedure. To prevent your printed messages being mixed with messages printed by other processes, you should open the printer for exclusive access. 2.
Using the Printer Control Language Communicating With Printers !Move to the top of the form: CALL CONTROL(PRINTER^NUM, POSITION, TOP^OF^FORM); IF <> THEN CALL DEBUG; . . !Close the printer: CALL FILE_CLOSE_(PRINTER^NUM); . . Using the Printer Control Language All HP printers support the printer control language (PCL). New printers introduced over the next few years will also support PCL. PCL allows you to control the printer by sending escape sequences to it.
Controlling the Printer Communicating With Printers This subsection describes some of the more common features of PCL. The following functions are among those supported by PCL: • • • Job-control commands let you select the number of copies you want printed and whether you want duplexing. Page-control commands let you establish the page length and margins and provide forms control. Font-management commands allow you to select fonts, establish style and stroke weight, and so on.
Controlling the Printer Communicating With Printers Two types of escape sequences can be sent to Tandem printers: two-character escape sequences and parameterized escape sequences. Two-character escape sequences have the following general format: Syntax for a two-character escape sequence: esc x esc is the escape character (ASCII %33). x is an ASCII character that specifies the function the printer is to perform.
Commonly Used PCL Escape Sequences Communicating With Printers This escape sequence is equivalent to the previous two examples. Note that the example specifies a lowercase “m” rather than an uppercase “M.” To send escape sequences to the printer, you must construct a string of ASCII characters according to the format of escape sequences given above and send those characters to the printer using the WRITE[X] procedure. This example sets the left and right margins.
Programming for Tandem Laser Printers Communicating With Printers Table 11-3.
Selecting a Printer Language (5577 Only) Communicating With Printers 5573 and 5573D printers, refer to the Tandem 5573 Laser LX Printer Reference Manual. Selecting a Printer Language (5577 Only) The 5577 laser printer can accept commands written in PCL or PostScript printer language. You can select the language in one of two ways: • • Using SETMODE function 260 Sending character sequences to the printer Note.
Using Job-Control Commands Communicating With Printers Use the following sequences to send the Universal Exit Language/Start PJL and @PJL enter-language commands: Escape sequence to send the Universal Exit Language/Start PJL command: esc%-12345X Character sequence to enter PostScript mode: @PJL enter language = PostScript Character sequence to enter PCL mode: @PJL enter language = PCL The following example selects PostScript mode: SBUFFER ':=' [%33,"%-12345X@PJL enter language = PostScript", %12]
Using Job-Control Commands Communicating With Printers Selecting Simplex or Duplex Mode If a job is printed in simplex mode, it is printed on one side of the paper. Jobs that print on both sides of the paper are duplex-mode jobs. You select simplex or duplex mode by writing an escape sequence with the following format to the printer: Escape sequence for setting simplex/duplex mode: esc&lmode-numberS mode-number is 0 for simplex, 1 for duplex with long-edge binding, or 2 for duplex with short-edge binding.
Using Job-Control Commands Communicating With Printers 3 Manual envelope feed 4 Lower paper tray 6 Envelope feeder The following example selects the manual paper feed as the paper source: SBUFFER ':=' [%33,"&l2H"] -> @S^PTR; CALL WRITEX(PRINTER^NUM,SBUFFER,@S^PTR '-' @SBUFFER); IF <> THEN ...
Using Page-Control Commands Communicating With Printers Using Page-Control Commands Page-control commands include a subset of escape sequences that allow you to control characteristics such as the size of the page, orientation, margins, and text spacing. This subsection presents some of the more common commands. Again, for complete details on all page-control commands, refer to the appropriate printer reference manual. The features described here are supported on all Tandem laser printers.
Using Page-Control Commands Communicating With Printers Setting the Logical Page Length The logical page length on Tandem laser printers is controlled by issuing an escape sequence with the following format: Escape sequence to set page size: esc&llinesP lines gives the maximum number of lines that each subsequent page can have. This is the size of the logical page. The logical page sets the bounds for future operations.
Printing Text Communicating With Printers Setting the Horizontal and Vertical Motion Indexes The horizontal motion index designates the distance between columns in 1/120-inch increments. Similarly, the vertical motion index designates the distance between rows in 1/48-inch increments.
Printing Text Communicating With Printers For internal fonts and downloaded soft fonts, you can alter the font characteristics including: • • • • • • • • The typeface (Courier, Times Roman, and so on) The symbol set; for example, to correspond to a national standard The character spacing (fixed or proportional) The pitch (number of characters per horizontal inch—proportional fonts only) The point size or character height The style (upright or italic) The stroke weight or boldness The orientation (portra
Printing Text Communicating With Printers Refer to the appropriate printer reference manual for a complete list of possible values for each of these escape sequences.
Resetting the Laser Printer Default Values Communicating With Printers !Subsequent text is not underlined: SBUFFER ':=' " and this is not. " -> @S^PTR; CALL WRITEX(PRINTER^NUM,SBUFFER,@S^PTR '-' @SBUFFER); IF <> THEN ... The above example prints the following text: This is underlined text, and this is not.
Using Page-Control Commands Communicating With Printers Using Page-Control Commands Page-control commands include a subset of escape sequences that allow you to control characteristics such as the length of the page and the left and right margins. This subsection presents some of the more commonly used commands. Again, for complete details on all page-control commands, refer to the printer reference manual.
Controlling Forms Movement Communicating With Printers Controlling Forms Movement Vertical positioning is done on the 5515/5516/5518 printers by looking up values in the vertical form control (VFC) table in printer memory. You can do this in two ways: • • Use the CONTROL procedure with operation 1 Use an escape sequence Using CONTROL Operation 1 to Position the Paper To vertically position the paper using CONTROL operation 1, you need to supply the function to be performed.
Controlling Forms Movement Communicating With Printers When you access the VFC table using an escape sequence (whether directly or indirectly using CONTROL operation 1), the printer advances to the next line that contains a 1 in the selected VFC channel. For example, if the printer is currently at line 5 and channel 6 is selected, the printer advances to line 10. Note. When you are accessing the VFC table using CONTROL operation 1, the supplied parameter refers to the VFC channel number offset by one.
Controlling Forms Movement Communicating With Printers To change the VFC table, send an escape sequence to the printer in the following form: Escape sequence for programming the VFC table: esc&lbyte-countWvfc-data This escape sequence moves the binary data provided in vfc-data into the VFC table. The first word of vfc-data corresponds to row 0, channels 1 through 16; the second word corresponds to row 1, channels 1 through 16, and so on. byte-count indicates the number of bytes in vfc-data.
Controlling Forms Movement Communicating With Printers The VFC table now contains the values shown in Table 11-4. Table 11-5.
Controlling Forms Movement Communicating With Printers Figure 11-1. Modified VFC Table VST120.
Printing Text Communicating With Printers Printing Text PCL supports several operations that affect the appearance of printed characters on Tandem matrix line printers.
Printing Text Communicating With Printers @S^PTR '-' @SBUFFER); IF <> THEN ... Note. You cannot change the pitch in the middle of a line. Underlining Text To underline text, write an escape sequence in the following format to the printer: Escape sequence for underlining text: esc&dD Escape sequence to turn off underlining: esc&d@ All text following the underline escape sequence is printed underlined up to the escape sequence that turns off underlining. By default, underlining is turned off.
Printing Text Communicating With Printers • Print groups of adjacent characters on top of each other by inserting backspace control codes. Using SETMODE 6 to Overstrike Characters You can use SETMODE function 6 to print a line on top of another line. SETMODE function 6 controls the spacing after you write a line to the printer. By default, a line feed and carriage return are performed after each line is printed. By calling SETMODE function 6, you can suppress the line feed.
Resetting the Printer to Default Values Communicating With Printers The following example shows the use of the backspace control code to overstrike one character: SBUFFER ':=' ["Denote blanks by b",%10,"/. CALL WRITEX(PRINTER^NUMBER, SBUFFER, @S^PTR '-' @SBUFFER); IF <> THEN ... "] -> @S^PTR; Again, the “/” character overstrikes the “b.
Recovering From a “Device Not Ready” Error Communicating With Printers Recovering From a “Device Not Ready” Error Your application must be able to handle a “not ready” or “paper out” condition. With some printers, either condition causes a “device not ready” error (refer to the printer manual). If either of these conditions arises, your program should send a message to the user or system operator. Your application should then wait for the user to respond, indicating that the printer is ready.
Sample Program for Using a Printer Communicating With Printers sent to the operator. However, if the information being printed is not considered critical, the line can be reprinted (and may thus be duplicated). Sample Program for Using a Printer The following example modifies the inventory program developed in Section 5, Communicating With Disk Files. It now includes an option to print the contents of the data file.
Sample Program for Using a Printer Communicating With Printers INT TERMNUM; !terminal file number STRUCT .
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! Here are a few DEFINEs to make it a little easier to format ! and print messages.
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when the ! file is not open when there is no file number for it. ! FILE^ERRORS is used when the file is open. ! ! The procedure also stops the program after displaying the ! error message.
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! This procedure writes a message on the terminal and checks ! for any error. If there is an error, it attempts to write ! a message about the error and the program is stopped. !-----------------------------------------------------------PROC WRITE^LINE(BUF,LEN); STRING .
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! Procedure to display a part record on the terminal !-----------------------------------------------------------PROC DISPLAY^RECORD; BEGIN PRINT^BLANK; ! Display part number: PRINT^STR("Part Number Is: ! Display part description: PRINT^STR("Part Description: ! " & PART^RECORD.DESCRIPTION FOR PART^RECORD.
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! Procedure to prompt user for input to build a new record. !-----------------------------------------------------------PROC ENTER^RECORD(TYPE); INT TYPE; BEGIN INT COUNT^READ; INT STATUS; STRING .NEXT^ADDR; DEFINE BLANK^FILL(F) = F ':=' " " & F FOR $LEN(F)*$OCCURS(F) - 1 BYTES #; PRINT^BLANK; ! ! ! If inserting a new record, prompt for a part number.
Sample Program for Using a Printer Communicating With Printers ! Prompt for the name of the supplier: SBUFFER ':=' "Enter Supplier Name: " -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); IF TYPE = NEW OR COUNT^READ > 0 THEN BEGIN COUNT^READ := $MIN(COUNT^READ,SUPPSIZE); BLANK^FILL(PART^RECORD.SUPPLIER); PART^RECORD.SUPPLIER ':=' SBUFFER FOR COUNT^READ; PART^RECORD.
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! Procedure to stop printing. This procedure positions the ! paper at the top of the form and closes the printer.
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! Procedure for printing records. The user selected "p." ! The procedure prints out the entire file, six records ! to a page. !-----------------------------------------------------------PROC PRINT^FILE; BEGIN STRING .
Sample Program for Using a Printer Communicating With Printers ! Read a record. Return to PARTS if end of file: CALL READX(PARTFILE^NUM,PART^RECORD,$LEN(PART^RECORD)); IF <> THEN BEGIN CALL FILE_GETINFO_(PARTFILE^NUM,ERROR); IF ERROR = 1 THEN BEGIN CALL FORMFEED^AND^CLOSE(PRINTERNUM); RETURN; END; CALL FILE^ERRORS(PARTFILE^NUM); END; ! Print the part number: START^LINE; S^PTR ':=' [%33,"&dDPart Number Is:",%33,"&d@ "] -> @S^PTR; S^PTR ':=' PART^RECORD.
Sample Program for Using a Printer Communicating With Printers ! Print the unit price: START^LINE; S^PTR ':=' [%33,"&dDUnit Price:",%33,"&d@ $"] -> @S^PTR; PUT^INT(PART^RECORD.UNIT^PRICE); CALL PRINT^OUT(PRINTERNUM,SBUFFER,@S^PTR '-' @SBUFFER); CALL PRINT^OUT(PRINTERNUM,SBUFFER,0); END; END; !-----------------------------------------------------------! Procedure for reading records. The user selected function ! "r." The start of the read is selected by approximate key ! positioning.
Sample Program for Using a Printer Communicating With Printers ! ! Loop reading and displaying records until user declines to read the next record (any response other than "y"): DO BEGIN PRINT^BLANK; ! ! ! Read a record from the part file. If end-of-file is reached, return control to the main procedure.
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! Procedure for updating a record. The user selected ! function "u." The user is prompted to enter the part ! number of the record to be updated, then the old contents ! are displayed on the user's terminal before prompting the ! user to enter the updated record. !-----------------------------------------------------------PROC UPDATE^RECORD; BEGIN INT COUNT^READ; INT ERROR; STRUCT .
Sample Program for Using a Printer Communicating With Printers BEGIN PRINT^BLANK; START^LINE; PUT^STR("No such record"); PRINT^LINE; RETURN; END ELSE CALL FILE^ERRORS(PARTFILE^NUM); END; ! Save the record for later comparison: SAVE^REC ':=' PART^RECORD FOR $LEN(PART^RECORD) BYTES; ! Display the record on the terminal: CALL DISPLAY^RECORD; ! Prompt the user for the updated record: CALL ENTER^RECORD(OLD); ! ! ! Now that the user has entered the changes, reread the record and check to see whether some
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! Procedure for inserting a record. The user selected ! function "i." The user is prompted to enter the new record. ! The procedure inserts the new record in the appropriate ! place in the file.
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! Procedure to process an illegal command. The procedure ! informs the user that the selection was other than "r," ! "u," "i," "p," or "x.
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! This procedure does the initialization for the program. ! It calls INITIALIZER to dispose of the startup messages. ! It opens the home terminal and the data file used by the ! program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .PARTFILE^NAME[0:MAXFLEN - 1]; INT PARTFILE^LEN; STRING .
Sample Program for Using a Printer Communicating With Printers !-----------------------------------------------------------! This is the main procedure. It calls the INIT procedure to ! initialize, then it goes into a loop calling GET^COMMAND to ! get the next user request and calling the procedure to ! carry out that request.
12 Communicating With Magnetic Tape Magnetic tapes used on a HP system can be labeled or unlabeled. A labeled tape contains standard ANSI, IBM, BACKUP, or TMF tape labels that identify files and tape volumes and control access. Any tape that has none of these tape labels is an unlabeled tape; it is up to the application to interpret any file or volume header information or to work without this information.
Communicating With Magnetic Tape Accessing Magnetic Tape: An Introduction Accessing Magnetic Tape: An Introduction Programmatic access to magnetic tapes is provided by the file-system procedures listed below: AWAITIO[X] Waits for the completion of outstanding I/O operations pending on the open magnetic tape unit when operating in nowait mode. CONTROL Controls tape positioning and rewind operations. Forward and backward positioning by record or file are supported. See Table 12-1.
Communicating With Magnetic Tape Accessing Magnetic Tape: An Introduction Table 12-1. Magnetic Tape CONTROL Operations (page 2 of 2) CONTROL Number Operation 10 Spaces backward by a given number of records. 24 Forces an end of volume. Unloads the current volume and requests the next volume. This operation applies only to labelled tape.
Communicating With Magnetic Tape Positioning the Tape 3. Perform read or write operations on the magnetic tape (using the READ[X] or WRITE[X] procedures). See Reading and Writing Tape Records later in this section for details. 4. Terminate access to the magnetic tape (using the FILE_CLOSE_ procedure). See Terminating Tape Access later in this section for details. The correct method for opening the magnetic tape device depends on whether the tape you access is labeled or unlabeled.
Spacing Forward and Backward by Files Communicating With Magnetic Tape Procedure calls that position by record block are valid for labeled and unlabeled tapes. The file-movement operations are redundant when dealing with labeled tapes because positioning is done using the FILESEQ and FILESECT values. The file-movement operations are necessary for accessing the desired file on unlabeled tapes. Tape positioning by record block applies to labeled and unlabeled tapes. Note.
Communicating With Magnetic Tape Spacing Forward and Backward by Files The next example shows CONTROL operation 8 used to space the tape backward by one file, starting from the finish point of the previous example. Note that the tape stops immediately before the EOF mark. LITERAL SPACE^BACK^FILES = 8; . . NUMBER^OF^FILES := 1; CALL CONTROL(TAPE^NUM, SPACE^BACK^FILES, NUMBER^OF^FILES); IF <> THEN ... VST050.VSD VST050.
Communicating With Magnetic Tape Spacing Forward and Backward by Record Blocks 12 Communicating With Magnetic Tape Spacing Forward and Backward by Record Blocks CONTROL operation 9 moves the tape forward (toward the EOT sticker) a specified number of record blocks. This operation stops when the end of the specified number of record blocks is encountered. If an EOF mark is encountered, error 1 (EOF detected) is returned to the program and the tape stops immediately after the EOF mark.
Communicating With Magnetic Tape Spacing Forward and Backward by Record Blocks NUMBER^OF^RECORDS); IF <> THEN ... VST053.VSD The next example shows the magnetic tape spacing forward a number of record blocks beyond the EOT sticker. The forward spacing continues because the tape has not yet reached the end of the file: NUMBER^OF^RECORDS := 4; CALL CONTROL(TAPE^NUM, SPACE^FWD^RECORDS, NUMBER^OF^RECORDS); IF <> THEN ... VST054.
Communicating With Magnetic Tape Spacing Forward and Backward by Record Blocks . NUMBER^OF^RECORDS := 2; CALL CONTROL(TAPE^NUM, SPACE^BACK^RECORDS, NUMBER^OF^RECORDS); IF <> THEN ... VST055.VSD If the tape is positioned immediately after the EOF mark (for example, following a space file forward operation), CONTROL operation 10 causes the tape to move to just before the same EOF mark. Error 1 is returned: NUMBER^OF^RECORDS := 5; CALL CONTROL(TAPE^NUM, SPACE^BACK^RECORDS, NUMBER^OF^RECORDS); IF <> THEN ..
Rewinding the Tape Communicating With Magnetic Tape NUMBER^OF^RECORDS); IF <> THEN ... VST057.VSD The next example tries to space backward five record blocks but encounters the BOT sticker. The tape stops immediately after the BOT sticker, and error 154 is returned to the program: NUMBER^OF^RECORDS := 5; CALL CONTROL(TAPE^NUM, SPACE^BACK^RECORDS, NUMBER^OF^RECORDS); IF <> THEN ... VST058.VSD Rewinding the Tape The CONTROL procedure supports several operations that enable your program to rewind a tape.
Communicating With Magnetic Tape Reading and Writing Tape Records Use CONTROL operation 5 to rewind the tape to the BOT sticker, leaving the tape online. This procedure call does not wait for completion. LITERAL REWIND^ONLINE = 5; . . CALL CONTROL(TAPE^NUM, REWIND^ONLINE); IF <> THEN ... Finally, CONTROL operation 6 rewinds the tape to BOT and leaves the tape online. Your program waits for the operation to finish. LITERAL REWIND^AND^WAIT = 6; . . CALL CONTROL(TAPE^NUM, REWIND^AND^WAIT); IF <> THEN ...
Reading Tape Records Communicating With Magnetic Tape . WHILE LOOP = 1 DO BEGIN RCOUNT := 2048; CALL READX(TAPE^NUM,SBUFFER, RCOUNT,COUNT^READ); IF <> THEN BEGIN CALL FILE_GETINFO_(TAPE^NUM, ERROR); IF ERROR = EOF THEN LOOP := 0 ELSE ........; END ELSE BEGIN !Process the record block returned in BUFFER. END; END; The first, second, and third reads each transfer 1024 bytes into SBUFFER, return 1024 in COUNT^READ, and set the condition code to CCE (the no-error condition code).
Communicating With Magnetic Tape Writing Tape Records 256 bytes are transferred into SBUFFER, 256 is returned in COUNT^READ, and the condition code is set to CCL. The call to FILE_GETINFO_ returns error number 21 (illegal count specified). After the read operation, the tape is positioned immediately before the beginning of the next record block on tape. Writing Tape Records Use the WRITE[X] procedure to write record blocks to magnetic tape.
Communicating With Magnetic Tape Writing Tape Records . CALL CONTROL(TAPE^NUM, WRITE^EOF); IF <> THEN ... Note that, for unlabeled tapes, closing a file does not write an EOF mark. For labeled tapes, the end of the tape is identified by the tape labeling mechanism. All the application needs to do is to detect the EOT sticker, stop writing, and close the file.
Blocking Tape Records Communicating With Magnetic Tape 12 Communicating With Magnetic Tape Blocking Tape Records A record is a collection of related information as seen by the application; for example, a data structure containing an account number, name, and balance. Records can be fixed or variable length. A record block contains the data that is written to or read from tape in one read or write operation. Record blocks within a tape file are typically the same length.
Working in Buffered Mode Communicating With Magnetic Tape Figure 12-2. Physical Tape Records Containing Records of Fixed Length VST061.VSD VST061.VSD Working in Buffered Mode We recommend using buffered mode to improve the performance of tape read and write operations. In buffered mode, the tape process, which is a system process, replies to write requests as soon as the data has been transferred from the application to the tape process buffer.
Communicating With Magnetic Tape Invoking and Revoking Buffered-Mode Operation file-system errors, an application closing the tape process when buffers are awaiting completion cannot be sure those data records have been successfully written to tape. Note. recommends setting a tape device to unbuffered mode before closing. Doing so ensures that all data records have been successfully written to tape.
Communicating With Magnetic Tape Flushing the Buffer BUFFERED^MODE, ENABLED); The FILE_OPEN_ request need not specify exclusive access in the call; the tape process enforces exclusive access by disallowing further opens if buffered mode is enabled and by disallowing buffered mode if there is more than a single opener. Error 12 (file in use) is returned if the SETMODE request is rejected for this reason.
Communicating With Magnetic Tape Buffered Mode for Streaming Devices (D-Series Only) The operation finishes when the synchronization is complete. Buffered Mode for Streaming Devices (D-Series Only) With streaming devices such as the 5120 cartridge tape, the device itself (or its integral formatter) buffers requests in a way similar to buffered mode.
An Example of Buffered-Mode Operation Communicating With Magnetic Tape Figure 12-3.
Communicating With Magnetic Tape Working With Standard Labeled Tapes Working With Standard Labeled Tapes The operating system provides support for magnetic tapes written with standard ANSI or IBM labels. The labeling mechanism allows for easy transfer of information between systems from different vendors using magnetic tape. Both the ANSI and IBM standards use labels to describe tape volumes, files, and file sections.
Accessing Labeled Tapes Communicating With Magnetic Tape Accessing Labeled Tapes You gain access to a labeled tape by passing a tape DEFINE name to the FILE_OPEN_ procedure. $ZSVR responds to the FILE_OPEN_ call by sending a message to the operator to mount the tape on any tape drive. (Recall that when handling labeled tapes, it is not necessary to identify the device.) ERROR := FILE_OPEN_(=TAPE^FILE^1, TAPE^NUM); IF ERROR <> 0 THEN ...
Communicating With Magnetic Tape Accessing Labeled Tapes Specifying the DEFINE CLASS Before setting any other DEFINE attributes for magnetic tape, you must first set the DEFINE class to “TAPE.” Doing so sets the default attributes in the working set to the default values for tape.
Accessing Labeled Tapes Communicating With Magnetic Tape Specifying Volume and File You do not need to specify the name of the tape device when accessing labeled tape. Instead, you identify the file you want by specifying the name of the tape volume and the name of the file on that volume. The tape can therefore be mounted on any device and the system will find it. If the tape is mounted on a tape drive on a remote system in the network, you also need to specify the system name in the SYSTEM attribute.
Communicating With Magnetic Tape Accessing Labeled Tapes Specifying the I/O Operation You must specify the type of I/O operation you want to perform on the labeled tape. You can write a new file, read from an existing file, or append to an existing file. You can specify the I/O operation using either the access parameter of the FILE_OPEN_ procedure or the USE DEFINE attribute.
Communicating With Magnetic Tape Accessing Labeled Tapes Specifying the Block and Record Sizes The BLOCKLEN, RECLEN, and RECFORM attributes allow you to specify how records are blocked into record blocks. Recall from Blocking Tape Records, earlier in this section, that a record is a collection of related information as seen by the application, and that one or more records can be blocked into one record block that is read from or written to tape in one operation.
Communicating With Magnetic Tape ² Accessing Labeled Tapes The RECFORM attribute specifies whether records are fixed length or variable length. It has a value “F” for fixed or “U” for undefined (variable). The following example sets the RECFORM attribute: ATTRIBUTE^NAME ':=' "RECFORM "; VALUE ':=' "F"; LENGTH := 1; ERROR := DEFINESETATTR(ATTRIBUTE^NAME, VALUE, LENGTH, DEFAULT^NAMES); IF ERROR > 0 THEN ...
Communicating With Magnetic Tape Accessing Labeled Tapes You will get an error if you try to write to the file before the expiration date. However, you can still read the file after the expiration date. • A generation group for the file and a version number within the generation group. Use the GEN attribute to identify the generation group and the VERSION attribute to specify the version. The generation group identifies a specific instance of a file.
Communicating With Magnetic Tape Writing to the Only File on a Labeled Tape Volume Setting Buffered Mode We recommend using buffered mode to increase throughput. However, to use buffered mode, the application must be able to recover from errors in any buffering mode it uses. See Recovering From Errors, later in this section, for details. In the case of a write operation, the application is allowed to continue as soon as the tape process has received the write request.
Communicating With Magnetic Tape Writing to the Only File on a Labeled Tape Volume 2. Create a working set for the DEFINE using successive calls to the DEFINESETATTR procedure. The working set should include the following: • • • • • • • • • • • The class of DEFINE (CLASS attribute). Set this value to “TAPE.” The type of labels used (LABELS attribute). Set this value to “ANSI” or “IBM.” The volume identifier (VOLUME attribute). Set this value to the value written to the tape in the volume label.
Communicating With Magnetic Tape Writing to the Only File on a Labeled Tape Volume The following example creates a DEFINE called =TAPEFILE^APPEND that describes a labeled tape file using standard ANSI labels: !Turn on DEFINE mode: NEW^VALUE := 1; ERROR := DEFINEMODE(NEW^VALUE, OLD^VALUE); IF ERROR > 0 THEN ...
Communicating With Magnetic Tape Writing to the Only File on a Labeled Tape Volume !Set the USE attribute to EXTEND: ATTRIBUTE^NAME ':=' "USE "; ATTRIBUTE^VALUE ':=' "EXTEND" -> @S^PTR; ERROR := DEFINESETATTR(ATTRIBUTE^NAME, ATTRIBUTE^VALUE, @S^PTR '-' @ATTRIBUTE^VALUE, DEFAULT^NAMES); IF ERROR <> 0 THEN ...
Communicating With Magnetic Tape Writing to a File on a Multiple-File Labeled Tape Volume Writing to the File Use the DEFINE created above for appending to the file as follows: 1. Open the DEFINE using the FILE_OPEN_ procedure. If the DEFINE attributes match the attributes in the tape label, then the file is opened. If you are opening the file with write-only access or with the USE attribute set to “OUT,” then the file is created and opened.
Communicating With Magnetic Tape Writing to a File on a Multiple-File Labeled Tape Volume create a DEFINE and then use the DEFINE for writing to the file. The difference is that the FILESEQ attribute specified in the DEFINE must identify the correct file. The following paragraphs show how to create a DEFINE for, and write records to, a file on a multiple-file labeled tape. Creating the DEFINE Create the DEFINE as follows: 1. Turn on DEFINEs by calling the DEFINEMODE procedure. 2.
Communicating With Magnetic Tape Writing to a File on a Multiple-File Labeled Tape Volume the same density as data already on the tape. Unlike when reading, when you write to a tape, the density is not automatically set for you. 3. Create the DEFINE using the DEFINEADD procedure. The following example creates a DEFINE called =TAPEFILE5^APPEND. It describes the fifth file on a labeled tape. This tape uses IBM labels.
Writing to a File on a Multiple-File Labeled Tape Volume Communicating With Magnetic Tape DEFAULT^NAMES); IF ERROR <> 0 THEN ... !Set the USE attribute to EXTEND: ATTRIBUTE^NAME ':=' "USE "; ATTRIBUTE^VALUE ':=' "EXTEND" -> @S^PTR; ERROR := DEFINESETATTR(ATTRIBUTE^NAME, ATTRIBUTE^VALUE, @S^PTR '-' @ATTRIBUTE^VALUE, DEFAULT^NAMES); IF ERROR <> 0 THEN ...
Writing to a File on a Multiple-File Labeled Tape Volume Communicating With Magnetic Tape !Create the DEFINE: DEFINE^NAME ':=' "=TAPEFILE5^APPEND ERROR := DEFINEADD(DEFINE^NAME); IF ERROR <> 0 THEN ... "; Writing to the File Use the DEFINE created above for appending to the file as described below. Note that you can append only to the last file on the tape. So, in this case, the fifth file must also be the last file. 1. Open the DEFINE using the FILE_OPEN_ procedure.
Communicating With Magnetic Tape Writing to a File on Multiple Labeled Tape Volumes . . Writing to a File on Multiple Labeled Tape Volumes The procedure for writing to a file that resides on multiple labeled tapes is similar to the procedure for writing to a file on a single tape reel. Again you use a DEFINE to describe the file and the type of operation you intend to perform. Then you open and write to the DEFINE.
Communicating With Magnetic Tape Writing to a File on Multiple Labeled Tape Volumes appending to a file, this value must also equal the corresponding value in the file label on the tape. • The tape density (DENSITY attribute). This value must be the same as the density of existing data on the tape to ensure that the new data gets written to tape at the same density as data already on the tape.
Communicating With Magnetic Tape Writing to a File on Multiple Labeled Tape Volumes DEFAULT^NAMES); IF ERROR <> 0 THEN ... !Set the FILESEQ attribute to 1: ATTRIBUTE^NAME ':=' "FILESEQ "; ATTRIBUTE^VALUE ':=' "1"; ATTRIBUTE^LEN := 1; ERROR := DEFINESETATTR(ATTRIBUTE^NAME, ATTRIBUTE^VALUE, ATTRIBUTE^LEN, DEFAULT^NAMES); IF ERROR <> 0 THEN ...
Writing to a File on Multiple Labeled Tape Volumes Communicating With Magnetic Tape DEFAULT^NAMES); IF ERROR <> 0 THEN ... !Create the DEFINE: DEFINE^NAME ':=' "=MY^TAPE^UPDATE ERROR := DEFINEADD(DEFINE^NAME); IF ERROR <> 0 THEN ... "; Writing to the File Use the DEFINE created in the previous example for writing to the file as described below. Note that the DEFINE refers to the last tape reel of a four-tape file. You can write or append only to the last tape in the file. 1.
Communicating With Magnetic Tape Reading From the Only File on a Labeled Tape Volume . . Reading From the Only File on a Labeled Tape Volume Like writing, reading from the only file on a labeled tape volume requires a DEFINE that accurately describes the file and the operation you intend to perform on the file. Then you read from the file identified by the created DEFINE. A DEFINE for reading differs from a DEFINE made for writing. You must set the USE attribute to “IN.
Reading From the Only File on a Labeled Tape Volume Communicating With Magnetic Tape The following example creates a DEFINE called =TAPEFILE^READ that describes a labeled tape file using standard ANSI labels: !Turn on DEFINE mode: NEW^VALUE := 1; ERROR := DEFINEMODE(NEW^VALUE, OLD^VALUE); IF ERROR > 0 THEN ...
Reading From the Only File on a Labeled Tape Volume Communicating With Magnetic Tape ATTRIBUTE^VALUE ':=' "IN" -> @S^PTR; ERROR := DEFINESETATTR(ATTRIBUTE^NAME, ATTRIBUTE^VALUE, @S^PTR '-' @ATTRIBUTE^VALUE, DEFAULT^NAMES); IF ERROR <> 0 THEN ... !Create the DEFINE: DEFINE^NAME ':=' "=TAPEFILE^READ ERROR := DEFINEADD(DEFINE^NAME); IF ERROR <> 0 THEN ... "; Reading From the File Use the DEFINE created above for reading from the file as follows: 1. Open the DEFINE using the FILE_OPEN_ procedure.
Communicating With Magnetic Tape Reading From a File on a Multiple-File Labeled Tape Volume CALL READX(TAPE^NUM,SBUFFER, RCOUNT,COUNT^READ); !Deblock the input buffer into four records: LOGICAL^BUFFER^1[0] ':=' SBUFFER[0] FOR 512; LOGICAL^BUFFER^2[0] ':=' SBUFFER[512] FOR 512; LOGICAL^BUFFER^3[0] ':=' SBUFFER[1024] FOR 512; LOGICAL^BUFFER^4[0] ':=' SBUFFER[1536] FOR 512; . .
Communicating With Magnetic Tape Reading From a File on a Multiple-File Labeled Tape Volume 3. Create the DEFINE using the DEFINEADD procedure. The following example creates a DEFINE called =FILE^FIVE^READ. It describes the fifth file on a labeled tape. This tape uses IBM labels. !Turn on DEFINE mode: NEW^VALUE := 1; ERROR := DEFINEMODE(NEW^VALUE, OLD^VALUE); IF ERROR > 0 THEN ...
Reading From a File on a Multiple-File Labeled Tape Volume Communicating With Magnetic Tape DEFAULT^NAMES); IF ERROR <> 0 THEN ... !Set the USE attribute to IN: ATTRIBUTE^NAME ':=' "USE "; ATTRIBUTE^VALUE ':=' "IN" -> @S^PTR; ERROR := DEFINESETATTR(ATTRIBUTE^NAME, ATTRIBUTE^VALUE, @S^PTR '-' @ATTRIBUTE^VALUE, DEFAULT^NAMES); IF ERROR <> 0 THEN ...
Communicating With Magnetic Tape Reading From a File on Multiple Labeled Tape Volumes TAPE^NUM); IF ERROR <> 0 THEN ... !Set buffered mode: CALL SETMODE(TAPE^NUM, BUFFERED^MODE, ON); IF <> THEN ... . .
Communicating With Magnetic Tape • • • • • • Reading From a File on Multiple Labeled Tape Volumes The type of labels used (LABELS attribute). Set this value to “ANSI” or “IBM.” The volume identifier (VOLUME attribute). This value should specify a list of volume names starting with the first volume where the file resides. The file identifier (FILEID attribute). The file identifier must be the same as the file identifier in the file label. The file sequence number (FILESEQ attribute).
Reading From a File on Multiple Labeled Tape Volumes Communicating With Magnetic Tape DEFAULT^NAMES); IF ERROR <> 0 THEN ... !Set the FILEID attribute to 4_TAPEFILE: ATTRIBUTE^NAME ':=' "FILEID "; ATTRIBUTE^VALUE ':=' "4_TAPEFILE" -> @S^PTR; ERROR := DEFINESETATTR(ATTRIBUTE^NAME, ATTRIBUTE^VALUE, @S^PTR '-' @ATTRIBUTE^VALUE, DEFAULT^NAMES); IF ERROR <> 0 THEN ...
Communicating With Magnetic Tape Reading From a File on Multiple Labeled Tape Volumes Reading From the File Use the DEFINE created above for reading the file as described below. Note that the DEFINE refers to the third tape of a four-tape file. 1. Open the DEFINE using the FILE_OPEN_ procedure. If the file section exists and the DEFINE attributes match those on the tape label, then the tape file is opened. The returned file number refers to the tape drive that the tape is mounted on. 2.
Communicating With Magnetic Tape Accessing a Labeled Tape File: An Example LOGICAL^BUFFER^3[0] ':=' SBUFFER[1024] FOR 512; LOGICAL^BUFFER^4[0] ':=' SBUFFER[1536] FOR 512; . . Accessing a Labeled Tape File: An Example In this subsection, the program used in Section 5, Communicating With Disk Files, for saving a daily log in an entry-sequenced disk file is modified. Now, because of the sequential nature of the application, this example will be used to show communication with magnetic tape.
Communicating With Magnetic Tape Writing the Program FILESEQ attribute must be set to 1 because the file is the first and only file on the tape. Therefore three DEFINEs are created as shown below.
Writing the Program Communicating With Magnetic Tape the record is put into the tape buffer and written to tape as a partial record block. If the user chooses to enter more records, the procedure blocks each record into the tape buffer until either the buffer contains four records or the user declines to enter more records. At this point the procedure writes the tape buffer to tape—one record block.
Communicating With Magnetic Tape Writing the Program BEGIN INT MSGCODE; STRUCT DEFAULT; BEGIN INT VOLUME[0:3]; INT SUBVOL[0:3]; END; STRUCT INFILE; BEGIN INT VOLUME[0:3]; INT SUBVOL[0:3]; INT FILEID[0:3]; END; STRUCT BEGIN INT INT INT END; STRING END; OUTFILE; VOLUME[0:3]; SUBVOL[0:3]; FILEID[0:3]; PARAM[0:529]; ?NOLIST, SOURCE $SYSTEM.SYSTEM.
Writing the Program Communicating With Magnetic Tape CALL WRITE^LINE(SBUFFER,0) ! #; Print a string: DEFINE PRINT^STR(S) = BEGIN START^LINE; PUT^STR(S); PRINT^LINE; Guardian Programmer’s Guide — 421922-014 12 - 56 END #;
Communicating With Magnetic Tape Writing the Program !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when ! the file is not open, so there is no file number for it. ! FILE^ERRORS is to be used when the file is open. ! ! The procedure also stops the program after displaying the ! message.
Communicating With Magnetic Tape Writing the Program !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and the error number are determined from the file ! number and FILE^ERRORS^NAME is then called to do the ! display. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
Communicating With Magnetic Tape Writing the Program !-----------------------------------------------------------! Procedure to open a labeled tape file by opening the ! appropriate CLASS TAPE DEFINE. !-----------------------------------------------------------PROC OPEN^TAPE^FILE; BEGIN INT ERROR; STRING .
Communicating With Magnetic Tape Writing the Program !-----------------------------------------------------------! This procedure executes when you press "r" in response to ! the function prompt in the main procedure. It prompts the ! user for the desired record, displays it on the terminal, ! then prompts for sequential reads. !-----------------------------------------------------------PROC READ^RECORD; BEGIN INT COUNT^READ; INT(32) RECORD^NUM; STRING .
Writing the Program Communicating With Magnetic Tape ! ! ! Execute loop if reading just selected, or user has requested to read an additional record. Exit loop if user declines to read next record: DO BEGIN PRINT^BLANK; ! Read a record block from the tape file: CALL READX(TAPENUM,TBUFFER,TBUFSIZE,COUNT^READ); IF <> THEN BEGIN CALL FILE_GETINFO_(TAPENUM,ERROR); IF ERROR = 1 THEN BEGIN PRINT^STR("No such record.
Communicating With Magnetic Tape Writing the Program IF <> THEN CALL FILE^ERRORS(TERMNUM); ! Display comments: CALL WRITEX(TERMNUM,LOG^RECORD.
Communicating With Magnetic Tape Writing the Program !-----------------------------------------------------------! Procedure to append a record to the file !-----------------------------------------------------------PROC APPEND^RECORD; BEGIN INT ERROR; INT COUNT^READ; INT SEQ^NUM := 0; ! Open the tape file and set buffered mode: CALL OPEN^TAPE^FILE; ! Blank tape buffer: TBUFFER[0] ':=' " "; TBUFFER[1] ':=' TBUFFER[0] FOR 1023; ! Initialize the index into the tape buffer: INDEX := 0; ! ! Write recor
Communicating With Magnetic Tape ! Writing the Program Put comments into record structure: LOG^RECORD.
Communicating With Magnetic Tape ! ! ! ! Writing the Program Flush to tape every 10 record blocks. Use modulo divide to detect tenth record.
Writing the Program Communicating With Magnetic Tape !-----------------------------------------------------------! Procedure to process an illegal command. The procedure ! informs the user that the selection was other than "r," ! "a," "c," or "x.
Communicating With Magnetic Tape Writing the Program !-----------------------------------------------------------! Procedure to save Startup message in a global structure. !-----------------------------------------------------------PROC SAVE^STARTUP^MESSAGE(RUCB,START^DATA,MESSAGE, LENGTH, MATCH) VARIABLE; INT INT INT INT INT .RUCB; .START^DATA; .MESSAGE; LENGTH; MATCH; BEGIN ! Copy the Startup message into the CI^STARTUP structure: CI^STARTUP.
Communicating With Magnetic Tape Writing the Program !-----------------------------------------------------------! Procedure to perform initialization for the program. It ! calls INITIALIZER to read and copy the Startup message ! into the global variables area and then opens the IN file ! specified in the Startup message. This procedure also ! checks whether labeled tape support is turned on.
Communicating With Magnetic Tape Writing the Program !-----------------------------------------------------------! This is the main procedure.
Communicating With Magnetic Tape Working With Unlabeled Tapes Working With Unlabeled Tapes Any tape that does not have standard ANSI, IBM or TMF labels (or for a backup tape either BACKUP or IBMBACKUP labels) is an unlabeled tape. Use the methods described in this subsection for handling either tapes produced by other vendors that don’t have IBM or ANSI labels or tapes produced on HP systems without using standard labeled tape processing.
Accessing Unlabeled Tapes Communicating With Magnetic Tape ERROR := DEFINESETATTR(ATTRIBUTE^NAME, ATTRIBUTE^VALUE, @S^PTR '-' @ATTRIBUTE^VALUE, DEFAULT^NAMES); IF ERROR <> 0 THEN ... !Set the DEVICE attribute to \SYS2.$TAPE1: ATTRIBUTE^NAME ':=' "DEVICE "; ATTRIBUTE^VALUE ':=' "\SYS2.$TAPE1" -> @S^PTR; ERROR := DEFINESETATTR(ATTRIBUTE^NAME, ATTRIBUTE^VALUE, @S^PTR '-' @ATTRIBUTE^VALUE, DEFAULT^NAMES); IF ERROR <> 0 THEN ...
Communicating With Magnetic Tape Accessing Unlabeled Tapes Blocking Tape I/O There is no support for blocking records that are written to an unlabeled tape. If you choose to do blocking, then your program must pack multiple records into one record block before the record gets written to tape. Similarly, on reading records from the tape, the program must do its own deblocking of record blocks back into records.
Communicating With Magnetic Tape Accessing Unlabeled Tapes The following example also sets the tape density to 1600 bits per inch: LITERAL TAPE^DENSITY = 66; . . DENSITY := 1; CALL SETMODE(TAPE^NUM, TAPE^DENSITY, DENSITY); IF <> THEN ... Here, the tape drive must already be open. The selected density becomes effective immediately.
Communicating With Magnetic Tape Writing to a Single-File Unlabeled Tape Similarly, you can convert ASCII code to EBCDIC for output as follows: FUP COPY $TAPE1,$TAPE2,EBCDICOUT Refer to the File Utility Program (FUP) Reference Manual for details on the FUP COPY command. Setting Buffered Mode As with labeled tape, we recommend using buffered mode to increase throughput.
Communicating With Magnetic Tape Writing to a Single-File Unlabeled Tape Writing a New File to a Scratch Tape Writing a file to magnetic tape involves two steps: 1. Write records to the tape device using the WRITE procedure; for example: !Write record blocks to tape: WHILE NOT DONE BEGIN . . !Write one record to tape: CALL WRITEX(TAPE^NUM, SBUFFER, WCOUNT); IF <> THEN ... . . IF THEN DONE = 1; END; 2. Terminate the file with an end-of-file mark and an indication of end of tape.
Communicating With Magnetic Tape Writing to a Single-File Unlabeled Tape Appending to the Only File on an Unlabeled Tape To append to the only file on an unlabeled tape, your program must do the following: 1. Space forward one file. The tape stops immediately after the first EOF mark: LITERAL SPACE^FWD^FILES = 7, SPACE^BACK^FILES = 8, WRITE^EOF = 2; . . NUMBER^OF^FILES := 1; CALL CONTROL(TAPE^NUM, SPACE^FWD^FILES, NUMBER^OF^FILES); IF <> THEN ... 2. Space backward one file.
Communicating With Magnetic Tape Writing to a Multiple-File Unlabeled Tape Writing to a Multiple-File Unlabeled Tape Writing records to a multiple-file unlabeled tape reel is similar to writing to a single-file tape reel except that you need to be sure that you write to the appropriate file on the tape. You can add a file to the end of the tape or append records to the last file on the tape. You can find the end of the last file on the tape by searching for two consecutive EOF marks.
Communicating With Magnetic Tape Writing to a Multiple-File Unlabeled Tape 3. Write records to the tape: WHILE DONE = 0; BEGIN . . CALL WRITEX(TAPE^NUM, SBUFFER, WCOUNT); IF <> THEN ... . . IF THEN DONE := 1; END; 4. Write two EOF marks to the tape to signify the end of the new file and the new end of information on the tape. CALL CONTROL(TAPE^NUM, WRITE^EOF); IF <> THEN ... CALL CONTROL(TAPE^NUM, WRITE^EOF); IF <> THEN ...
Communicating With Magnetic Tape Writing to a File on Multiple Unlabeled Tape Reels READX(TAPE^NUM, SBUFFER, RCOUNT, COUNT^READ); IF <> THEN BEGIN CALL FILE_GETINFO_(TAPE^NUM, ERROR); IF ERROR = 1 THEN END^OF^TAPE := YES ELSE ... !other error END; END; 2. Space backward two EOF marks to position the tape at the end of the last file on the tape: NUMBER^OF^FILES := 2; CALL CONTROL(TAPE^NUM, SPACE^BACK^FILES, NUMBER^OF^FILES); IF <> THEN ... 3. Write records to the tape: WHILE NOT DONE; BEGIN . .
Communicating With Magnetic Tape Writing to a File on Multiple Unlabeled Tape Reels Writing Tape Headers We recommend using tape headers to identify magnetic tape reels, especially where tape files span multiple tapes. Because we recommend using two EOF marks to denote the end of information on a tape, headers are needed to identify where a multiple-reel file starts and stops. For example, when reading a tape sequentially, your program encounters an EOF mark.
Communicating With Magnetic Tape Writing to a File on Multiple Unlabeled Tape Reels WCOUNT); IF <> THEN BEGIN CALL FILE_GETINFO_(TAPE^NUM, ERROR); IF ERROR = 150 BEGIN CALL CONTROL(TAPE^NUM, WRITE^EOF); IF <> THEN BEGIN CALL FILE_GETINFO_(TAPE^NUM, ERROR); IF ERROR <> 150 THEN ... END; CALL CONTROL(TAPE^NUM, WRITE^EOF); IF <> THEN BEGIN CALL FILE_GETINFO_(TAPE^NUM, ERROR); IF ERROR <> 150 THEN ... END; CALL CONTROL(TAPE^NUM,REWIND^AND^UNLOAD); IF <> THEN ...
Communicating With Magnetic Tape Reading From a Single-File Unlabeled Tape Reading From a Single-File Unlabeled Tape When reading records from a tape reel containing one file, your program must do the following: 1. Space forward or backward to the record that you intend to read. If you try to space backward too far, the tape stops at the BOT sticker and the file system returns error 154. If the program is allowed to continue, it will then read the first record.
Communicating With Magnetic Tape Reading From a Multiple-File Unlabeled Tape Reading From a Multiple-File Unlabeled Tape Reading records from a multiple-file unlabeled tape reel is similar to the single-file case except that your program must also space the tape forward or backward to the appropriate file. The steps your program must perform are outlined below: 1. File space forward or backward to the appropriate file.
Communicating With Magnetic Tape Reading From a File on Multiple Unlabeled Tape Reels Reading From a File on Multiple Unlabeled Tape Reels The technique for reading records from a multiple-reel file depends in part on what information you have put in the tape header. Typically, your program is going to use the header information to determine whether the current reel is the first or last reel in the file.
Recovering From Errors Communicating With Magnetic Tape Recovering From Errors The tape process attempts automatic recovery for all tape I/O operations. However, it is the application’s responsibility to ensure that the tape gets positioned correctly following an error. For example, if a power failure or other hardware error occurs while a tape read or write is taking place, it is indeterminate where the tape is positioned at the point of failure.
Communicating With Magnetic Tape Recovering From Errors Figure 12-4. Example of a WRITE Error in Buffered Mode VST063.VSD The most commonly encountered tape errors are listed in Table 12-4. See the Guardian Procedure Errors and Messages Manual for a complete list of all file-system errors. The Guardian Procedure Errors and Messages Manual provides details on the cause and effect of each of these errors, as well as the recommended action.
Communicating With Magnetic Tape Recovering From “Device Not Ready” Errors Table 12-4. Commonly Encountered Tape Errors File System Error Number Caused by...
Communicating With Magnetic Tape Recovering From Path Errors (device not ready) or error 218 (interrupt timeout). After power is restored and the tape unit is again accessed, a subsequent call to FILE_GETINFO_ returns error 153 (tape drive power on). It is the responsibility of the application to ensure the correct tape is loaded following a power failure. Tape units, if a tape is loaded, are automatically put back into an operating (ready) state when power is restored.
Accessing an Unlabeled Tape File: An Example Communicating With Magnetic Tape ² If you are reading and sequence numbers were written on tape, keep track of the sequence number of the current record. Then if a path error occurs, retry the operation. If the expected sequence number is not read, meaning that a record was skipped over when the path error occurred, backspace the tape x records, where x is the sequence number on tape minus the last known sequence number.
Accessing an Unlabeled Tape File: An Example Communicating With Magnetic Tape program does not know which write operation caused the error. Therefore this procedure backspaces the tape to the last correctly written record and displays it. The user then has the option of reentering the data submitted since the displayed record or exiting the program. • The TAPE^READ^ERRORS procedure is called by READ^RECORD if an error is encountered when a record block is read from tape.
Communicating With Magnetic Tape INT Accessing an Unlabeled Tape File: An Example .RECORD^POINTER := @LOG^RECORD[0]; STRUCT .CI^STARTUP; BEGIN INT MSGCODE; STRUCT DEFAULT; BEGIN INT VOLUME[0:3]; INT SUBVOL[0:3]; END; STRUCT INFILE; BEGIN INT VOLUME[0:3]; INT SUBVOL[0:3]; INT FILEID[0:3]; END; STRUCT OUTFILE; BEGIN INT INT INT END; STRING END; !Startup message VOLUME[0:3]; SUBVOL[0:3]; FILEID[0:3]; PARAM[0:529]; ?NOLIST, SOURCE $SYSTEM.SYSTEM.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! Here are some DEFINEs to make it easier to format and print ! messages.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when ! the file is not open, so there is no file number for it. ! FILE^ERRORS is to be used when the file is open. ! ! The procedure also stops the program after displaying the ! message.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and the error number are determined from the file ! number and FILE^ERRORS^NAME is then called to do the ! display. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! Procedure for responding to errors incurred while reading ! from magnetic tape. This procedure tries the read again. If ! sequence numbers are inconsistent, then a read was skipped ! due to the error. The procedure compensates by backspacing ! over two records.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example BEGIN PRINT^STR("Read error: Unable to Verify sequence.
Communicating With Magnetic Tape ! Accessing an Unlabeled Tape File: An Example Display date from last good record: CALL WRITEX(TERMNUM,LOG^RECORD.DATE, $LEN(LOG^RECORD.DATE)); IF <> THEN CALL FILE^ERRORS(TERMNUM); ! Display comments from last good record: CALL WRITEX(TERMNUM,LOG^RECORD.COMMENTS, $LEN(LOG^RECORD.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! Procedure to rewind the tape to BOT, checking that the ! tape is loaded. If not, then the rewind operation ! results in error 100. The user is prompted to load the ! tape before continuing.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! This procedure executes when you press "r" in response to ! the function prompt in the main procedure. It prompts the ! user for the desired record, displays it on the terminal, ! then prompts for sequential reads. !-----------------------------------------------------------PROC READ^RECORD; BEGIN INT COUNT^READ; INT(32) RECORD^NUM; STRING .
Communicating With Magnetic Tape ! ! ! ! Accessing an Unlabeled Tape File: An Example Rewind tape to BOT, leave online. Since this might be the first access to tape, the code retries the operation for error 100.
Communicating With Magnetic Tape ! ! ! ! Check for incomplete record block. If this record is blank, set INDEX 4 in preparation for reading the next record block. Also set SBUFFER to "Y" in case this is the first record selected: IF LOG^RECORD.SEQ^NUM = " BEGIN INDEX := 4; SBUFFER[0] ':=' "Y"; END ELSE ! Accessing an Unlabeled Tape File: An Example " THEN Process the log record: BEGIN ! Display date from the record: CALL WRITEX(TERMNUM,LOG^RECORD.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! Procedure to append a record to the file. !-----------------------------------------------------------PROC APPEND^RECORD; BEGIN INT ERROR; INT COUNT^READ; ! ! ! ! ! Rewind tape and position the tape to EOF. Since this might be the first access to tape, check whether the tape is ready by checking for error 100.
Communicating With Magnetic Tape ! ! Accessing an Unlabeled Tape File: An Example Write records to file.
Communicating With Magnetic Tape ! Accessing an Unlabeled Tape File: An Example Prompt the user to enter additional records: PRINT^BLANK; SBUFFER ':=' "Do You Wish to Enter Another Record (y/n)? " -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); ! Increment the index into the record block: INDEX := INDEX + 1; ! ! ! Send record block to tape process if no more records, or if record block full.
Communicating With Magnetic Tape ! Accessing an Unlabeled Tape File: An Example Write EOF marks to end of tape: CALL CONTROL(TAPENUM,2); IF <> THEN CALL FILE^ERRORS(TAPENUM); CALL CONTROL(TAPENUM,2); IF <> THEN CALL FILE^ERRORS(TAPENUM); END; !-----------------------------------------------------------! Procedure to initialize a scratch tape with two EOF marks. !-----------------------------------------------------------PROC SCRATCH^TAPE; BEGIN ! ! ! Make sure tape is at BOT.
Accessing an Unlabeled Tape File: An Example Communicating With Magnetic Tape !-----------------------------------------------------------! Procedure to process an illegal command. The procedure ! informs the user that the selection was other than "r," ! "a," "i," or "x.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! Procedure to save Startup message in a global structure. !-----------------------------------------------------------PROC SAVE^STARTUP^MESSAGE(RUCB,START^DATA,MESSAGE, LENGTH, MATCH) VARIABLE; INT INT INT INT INT .RUCB; .START^DATA; .MESSAGE; LENGTH; MATCH; BEGIN ! Copy the Startup message into the CI^STARTUP structure: CI^STARTUP.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! Procedure to perform initialization for the program. It ! calls INITIALIZER to read and copy the Startup message ! into the global variables s area and then opens the IN file ! specified in the Startup message. This procedure also ! opens the tape file and sets buffered mode for tape access.
Communicating With Magnetic Tape Accessing an Unlabeled Tape File: An Example !-----------------------------------------------------------! This is the main procedure !-----------------------------------------------------------PROC LOGGER MAIN; BEGIN STRING CMD; CALL INIT; ! Loop indefinitely until user selects function x: WHILE 1 DO BEGIN ! Prompts for the next function to perform: CMD := GET^COMMAND; ! Call function selected by user: CASE CMD OF BEGIN "r", "R" -> CALL READ^RECORD; "a", "A" -> CALL A
13 Manipulating File Names This section describes how an application program can manipulate file names or the names of entities, such as nodes or volumes, that make up parts of file names. See Section 2, Using the File System, if you are unsure about the format of file names. A typical use of the features described here is to manipulate file names or file-name patterns presented to a program using a TACL command.
Identifying Portions of File Names Manipulating File Names Identifying Portions of File Names Many of the procedures described in this section need to identify portions of file names or names of other entities such as nodes, volumes, and subvolumes. For example, if you want to change a subvolume name in a permanent disk-file name, you need a way of specifying to a procedure that the string you supply is to replace the subvolume name.
Working With File-Name Patterns Manipulating File Names Defining a File-Name “Piece” A piece of a file name contains one or more consecutive parts of a file name. Many file-name manipulation procedures require a piece parameter. When a file-name piece consists of just one part, the level parameter is enough to specify the desired part. You can supply a file-name suffix as a piece. Here, the piece consists of the part identified by the level parameter plus all parts to the right of that part.
Scanning, Resolving, and Unresolving File Names Manipulating File Names The following examples show the use of wild-card characters: *Z* Matches all file names containing the letter Z in the current subvolume $S.* Matches all locations of the spooler on the current system *.*.* Matches all permanent files on the current system, as well as processes with two qualifiers \*.
Scanning a String for a Valid File Name Manipulating File Names You pass a string to the FILENAME_SCAN_ procedure for testing. If the file name or pattern is valid, the procedure returns the following information: • • • The length in bytes of the file name or pattern. An indication of the kind of object that the name or pattern identifies: file name, file-name pattern, or DEFINE name.
Scanning a String for a Valid File Name Manipulating File Names Scanning File-Name Patterns To accept a file-name pattern in the input string of the FILENAME_SCAN_ procedure, you need to set the accept-pattern flag (bit 15) in the options parameter to 1. The options parameter goes at the end of the parameter list: LITERAL ACCEPT^PATTERNS = %B0000000000000001; . . OPTIONS := ACCEPT^PATTERNS; ERROR := FILENAME_SCAN_(STRING^BUFFER:STRING^LENGTH, COUNT,KIND, LEVEL, OPTIONS); IF ERROR <> 0 THEN ...
Resolving Names Manipulating File Names Resolving Names To resolve a name into its fully qualified form, you use the FILENAME_RESOLVE_ procedure. This procedure takes a string value containing a partially resolved name as its input, uses the default values to replace any missing parts, and then returns the fully qualified name.
Resolving Names Manipulating File Names The following examples show how the FILENAME_RESOLVE_ procedure expands some file names. The examples assume default values of \SYSA.$OURVOL.MYSUB: Input File Name Output File Name PROGA \SYSA.$OURVOL.MYSUB.PROGA $THEIRVOL.OLDSUB.PROGA \SYSA.$THEIRVOL.OLDSUB.PROGA \SYSB.$OURVOL.HERSUB.PROGA \SYSB.$OURVOL.HERSUB.
Resolving Names Manipulating File Names LEVEL, OPTIONS); IF ERROR <> 0 THEN ... ! Error condition ELSE BEGIN IF LEVEL = 1 THEN OPTIONS := RESOLVE^SUBVOL ELSE OPTIONS := 0; ERROR := FILENAME_RESOLVE_(STRING^BUFFER:COUNT, FULLNAME:MAXLEN, FULL^LENGTH, OPTIONS); IF ERROR <> 0 THEN ... ! Error condition END; Resolving DEFINE Names The FILENAME_RESOLVE_ procedure does not normally modify DEFINE names.
Resolving Names Manipulating File Names .
Resolving Names Manipulating File Names Rejecting DEFINEs That are not Resolved to a File Name You have the option to reject any DEFINEs that are not resolved to a file name. You set the DEFINE-reject flag (bit 10) in the options parameter to 1 to request this feature. Instead of returning the name of such a DEFINE, FILENAME_RESOLVE_ returns error 13. This option can be used alone or with the preceding options.
Resolving Names Manipulating File Names • DEFINE mode is turned off. The following example searches the subvolume list in a search DEFINE named =FINDIT.
Truncating Default Parts of File Names Manipulating File Names An alternative way to specify the override name is to use a map DEFINE with the same name as the file ID in the input string, prefixed with an equal sign (=). You can do this by setting the automatic-override flag (bit 8) in the options parameter to 1 before calling the FILENAME_RESOLVE_ procedure. The following example resolves the file name in a DEFINE called =PROGA.
Truncating Default Parts of File Names Manipulating File Names Truncating a Specified Subset of the Default Values Alternatively, you can request that all default values to the left of a specified file-name part be removed from the file name. Here, you need to use the level parameter to specify the level of the first part of the name that will not be removed. The following example selects the device (or process) level.
Extracting Pieces of File Names Manipulating File Names IF ERROR <> 0 THEN ... ALT^DEFAULTS); !Error condition Truncating Default Parts of File Names: Some Examples The following examples show how the FILENAME_UNRESOLVE_ procedure deals with file names, given that the default values are \SYSA.$OURVOL.MYSUB: Input File Name Leve l Output File Name \SYSA.$OURVOL.MYSUB.PROGB 0 $OURVOL.MYSUB.PROGB \SYSB.$YOURVOL.HISSUB.FILEA 0 \SYSB.$YOURVOL.HISSUB.FILEA \SYSA.$THEIRVOL.HERSUB.FILEB 0 $THEIRVOL.
Extracting Pieces of File Names Manipulating File Names ERROR := FILENAME_DECOMPOSE_(FNAME:LENGTH, PART:MAXLEN, PART^LENGTH, SUBVOL^LEVEL); IF ERROR <> 0 THEN ... !Error condition Here, the file name is passed in FNAME. The subvolume name is returned in PART and its length in PART^LENGTH. MAXLEN is set to 16 to allow for the maximum size of a subvolume pattern. The part that you want returned is specified in the level parameter (SUBVOL^LEVEL in the previous example).
Extracting Pieces of File Names Manipulating File Names If, for example, the input string is “$OURVOL.MYSUB.PROGA” and you want the filename prefix returned up to and including the subvolume, then “$OURVOL.MYSUB” is returned. The node name, although specified in the default values, is not returned. The code to execute this example is shown below: LITERAL MAXLEN = 256; LITERAL SUBVOL^LEVEL = 2; LITERAL EXTRACT^PREFIX = %B0000000000000010; LITERAL NO^DEFAULTS = %B0000000000000100; . .
Modifying Portions of a File Name Manipulating File Names SUBPART); IF ERROR <> 0 THEN ... !Error condition Extracting Pieces of File Names: Some Examples The following examples list some file names and the corresponding output from the FILENAME_DECOMPOSE_ procedure. The examples assume that the current default values are \SYS.$OURVOL.MYSUB: Input Name Leve l Option s $YOURVOL.HISSUB.FILEA 0 $YOURVOL.HISSUB.FILEA 0 FILE1 0 FILE1 0 suffix $OURVOL.MYSUB.FILE1 FILE1 0 prefix \SYS.
Modifying One Part of a File Name Manipulating File Names Modifying One Part of a File Name Use the level parameter to specify the part of the file name you want to change. The following example replaces the volume name of the file name \SYSA.$YOURVOL.RECORDS.LOGFILE: LITERAL MAXLEN = 256; LITERAL VOLUME^LEVEL = 0; . . FNAME ':=' "\SYSA.$YOURVOL.RECORDS.
Replacing a Subpart of a Process ID Manipulating File Names SUBVOL^LEVEL, SUFFIX); IF ERROR <> 0 THEN ... !Error condition Replacing a Subpart of a Process ID To replace any subpart of a process ID, you need to use the subpart parameter of the FILENAME_EDIT_ procedure. The subpart parameter specifies which element of the process identifier you intend to change. For named processes, you can modify the process name or its sequence number.
Manipulating File Names Searching For and Matching File-Name Patterns The following example compares a permanent disk-file name with a map DEFINE name: FNAME1 ':=' "\SYSA.$OURVOL.MYSUB.PROGA" -> @S^PTR; FNAME1^LENGTH := @S^PTR '-' @FNAME1; FNAME2 ':=' "=MYPROG" -> S^PTR; FNAME2^LENGTH := @S^PTR '-' @FNAME2; STATUS := FILENAME_COMPARE_(FNAME1:FNAME1^LENGTH, FNAME2:FNAME2^LENGTH); The procedure accepts partially qualified file names and implicitly expands them to their fully qualified form before comparing.
Establishing the Start of a File-Name Search Manipulating File Names make special provisions when searching for process names, set up an asynchronous search, and report specific kinds of system or device errors encountered during a search. The following paragraphs describe these options. Specifying the Search Pattern To use the FILENAME_FINDSTART_ procedure, you must pass to it the file name pattern to search for, along with its length.
Establishing the Start of a File-Name Search Manipulating File Names Setting Up a Search for a Specific Type of Device The FILENAME_FINDSTART_ procedure allows you to restrict the output of a search to files of a specified device type or subdevice type. In addition, if you set the not-device-type flag (bit 14) or the not-subdevice-type flag (bit 13) in the options parameter, you can restrict the report to all but the specified device type and subdevice type.
Manipulating File Names Establishing the Start of a File-Name Search Setting Up a Search for Process Qualifier Names For the qualifier names of a process to be seen by the procedures that search for file names, the process must indicate its ability to perform qualifier name searches. It does so by issuing a PROCESS_SETINFO_ procedure call as follows: LITERAL QUALIFIER^INFO = 49; . .
Manipulating File Names Establishing the Start of a File-Name Search The following example starts a search at the file following \SYSB.$ARCHIVE.S110189 for the file-name pattern \*.*.*.*. The search is limited to disk files: LITERAL SKIP^IF^SAME = %B0000000000000001; . . SEARCH^PATTERN ':=' "\*.*.*.*" -> @S^PTR; PATTERN^LENGTH := @S^PTR '-' @SEARCH^PATTERN; DEVICE^TYPE := 3; OPTIONS := SKIP^IF^SAME; START^NAME ':=' "\SYSB.$ARCHIVE.
Finding the Next Matching File Name Manipulating File Names Reporting Device or System Failures You can choose to receive notification of failed or offline devices and systems encountered during a search. To be sure that such errors are always reported, set the report-off-line flag (bit 12) in the options parameter to 1 before calling FILENAME_FINDSTART_. The following example sets the report-off-line flag to 1: LITERAL REPORT^OFFLINE = %B0000000000001000; . . SEARCH^PATTERN ':=' "\*.*.*.
Finding the Next Matching File Name Manipulating File Names The FILENAME_FINDNEXT_ procedure requires the search ID returned by the FILENAME_FINDSTART_ procedure. From this parameter, the FILENAME_FINDNEXT_ procedure can derive the pattern to search for. The FILENAME_FINDNEXT_ procedure normally performs a waited search. If the search was set up nowait, then the search proceeds asynchronously.
Finding the Next Matching File Name Manipulating File Names bytes given by the value in word 8. If the search returns an error, the error number is returned in word 2. If your program is running multiple concurrent searches, then you will also need to set the tag parameter in the FILENAME_FINDNEXT_ procedure call. You can then check which search is finishing by comparing words 9 and 10 of the FILENAME_FINDNEXT_ completion message with the tag supplied in the procedure call.
Finding the Next Matching File Name Manipulating File Names IF ERROR = 6 THEN BEGIN !Continue processing based on message number: CASE BUFFER[0] OF BEGIN !If message is nowait return from !FILENAME_FINDNEXT_, check word 2 of message for !search error. If no error, move the name string !out of the message and into the NAME variable: -109 -> BEGIN IF BUFFER[2] = 0 THEN NAME ':=' BUFFER[14] FOR BUFFER[8]; END; . .!Other system messages: OTHERWISE ->... END; END !Or if it is not a system message: ELSE ...
Finding the Next Matching File Name Manipulating File Names Word 2 For disks, contains the object type. If > 0, then the returned name refers to an SQL object type. If 0, then the returned name refers to a non-SQL file. If -1, then the returned name refers to a subvolume or volume.
Terminating the File-Name Search Manipulating File Names If you continue searching from one of these errors by issuing another call to the FILENAME_FINDNEXT_ procedure, the set of names subordinate to the entity in error is skipped and the search continues with the next entity at the same level as the erroneous entity. For errors that do not return a name, it is generally not worth retrying the search because the condition causing the error is likely to recur.
File-Name Matching Manipulating File Names BEGIN 2 -> 1 -> 0 -> OTHERWISE -> END; !Complete match !Incomplete match !No match !Error Testing for an Incomplete Match An incomplete match status value (1) is returned if the name under test matches the left-hand portion of a pattern but not the entire pattern. For example, if the name under test is \SYSA.$OURVOL and the pattern is \*.*.*, then the procedure returns an incomplete match.
Manipulating File Names: An Example Manipulating File Names !Check for an incomplete match between the volume level !prefix and the complete file-name pattern: STATUS := FILENAME_MATCH_(PREFIX:PREFIX^LENGTH, PATTERN:PATTERN^LENGTH); CASE STATUS OF BEGIN !Incomplete match: 1 -> BEGIN !Check for a complete match STATUS := FILENAME_MATCH_(FULLNAME:FULL^LENGTH, PATTERN:PATTERN^LENGTH); CASE STATUS OF BEGIN 2 -> !Complete match 0 -> !No match OTHERWISE -> !Error condition END; END; !No match: 0 -> !Error: OTHE
Manipulating File Names: An Example Manipulating File Names • • A map DEFINE. The name contained in the map DEFINE is expanded to its fully qualified form and displayed. A name pattern. Every file name that matches the name pattern is displayed in its fully qualified form. Because the program reads and processes the Startup message, the user can specify the input and output file names. For a detailed discussion of the Startup message, refer to Section 8, Communicating With a TACL Process.
Manipulating File Names: An Example Manipulating File Names • • The FIND^FILES procedure searches for all file names that match a given pattern. It calls PRINT^NAME for each match that it finds. The FILE^ERRORS procedure reports file-system errors by sending the error number to the output file. The TAL source code for this program follows. ? INSPECT, SYMBOLS, NOCODE, NOMAP ?NOLIST, SOURCE $TOOLS.ZTOOLD00.
Manipulating File Names: An Example Manipulating File Names INT PARAM^LEN; !length of PARAM string ?NOLIST, SOURCE $SYSTEM.SYSTEM.EXTDECS0(INITIALIZER, ? FILE_OPEN_,FILENAME_SCAN_,FILENAME_RESOLVE_, ? FILENAME_FINDSTART_,FILENAME_FINDNEXT_, ? FILENAME_FINDFINISH_,WRITEX,PROCESS_STOP_,DNUMOUT, ? OLDFILENAME_TO_FILENAME_,FILE_GETINFO_) ? LIST !-----------------------------------------------------------! These DEFINEs help to format and print messages.
Manipulating File Names: An Example Manipulating File Names !-----------------------------------------------------------! Procedure to write a file name to the output file. !-----------------------------------------------------------PROC PRINT^NAME(NAME,LENGTH); INT LENGTH; STRING .
Manipulating File Names: An Example Manipulating File Names !-----------------------------------------------------------! Procedure to perform initialization for the program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .
Manipulating File Names: An Example Manipulating File Names !-----------------------------------------------------------! Procedure to find all file names that match a given pattern ! and print each file name. !-----------------------------------------------------------PROC FIND^FILES(PATTERN,LENGTH); INT LENGTH; STRING .PATTERN; BEGIN INT SEARCH^ID; STRING .
Manipulating File Names: An Example Manipulating File Names !-----------------------------------------------------------! Main procedure determines kind of file name or pattern, ! resolves the name, and calls either PRINT^NAME for file ! names or DEFINEs or FIND^FILES for a file-name pattern.
Manipulating File Names: An Example Manipulating File Names ! If it is a pattern: 1 -> BEGIN ! Resolve pattern to fully qualified form: ERROR := FILENAME_RESOLVE_( CI^STARTUP.
14 Using the IOEdit Procedures The IOEdit package is made up of a set of routines that allow an application to read and write EDIT files (files with file code 101). The procedure call interface to the IO-Edit routines allows access to EDIT files from any supported language: TAL, C, COBOL85, Pascal, and FORTRAN. This section describes how to use the procedure-call interface to the IOEdit routines.
Overview of IOEdit Using the IOEdit Procedures If your application cannot satisfy all the above restrictions you must use Enscribe files or NonStop SQL files. Overview of IOEdit Before discussing how you can use IOEdit to perform operations on EDIT files, some of the major concepts of the IOEdit package will be introduced.
When Should You Use IOEdit? Using the IOEdit Procedures Table 14-1. Advantages of IOEdit Over SIO (page 2 of 2) If you want to... Then IOEdit is better than SIO because... Sequentially read a file from its start to its middle, then start writing, deleting any existing records from that point; that is, simulate a tape. You can do this using IOEdit, but not using SIO. Have faster throughput.
Line Numbers and Records Using the IOEdit Procedures Table 14-2 indicates when SIO works better than IOEdit. Table 14-2. Advantages of SIO Over IOEdit If you want to... Then SIO is better than IOEdit because... Use an error file for reporting errors. SIO uses an error file; IOEdit does not. Write long lines. SIO has a write-fold feature that allows a long line to be divided into several shorter lines; IOEdit has no such feature. Perform I/O with file types other than EDIT files and T-Text files.
Packed Line Format Using the IOEdit Procedures Note that blank lines have a number. Inserted lines use up to three levels of index or “point” numbers. An example of how point numbers might be applied is given below: If a line is inserted between... Then the new line is given line number... lines 6 and 7 6.1 lines 6 and 6.1 6.01 lines 6 and 6.01 6.001 lines 6 and 6.001 6.001, and the original line 6.001 is renumbered A record number is a line number multiplied by 1000.
Packed Line Format Using the IOEdit Procedures Figure 14-1. An IOEdit Packed Line VST078.VSD The first byte of the packed line is the header byte. It contains the length of the packed line. If the line is blank, then the header byte contains 0 and there are no following line segments. If the header byte is nonzero, then one or more line segments follow. Each line segment contains a header byte and from 0 through 15 nonblank characters.
The EDIT File Segment Using the IOEdit Procedures As an example, consider the following text line from a program source file (the pound signs, #, represent blanks): ###CALL#FILE_OPEN_(FILE^NAME,FILE^NUM);#######!opens#the#file The corresponding packed form contains seven line segments and a line header byte as follows: VST079.
Creating, Opening, and Initializing an IOEdit File Using the IOEdit Procedures Creating, Opening, and Initializing an IOEdit File Creating, opening, and initializing an EDIT file can all be done with one call to the OPENEDIT_ procedure. Table 14-3 shows when OPENEDIT_ performs each of these functions. Table 14-3. Functions of the OPENEDIT_ Procedure If the EDIT file... Then the OPENEDIT_ procedure call...
Opening a Nonexistent File Using the IOEdit Procedures ² • • The access mode is read-only by default. Note that this is different from FILE_OPEN_ which has a default access mode of read/write. The exclusion mode is shared by default, as for the FILE_OPEN_ procedure. Nowait I/O is implied by default. Note that this is different from the default mode used by FILE_OPEN_. Moreover, the behavior of nowait I/O is different for IOEdit than for Enscribe files.
Initializing an Already Open File Using the IOEdit Procedures To create a new file, you must set the access mode to read/write. Otherwise, there is no difference between calling OPENEDIT_ to open an existing file and calling OPENEDIT_ to open a file that does not yet exist. The following example creates and opens a file called MYFILE: LITERAL READ^WRITE = 0; . . FILE^NAME ':=' "$USERVOL.MYSUBVOL.
Reading and Writing an IOEdit File Using the IOEdit Procedures Reading and Writing an IOEdit File This subsection discusses some of the operations you can perform that relate to I/O with EDIT files. Specifically, it covers how to perform the following operations: • • • • • • • Set the starting point for a sequential I/O operation using the POSITIONEDIT, READEDIT, or WRITEEDIT procedure. Perform sequential reading.
Selecting a Starting Point Using the IOEdit Procedures Selecting a Starting Point Most I/O operations involve reading or writing sequential data. However, unlike other available methods of access to EDIT files, the IOEdit routines allow you to start your read or write operation at any record in the file.
Performing Sequential Reading Using the IOEdit Procedures The record number you supply to WRITEEDIT must not already exist; otherwise, error 10 is returned. Performing Sequential Reading Once you have established the starting point for a sequential read operation, you can read each record in turn by repeated calls to either the READEDIT or READEDITP procedure.
Performing Sequential Writing Using the IOEdit Procedures Typically, when you write lines to a file, you either append them to the end of the file or insert them between existing lines in the file. The following paragraphs describe how to perform these operations. Appending to a File To append new lines of text to the end of an EDIT file you must: 1. Position the next-record pointer to the end of the file. 2. Set the record increment to the desired value. 3.
Setting and Getting the Record Number Increment Using the IOEdit Procedures Inserting Lines Typically, you insert text after the current line in the file. To do this, you need to choose an ascending sequence of record numbers that are all greater than the current record number and all less than the next record number. The following sequence outlines one approach: 1. Determine the current record number using the GETPOSITIONEDIT procedure. 2.
Handling “File Full” Errors Using the IOEdit Procedures To renumber lines, supply the NUMBEREDIT procedure with the range of lines you need to have renumbered, the new record number for the start of the renumbered set of lines, and the record number increment.
Line Backspacing Using the IOEdit Procedures The following example deletes the lines between two records obtained by calls to the GETPOSITIONEDIT procedure: START^DELETE := GETPOSITIONEDIT(FILE^NUM); . . END^DELETE := GETPOSITIONEDIT(FILE^NUM): ERROR := DELETEEDIT(FILE^NUM,START^DELETE,END^DELETE); IF ERROR > 0 THEN ... Line Backspacing The BACKSPACEEDIT procedure performs the equivalent of a FORTRAN BACKSPACE statement on the file. The purpose of the BACKSPACEEDIT procedure is as follows.
Using Nowait I/O With IOEdit Files Using the IOEdit Procedures Using Nowait I/O With IOEdit Files If your program issues a call to AWAITIO with -1 specified for the file number (meaning wait until any outstanding I/O request from this process is finished), you may get a completion status for a nowait I/O operation previously started by IOEdit.
Closing an IOEdit File Using the IOEdit Procedures Closing an IOEdit File This subsection discusses how to use the CLOSEEDIT_ and CLOSEALLEDIT procedures. In addition to closing EDIT files, these procedures also cause the IOEdit buffers in the EFS to be copied to disk. Caution. You must close EDIT files explicitly. If you allow EDIT files to be closed implicitly by stopping the process, then you will lose the contents of the IOEdit buffers.
15 Using the Sequential Input/Output Procedures The sequential input/output (SIO) procedures provide a higher-level interface than the interface provided by using the file system procedures directly. They are intended for processing sequential I/O streams, particularly of displayable or printable text. Specifically, they can be used for text that might be directed to or from a variety of text sources or destinations, such as terminals, printers, spoolers, structured disk files, and EDIT files.
Using the Sequential Input/Output Procedures An Introduction to the SIO Procedures An Introduction to the SIO Procedures An application process can use the following SIO procedures to sequentially access files: CHECK^BREAK Checks whether the BREAK key has been pressed. CHECK^FILE Retrieves characteristics and state information about SIO files. CLOSE^FILE Closes a file that was opened for SIO.
Using the Sequential Input/Output Procedures Steps for Writing a Program For ease of programming, all structures and literals required by the SIO procedures, including the FCB and the common FCB, are predefined in a file called GPLDEFS in the $SYSTEM.SYSTEM subvolume. This file must be sourced into your program if you intend to use the SIO procedures. Steps for Writing a Program To use the SIO procedures, your program must perform the sequence of operations outlined below: 1.
Using the Sequential Input/Output Procedures Differences Between TNS/R Native and TNS Procedures CHECK^FILE, and INITIALIZER calls, have different forms in the native and TNS environments.
Using the Sequential Input/Output Procedures Initializing SIO Files Using TAL or pTAL DEFINEs For convenience in writing programs to run in both the native and TNS environments, the native form of the INITIALIZER call can also be used in TNS procedures, in which case it overrides the processing of contiguous FCBs. See Using the INITIALIZER Procedure, later in this section, for more information about calling INITIALIZER.
Using the Sequential Input/Output Procedures Setting Up the SIO Data Structures 1. Allocate space for the SIO data structures using TAL or pTAL DEFINEs provided in the $SYSTEM.SYSTEM.GPLDEFS file. You need to allocate space for each FCB, the RUCB, and the common FCB. The DEFINEs also provide initial values. 2. Assign a logical file name to each file that the SIO procedures will access (optional). 3. Complete the initialization of the FCBs by calling the INITIALIZER procedure.
Setting Up the SIO Data Structures Using the Sequential Input/Output Procedures Preparing the SIO File Control Blocks Use a DEFINE named ALLOCATE^FCB or a DEFINE named ALLOCATE^FCB^D00 to set up an FCB for each file that the SIO procedures will access. Use ALLOCATE^FCB^D00 for $RECEIVE and the common FCB. The created FCB will identify its opener by process handle. Use ALLOCATE^FCB for all files other than $RECEIVE and the common FCB. Note.
Setting Up the SIO Data Structures Using the Sequential Input/Output Procedures • To substitute the home terminal name, use the following string: VST067.VSD • To substitute a temporary file name, use the following string: VST067.VSD VST068.VSD To specify a complete file name, you also use exactly 12 words. The format of the name depends on whether the file is a disk file, a process, or a device file. Figure 15-1 shows the valid formats.
Using the Sequential Input/Output Procedures Setting Up the SIO Data Structures Figure 15-1. File-Name Conventions for SIO File VST069.VSD To access files across the network, you need to include the network number in the file name. Indicate a network number by putting a backslash (\) in the first byte and the system number in the second byte. The dollar sign ($) that normally starts the volume, process, or device name is then omitted, leaving 6 bytes to identify the volume, process, or device.
Assigning a Logical File Name Using the Sequential Input/Output Procedures The following examples allocate space for two FCBs, one for the input file and one for the output file. The default file names for the input and output files will be read from the Startup message by the INITIALIZER procedure: ALLOCATE^FCB(INFILE," ALLOCATE^FCB(OUTFILE," #IN #OUT "); "); Note. The TNS/R native form of the INITIALIZER procedure call differs from the TNS form.
Using the Sequential Input/Output Procedures Assigning a Logical File Name TAL Example: INT .BUF[0:11]; STRING .SBUF := @BUF '<<' 1; . . SBUF ':=' [5, "INPUT"]; CALL SET^FILE(INFILE, ASSIGN^LOGICALFILENAME, @BUF); SBUF ':=' [6, "OUTPUT"]; CALL SET^FILE(OUTFILE, ASSIGN^LOGICALFILENAME, @BUF); SBUF ':=' [5, "LFILE"]; CALL SET^FILE(DFILE, ASSIGN^LOGICALFILENAME, @BUF); pTAL/TAL Example: INT ERROR; INT .BUF[0:11]; ?IF PTAL !Begin pTAL statements STRING .
Using the INITIALIZER Procedure Using the Sequential Input/Output Procedures Using the INITIALIZER Procedure The INITIALIZER procedure sets up the SIO FCBs using information from the RUCB and messages read from the $RECEIVE file as shown in Figure 15-2. Figure 15-2.
Using the Sequential Input/Output Procedures Using the INITIALIZER Procedure 2. Opens the $RECEIVE file and reads an Open message from the mom process. If INITIALIZER receives an Open message from any process other than its mom process, it replies with error 100. If it receives any other message from a process other than its mom process, it replies with error 60. 3.
Using the Sequential Input/Output Procedures Setting Up File Access This form of the INITIALIZER call also works in TAL programs. However, because TAL does not support the WADDR data type, you must include a declarative such as the following in your TAL program: DEFINE WADDR INT #; For TNS callers, the basic INITIALIZER call has the form: CALL INITIALIZER(CONTROL^BLOCK); To call the INITIALIZER procedure without reading Assign or Param messages, use the following call. Native callers: FLAGS := 0; FLAGS.
Using the Sequential Input/Output Procedures Setting Up File Access You set file characteristics by putting information into the FCB. There are two ways to do this: • • The user of your program can use ASSIGN commands to set file characteristics at run time. Your program must call INITIALIZER to accept Assign messages and give the file a logical file name using the ASSIGN^LOGICALFILENAME option of the SET^FILE call. You can set the file characteristics programmatically using calls to SET^FILE.
Using the Sequential Input/Output Procedures Setting Up File Access If the access mode is not specified, its default value depends on the file type as follows: File Type Access Mode Operator process Read/write Process Read/write $RECEIVE Read/write Disk file Read/write Terminal Read/write Printer Write Magnetic tape Read/write For more details about access modes, refer to Section 3, Coordinating Concurrent File Access.
Using the Sequential Input/Output Procedures Setting Up File Access Specifying the Logical-Record Length Use the ASSIGN REC command to specify the logical-record length at run time. The record-size parameter specifies the length. The following example specifies a logical-record length of 256 bytes: 3> ASSIGN INPUT^FILE,,REC 256 Use the SET^FILE ASSIGN^RECORDLENGTH operation to programmatically set the logical-record length. You set the new-value parameter to the number of bytes.
Using the Sequential Input/Output Procedures Setting Up File Access Specifying Extent Sizes For disk SIO files, you can set the primary and secondary extent sizes only if the subsequent OPEN^FILE call will create the file (see Opening and Creating SIO Files, later in this section). If the file already exists, then the new extent size is ignored. For a general discussion of extents, refer to Section 2, Using the File System.
Using the Sequential Input/Output Procedures Reassigning a Physical File Name to a Logical File You can also set the physical-block length programmatically using the SET^FILE ASSIGN^BLOCKLENGTH operation. The following example also sets the block length to 2048 bytes: BLOCK^LENGTH := 2048; CALL SET^FILE(DFILE, ASSIGN^BLOCKLENGTH, BLOCK^LENGTH); If you do not specify a block length, then no blocking is performed.
Sample Initialization Using the Sequential Input/Output Procedures ?INSPECT, SYMBOLS ?NOLIST, SOURCE $SYSTEM.SYSTEM.
Sample Initialization Using the Sequential Input/Output Procedures !-----------------------------------------------------------!Procedure for initializing all SIO files used by this !application. !-----------------------------------------------------------PROC INITIALIZE^FILES; BEGIN INT INT INT INT .INFNAME; .OUTFNAME; ERR; .BUF[0:39]; STRING .
Using the Sequential Input/Output Procedures ! ! ! Sample Initialization If interactive, then set up the access mode for the input file for reading and writing.
Using the Sequential Input/Output Procedures Opening and Creating SIO Files 15 Using the Sequential Input/Output Procedures Opening and Creating SIO Files This subsection shows how to use the OPEN^FILE procedure to open SIO files. In addition to making files available to the SIO procedures that perform I/O, the OPEN^FILE procedure also sets up many file characteristics.
Using the Sequential Input/Output Procedures Opening SIO Files: Simplest Form To clear a flag, set the flag bit in flags-mask but not in flags (because the flags parameter defaults to 0). For example: FLAGS^MASK := AUTO^TOF; CALL OPEN^FILE(COMMON^FCB, OUTFILE, !block^buffer!, !block^bufferlen!, !flags!, FLAGS^MASK); If AUTO^TOF is not specified in flags-mask, then the flag value for the file is unchanged regardless of the value in flags.
Using the Sequential Input/Output Procedures Block Buffering With SIO Files If the file already exists, then the above open will proceed, ignoring the request to create the file. You can, however, cause the open to fail if the file already exists by setting the MUSTBENEW flag. If the MUSTBENEW flag is set when you attempt to create a file, and if the file already exists, the OPEN^FILE call returns error 10: CALL SET^FILE(OUTFILE, ASSIGN^OPENACCESS, WRITE^ACCESS); . .
Using the Sequential Input/Output Procedures ² Purging Data When Opening If a block buffer is used in a TNS caller, it must be located in G[0:32767] of the data area. This limitation does not apply to native callers. See Section 16, Creating and Managing Processes, for details. The following example specifies a block buffer named OUTBLKBUF that is 4096 bytes long: LITERAL OUTBUFLEN = 4096; INT .OUTBLKBUF[0:OUTBUFLEN -1]; . .
Using the Sequential Input/Output Procedures Reading and Writing SIO Files pTAL example: CALL CHECK^FILE(INFILE, FILE^FILENAME^ADDR, @INFNAME); TAL example: @INFNAME := CHECK^FILE(INFILE, FILE^FILENAME^ADDR); One example of file status that you might want to retrieve is whether an I/O operation is outstanding on a file. You may need this information, for example, before calling WAIT^FILE to check for completion of an I/O operation.
Using the Sequential Input/Output Procedures Handling Basic I/O With SIO Files Refer to Handling Nowait I/O, later in this section, for information about reading and writing to SIO files opened for nowait I/O. Refer to Handling Interprocess Messages for information on how to read and write to the $RECEIVE file using SIO procedures. Handling Basic I/O With SIO Files The SIO routines permit only sequential access to files (except when reading files in EDIT format).
Using the Sequential Input/Output Procedures Changing the Interactive Read Prompt Writing Records With SIO Procedures To write a record to a file, you must supply the WRITE^FILE procedure with the name of the FCB of the file to write to and of the buffer that contains the record to be written: CALL WRITE^FILE(OUTFILE, WRITE^BUFFER, WRITE^COUNT); Changing the Interactive Read Prompt Normally, a READ^FILE call against a terminal file displays a question mark (?) character on the terminal to prompt for inpu
Using the Sequential Input/Output Procedures Handling Long Writes Handling Long Writes The OPEN^FILE and SET^FILE procedures provide options that allow write operations that are longer than the logical-record length without losing any data. The SIO routines normally truncate write operations that are longer than the logicalrecord length, saving only that part of the write that fits into the logical record. In such cases, the system does not issue an error.
Using the Sequential Input/Output Procedures Handling Padding Characters The next example uses the SET^FILE procedure to permit long writes. Here, the writefold feature is turned on just before a record is written to the file. Then it is turned off again after the record is written. Subsequent writes are then limited to one logical record. LITERAL ON = 1; LITERAL OFF = 0; RECORD^LENGTH := 16; CALL SET^FILE(DFILE, ASSIGN^RECORDLENGTH, RECORD^LENGTH); CALL OPEN^FILE(COMMON^FCB, DFILE); . .
Using the Sequential Input/Output Procedures Handling Padding Characters Trimming Trailing Blanks Before Writing Use the WRITE^TRIM flag with the OPEN^FILE procedure or the SET^FILE SET^WRITE^TRIM operation to remove trailing blanks from a record before writing the record to a file. By default, write-trailing-blank-trimming is turned on.
Handling Padding Characters Using the Sequential Input/Output Procedures VST071.VSD Padding Short Records With Blanks When Writing Use the WRITE^PAD flag with the OPEN^FILE procedure or the SET^FILE SET^WRITE^PAD operation to pad a short record with blanks before writing the record to a file. By default, write-blank-padding is turned on for disk files with fixed-length records and turned off for all other files.
Handling Padding Characters Using the Sequential Input/Output Procedures CALL OPEN^FILE(COMMON^FCB, DFILE, !block^buffer!, !block^bufferlen!, WRITE^PAD, WRITE^PAD); . . WRITE^BUFFER ':=' "This is a record" -> @S^PTR; CALL WRITE^FILE(DFILE, WRITE^BUFFER, @S^PTR '-' @SWRITE^BUFFER); VST072.VSD Trimming Trailing Blanks on Reading By default, trailing blanks are trimmed from records after reading.
Handling Padding Characters Using the Sequential Input/Output Procedures The following statements turn it back on again using SET^FILE: LITERAL ON = 1; . . CALL SET^FILE(DFILE, SET^READ^TRIM, ON); In the next example, the READ^FILE call with read-trailing-blank-trimming turned off would indicate that 24 bytes have been read (the length of the record).
Using the Sequential Input/Output Procedures Writing to a Printer Writing to a Printer A program that uses SIO procedures can control or write to a printer in a way similar to programs that use regular Guardian procedure calls. Such programs can use the printer control language (PCL) as described in Section 11, Communicating With Printers, and in the user guide for your printer. A program can also enable level-3 spooling through SIO.
Using the Sequential Input/Output Procedures Accessing EDIT Files Issuing CONTROL Functions Use the forms-control-code parameter of the WRITE^FILE procedure to provide forms control for the printer. This parameter can take any value that is valid for the second parameter of CONTROL operation 1. You should not, however, try to use the CONTROL procedure; instead use WRITE^FILE. The following example issues a form feed before writing the contents of the write buffer to the file: LITERAL FORM^FEED = 0; . .
Using the Sequential Input/Output Procedures Opening an EDIT File Opening an EDIT File The way you open an EDIT file depends on whether you will read from the file or write to the file. Specifically, the minimum size of the block buffer is different for reading and for writing. The block buffer provides space for the EDIT file pages as they are assembled or disassembled.
Using the Sequential Input/Output Procedures Handling Nowait I/O contain the current file position in an internal format. Note that the current position points to the start of the record that sequentially follows the last record read. 2. Continue processing the EDIT file. 3. Later when you want to return to the saved position, you simply restore the three words to the block buffer and then reposition the file pointer by issuing the SET^FILE SET^EDITREAD^REPOSITION operation.
Waiting for One File Using the Sequential Input/Output Procedures 1. Issue a single SIO operation against one file 2. Continue processing while the I/O takes place 3. Finish the I/O with a call to the WAIT^FILE procedure Figure 15-3 shows nowait I/O applied to the READ^FILE procedure. The same model also applies to the WRITE^FILE procedure. Figure 15-3. Nowait I/O Applied to a Single SIO File VST074.VSD To perform nowait I/O on a file, you must open that file specifically for nowait I/O.
Using the Sequential Input/Output Procedures Waiting for One File Once the file is open, you can issue an I/O operation against it, such as the READ^FILE operation shown below. Note that the sixth parameter must contain a positive number for a nowait operation: CALL READ^FILE(DFILE, READ^BUFFER, BYTES^READ, !prompt^count!, !max^read^count!, 1); Nothing is returned at this point in either the read buffer or the count of bytes read, because the I/O operation has not yet finished.
Waiting for Any File Using the Sequential Input/Output Procedures 15 Using the Sequential Input/Output Procedures Waiting for Any File You can use SIO procedures to concurrently apply nowait I/O to multiple files. In other words, you can issue nowait I/O operations against more than one file and then wait for any of the I/O operations to finish. Here, you need to use the AWAITIO procedure as well as the WAIT^FILE procedure to complete the I/O operation.
Using the Sequential Input/Output Procedures Waiting for Any File Guardian procedures on the same file. You therefore need to obtain the file numbers to be able to access the files using the AWAITIO call. 3. Issue a nowait I/O operation against each file. Here, READ^FILE calls are issued. 4. Issue the AWAITIO call to receive the first I/O operation that finishes. You need to set the file number to -1, indicating that a response from any file will do.
Using the Sequential Input/Output Procedures Waiting for Any File !prompt^count!, !max^read^count!, 1); CALL READ^FILE(TERM2,READ^BUFFER,BYTES^READ, !prompt^count!, !max^read^count!, 1); !Use the AWAITIO call to receive the first I/O to finish: FNUM := -1; CALL AWAITIO(FNUM, !buffer^address!, RCOUNT); !If response received from terminal 1: IF FNUM := T1^FNUM THEN BEGIN !Establish the error value caused by AWAITIO: CALL FILE_GETINFO_(T1^FNUM,ERROR); !Update the FCB to indicate I/O complete: CALL SET^FILE(T
Using the Sequential Input/Output Procedures Handling Interprocess Messages !Or if response came from terminal 2: BEGIN !Establish the error value returned from AWAITIO: CALL FILE_GETINFO_(T2^FNUM,ERROR); !Update the FCB to indicate I/O complete: CALL SET^FILE(TERM2,SET^PHYSIOOUT,0); !Update the FCB with the number of bytes transferred: CALL SET^FILE(TERM2,SET^COUNTXFERRED,RCOUNT); !Update the FCB with the error number returned from the !FILE_GETINFO_ call following AWAITIO: CALL SET^FILE(TERM2,SET^ERROR,
Using the Sequential Input/Output Procedures Passing Messages and Reply Text Between Processes communication, however, is no different than for any other use of nowait I/O. Refer to Handling Interprocess Messages earlier in this section. Before sending and receiving messages, all processes involved must have initialized file FCBs for each file accessed by SIO procedures and must also have initialized the common FCB. Refer to Initializing SIO Files Using TAL or pTAL DEFINEs, earlier in this section.
Passing Messages and Reply Text Between Processes Using the Sequential Input/Output Procedures Sending and Receiving Messages in Two-Way Communication: An Example Figure 15-5 shows an example of two-way communication between a requester process and a server process. These processes pass data between terminal users in both directions. Each process reads some data from its input file and then sends the data to its output file and waits for a response.
Using the Sequential Input/Output Procedures Passing Messages Between Processes: No Reply Data Passing Messages Between Processes: No Reply Data If no data is expected in the reply from the server process, then the requester process can send messages to the server by issuing WRITE^FILE calls instead of READ^FILE. The reply is received as soon as the server reads the message, but it contains no data. There is no need for the server to explicitly send a reply.
Using the Sequential Input/Output Procedures Passing Messages Between Processes: No Reply Data The following example reads a message from $RECEIVE: CALL READ^FILE(RECV^FCB, READ^BUFFER, BYTES^READ); If you choose not to explicitly reply to the message, then on the next READ^FILE call issued by the server against $RECEIVE, SIO will notice that the preceding READ^FILE was never matched with a WRITE^FILE. SIO will then send a reply for the preceding READ^FILE.
Using the Sequential Input/Output Procedures Passing Messages Between Processes: No Reply Data Figure 15-6. Requester and Server Processes in One-Way Communication (pTAL Version) VST143.
Using the Sequential Input/Output Procedures Passing Messages Between Processes: No Reply Data Figure 15-7. Requester and Server Processes in One-Way Communication (TAL Version) VST077.
Using the Sequential Input/Output Procedures Communicating With Multiple Processes Communicating With Multiple Processes A server process can respond to messages from more than one requester process using either the one-way or the two-way communication model. Normally, however, SIO permits only one process pair to have the server process open at a time. To allow multiple openers, you need to handle the Open and Close messages in your application rather than let SIO handle them.
Using the Sequential Input/Output Procedures Selecting or Masking System Messages When keeping track of openers, SIO maintains a list of openers and limits the number of openers to one process pair. If more processes attempt to open your process, the SIO procedures reject the extra open attempts. To control attempts to open a process, SIO monitors the following system messages: -2 (Processor down) If the list of openers includes a process from the failing CPU, then SIO removes that process from the list.
Using the Sequential Input/Output Procedures Reading System Messages If the bit is set to 1, then your process receives the corresponding system message. If the bit is 0, then your process does not receive the message. The following example accepts only messages that indicate an attempted open or close of the current process. Bit 14 in the second word of the mask corresponds to the Open message. The example shows both the native and TNS forms of the SET^FILE call.
Using the Sequential Input/Output Procedures Taking BREAK Ownership This subsection is concerned with handling the BREAK key on a terminal that is initialized and opened as an SIO file. For details on how to handle the BREAK key for terminal files opened with regular Guardian procedure calls, refer to Section 10, Communicating With Terminals. Briefly, the purpose of the BREAK key is to allow the program user to signal the process.
Using the Sequential Input/Output Procedures Checking for a Break Message To take BREAK ownership, you must supply the FCB name for the terminal from which your program will accept Break messages: CALL TAKE^BREAK(TERM^FCB); The effect of using TAKE^BREAK with an SIO file is like issuing a call to SETMODE function 11 on a regular Guardian file, with parameter 2 of the SETMODE call equal to 0.
Using the Sequential Input/Output Procedures Handling BREAK Ownership: An Example procedure with the FCB name for the terminal from which you were accepting BREAK ownership: CALL GIVE^BREAK(TERM^FCB); Handling BREAK Ownership: An Example The following example responds to the BREAK key pressed at the terminal designated as the input file in the Startup message. Under normal operation, the process executes without interacting with the user.
Using the Sequential Input/Output Procedures Handling BREAK Ownership: An Example !-----------------------------------------------------------!Procedure called when CHECK^BREAK detects BREAK. It prompts !the user for some input and then writes the input to the !output file. !-----------------------------------------------------------PROC BREAK^PRESSED; BEGIN INT .
Handling BREAK Ownership With $RECEIVE Handled as a Non-SIO File Using the Sequential Input/Output Procedures ! Open the input file: CALL OPEN^FILE(COMMON^FCB,INFILE); ! Open the output file: CALL OPEN^FILE(COMMON^FCB,OUTFILE); ! Take ownership of the BREAK key pressed at the terminal: CALL TAKE^BREAK(INFILE); ! Loop indefinitely: WHILE 1 = 1 DO BEGIN ! ! Check whether the BREAK key has been pressed.
Using the Sequential Input/Output Procedures Handling SIO Errors The Break indication can then be picked up later by a CHECK^BREAK procedure call after you have closed $RECEIVE. CHECK^BREAK can then open $RECEIVE as an SIO file and check the indication set by the SET^FILE SET^BREAKHIT operation. Handling SIO Errors One of the advantages of using SIO procedures is that error handling is automatic.
Using the Sequential Input/Output Procedures Handling Fatal Errors SET^PRINT^ERR^MSG, NEW^VALUE); Redirecting Error Messages You can redirect error messages to an alternate file. Again you have two choices: you can specify redirection when you open a file or you can use SET^FILE. Use the error-file-fcb parameter in the OPEN^FILE procedure to set the errormessage file while opening a file.
Using the Sequential Input/Output Procedures Handling Fatal Errors abnormally terminating the process. The following example sets the ABORT^OPENERR flag: FLAGS := ABORT^OPENERR; FLAGS^MASK := ABORT^OPENERR; CALL OPEN^FILE(COMMON^FCB, INFILE, !block^buffer!, !block^bufferlen!, FLAGS, FLAGS^MASK); • Clearing the ABORT^OPENERR flag allows the process to continue, in spite of fatal errors. If an error occurs, the open will not finish and an error message is sent to the error file.
Using the Sequential Input/Output Procedures Handling Retryable Errors !block^buffer!, !block^bufferlen!, ABORT^XFERERR, ABORT^XFERERR); . . ERROR := READ^FILE(INPUT,BUFFER,RCOUNT); IF ERROR <> 0 THEN ... !Process nonfatal error. The next example clears the ABORT^XFERERR flag: CALL SET^FILE(INFILE, SET^ABORT^XFERERR, 0); . . ERROR := READ^FILE(INPUT, BUFFER, RCOUNT); IF ERROR <> O THEN ... !Process any error.
Using the Sequential Input/Output Procedures Handling Retryable Errors After sending the error message to one or more destinations, SIO retries the operation that caused the error. If the device in error is on the same system as the error file and if the error file is not $0, SIO expects the user to make the device ready before responding to the message. SIO therefore retries the operation immediately, and only once.
Using the Sequential Input/Output Procedures Closing SIO Files Errors Resulting From Path or Device Failure Errors that result from a path or device failure include the following: Error 120 Data parity Error 200 Device is owned by the other port Error 201 through 231 Path error Error 240 Line handler error; did not get started For error 201 (unable to communicate over this path), SIO retries the error once.
Using the Sequential Input/Output Procedures Allocating FCBs The following list introduces the steps involved in initializing SIO files without using TAL or pTAL DEFINEs: 1. Allocate space for each FCB. This step is required. 2. Initialize an empty FCB for each file. This step is required. 3. Specify the physical file name for each FCB. This step is required. 4. Set up the file-access characteristics. This step is optional; if omitted, the system uses default values.
Using the Sequential Input/Output Procedures Initializing FCBs Initializing FCBs You now need to associate each of the FCBs just allocated with a physical file. For FCBs allocated with FCBSIZE, use the SET^FILE INIT^FILEFCB operation.
Using the Sequential Input/Output Procedures Sample Initialization When dynamically allocating FCBs, you can set parameters only by using the SET^FILE procedure, not by using ASSIGNs. See Setting Up File Access earlier in this section for details. Sample Initialization The following procedure performs the same operations as the sample initialization shown earlier in this section under “Initializing SIO Files With the INITIALIZER Procedure.” This example, however, does not use INITIALIZER.
Using the Sequential Input/Output Procedures Sample Initialization !-----------------------------------------------------------!Procedure to initialize file control blocks for the input !file and output file specified in the Startup message, as !well as a separate data file. !-----------------------------------------------------------PROC INIT; BEGIN ! Initialize the $RECEIVE file and common FCB: ?IF PTAL !Begin pTAL statements STRING .
Sample Initialization Using the Sequential Input/Output Procedures !priority!, MOMS^PHANDLE); ! ! ! The following statement calls a DEFINE which executes the appropriate (native or TNS) form of the SET^FILE call.
Using the Sequential Input/Output Procedures Using the SIO Procedures: An Example @OUTFILE := @INFILE ELSE ! ! If the process is not run interactively, initialize the output file: BEGIN CALL SET^FILE(OUTFILE,INIT^FILEFCB); ! ! ! The following statement calls a DEFINE which executes the appropriate (native or TNS) form of the SET^FILE call.
Using the Sequential Input/Output Procedures Using the SIO Procedures: An Example You can create the data file using the FUP program. If the data file does not exist when you run the program, the program will create it as an EDIT file. Once you start the program, you can perform read and write operations against the data file. The initial read operation always returns the first record in the file. Writes are always appended to the file.
Using the Sequential Input/Output Procedures Using the SIO Procedures: An Example additional records. The procedure returns when either the user declines to read any more or the program reaches the end of the disk file. • • • • The WRITE^RECORDS procedure is similar to READ^RECORDS but appends records to the data file instead of reading. This procedure is called by the main program when the user selects appending from the main menu.
Using the Sequential Input/Output Procedures Using the SIO Procedures: An Example !This is a TAL example ?INSPECT, SYMBOLS, NOMAP ?NOLIST, SOURCE $SYSTEM.SYSTEM.GPLDEFS ?LIST !Allocate the RUCB and the common FCB: ALLOCATE^CBS(RUCB,COMMON^FCB,2); !Allocate an FCB for each SIO file: ALLOCATE^FCB(INFILE," #IN ALLOCATE^FCB(OUTFILE," #OUT LITERAL LITERAL INT INT INT STRING STRING "); "); BUFSIZE = 128; OUTBLKLEN = 4096; .OUTBLKBUF[0:OUTBLKLEN/2 -1]; .
Using the SIO Procedures: An Example Using the Sequential Input/Output Procedures !-----------------------------------------------------------! The following DEFINEs make it easier to format and print ! messages.
Using the Sequential Input/Output Procedures Using the SIO Procedures: An Example !-----------------------------------------------------------! Procedure to prompt the user for the next function to ! perform: ! ! "r" to read records ! "a" to append records ! "x" to exit the program ! ! The selection made is returned as the result of the call.
Using the Sequential Input/Output Procedures Using the SIO Procedures: An Example !-----------------------------------------------------------! Procedure to open the input and output files. The ! procedure opens structured disk files with a block buffer. ! Unstructured disk files are given no block buffer. The ! output file is opened first with write-only access to ! create it if it does not already exist. The procedure ! closes the file then reopens it with read/write access.
Using the Sequential Input/Output Procedures Using the SIO Procedures: An Example !-----------------------------------------------------------! Procedure to read records from the data file and display ! them on the terminal. Additional records are prompted for.
Using the Sequential Input/Output Procedures ! Using the SIO Procedures: An Example Prompt the user to read another record: PRINT^BLANK; SBUFFER ':=' "Do You Wish to Read Another Record (y/n)? " -> @S^PTR; CALL READ^FILE(INFILE,BUFFER,COUNT^READ, @S^PTR '-' @SBUFFER,BUFSIZE); END; END UNTIL NOT (SBUFFER = "y" OR SBUFFER = "Y"); ! Close the output file: CALL CLOSE^FILE(OUTFILE); END; Guardian Programmer’s Guide — 421922-014 15 - 79
Using the Sequential Input/Output Procedures Using the SIO Procedures: An Example !-----------------------------------------------------------! Procedure to write a new record to the disk file.
Using the SIO Procedures: An Example Using the Sequential Input/Output Procedures !-----------------------------------------------------------! Procedure to exit the program. !-----------------------------------------------------------PROC EXIT^PROGRAM; BEGIN ! Close all SIO files: CALL CLOSE^FILE(COMMON^FCB); ! Stop the program: CALL PROCESS_STOP_; END; !-----------------------------------------------------------! Procedure to respond to an invalid function.
Using the Sequential Input/Output Procedures ! Assign a logical file name to each SIO file: SBUF CALL SBUF CALL ! Using the SIO Procedures: An Example ':=' [5,"INPUT"]; SET^FILE(INFILE,ASSIGN^LOGICALFILENAME,@BUF); ':=' [6,"OUTPUT"]; SET^FILE(OUTFILE,ASSIGN^LOGICALFILENAME,@BUF); Initialize the FCBs: CALL INITIALIZER(RUCB); ! ! Get the physical file names for the input and output files: @INFNAME := CHECK^FILE(INFILE,FILE^FILENAME^ADDR); @OUTFNAME := CHECK^FILE(OUTFILE,FILE^FILENAME^ADDR); ! Make s
Using the Sequential Input/Output Procedures Using the SIO Procedures: An Example !-----------------------------------------------------------! Main procedure prompts the user to enter a function to read ! or write to the disk file or exit the program.
16 Creating and Managing Processes This section shows how to use Guardian procedures to manage Guardian processes.
Creating and Managing Processes Process Identifiers A process consists of the following: • • • Code areas in virtual memory that contain the instruction codes to be executed. These code areas are shared by all processes in the same CPU that execute the same program file. The instructions in the code areas in virtual memory are derived from the code part of the program file on disk. Data areas in virtual memory that contain the program variables and temporary storage that is private to the process.
Creating and Managing Processes Programs and Processes Process Handles A process handle can be considered to be the address at which a process resides. The process handle is 10 words long and contains the following information: • The process identification number (PIN) which is unique among all current processes on a given CPU. PIN values range from 0 to 65534. PINs 0 through 255 are called low PINs; PINs 256 through 65534 are called high PINs.
Creating and Managing Processes • • • • Programs and Processes TNS/E native programs TNS/R native programs TNS programs Accelerated programs A native process—a process that runs in native mode—consists entirely of nativecompiled instructions. Those instructions are RISC instructions on a TNS/R system and Itanium instructions on a TNS/E system. A native process is initiated by executing a native program.
Creating and Managing Processes Process Organization Kinds of Processes A TNS/R native process is a process that is initiated by executing a TNS/R native program. A native process executes in the native operating environment of the TNS/R CPU. A TNS process is a process that is initiated by executing a TNS or accelerated program. A TNS process executes within an emulated TNS operating environment.
Creating and Managing Processes • • • • • Process Organization UL (user library) SC (system code) SL (system library) SCr (system code RISC) SLr (system library RISC) For TNS/R native processes the code spaces are: • • • • • • UCr (user code RISC) Native UL (user library) SRL (shared run-time libraries) SCr (system code RISC) SLr (system library RISC) Ordinary DLLs (G06.
Process Organization Creating and Managing Processes procedure name. For example, the HEAPSORTX_ procedure in the system library calls a comparison routine, typically in user code, that is passed as a procedure parameter. However, such calls are not possible between TNS and native procedures except where a TNS procedure is calling a native procedure via its To-RISC shell.
Creating and Managing Processes Process Organization of the Guardian procedures are in both sets. Sometimes there are distinct TNS and native procedures having the same name and basic function, but more often a ToRISC shell lets the native code serve both kinds of caller. The SYSTEMENTRYPOINT procedure accepts names from the TNS set and returns 16-bit labels for TNS procedures and To-RISC shells in the system library.
Creating and Managing Processes Process Organization Data Spaces for Native Processes A TNS/R native process has the following data segments: • • • • • A globals-heap segment, containing program global data and, optionally, a heap. A main stack segment, containing the stack for unprivileged native procedures. A priv stack segment, containing the stack for privileged native procedures. Zero or more SRL data segments, used for optional global data owned by each shared run-time library (SRL).
Creating and Managing Processes Process Security maximum stack size via an eld or PROCESS_LAUNCH_ parameter up to a limit of 32MB. Also on both platforms, the heap can grow to the maximum size of the globals-heap segment less the size of the global data. On the TNS/R platform, the maximum globals-heap size is 1.1 GB. On the TNS/E platform, the maximum globals-heap size is 1536 MB. If your native program needs additional space for user data, you can add extended data segments to your process.
Creating and Managing Processes Relationship With Other Processes You can set the process access ID equal to the owner ID of the object file instead of to the process access ID of the creator process. Doing so gives the new process the access permissions of the file owner instead of the creator. The owner of the object file must set up this feature, however, either by using the FUP SECURE PROGID command or programmatically by using a call to the SETMODE procedure.
Relationship With Other Processes Creating and Managing Processes operating system uses the DCT entry to find out where to send the process-deletion message (system message -101). Note that because the DCT is a system-wide table, the operating system can find this information even if process $B was deleted because of a CPU failure. Process $A is also the ancestor of process $C. Because $C is a process pair, process $A gets the deletion message only if both members of the process pair are deleted.
Creating and Managing Processes Relationship With Other Processes Relationship of Processes Within a Job A job is a collection of related processes that are grouped for batch processing. The NetBatch utility identifies the job that a process belongs to by the job number held in the PCB of each process; processes belonging to the same job have the same job number. A job ancestor is the process that started the first process in a job.
Relationship With a Home Terminal Creating and Managing Processes Figure 16-3. Job Ancestor Relationships VST082.VSD Relationship With a Home Terminal Associated with each process is its home terminal. The IN and OUT files for the process is, by default, the home terminal. The home terminal is usually the same as for the creator of the process; it is passed to a new process when the process is created.
Creating and Managing Processes Process Subtype 16 Creating and Managing Processes Process Subtype The Guardian process subtype is an attribute that can be set at compile/bind time. A terminal-simulation program, for example, needs to be assigned process subtype 30 to allow it to assign itself a terminal-device type. See Section 24, Writing a Terminal Simulator, for an example. All HP compilers that are not native and the HP linkers let you set the process subtype. The default process subtype is zero.
Creating and Managing Processes Process Priority Table 16-1. Priority Values for System and User Processes System Process Priority User Process Disk I/O processes TMF monitor Memory manager 220 211 210 . . . Operator process, $NCP, monitor, I/O processes other than disk, and so on 209 . . 200 . . . 200 $TMP 190 TACL processes used to run application processes Application processes Priority 199 . .
Creating and Managing Processes Process Priority Figure 16-4. Execution Priority Example VST083.VSD Notice that CPU time alternates between the two processes with priority 199. When one process is suspended for I/O, the other process runs. The only time that the process with priority 150 executes is when both of the other processes are suspended. Additionally, the lower-priority process is immediately suspended when a higher-priority process becomes ready.
Creating and Managing Processes Process Execution Process Execution From creation to deletion, a process enters and leaves several process states. Figure 16-5 shows the various process states that a process can be in. Figure 16-5. Process States VST084.VSD The states shown in Figure 16-5 are described as follows: Debug and Inspect Either the Debug or Inspect debugger has been invoked on the process.
Creating and Managing Processes Creating Processes The runnable state is of most interest. Only processes that are in the runnable state are capable of using the CPU. Figure 16-6 shows the possible substates of a runnable process. Figure 16-6. Runnable Processes VST085.VSD The CPU is composed of one or more IPUs, each of which can execute a process. Associated with each IPU is a list of READY processes to be run on that IPU which is called a ready list.
Creating and Managing Processes Creating Processes The PROCESS_CREATE_ procedure does not allow you to specify values for some of the attributes that are associated with TNS/R native processes. Because it has less function than the PROCESS_LAUNCH_ procedure, PROCESS_CREATE_ is not discussed further in this section. (However, PROCESS_CREATE_ continues to be used in code examples in other sections of this guide.
Creating and Managing Processes Using the PROCESS_LAUNCH_ Procedure Using the PROCESS_LAUNCH_ Procedure The only required parameter to the PROCESS_LAUNCH_ procedure is a structure that allows you to specify values establishing the attributes of the new process. In the DLAUNCH file, which is located on $SYSTEM.SYSTEM, this structure is defined as follows: STRUCT PROCESS_LAUNCH_PARMS_(*) FIELDALIGN(SHARED2); BEGIN INT VERSION; ! version of the structure INT LENGTH; ! length of the structure STRING .
Creating and Managing Processes Using the PROCESS_LAUNCH_ Procedure programs, declarations are contained in the ZSYSC file. For further information about using the ZSYS* files, refer to the subsection Using Parameter Declarations Files in Section 1, Introduction to Guardian Programming. The main output of the PROCESS_LAUNCH_ procedure is returned by a parameter that is also formatted as a structure.
Creating and Managing Processes Creating an Unnamed Process Creating an Unnamed Process Remember that processes can be named or unnamed. To create an unnamed process, you call the PROCESS_LAUNCH_ procedure with the NAME_OPTIONS field set to 0 in the input parameter structure. To set the NAME_OPTIONS field, you can use the ZSYS^VAL^PCREATOPT^NONAME literal from the ZSYSTAL file. All you need to supply is the program file name. The following example creates an unnamed process: ?NOLIST ?SOURCE $SYSTEM.
Creating and Managing Processes Creating a Named Process The following example supplies a process name: #include #include "$system.zsysdefs.zsysc(zsys_ddl_smsg_proccreate, \ process_constant)" #include . . process_launch_parms_def paramList = P_L_DEFAULT_PARMS_; zsys_ddl_smsg_proccreate_def outputList; short error, errorDetail, outputListLen; . . paramList.program_name = "PROGFILE"; paramList.program_name_len = sizeof("PROGFILE") - 1; paramList.
Creating and Managing Processes Creating a Named Process name. You use the options parameter and, if appropriate, the nodename:length parameter to specify whether you want to add a node name to the process name. The following example requests a 6-character process name to include a node name. The name is returned in the PROCESS^NAME parameter. This, and the remaining examples are in TAL. LITERAL SIX^CHARACTERS = 1, INCLUDE^NODENAME = 0; . .
Creating and Managing Processes Creating a Process in a Nowait Manner Creating a Process in a Nowait Manner If you call the PROCESS_LAUNCH_ procedure with the NOWAIT_TAG field of the input parameter structure set to any value other than -1, your process returns immediately without waiting for completion of the operation. Instead, your process receives notification with system message -102 (the nowait PROCESS_LAUNCH_ or PROCESS_CREATE_ completion message) when the operation finishes.
Creating and Managing Processes Analyzing Process-Creation Errors ERROR := PROCESS_LAUNCH_( PARAM_LIST, ERROR_DETAIL); IF ERROR <> 0 THEN ... . . CALL READUPDATEX(RCV^NUM,SBUFFER,RCOUNT,BYTES^READ); IF <> THEN ... IF BUFFER[0] = -102 THEN !Process create BEGIN ! completion message IF BUFFER [13] <> 0 THEN ...
Creating and Managing Processes Specifying Process Attributes and Resources OUTPUT_LIST_LEN); IF ERROR <> 0 THEN BEGIN CASE ERROR OF BEGIN '1' -> CALL FILE^ERRORS(ERROR_DETAIL); !To process the ! file-system error '2' -> CALL PARAM^ERROR(ERROR_DETAIL); !To process the ! parameter error . .
Creating and Managing Processes Specifying Process Attributes and Resources Figure 16-7 summarizes how these conditions affect whether new processes run at a high PIN. A description of each of these entities follows. The Force-Low Flag If you set bit 31 (the force-low flag) to 1 in the CREATE_OPTIONS field of the input parameter structure when you call PROCESS_LAUNCH_, the new process will run at a low PIN. (Note that processes started with the C-series NEWPROCESS procedure always run at a low PIN.
Creating and Managing Processes Specifying Process Attributes and Resources The nld and Binder utilities only need to set this attribute once, either after building the object file (using the CHANGE command) or while building the object file (using the SET command). Caution. Some C-series system procedures (such as MYPID) do not support high PINs. If your program contains any such calls then the process will stop with a run-time error.
Creating and Managing Processes Specifying Process Attributes and Resources Starting a High-PIN Process Programmatically: An Example The following code fragment creates a new process to run at a high PIN or low PIN depending on the inherited force-low characteristic. Note that CREATE_OPTIONS.<31> is set to 0 in the input parameter structure. This example assumes that the HIGHPIN file attribute is set in the program file of the process that you are creating: . . PARAM_LIST.
Creating and Managing Processes Specifying Process Attributes and Resources OUTPUT_LIST:$LEN(OUTPUT_LIST), OUTPUT_LIST_LEN); Sizing the TNS User Data Area If you are creating a TNS process, you can use any of the following methods to specify the number of pages of user data area that your new process can occupy: • • • • The DATAPAGES compiler directive or pragma; see the appropriate compiler manual for details. The Binder program; see the Binder Manual for details.
Creating and Managing Processes Specifying Process Attributes and Resources 34, or a DEFINE error 2052, then you need extra PFS space for your program to run. You can create a PFS of up to one megabyte. If you need to reduce the amount of virtual memory used by your process, you might be able to reduce the PFS size to a minimum of 128 KB. You can use a linker (either the Binder or the nld utility) to specify the size of the PFS. (See the Binder Manual or the nld and noft Manual).
Creating and Managing Processes Specifying Process Attributes and Resources PROCESS_LAUNCH_ procedure are unused except for information purposes (that is, to support programs that use the swap file name to determine the volume on which to create temporary files). The actual swap space is handled by the Kernel-Managed Swap Facility (KMSF), regardless of whether these fields are used.
Creating and Managing Processes Specifying Process Attributes and Resources ERROR_DETAIL, OUTPUT_LIST:$LEN(OUTPUT_LIST), OUTPUT_LIST_LEN); IF ERROR <> 0 THEN ... Specifying a Device Subtype You can assign a device subtype attribute to a process at compile or link time. (There is no input parameter to PROCESS_LAUNCH_ for specifying a subtype attribute.) One use for giving a process a device subtype is when creating a terminal simulation process as described in Section 24, Writing a Terminal Simulator.
Creating and Managing Processes Specifying Process Attributes and Resources When you start a job, you assign the job number to the first process in the job when you create the first process. The process that calls the PROCESS_LAUNCH_ procedure with a value specified in the JOBID field is known as the job ancestor (also known as the godmother or GMOM).
Creating and Managing Processes • • • Specifying Process Attributes and Resources By default (bits 27 and 28 set to 0), only DEFINEs in the context of the current process are propagated to the new process. Set bit 28 to 1 and bit 27 to 0 to propagate DEFINEs saved by the current process. The address of the propagated DEFINE is passed in the DEFINES field of the input parameter structure of the PROCESS_LAUNCH_ procedure call.
Creating and Managing Processes Sending the Startup Sequence to a Process Set CREATE_OPTIONS.<25> to 0 to cause D-series behavior. With this option, the Process-deletion message is sent only to the caller of PROCESS_LAUNCH_ or to the instance of the process pair that contains the caller of PROCESS_LAUNCH_. An “instance” is any process in an unbroken chain of primary and backup processes. Every process that is part of an instance has the same sequence number. Set CREATE_OPTIONS.
Creating and Managing Processes Sending and Receiving the Startup Message 8. Close the new process. Sending and Receiving the Startup Message The following example shows two processes. The first process creates the second process and then sends it the Startup message. The first program is an extension of the program shown in Section 8, Communicating With a TACL Process, that receives the Startup message. This example first reads its own Startup message by calling INITIALIZER.
Creating and Managing Processes Sending and Receiving the Startup Message ?NOLIST, SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?LIST !Global parameters LITERAL MAXLEN = ZSYS^VAL^LEN^FILENAME; !Maximum file-name length INT OUTNUM; !OUT file number INT INNUM; !IN file number INT .S^PTR; !pointer to end of string (word address) STRUCT .
Creating and Managing Processes Sending and Receiving the Startup Message !-----------------------------------------------------------! Procedure to perform initialization for the program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .IN^NAME[0:MAXLEN - 1]; !string ! name INT INNAME^LEN; !length STRING .
Creating and Managing Processes Sending and Receiving the Startup Message !-----------------------------------------------------------! Main procedure calls INIT to read and save the Startup ! message and open the IN and OUT files, creates a new ! process, then passes on its own Startup message to the ! new process.
Creating and Managing Processes ! ! ! Sending and Receiving the Startup Message Send the new process the Startup message. The receiving process reply with file-system error ZFIL^ERR^CONTINUE indicating that it is ready to receive Assign messages.
Creating and Managing Processes Sending and Receiving the Startup Message ? NOLIST ? SOURCE $SYSTEM.SYSTEM.EXTDECS0(INITIALIZER,FILE_OPEN_, ? PROCESS_STOP_,OLDFILENAME_TO_FILENAME_) ? LIST !-----------------------------------------------------------! Procedure to save the Startup message in a global ! structure. !-----------------------------------------------------------PROC START^IT(RUCB,START^DATA,MESSAGE,LENGTH,MATCH) VARIABLE; INT .RUCB,.START^DATA,.MESSAGE,LENGTH,MATCH; BEGIN CI^STARTUP.
Creating and Managing Processes Sending and Receiving the Startup Message !-----------------------------------------------------------! Procedure to perform initialization for the program. !-----------------------------------------------------------PROC INIT; BEGIN STRING .IN^NAME[0:MAXLEN - 1]; !string ! name INT INNAME^LEN; !length STRING .
Creating and Managing Processes Sending and Receiving Assign and Param Messages !-----------------------------------------------------------! Main procedure calls INIT to read and save the Startup ! message and open the IN and OUT files, creates a new ! process, then passes on its own Startup message to the new ! process.
Creating and Managing Processes Deleting Processes A simple way to check whether the condition that caused one of these messages caused a specific process to stop is to use the CHILD_LOST_ procedure. You supply the CHILD_LOST_ procedure with the message read from $RECEIVE and the process handle of the child process that you wish to monitor. For example: INT BUFFER[0:511]; STRING .SBUFFER := @SBUFFER '<<' 1; . .
Creating and Managing Processes Deleting Your Own Process The Process deletion message contains the following information: Structure of the Process deletion message: sysmsg[0] sysmsg[1] FOR 10 WORDS sysmsg[11] FOR 4 WORDS sysmsg[15] not sysmsg[16] sysmsg[17] sysmsg[18] FOR 6 WORDS sysmsg[24] FOR 10 WORDS sysmsg[34] sysmsg[35] sysmsg[36] of sysmsg[37].<0:14> sysmsg[37].
Creating and Managing Processes Deleting Your Own Process completion code information. Abnormal termination and completion codes are described in the following paragraphs. Abnormal Deletion You can indicate abnormal deletion by setting the options parameter to 1 for abnormal deletion. The operating system sends out the Process deletion message with bit 15 of word 37 set to 1.
Creating and Managing Processes Deleting Other Processes COMPLETION^CODE, TERMINATION^INFO, SPI^SUBSYSTEM^ID, MESSAGE^TEXT:TEXT^LENGTH) For details about SPI error numbers and SPI subsystem identifiers, refer to the SPI Programming Manual. Programs not using SPI can use the termination-info and text:length parameters to report arbitrary integer and string values, respectively. TACL displays nonzero termination-info and non-empty text for processes that it runs.
Creating and Managing Processes Using Stop Mode to Control Process Deletion . . !Delete the process created earlier: ERROR := PROCESS_STOP_(OUTPUT_LIST.Z^PHANDLE); IF ERROR <> 0 THEN ... In this case, the process issuing the PROCESS_STOP_ procedure call also receives the Process-deletion message because it is the creator of the process. Using Stop Mode to Control Process Deletion You can use the SETSTOP procedure to determine who has the authority to delete your process.
Creating and Managing Processes Suspending and Activating Processes files and devices it has exclusive access to. However, if the error returned is 0, then the process will not execute any more code. The best way to ensure that the process is terminated and its resources freed is to wait for system message -101 (Process deletion), which is sent to its creator when the process terminates. Suspending and Activating Processes Remember that a process can alternate between the suspended and runnable states.
Creating and Managing Processes Activating Another Process If the process identified by PROCESS^HANDLE does not exist, then the PROCESS_SUSPEND_ procedure returns error 14. If your process does not have the authority to suspend the identified process, then the PROCESS_SUSPEND_ procedure returns error 48.
Creating and Managing Processes • • • • • • • Getting Process Information The process handle The process descriptor Information about related processes, such as the process handle of the job ancestor, the job ID, and the process handle of the mom process The name and length of the program file The name and length of the swap file The execution priority Process timing information (see Section 18, Managing Time) PROCESS_GETINFOLIST_ provides lists of more detailed information about a specific process or a
Creating and Managing Processes Getting Process Information The next example returns the creator access ID and process access ID of the process named $P2: PROCESS^NAME ':=' "$P2" -> @S^PTR; NAME^LENGTH := @S^PTR '-' @PROCESS^NAME; ERROR := FILENAME_TO_PROCESSHANDLE_(PROCESS^NAME:NAME^LENGTH, PROCESS^HANDLE); IF ERROR <> 0 THEN ...
Creating and Managing Processes Getting Process Information Getting List Information About One Process To use the PROCESS_GETINFOLIST_ procedure to get list information about one process, you supply the procedure with an identifier of the process you want detailed information about. The identifier can be the process handle or a combination of the node name, CPU number, and PIN.
Creating and Managing Processes Getting Process Information In the example above, RETURN^ATTRIBUTE^LIST contains a list of integer values that specify the information you want returned. The procedure returns the information in one array: RETURN^VALUES^LIST. The attribute values are returned in the order in which they were requested in RETURN^ATTRIBUTES^LIST. The attribute values returned by PROCESS_GETINFOLIST_ can be of any valid data type.
Creating and Managing Processes Getting Process Information ERROR := PROCESS_GETINFOLIST_(!cpu!, PIN, !nodename:length!, !process^handle!, RETURN^ATTRIBUTE^LIST, RETURN^ATTRIBUTE^COUNT, RETURN^VALUES^LIST, RETURN^VALUES^MAXLEN, RETURN^VALUES^LEN, ERROR^DETAIL, SEARCH^OPTION, SEARCH^ATTRIBUTE^LIST, SEARCH^ATTRIBUTE^COUNT, SEARCH^VALUES^LIST, SEARCH^VALUES^LEN); The search-option parameter, when set to 2, causes the procedure to search all processes with a PIN greater than or equal to the PIN specified in t
Creating and Managing Processes Getting Process Information OPTIONS := 1; DO BEGIN ERROR := PROCESS_GETPAIRINFO_(!process^handle!, PROCESS^NAME:MAXLEN, NAME^LENGTH, !primary^processhandle!, !backup^processhandle!, SEARCH^INDEX, !ancestor^processhandle!, NODE^NAME:NODENAME^LENGTH, OPTIONS); CASE ERROR OF BEGIN 2 -> BEGIN ! Process parameter error . . END; 3 -> BEGIN ! Process bounds error . . END; 10 -> BEGIN ! Process error: unable to communicate with node . .
Creating and Managing Processes Setting Process Information Using the PROCESSHANDLE_GETMINE_ Procedure A process can retrieve its own process handle by calling the PROCESSHANDLE_GETMINE_ procedure. While you can achieve the same result by passing a null process handle to the PROCESS_GETINFO_ procedure, PROCESSHANDLE_GETMINE_ performs this task more efficiently and without the need to initialize the process handle: INT .MYPHANDLE[0:ZSYS^VAL^PHANDLE^WLEN - 1]; . .
Creating and Managing Processes Manipulating Process Identifiers For example, the following call to PROCESS_SETINFO_ sets the qualifier-info-available attribute: LITERAL QUALIFIER^INFO^AVAILABLE = 49; . .
Creating and Managing Processes Retrieving Information From a Process Handle FILENAME_TO_PROCESSHANDLE_ and PROCESSHANDLE_TO_FILENAME_ procedures to convert between process handles and process file names. Retrieving Information From a Process Handle To retrieve information from a process handle, use the PROCESSHANDLE_DECOMPOSE_ procedure.
Creating and Managing Processes Converting Between Process Handles and Process File Names and unnamed process file names. For a named process, the operating system looks up the process handle in the DCT, and it returns error 14 (device does not exist) if the process name does not exist. For unnamed processes, the operating system does not check for the existence of the process. It creates a process handle from the information provided in the process file name.
Creating and Managing Processes Controlling the IPU Affinity of Processes NAME^LENGTH, OPTIONS); Converting a Process Handle Into a Process String To convert a process handle into a string, use the PROCESSHANDLE_TO_STRING_ procedure. This procedure is useful for producing a more readable output than that produced by the PROCESSHANDLE_TO_FILENAME_ procedure, particularly for unnamed processes. For example, a process file name returned by PROCESSHANDLE_TO_FILENAME_ could be “$:2:137:987654321.
Creating and Managing Processes Controlling the IPU Affinity of Processes Note. Beginning with the J06.12 and H06.23 RVUs, there are two instances of the TSMSGIP process active on each CPU on quad-core (4-IPU) systems, both of which by default are assigned to IPU 0. However, prior to J06.14 and H06.25, only the first instance is active and available to do work. The TSMSGIP process handles interrupts generated by inter-CPU Message System traffic. Starting with J06.14 and H06.
Creating and Managing Processes Controlling the IPU Affinity of Processes The IPUAFFINITY_GET_ procedure can be used to get the current IPU a process associated with, as well as an indicator if the process can be the target of an IPUAFFINITY_SET call. The IPUAFFINITY_CONTROL_ procedure is used to control Process Scheduler characteristics. It can cause the process scheduler to stop scheduling (that is changing) all processes with soft affinity or all DP2 processes (group affinity processes).
17 Managing Memory An Introduction to Memory-Management Procedures The following system procedures are available for managing memory from your application: ADDRESS_DELIMIT_ Returns information about a particular area of the user’s logical address space, including the addresses of the first and last bytes in that area. GETPOOL_PAGE Obtains a block of memory from a buffer pool.
Managing the User Data Areas Managing Memory SEGMENT_GETINFO_ Returns information about an allocated extended data segment. The information returned may include the size of the extended data segment or the name of the associated swap file. SEGMENT_USE_ Makes a selectable segment current. The current selectable segment is the only selectable segment that your process can access. (A flat segment need not be made current; your process can access all allocated flat segments.
Managing the TNS User Data Segment Managing Memory Specifying the Size of the TNS User Data Segment You can specify the size of the user data segment to be any number of data pages of 2048 bytes each, up to a maximum of 128K bytes. You can set this size either by using the compiler or Binder program or when you call the PROCESS_CREATE_ or PROCESS_LAUNCH_ procedure. To supply the size of the user data segment at compile/bind time, you set the compiler directive for the appropriate high-level language.
Managing Memory Managing the TNS User Data Segment S (stack) register points to the last entry in the stack; in this case, it points to the dummy stack marker. When the main procedure executes, its local variables get added to the stack as shown in Figure 17-2(b). The S register points to the last location of the local variables.
Managing the TNS User Data Segment Managing Memory Figure 17-2. The Data Stack VST088.VSD Figure 17-2(e) shows what happens when procedure 2 returns. The process continues in the calling procedure at the program address immediately following the call to the procedure that just returned. It does this by restoring the saved values for the P register, E register, and L register. The S register moves back to the end of the local variables for procedure 1.
Managing the Native User Data Areas Managing Memory the values saved on the stack so that processing continues at the address immediately following the call to the procedure that just returned. The S register points to the last location of the local variables of the main procedure. The TNS user space also includes a main stack and a priv stack, which are used when a TNS procedure calls a TNS/R native procedure. When a nonprivileged native procedure is called, execution switches to the main stack.
Managing the Native User Data Areas Managing Memory • • • • • • A main register stack engine (RSE) backing store for nonprivileged procedures A privileged RSE backing store for privileged procedures Zero or more DLL data segments Zero or more private DLL code segments A process file segment (PFS), used by the operating system Optional program-allocated extended data segments (selectable or flat segments) Figure 17-3 shows the layout of the data segments for a native process.
Managing the Native User Data Areas Managing Memory Figure 17-3. Data Segments for TNS/R Native Processes Address%h08000000 Global Variables GlobalHeap Segment Direction of growth Heap (C, C++ programs) Area used for flat segments and growth of heap and main stack Alt PFS PFS Address%h6CB00000 Direction of growth Public DLL Data Priv Stack Address %h60000000 Address %h70000000 Data Stack VST144.
Managing Memory Managing the Native User Data Areas globals-heap size is 1.1 GB. On the TNS/E platform, the maximum globals-heap size is 1536 MB. If your program needs data areas in addition to the area provided by the globals-heap segment, you can allocate one or more flat segments or selectable segments, as described later in this section under Using (Extended) Data Segments.
Managing Memory Managing the Native User Data Areas Figure 17-4(c) shows what happens when procedure 1 calls procedure 2. Again the calling procedure places up to four parameters into registers, stores any additional parameters into its callout area (which becomes the callin area for procedure 2), and stores the return address into the ra register. Additional procedures can be called in this way.
Managing the Native User Data Areas Managing Memory Figure 17-4(d) and 17-4(e) show how the main stack contracts as the called procedures return control to the calling procedures.
Managing the Native User Data Areas Managing Memory Figure 17-4. Native Main Stack VST145.
Managing the Native User Data Areas Managing Memory Changing the Maximum Size of the Heap The heap is managed by the Common Run-Time Environment (CRE). It is created at a system-defined initial size and is increased automatically as needed during process execution. For pre-G05 systems, the maximum allowable size of the heap defaults to the difference between 128 megabytes (the maximum combined size of the heap and globals area) and the size of the globals area (fixed at process creation time).
Managing the Native User Data Areas Managing Memory unnamed process and set the upper bound of the main stack to 10 megabytes. The example sets the mainstack^max parameter to 10000000D. STRING PROG_NAME[0:ZSYS^VAL^LEN^FILENAME-1]; INT .EXT ERROR_DETAIL, OUTPUT_LIST_LEN; STRUCT OUTPUT_LIST(ZSYS^DDL^SMSG^PROCCREATE^DEF); STRUCT PARAM_LIST(PROCESS_LAUNCH_PARAMS); . . PARAM_LIST ':=' P_L_DEFAULT_PARMS_; !initialize param struct PROG_NAME ':=' "PROGFILE" -> @S^PTR; !program file name @PARAM_LIST.
Checking the Bounds of Your Data Areas Managing Memory • A nonzero value indicates that there was not enough room and the stack could not be enlarged. The value also indicates the reason why the stack could not be enlarged (for example, the maximum allowable stack size would be exceeded, or memory or swap space could not be allocated). If called from a nonprivileged procedure, HEADROOM_ENSURE_ operates on the main stack; if called from a privileged procedure, it operates on the priv stack.
Using (Extended) Data Segments Managing Memory address-descriptor output parameter to obtain a set of flags that describe the area. The following example can be run in both the native and TNS environments. In the TNS environment, the address of a local variable contained in the user data segment is passed to ADDRESS_DELIMIT_. The procedure returns the addresses of the first byte (HIGH^ADDR) and last byte (LOW^ADDR) of the user data segment, which are then used to determine its size.
Overview of Selectable Segments Managing Memory extended data segments. There are two types of extended data segments: flat segments and selectable segments. A flat segment can be any size up to 128 megabytes on G04.00 and all earlier G-series releases and all D-series releases; a selectable segment can be any size up to 127.5 megabytes on G04.00 and all earlier G-series releases and all D-series releases. A flat segment or a selectable segment can be up to 1120 megabytes on G05.
Overview of Selectable Segments Managing Memory Figure 17-5. Relative Location of Selectable Segments Process Private Space/ Non-global addresses Address Octal 377777 (128K Bytes) Address 0 User Data Segment Selectable Segment Base Address =Octal 2000000 (0.5 megabytes) Current Selectable Segment Other Allocated Selectable Segments Maximum Address = Octal 777777777 (128 megabytes) Main Stack Segment VST146.
Overview of Flat Segments Managing Memory 17 Managing Memory Overview of Flat Segments The second type of extended data segment is the flat segment. You can allocate space for multiple flat segments, and all are accessible to the process that allocated them. Each flat segment is allocated at a different starting address on a 32-megabyte boundary (G04.00 and earlier G-series releases and all D-series releases; there is no boundary. In G05.00 and later G-series releases, only a limit of 1120 megabytes.
Which Type of Segment Should You Use? Managing Memory Figure 17-6. Relative Location of Flat Segments Address 0 Address %h08000000 Used by TNS processes and selectable segments Globals-Heap Segment Flat Segment A Flat Segment B Flat Segment C Process Private Space/Non-global Addresses Main Stack Segment VST147.VSD Which Type of Segment Should You Use? Selectable segments are a carryover from earlier architectures. They will continue to be supported on newer systems.
Managing Memory Using Selectable Segments in TNS Processes statements; use of the MOVEX procedure is not required. Depending on the number of SEGMENT_USE_ and MOVEX calls in your program, removing them can provide a significant performance enhancement. • • • • Flat segments provide access to more virtual memory and enable you to access areas of memory that were previously inaccessible.
Accessing Data in Extended Data Segments Managing Memory The program can safely create or share a selectable segment without selecting it, instead using the MOVEX procedure to copy data between it and the normal program environment. Accessing Data in Extended Data Segments You can access data in flat segments and selectable segments by using Guardian procedures.
Attributes of Extended Data Segments Managing Memory Attributes of Extended Data Segments Normally, an extended data segment is private to the process that owns it, allows both read and write access, and is created with its intended size.
Allocating Extended Data Segments Managing Memory Allocating a Selectable Segment Using the SEGMENT_ALLOCATE_ procedure call, you can allocate one or more selectable segments. Each selectable segment can be as many as: • • • 127.5 megabytes long on G04.00 and earlier G-series releases and all D-series releases 1120 megabytes long on G05.
Allocating Extended Data Segments Managing Memory • • • The size of the segment in bytes. The maximum size of a flat segment is 1120 megabytes in the TNS/R environment and 1536 megabytes in the TNS/E environment. In case of native mode C / C++ applications, the maximum size of a flat segment may not be 1120 MB since the address space used for flat segments is also used for the heap. The base-address parameter. You can specify this parameter as either an output parameter or an input parameter.
Allocating Extended Data Segments Managing Memory Example of Allocating Extended Data Segments The following example allocates four flat segments of 100,000 bytes each. These flat segments are identified by segment IDs 0 through 3: OPTIONS := 0; OPTIONS.
Managing Memory Allocating Extended Data Segments The difference between the temporary swap file and the permanent swap file is that when the extended data segment is later deallocated, the permanent swap file remains on disk and can be accessed after the segment is deallocated. It can be used, for example, to set the initial data in another segment that later uses it as a swap file; thus, it can serve as a record of a past state.
Managing Memory Checking Whether an Extended Data Segment Is Selectable or Flat Checking Whether an Extended Data Segment Is Selectable or Flat Use the SEGMENT_GETINFO_ procedure to check whether a previously allocated extended data segment is a selectable segment or flat segment.
Managing Memory Referencing Data in an Extended Data Segment You can also specify the optional base-address output parameter to return the address of the start of the allocated segment, along with the optional error-detail parameter to return a value giving more information about any nonzero error value.
Referencing Data in an Extended Data Segment Managing Memory Referencing Data in a Selectable Segment Following are extended pointers for STRING, INT, and INT(32) data types. The pointers will be used to access a selectable segment. INT(32) BASE^ADDR; STRING .EXT STR^PTR; INT .EXT INT^PTR; INT(32) .
Managing Memory Referencing Data in an Extended Data Segment Figure 17-7. Referencing a Selectable Segment VST090.
Referencing Data in an Extended Data Segment Managing Memory Referencing Data in a Flat Segment Following are extended pointers for STRING and INT data types. The pointers will be used to access data in two flat segments. INT(32) BASE^ADDR^A; STRING .EXT STR^PTR^A; INT .EXT INT^PTR^A; INT(32) BASE^ADDR^B STRING .EXT STR^PTR^B; INT .
Referencing Data in an Extended Data Segment Managing Memory Figure 17-8. Referencing Flat Segments VST141.
Managing Memory Checking the Size of an Extended Data Segment Checking the Size of an Extended Data Segment To determine the size of a flat segment or a selectable segment (regardless of whether or not the selectable segment is currently in use), supply the SEGMENT_GETINFO_ procedure with the appropriate segment ID.
Managing Memory Transferring Data Between an Extended Data Segment and a File SEGMENT^ID := 1; SIZE := 8000D; MAX^SIZE := 64000D; ERROR := SEGMENT_ALLOCATE_(SEGMENT^ID, SIZE, !swap^file:length!, ERROR^DETAIL, !pin!, !segment^type!, BASE^ADDRESS[I], MAX^SIZE); IF ERROR <> 0 THEN CALL ERROR^HANDLER; . . ERROR := RESIZESEGMENT (SEGMENT^ID, MAX^SIZE); IF ERROR <> 0 THEN ... Note. RESIZESEGMENT is a resource-intensive procedure. You should therefore avoid frequent calls to this procedure.
Transferring Data Between an Extended Data Segment and a File Managing Memory For example, the following statements transfer 10 bytes of data from the beginning of the extended data segment to the terminal: INT(32) .EXT EXT^PTR := BASE^ADDRESS; . . WCOUNT := 10; CALL WRITEX(TERM^NUM,EXT^PTR,WCOUNT); In the above example, BASE^ADDRESS is the byte address of the beginning of the extended data segment.
Managing Memory Moving Data Between Extended Data Segments To check for completion of a nowait operation on an extended data segment, use the AWAITIOX procedure. Moving Data Between Extended Data Segments The method to use for transferring data between two extended data segments depends on whether the segments are selectable segments or flat segments.
Checking Address Limits of an Extended Data Segment Managing Memory If the MOVEX call was successful, then the call returns an error value of 0. Any other value indicates an error. Typical causes of error are a nonexistent data segment (error 2) or an out-of-bounds address (error 22). The following example uses assignment statements to perform a similar move between flat segments: INT(32) BASE^ADDR1; INT(32) BASE^ADDR2; INT(32) .EXT SOURCE^PTR; INT(32) .EXT TARGET^PTR; OPTIONS.
Sharing an Extended Data Segment Managing Memory . . ERROR := ADDRESS_DELIMIT_ (@MY^EXT^DATA, ! low^address !, HIGH^ADDR, ! address^descriptor ! , SEGMENT^ID, ERROR^DETAIL); IF ERROR <> 0 THEN CALL ERROR^HANDLER; Sharing an Extended Data Segment Processes that share data can choose to share extended data segments. You do this by setting appropriate parameters in the SEGMENT_ALLOCATE_ procedure call. Note.
Sharing an Extended Data Segment Managing Memory • • The process access ID of the group manager for the process access ID of the process that allocated the extended data segment The super ID To specify sharing using the PIN method, your process must call the SEGMENT_ALLOCATE_ procedure and specify the PIN of the process that allocated the extended data segment, along with the segment ID known by the process that allocated the extended data segment.
Managing Memory Determining the Starting Address of a Flat Segment SEGMENT^TYPE); IF ERROR <> 0 THEN CALL ERROR^HANDLER; Note that it is not necessary to specify the segment size, because the segment already exists. Considerations for Sharing a Flat Segment Flat segments can be shared only with flat segments allocated with specific segment IDs.
Deallocating an Extended Data Segment Managing Memory !length!, ERROR^DETAIL, @SEG^PTR); IF ERROR <> 0 THEN CALL ERROR^HANDLER; Deallocating an Extended Data Segment When you have finished accessing an extended data segment, you can deallocate it by supplying the appropriate segment ID to the SEGMENT_DEALLOCATE_ procedure. The SEGMENT_DEALLOCATE_ procedure returns an error value of 0 if the deallocation was successful; any other value indicates that the operation was unsuccessful.
Defining a Memory Pool Managing Memory For information on segment allocation and specifying a current segment, see Readonly segments cannot be extensible. and Making a Selectable Segment Current, earlier in this section. The following paragraphs describe how to define a memory pool and how to obtain storage from a memory pool. Defining a Memory Pool Use the POOL_DEFINE_ procedure to define a memory pool.
Defining a Memory Pool Managing Memory Figure 17-9. Defining a Memory Pool VST091.
Defining a Memory Pool Managing Memory The following example sets up the memory pool shown in Figure 17-9. The example allocates an extended data segment, makes that segment current, and defines a pool within that segment: INT ERROR; INT OPTIONS; INT SEGMENT^ID; INT OLD^SEGMENT^ID; INT(32) SEGMENT^SIZE; INT ERROR^DETAIL; INT(32) BASE^ADDRESS; INT .EXT BASE^PTR := BASE^ADDRESS; INT .EXT POOL^START; INT(32) MAX^POOLSIZE; . . !Allocate a flat segment of 4000 bytes. OPTIONS.
Getting Space in a Memory Pool Managing Memory Getting Space in a Memory Pool After defining a pool, your process can obtain blocks of space from that pool by calling the POOL_GETSPACE_ procedure. You must specify the pool from which you want to obtain blocks by indicating the starting address of the pool. You must also specify the size of the block you require in bytes.
Returning Memory Pool Space Managing Memory @BLK^PTR := POOL_GETSPACE_ (POOL^START, BLK^SIZE, ERROR); IF ERROR <> 0 THEN CALL ERROR^HANDLER; . . BLK^PTR[4] := 12; Returning Memory Pool Space When your process no longer needs a block of space it obtained from a memory pool, your process can return the block to the memory pool by calling the POOL_PUTSPACE_ procedure. Once a block of data is returned to the memory pool, that storage space becomes available for assignment to other storage blocks.
Getting Information About a Memory Pool Managing Memory additional space exists in the segment and is not occupied by other data. If the pool is defined at the end of its segment (that is, there is no room to resize the pool), the RESIZESEGMENT procedure can be used to extend the segment before calling POOL_RESIZE_. Getting Information About a Memory Pool Use the POOL_GETINFO_ procedure to return information about a memory pool.
18 Managing Time Time management involves two related concepts: • • Creating and manipulating timestamps—that is, finding out what the time is Performing timing operations—that is, finding out how much time it takes to perform a given task This section begins by describing how timestamps are generated, what kind of timestamps are available, and the different ways in which the time of day can be represented; see How the System Keeps Time.
Managing Time Time Zones and Daylight Saving Time an external reference clock. Typically a reference clock is associated with an NTP server on the LAN or WAN. HP NonStop™ Time Synchronization (TimeSync) synchronizes the NonStop system clocks for all current NonStop and Neoview systems. It has the ability to act as an NTP client, an NTP server, or both simultaneously. For more information about TimeSync, see the Time Synchronization User’s Guide.
Managing Time 128-Bit, 64-Bit, and 48-Bit Timestamps 12:00 (noon) Greenwich mean time (Julian proleptic calendar). This timestamp can represent either Greenwich mean time (GMT), local standard time (LST), or local civil time (LCT). There is no way to examine a Julian timestamp and determine which time it represents. Related to the Julian timestamp is a 32-bit Julian day number, giving the number of days since January 1, 4713 B.C.
Managing Time Using the Time Management Procedures Using the Time Management Procedures The system provides several procedures that you can use to manipulate 128-bit timestamps, 64-bit timestamps. or 48-bit timestamps. These procedures can convert timestamps into other representations of date and time and perform further manipulations of these representations. A 128-bit timestamp can: • • • Extract the Julian timestamp from the 128-bit timestamp using the TS_UNIQUE_CONVERT_TO_JULIAN_ procedure.
Managing Time Using the Time Management Procedures Figure 18-1. Time Management Procedures VST125.
Managing Time Time and Date Manipulation Time and Date Manipulation This subsection describes how to use the system procedures that obtain or manipulate Julian timestamps (64 bits) or 48-bit timestamps.
Managing Time Working With 64-Bit Julian Timestamps at the last system generation, or the number of microseconds since the last cold load. You choose the timestamp you want by setting the type parameter as follows: 0 The current GMT 1 GMT at the last system cold load 2 GMT at the last system generation 3 Microseconds since cold load The current GMT is the default timestamp.
Managing Time Working With 64-Bit Julian Timestamps TYPE := SINCE^COLD^LOAD; TIME1 := JULIANTIMESTAMP(TYPE); . . TYPE := SINCE^COLD^LOAD; TIME2 := JULIANTIMESTAMP(TYPE); INTERVAL := TIME2 - TIME1; Obtaining a Julian Timestamp: Remote Node If you are dealing with timestamps on a remote node, then the relevant Julian timestamp is the one that is generated on that node.
Managing Time Working With 64-Bit Julian Timestamps ITEM^LIST; INT FIXED FIXED LENGTH, NUMBER^OF^ITEMS, RESULTMAX, ERROR, NODE^NAME[0:3], NODE^NUMBER,REMOTE^ERROR, .S^PTR; TIME^OF^LAST^UPDATE = RESULT; TIME^BEFORE, REMOTE^TIME, TIME^AFTER, DELAY^TIME, REMOTE^GMT, TIME^SINCE^LAST^UPDATE; STRING FILENAME[0:ZSYS^VAL^LEN^FILENAME - 1]; . . !Get time of last update: FILENAME ':=' "\SYS2.$APPLS.FILES.
Managing Time Working With 64-Bit Julian Timestamps !Compute time since last update: TIME^SINCE^LAST^UPDATE := REMOTE^GMT - TIME^OF^LAST^UPDATE; END; Converting Between a Julian Timestamp and a Gregorian Date and Time To obtain a Gregorian date and the time of day from a Julian timestamp, you supply the INTERPRETTIMESTAMP procedure with the Julian timestamp.
Managing Time Working With 64-Bit Julian Timestamps outside the range 1 through 4000, then bit 0 (the most significant bit) is set to 1; if the month is specified outside the range 1 through 12, then bit 1 is set; and so on. Converting a GMT Timestamp Into Local Time To convert a Julian timestamp representing GMT into a Julian timestamp representing local time, or to convert a local Julian timestamp into a GMT Julian timestamp, you can use the CONVERTTIMESTAMP procedure.
Managing Time Working With Julian Day Numbers TIME1 := JULIANTIMESTAMP(SINCE^COLD^LOAD); . . TIME2 := JULIANTIMESTAMP(SINCE^COLD^LOAD); INTERVAL := TIME2 - TIME1; DAYS := INTERPRETINTERVAL(INTERVAL,HOURS, MINUTES,SECONDS, MILLISECS,MICROSECS); Working With Julian Day Numbers For operations that require the date but not necessarily the time of day, you can measure time using Julian day numbers. A Julian day number is the number of days since January 1, 4713 B.C.
Managing Time Working With 48-Bit Timestamps Converting Between Julian Day Numbers and Gregorian Dates To convert a Julian day number into a Gregorian date, you supply the INTERPRETJULIANDAYNO procedure with the Julian day number. The procedure returns the Gregorian date in the parameters year, month, and day. The Julian day number must be greater than or equal to 1,721,119, and no greater than 3,182,395, which refers to December 31, year 4000 of the Gregorian calendar.
Managing Time Working With 48-Bit Timestamps network node in another time zone. Such a timestamp could be used for displaying the local time. When working with 48-bit timestamps, you can perform the following operations: • • Obtain a 48-bit timestamp (TIMESTAMP procedure) Convert a 48-bit timestamp into a Gregorian date and time (CONTIME procedure) The following paragraphs describe how to perform these operations.
Managing Time Timing in Elapsed Time and Timing in Process Time Timing in Elapsed Time and Timing in Process Time This subsection describes how to start and cancel process timers and perform other timing functions. You can time processes in elapsed time or in process time. Elapsed time is time as measured by the CPU clock, independent of the state of any process. Process time is the time that a process is active.
Managing Time Setting and Canceling Timers: Elapsed Time procedure in the TNS/E environment for time granularity reflected in microseconds instead of centiseconds.) • • • Cancel a timer that runs in elapsed time (CANCELTIMEOUT procedure in the TNS and native environments. Use the TIMER_STOP_ procedure in the TNS/E environment for time granularity reflected in microseconds instead of centiseconds.
Managing Time Setting and Canceling Timers: Process Time CANCELTIMEOUT or TIMER_STOP_ procedures. CANCELTIMEOUT or TIMER_STOP_ uses this value to distinguish between multiple timers. The Time signal message received from $RECEIVE when the timer expires has the following format: Format of system message -22 (Time signal message): sysmsg[0] sysmsg[1] = -22 = First parameter supplied by SIGNALTIMEOUT; default value 0 sysmsg[2] FOR 2 = Second parameter supplied by SIGNALTIMEOUT; default value 0D Note.
Managing Time Setting and Canceling Timers: Process Time receive system message -26 (the Process time signal message) in its $RECEIVE file when the timer expires. To start the process timer, supply the SIGNALPROCESSTIMEOUT procedure with the time period in 0.01-second units. You can also supply this procedure with values that will allow the timer to be identified in the message read from $RECEIVE.
Managing Time Timing Your Process Timing Your Process To find out how much processing time your process has used, you can call the MYPROCESSTIME procedure. This procedure returns the number of microseconds that the process has been active. For example: FIXED PROCESS^TIME; . . PROCESS^TIME := MYPROCESSTIME; Timing Another Process To find out how much processing time has been used by a process other than your own, you can use the PROCESS_GETINFO_ procedure and supply the process-time parameter.
Managing Time Measuring Long Time Intervals Measuring Long Time Intervals The SIGNALTIMEOUT procedure measures elapsed time according to the internal clock of the CPU in which the calling process is executing. Typically, the CPU clocks in a system run at slightly different speeds. Recall that the system determines system time by taking the average of all the CPU times in the system, and then establishes adjustment values for the various CPU clocks in order to synchronize them.
Managing Time A Sample Long-Range Timer JULIANTIMESTAMP procedure shows that the desired system time has not yet been reached. The user can run the program with no parameters to see the current system time. For example, 10> RUN TIMER causes the program to display the system time, in both GMT and LCT, and then to terminate. The user can set a timer by specifying a time of day in the form hour minute. A 24-hour clock is always used.
Managing Time A Sample Long-Range Timer jt = CONVERTTIMESTAMP(jt,0,,&err); INTERPRETTIMESTAMP(jt,DT); if (err) printf(" *** error: %d",err); else printf(" LCT: %d/%02d/%02d %02d:%02d:%02d.%03d%03d", DT[0],DT[1],DT[2],DT[3],DT[4],DT[5],DT[6],DT[7]); printf(" %s\n",txt); } /* * Computes the Greenwich mean time (GMT) at the next local * civil time (LCT) occurrence of the specified hour and * minute. If the time has already passed today, the GMT is * computed for the target time tomorrow.
Managing Time A Sample Long-Range Timer /* Convert target LCT to GMT */ *target = CONVERTTIMESTAMP(jts_target, LCT_TO_GMT, MY_NODE, &err); return err; } /* * Checks if the target has been reached. If not, sets a timer * for half the remaining interval. Returns 0 when target time * has been reached, -1 otherwise. * * N.B: This function rounds *up* the interval, so when the * target is reached it might overshoot up to 0.01 second.
Managing Time A Sample Long-Range Timer void main(int argc, char **argv) { int64 jts_gmt_target; int business_day; short recv_num; int16 read_data; int hour, min; short err, last_err, status; if (argc < 2) { showTime(JULIANTIMESTAMP(0), "time now"); STOP(); } hour = atoi(argv[1]); min = atoi(argv[2]); /* * Get the file number for $RECEIVE. System messages * and waited I/O are enabled by default.
Managing Time Managing System Time Managing System Time This subsection describes system-clock functions such as setting the system clock and checking the system clock. Remember that system time is the result of periodically synchronizing the clocks in the system using a clock-averaging algorithm. By taking the average value of the various CPU clocks, the system creates the concept of system time.
Managing Time • • Checking the System Clock Modify an existing transition in the DST table (DST_TRANSITION_MODIFY_ procedure ) Query a transition from the DST table (DST_GETINFO_ procedure) The following paragraphs describe how to perform these operations. Checking the System Clock You can check the system time either by issuing the TIME command at the commandinterpreter prompt or by calling the TIME procedure.
Managing Time Setting the System Clock error codes, see SYSTEMCLOCK_SET_ in the Guardian Procedure Calls Reference Manual. SETSYSTEMCLOCK has the advantage that it is implemented in all RVUs; on any RVU that supports both procedures, it accepts all the same parameter values as SYSTEMCLOCK_SET_, so it can be used with identical effect. You can set the system clock either programmatically using the SETSYSTEMCLOCK procedure or interactively from the TACL prompt using the SETTIME command.
Managing Time Setting the System Clock unsigned long t; long long JulianGMT; char *p; int result; puts("SSCK: SETSYSTEMCLOCK utility\n" "(c) Copyright 2012 Hewlett-Packard Development " "Company, L.P.
Managing Time Interacting With the DST Transition Table Interacting With the DST Transition Table The daylight-saving-time (DST) transition table provides a way of indicating the time and date at which transitions to and from daylight saving time will be made. On Dseries systems, you can set the DAYLIGHT_SAVING_TIME entry in the configuration file to NONE, USA66, or TABLE during system generation. See the System Generation Manual for details about the configuration file.
Managing Time Interacting With the DST Transition Table Using the ADDDSTTRANSITION Procedure You supply the ADDDSTTRANSITION procedure with two Julian timestamps and an offset. The timestamps specify the beginning and the end of a time period. The offset specifies the number of seconds that the LCT offsets the LST for the specified period. The following example sets three transitions. Note that successive calls to ADDDSTTRANSITION must load the DST table in time sequence with no gaps.
Managing Time Interacting With the DST Transition Table ERROR^MASK); IF ERROR^MASK <> 0 THEN CALL INVALID^DATE; OFFSET := 0; CALL ADDDSTTRANSITION(LOW^GMT,HIGH^GMT,OFFSET); !Third DST period; April 12, 1992 through October 18, 1992: LOW^GMT := HIGH^GMT; DATE^AND^TIME[0] := 1992; !year DATE^AND^TIME[1] := 10; !month DATE^AND^TIME[2] := 18; !day DATE^AND^TIME[3] := 2; !hour DATE^AND^TIME[4] := 0; !minute DATE^AND^TIME[5] := 0; !second DATE^AND^TIME[6] := 0; !millisecond DATE^AND^TIME[7] := 0; !microsecond H
Managing Time Interacting With the DST Transition Table Note the following rules when adding entries to the DST table: 1. The z_lowgmt and z_highgmt fields must have values between 1/1/1 and 12/31/10000. 2. There must be no existing entry in the DST table for which both of the following are true: • • The entry has a nonzero offset The entry overlaps the time period bounded by z_lowgmt and z_highgmt fields. 3. The DST table stores entries with nonzero offset.
Managing Time Interacting With the DST Transition Table if (error != ZSYS_VAL_DST_OK) errorExit(); /* Second DST period; October 20, 1991 through April 12, 1992, Offset = 0 */ /* Since Offset = 0, there is no need to explicitly add this entry.
Managing Time Interacting With the DST Transition Table The following rules have to be kept in mind while deleting entries from the DST table: 1. Only transitions that already exist in the table can be deleted. Deleting an entry that has a zero offset has no effect and the table remains unaltered. 2. An attempt to delete the entry that is currently in effect is not allowed when the offset field of that entry has a nonzero value. The DST_TRANSITION_MODIFY_ procedure may be used to delete such an entry.
Managing Time Interacting With the DST Transition Table Using the DST_TRANSITION_MODIFY_ Procedure You supply the DST_TRANSITION_MODIFY_ procedure with pointers to two zsys_ddl_dst_entry_def structures with their fields filled in. The first of these structures describes an existing entry in the DST table that has to be modified. The second structure describes a new entry that will replace the entry that needs to be modified.
Managing Time Interacting With the DST Transition Table dateAndTime[7] = 0; /* microsecond */ timeStampHigh = COMPUTETIMESTAMP(dateAndTime, &errorMask); if (errorMask != 0) errorExit(); oldDSTEntry.z_lowgmt = timeStampLow; oldDSTEntry.z_highgmt = timeStampHigh; oldDSTEntry.z_offset = 3600; /* seconds in 1 hour */ oldDSTEntry.z_version = DST_VERSION_SEP1997; newDSTEntry.z_lowgmt = timeStampLow; newDSTEntry.z_highgmt = timeStampHigh; newDSTEntry.z_offset = 7200; /* seconds in 2 hours */ newDSTEntry.
19 Formatting and Manipulating Character Data This section describes how to use the character formatting and editing capabilities of the operating system. Included here are discussions of the following: • • • How to use the formatter (FORMATCONVERT[X] and FORMATDATA[X] procedures) for presenting data in an organized way, such as for displaying tabulated data. Using the Formatter provides details.
Formatting and Manipulating Character Data Format-Directed Formatting The FORMATDATA and FORMATDATAX procedures are also identical except that FORMATDATA requires that all of its reference parameters be 16-bit addresses, while FORMATDATAX accepts extended (32-bit) addresses for all of its reference parameters. The FORMATCONVERT procedure is used in combination with FORMATDATA, while the FORMATCONVERTX conversion is used in combination with FORMATDATAX. Note.
Format-Directed Formatting Formatting and Manipulating Character Data Formatting Output Figure 19-1 shows the role of the FORMATCONVERT[X] and FORMATDATA[X] procedures in formatting data for output according to a specified format. Figure 19-1. Format-Directed Formatting VST094.VSD Setting bit 15 of the flags parameter to zero specifies that the FORMATDATA[X] procedure will perform output formatting. Setting bit 2 to zero specifies format-directed formatting.
Formatting and Manipulating Character Data Format-Directed Formatting The FORMATDATA[X] procedure takes the data items pointed to by the data descriptors and formats them according to the internal format provided by the FORMATCONVERT[X] procedure. FORMATDATA[X] places the output in the I/O buffers. The FORMATDATA[X] procedure reads the edit descriptors from left to right and retrieves data descriptors from the top of the data descriptor list when required by the edit descriptor.
Format-Directed Formatting Formatting and Manipulating Character Data Figure 19-2. Formatting Input VST095.
Formatting and Manipulating Character Data Format-Directed Formatting Introduction to Edit Descriptors As indicated previously, for both input formatting and output formatting you need to supply the FORMATCONVERT[X] procedure with a sequence of edit descriptors that specify how the FORMATDATA[X] procedure will format the data. This sequence of edit descriptors must be supplied in an external (ASCII) format to FORMATCONVERT[X].
Format-Directed Formatting Formatting and Manipulating Character Data Several examples of common uses of edit descriptors are given throughout the remainder of this subsection. For a description of every edit descriptor, modifier, and decoration, see the Guardian Procedure Calls Reference Manual. Introduction to Data Descriptors Each data descriptor describes an internal data item as shown in Figure 19-3: Figure 19-3. Contents of a Data Descriptor VST096.VSD The data pointer points to the item of data.
Format-Directed Formatting Formatting and Manipulating Character Data Formatting Numbers, Text, and Other Data Items There are several edit descriptors that you can use to process and format data for output: A Formats ASCII-coded text; the input is usually a string type but could also be numeric—the binary numbers are interpreted as ASCII characters. B Converts a number from its internal representation into ASCII code for output as a binary number according to a specified format.
Formatting and Manipulating Character Data Format-Directed Formatting The code fragment shown below processes some alphanumeric characters and some numeric characters. The first part of the code fragment sets up the edit descriptors for seven alphanumeric data items to be retrieved using the first data descriptor and displayed with five characters each, and for seven integer data items to be retrieved from the array pointed to by the second data descriptor and displayed in five character positions each.
Format-Directed Formatting Formatting and Manipulating Character Data !Format the data: ERROR := FORMATDATA(BUFFERS, BUFFER^LENGTH, NUM^BUFFERS, BUFFER^ELEMENTS, WFORMAT, VLIST, VLIST^LEN, FLAGS); IF ERROR <> 0 THEN ... Figure 19-4 shows the operation of the FORMATDATA procedure for this example. (For simplicity the FORMATCONVERT procedure is omitted from this figure.) Figure 19-4. Formatting Numbers and Text VST097.
Formatting and Manipulating Character Data 19 Formatting and Manipulating Character Data Using Buffer Control It is often convenient to use multiple buffers for output from the FORMATDATA[X] procedure. In addition to making it easier to handle larger amounts of output data, multiple buffers also help format data into lines for output, because you can then issue one WRITE procedure call for each buffer. To terminate a buffer and start a new one, you put a slash (/) character in the edit descriptor string.
Formatting and Manipulating Character Data VLIST[5].ELEMENT^PTR := @INT^ARRAY5; VLIST[5].ELEMENT^OCCURS := 2; !Format the data: ERROR := FORMATDATA(BUFFERS,BUFFER^LENGTH,NUM^BUFFERS, BUFFER^ELEMENTS,WFORMAT,VLIST,VLIST^LEN, FLAGS); IF ERROR <> 0 THEN ... Figure 19-5 shows how the code fragment presented above works. Figure 19-5. Buffer Control VST098.
Formatting and Manipulating Character Data Formatting Literals You can include literals in your edit-descriptor string by enclosing each literal in single quotation marks. The FORMATDATA[X] procedure copies these literals directly to the output buffers without accessing a data descriptor. The following example produces the same output as the previous example. However, because the days of the week are constant values whatever the month, these values can be expressed as literals.
Formatting and Manipulating Character Data Figure 19-6. Formatting Literals VST099.
Formatting and Manipulating Character Data Tabulating Data You can tabulate data by including tabulation edit descriptors in the edit-descriptor string. Any of the following are valid forms of tabulation descriptor: Tn Transmission of a character to or from a buffer is to occur at the nth character position in the buffer. The first character in the buffer is numbered 1. TLn Transmission of the next character to or from a buffer is to occur at n character positions to the left of the current position.
Formatting and Manipulating Character Data !Set up list elements that point to the above arrays: VLIST^LEN := 7; FLAGS := 0; VLIST[0].ELEMENT^PTR := @MONTH; VLIST[0].ELEMENT^SCALE := 0; VLIST[0].ELEMENT^TYPE := 0; VLIST[0].ELEMENT^LENGTH := 10; VLIST[0].ELEMENT^OCCURS := 1; VLIST[1].ELEMENT^PTR := @INT^YEAR; VLIST[1].ELEMENT^SCALE := 0; VLIST[1].ELEMENT^TYPE := 2; VLIST[1].ELEMENT^LENGTH := 2; VLIST[1].ELEMENT^OCCURS := 2; VLIST[2].ELEMENT^PTR := @INT^ARRAY1; VLIST[3].ELEMENT^PTR := @INT^ARRAY2; VLIST[4].
Formatting and Manipulating Character Data Figure 19-7. Tabulating Data VST100.
Formatting and Manipulating Character Data Applying a Scale Factor You can apply a scale factor to move the position of the decimal point in a fixed-point or floating-point number. Once you set a scale factor, it remains in effect until you change it. The scale factor descriptor has the format Pn, where n is the number of places by which the implied decimal point moves. This edit descriptor affects all subsequent D, E, F, and G edit descriptors. Compare the following two sets of examples.
Formatting and Manipulating Character Data Sample Program: Formatting Output The following sample program is a complete program for printing the calendar page as illustrated in Figure 19-7. This example shows all data declarations and includes error checking for the FORMATCONVERT and FORMATDATA procedures. The last lines of the example print out the contents of the buffers on the home terminal. ?INSPECT,SYMBOLS, NOLIST ?SOURCE $TOOLS.ZTOOLD04.
Formatting and Manipulating Character Data !-----------------------------------------------------------! Procedure to write a line on the terminal. !-----------------------------------------------------------PROC WRITE^LINE (BUF, LEN); STRING .BUF; INT LEN; BEGIN CALL WRITEX(TERM^NUM,BUF,LEN); IF <> THEN CALL PROCESS_STOP_; END; !-----------------------------------------------------------! Procedure to display formatted data on the terminal.
Formatting and Manipulating Character Data ! Literals and variables used by FORMATDATA: LITERAL BUFFER^LENGTH = 80; !length of one output ! buffer STRUCT BUF^REF(*); !data structure for an BEGIN ! output buffer STRING BYTES[0:BUFFER^LENGTH - 1]; END; LITERAL NUM^BUFFERS = 13; !max number of output ! buffers STRUCT .BUFFERS(BUF^REF) [0:NUM^BUFFERS - 1]; !Data ! structures for output ! buffers INT .
Formatting and Manipulating Character Data ! Set up the edit descriptors and convert to internal form: EFORMAT ':=' ["TR17,A8,TL22,2(I4,TR28),//,", "TR3,'SUN',TR2,'MON',TR2,'TUE',TR2,'WED',", "TR2,'THU',TR2,'FRI',TR2,'SAT'//", "7(I5)//7(I5)//7(I5)//7(I5)//7(I5)"] -> @S^PTR; SCALES := 0; CONVERSION := 1; EFORMATLEN := @S^PTR '-' @EFORMAT; ERROR := FORMATCONVERT(IFORMAT,IFORMATLEN,EFORMAT, EFORMATLEN, SCALES, SCALE^COUNT, CONVERSION); IF ERROR <= 0 THEN BEGIN START^LINE; IF ERROR = 0 THEN PUT^STR("Internal
Formatting and Manipulating Character Data ! Set up list elements that point to the above arrays: VLIST^LEN := 7; FLAGS := 0; VLIST[0].ELEMENT^PTR := @MONTH; VLIST[0].ELEMENT^SCALE := 0; VLIST[0].ELEMENT^TYPE := 0; VLIST[0].ELEMENT^LENGTH := 10; VLIST[0].ELEMENT^OCCURS := 1; VLIST[1].ELEMENT^PTR := @INT^YEAR; VLIST[1].ELEMENT^SCALE := 0; VLIST[1].ELEMENT^TYPE := 4; VLIST[1].ELEMENT^LENGTH := 4; VLIST[1].ELEMENT^OCCURS := 2; VLIST[2].ELEMENT^PTR := @INT^ARRAY1; VLIST[3].
Formatting and Manipulating Character Data ! Check for errors: IF ERROR <> 0 THEN BEGIN START^LINE; CASE ERROR OF BEGIN 267 -> PUT^STR("Buffer Overflow"); 268 -> PUT^STR("No Buffer"); 270 -> PUT^STR("Format Loopback"); 271 -> PUT^STR("EDIT Item Mismatch"); 272 -> PUT^STR("Illegal Input Character"); 273 -> PUT^STR("Bad Format"); 274 -> PUT^STR("Numeric Overflow"); OTHERWISE -> PUT^STR("Unexpected Error"); END; PRINT^LINE; CALL PROCESS_STOP_; END; ! Print the contents of the buffers on the terminal: I :=
Formatting and Manipulating Character Data List-Directed Formatting !-----------------------------------------------------------! Main procedure performs initialization. !-----------------------------------------------------------PROC CALENDAR MAIN; BEGIN STRING .
List-Directed Formatting Formatting and Manipulating Character Data Formatting List-Directed Input Figure 19-8 shows how list-directed formatting works for input. Figure 19-8. List-Directed Formatting VST101.VSD To specify list-directed formatting, you need to set bit 2 of the flags parameter to 1. To specify input, you set bit 15 of the flags parameter to 1. Here, the FORMATDATA[X] procedure takes data values from the input buffers and matches them with data descriptors from the data descriptor list.
Formatting and Manipulating Character Data List-Directed Formatting In addition to the value-separation characters described earlier, you also need to be aware of the following rules and special values: • Data to be saved as character strings must be enclosed in single quotation marks in the input buffer; otherwise, the FORMATDATA[X] procedure will return error 272 (illegal input character). Any other special characters, such as spaces, commas, slashes, and asterisks, can appear in the string.
Formatting and Manipulating Character Data List-Directed Formatting The user responds to the second prompt by typing a name. The name is a character string and must be enclosed in single quotation marks: Enter your name (up to 20 characters): 'Tom Sawyer' The program puts the name into the second of the input buffers. Once again, the program fills the buffer with blanks before reading from the terminal, eliminating the need to type a value-termination character after the name.
List-Directed Formatting Formatting and Manipulating Character Data !-----------------------------------------------------------! Here are a few DEFINEs to make it a little easier to ! format and print messages.
List-Directed Formatting Formatting and Manipulating Character Data !-----------------------------------------------------------! Procedure to format list-directed input.
List-Directed Formatting Formatting and Manipulating Character Data ! Initialize variables for month, date, year, and name: MONTH ':=' " DATE := 0; YEAR := 0000; NAME ':=' [10 * [" ! ! ! "; "]]; Set up data descriptors that point to the above variables: VLIST[0].ELEMENT^PTR VLIST[0].ELEMENT^SCALE VLIST[0].ELEMENT^TYPE VLIST[0].ELEMENT^LENGTH VLIST[0].ELEMENT^OCCURS := := := := := @MONTH; 0; 0; 10; 1; VLIST[1].ELEMENT^PTR VLIST[1].ELEMENT^SCALE VLIST[1].ELEMENT^TYPE VLIST[1].
List-Directed Formatting Formatting and Manipulating Character Data ! Repeat formatting loop if erroneous input: PROMPT^AGAIN: ! Blank the buffers: I := 0; WHILE I < NUM^BUFFERS DO BEGIN BUFFERS[I] ':=' [40 * [" I := I + 1; END; ! "]]; Prompt for input and copy into buffers: SBUFFER ':=' "Enter 'month' date year: " -> @S^PTR; CALL WRITEREADX(TERM^NUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,BYTES^READ); IF <> THEN CALL PROCESS_STOP_; BUFFERS[0] ':=' SBUFFER FOR BYTES^READ BYTES; SBUFFER ':=' "Enter 'nam
Formatting and Manipulating Character Data ! ! List-Directed Formatting Check for errors.
Formatting and Manipulating Character Data List-Directed Formatting !-----------------------------------------------------------! Main procedure provides initialization !-----------------------------------------------------------PROC INIT MAIN; BEGIN STRING INT ! .
Formatting and Manipulating Character Data Manipulating Character Strings Manipulating Character Strings Without using the formatter, there are several operations that you can perform on character strings: • • • • Convert a string of ASCII numeric characters into a binary number (NUMIN and DNUMIN procedures) or convert a binary number into an ASCII string for output (NUMOUT and DNUMOUT procedures). See Converting Between Strings and Integers later in this subsection.
Case Shifting Character Strings Formatting and Manipulating Character Data The following example reads some ASCII input from a terminal. The input is expected to be numeric data so the DNUMIN procedure is used to convert the number from ASCII representation into a binary number. BASE := 10; !Read from the terminal into the input buffer: CALL READX(TERMNUM,SBUFFER,BUFSIZE,COUNT^READ); IF <> THEN ...
Editing a Character String Formatting and Manipulating Character Data Upshifting a Character String To upshift a character string, you supply the SHIFTSTRING procedure with the string to be upshifted and the length of the string. To specify upshifting, you must set bit 15 of the casebit parameter to 0 (the default value). The following example converts an input string to all uppercase letters: STRING ':=' INPUT^BUFFER FOR 10; STRING^LENGTH := 10; CASE^BIT.
Editing a Character String Formatting and Manipulating Character Data Note that the data parameter contains the string to be edited on input and the edited string on output.
Editing a Character String Formatting and Manipulating Character Data You can supply multiple commands in the same template by separating the commands with two slashes. For example: Before string: Template: After string: fup filea,filrv idup // rec fup dup filea,filec Editing Commands: An Example The following sample program features a command interpreter with the ability to accept an FC command typed by the user. By typing “FC,” the user is given the opportunity to edit the last command entered.
Editing a Character String Formatting and Manipulating Character Data ?INSPECT,SYMBOLS,NOMAP,NOCODE ?NOLIST, SOURCE $SYSTEM.SYSTEM.ZSYSTAL; ?LIST !Global literals and variables: INT TERM^NUM; STRUCT .
Formatting and Manipulating Character Data Editing a Character String !-----------------------------------------------------------! Integer procedure edits the command buffer and returns 1 ! if edited command should be executed. This procedure ! allows the user a change of mind about editing the command ! by returning 0. !-----------------------------------------------------------INT PROC FC(COMMAND,LAST^COMMAND,NUM,SAVE^NUM); STRING .COMMAND; STRING .LAST^COMMAND; INT .NUM; INT .
Formatting and Manipulating Character Data ! ! Editing a Character String Set the FC prompt to " ." and read template typed by user: TEMPLATE^ARRAY ':=' " .
Editing a Character String Formatting and Manipulating Character Data ! ! Loop until user responds to FC prompt with a carriage return only: UNTIL NOT TEMPLATE^LENGTH; ! Return to command interpreter to execute edited command: RETURN 1; END; !-----------------------------------------------------------! Procedure prompts the user for a command and then processes ! the command. This procedure loops indefinitely until the ! user types the EXIT command.
Formatting and Manipulating Character Data ! ! ! ! ! ! ! Editing a Character String If the command is "FC" then call the FC procedure, returning 1 if the fix is accepted or 0 if it is not. If the command is EXIT, then stop the program. If the command is any other valid command, then process the command (this program simply displays the command name).
Formatting and Manipulating Character Data Editing a Character String !-----------------------------------------------------------! Procedure to save the Startup message in a global ! structure. !-----------------------------------------------------------PROC SAVE^STARTUP^MESSAGE(RUCB,START^DATA,MESSAGE,LENGTH, MATCH) VARIABLE; INT .RUCB, .START^DATA, .MESSAGE, LENGTH, MATCH; BEGIN ! Copy the Startup message into the CI^STARTUP structure: CI^STARTUP.
Formatting and Manipulating Character Data Sorting Characters !-----------------------------------------------------------! Main procedure initializes the IN file and then calls the ! command interpreter. !-----------------------------------------------------------PROC INITIALIZE MAIN; BEGIN ! Initialize the IN file: CALL INITIALIZE^TERMINAL; ! Call the command interpreter: CALL COMMAND^INTERPRETER; END; Sorting Characters Use the HEAPSORT[X_] procedure to sort an array in memory.
Formatting and Manipulating Character Data Sorting Characters ?INSPECT,SYMBOLS ?NOLIST, SOURCE $TOOLS.ZTOOLD04.ZSYSTAL; ?LIST !Literals: LITERAL ELEMENT^SIZE = 6; LITERAL MAXFLEN = ZSYS^VAL^LEN^FILENAME; !Global variables: INT INT TERM^NUM; ERROR; ?NOLIST ?SOURCE $SYSTEM.SYSTEM.EXTDECS0(HEAPSORTX_,PROCESS_GETINFO_, ? WRITEX,PROCESS_STOP_,INITIALIZER,FILE_OPEN_) ?LIST !-----------------------------------------------------------! Procedure to sort two strings A and B.
Sorting Characters Formatting and Manipulating Character Data !-----------------------------------------------------------! Procedure to initialize an array with string values and ! then call HEAPSORTX_ to sort them. By calling ASCENDING, ! it sorts them into ascending order. !-----------------------------------------------------------PROC SORTING; BEGIN INT I; INT(32) NUMBER^OF^ELEMENTS; ! ! ! ! STRUCT ARRAY^REF(*); BEGIN STRING ELEMENT[0:11]; END; !structure defining an ! array element STRUCT .
Formatting and Manipulating Character Data Programming With Multibyte Character Sets !-----------------------------------------------------------! Main procedure performs initialization !-----------------------------------------------------------PROC SORTER MAIN; BEGIN STRING .
Formatting and Manipulating Character Data Programming With Multibyte Character Sets In addition to the character sets listed above, HP also provides external support for the following character sets: • • • • • • IBM Kanji IBM Kanji Mixed JEF (Fujitsu) Kanji JEF (Fujitsu) Kanji Mixed NEC Kanji JIS Kanji The operating system provides procedures that convert between each of the above character sets and internal Tandem Kanji codes.
Formatting and Manipulating Character Data Checking for Multibyte Character-Set Support This subsection does not cover the procedures that were written primarily to support other HP subsystems, although these procedures are nonprivileged and available to all users. These procedures include: • • • • • The MBCS_CHARSTRING_ procedure used by the SCOBOL compiler for testing a text string to see whether it contains only multibyte characters.
Formatting and Manipulating Character Data Determining the Default Character Set If the result indicates support for one or more external character sets, then it will also indicate support for the corresponding internal character set. For example, if IBM Kanji is supported, then Tandem Kanji is also supported. Support for an external character set also indicates that the appropriate conversion and formatting routines are available on your system.
Formatting and Manipulating Character Data Analyzing a Multibyte Character String Any value that the pointer attains is a valid starting point for any other multibyte operation. The following example shows the intended use of the MBCS_CHAR_ procedure: !Set up the pointer to address the first byte of the text !string: @TESTMBCSCHAR := @TEXT^STRING[0]; !Loop, checking each character, as long as you are processing !a mixed text string: WHILE...
Formatting and Manipulating Character Data Dealing With Fragments of Multibyte Characters Dealing With Fragments of Multibyte Characters If a read operation of a text string of multibyte characters finishes when the specified read count is satisfied, then you cannot be sure whether the last byte read is the last byte of a character or the first byte of a multibyte character. If it is the first byte of a multibyte character, then its meaning is lost without the trailing byte.
Case Shifting With Multibyte Characters Formatting and Manipulating Character Data character size. To find the character size of a multibyte character set, use the MBCS_CHARSIZE_ procedure. To use the MBCS_CHARSIZE_ procedure, you must supply it with the number of the character set (as returned by the MBCS_CODESETS_SUPPORTED_ procedure).
Formatting and Manipulating Character Data Sample Program To use the MBCS_TESTBYTE_ procedure, you must supply it with the buffer containing the string to be tested, the length of the buffer, and an index into the buffer identifying the byte to be tested.
Sample Program Formatting and Manipulating Character Data ?INSPECT,SYMBOLS,NOMAP,NOCODE ?NOLIST, SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?LIST !Global literals and variables: INT TERM^NUM; STRUCT .
Formatting and Manipulating Character Data Sample Program !-----------------------------------------------------------! Integer procedure edits the command buffer and returns 1 ! if edited command should be executed. This procedure ! allows the user a change of mind about editing the command ! by returning 0. !-----------------------------------------------------------INT PROC FC(COMMAND,LAST^COMMAND,NUM,SAVE^NUM); STRING .COMMAND; STRING .LAST^COMMAND; INT .NUM; INT .
Formatting and Manipulating Character Data ! ! Sample Program Set the FC prompt to " ." and read template typed by user: TEMPLATE^ARRAY ':=' " .
Formatting and Manipulating Character Data ! ! ! ! Sample Program Upshift all characters in the edited command in case any characters were typed in lowercase (assumes default multibyte character set; otherwise you would need to use MBCS_CHAR_ and MBCS_SHIFTSTRING_): CALL SHIFTSTRING(COMMAND,NUM,0); END ! ! Loop until user responds to FC prompt with a carriage return only: UNTIL NOT TEMPLATE^LENGTH; ! Return to command interpreter to execute edited command: RETURN 1; END; Guardian Programmer’s Guide
Sample Program Formatting and Manipulating Character Data !-----------------------------------------------------------! Procedure prompts the user for a command and then processes ! the command. This procedure loops indefinitely until the ! user types the EXIT command. !-----------------------------------------------------------PROC COMMAND^INTERPRETER; BEGIN STRING .
Formatting and Manipulating Character Data ! ! ! Sample Program Upshift all characters in the edited command in case any characters were typed in lowercase (again assuming default multibyte character set: CALL SHIFTSTRING(COMMAND,NUM,0); ! ! ! ! ! ! ! If the command is "FC" then call the FC procedure, returning 1 if the fix is accepted or 0 if it is not. If the command is EXIT, then stop the program.
Formatting and Manipulating Character Data Sample Program !-----------------------------------------------------------! Procedure to save the Startup message in a global ! structure. !-----------------------------------------------------------PROC SAVE^STARTUP^MESSAGE(RUCB,START^DATA, MESSAGE,LENGTH,MATCH) VARIABLE; INT .RUCB, .START^DATA, .MESSAGE, LENGTH, MATCH; BEGIN ! Copy the Startup message into the CI^STARTUP structure: CI^STARTUP.
Formatting and Manipulating Character Data Sample Program !-----------------------------------------------------------! Procedure to open the terminal file specified in the IN ! file of the Startup message and check that multibyte ! character sets are supported. The program stops if ! multibyte character sets are not supported. !-----------------------------------------------------------PROC INITIALIZE^TERMINAL; BEGIN INT(32) STRING STRING STRING INT INT ! RESULT; .S^PTR; .BUFFER[0:71]; .
Formatting and Manipulating Character Data Sample Program !-----------------------------------------------------------! Main procedure initializes the IN file and then calls the ! command interpreter.
20 Interfacing With the ERROR Program The ERROR process returns error-message text associated with a file-system error number. You can access the ERROR process in one of the following ways: • • By typing the ERROR command and a file-system error number in response to the command-interpreter prompt. The ERROR process responds by displaying the error number and associated text on the terminal or designated OUT file.
Interfacing With the ERROR Program Creating an ERROR Process Creating an ERROR Process To create the ERROR process, you can use the PROCESS_CREATE_ procedure. You can create the process in a waited or nowait manner. The following example creates a named ERROR process and waits for the creation to finish: LITERAL MAXPDLEN = ZSYS^VAL^LEN^PROCESSDESCR; STRING OBJFILENAME; !object file name INT .
Interfacing With the ERROR Program Opening an ERROR Process ERROR := PROCESS_CREATE_(OBJFILENAME:OBJFILENAME^LENGTH, !library^filename:library^file^len!, !swap^filename:swap^file^len!, !ext^swap^file^name:ext^swap^len!, !priority!, !processor!, PROCESS^HANDLE, !error^detail!, NAME^OPTION, !name:length!, PROCESS^DESCR:MAXPDLEN, PROCESS^DESCR^LEN, NOWAIT^TAG); IF ERROR <> 0 THEN ... . . CALL READUPDATEX(RCV^NUM,SBUFFER,RCOUNT,BYTES^READ); IF <> THEN ...
Interfacing With the ERROR Program Sending an ERROR Process a Startup Message Sending an ERROR Process a Startup Message After opening the ERROR process file, you must use the WRITE[X] procedure to send the ERROR process a Startup message. This Startup message must contain the following information: • • • -1 in the first word to identify the message as the Startup message. The $RECEIVE file as the OUT file.
Interfacing With the ERROR Program Reading and Processing Error-Message Text Reading and Processing Error-Message Text To read the error-message text, you issue a WRITEREAD[X] procedure call against the ERROR process. Because most messages need more than one line of information, you need to issue the WRITEREAD[X] procedure several times to read the entire message text.
Using the ERROR Process: An Example Interfacing With the ERROR Program Using the ERROR Process: An Example The following sample program interfaces with the ERROR process to print file-system error messages on the home terminal. The example is made up of the following procedures: • • • The PRINT^ERROR procedure is called by the FILE^USER (main) procedure whenever a file-system error occurs. This procedure takes one formal parameter: the file number of the file against which the error occurred.
Interfacing With the ERROR Program Using the ERROR Process: An Example STRING PARAMS[0:529]; END; STRING .S^STARTUP := @CI^STARTUP[0] '<<' 1; ?NOLIST ?SOURCE $SYSTEM.SYSTEM.
Interfacing With the ERROR Program Using the ERROR Process: An Example !-----------------------------------------------------------! Procedure to print error text on the home terminal. Runs ! and opens the error program, sends it an error number and ! receives the error-message text. !-----------------------------------------------------------PROC PRINT^ERROR(FNUM); INT FNUM; BEGIN INT !file number of file with ! error against it !process handle of ERROR ! process STRING .
Interfacing With the ERROR Program Using the ERROR Process: An Example IF ERROR^RETURN <> 0 THEN BEGIN SBUFFER ':=' "Unable to create Error process. " -> @S^PTR; CALL WRITEX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER); CALL PROCESS_STOP_(!process^handle!, !specifier!, ABEND); END; ! Open the ERROR process: ERROR := FILE_OPEN_(PROC^DESCR:PROC^DESCR^LEN,PROCNUM); IF ERROR <> 0 THEN BEGIN SBUFFER ':=' "Unable to open Error process.
Interfacing With the ERROR Program ! Using the ERROR Process: An Example Loop while ERROR program still sending text: EOF := 0; WHILE NOT EOF DO BEGIN ! Read the error-message text into the buffer: CALL WRITEREADX(PROCNUM,S^STARTUP,0,132,COUNT^READ); !Set flag if end of message text: IF > THEN EOF := 1; !Print buffer if not end of message text: ELSE BEGIN CALL WRITEX(TERMNUM,S^STARTUP,COUNT^READ); IF <> THEN BEGIN SBUFFER ':=' "Unable to communicate with Error process.
Interfacing With the ERROR Program Using the ERROR Process: An Example !-----------------------------------------------------------! Procedure to copy the Startup message into a global ! structure. !-----------------------------------------------------------PROC SAVE^STARTUP^MESSAGE(RUCB,START^DATA,MESSAGE, LENGTH,MATCH)VARIABLE; INT .RUCB; INT .START^DATA; INT .MESSAGE; INT LENGTH; INT MATCH; BEGIN ! Copy the Startup message into the CI^STARTUP structure: CI^STARTUP.
Interfacing With the ERROR Program Using the ERROR Process: An Example !-----------------------------------------------------------! Main procedure initializes the home terminal and calls the ! PRINT^ERROR procedure. !-----------------------------------------------------------PROC FILE^USER MAIN; BEGIN ! Read Startup message: CALL INIT; . .
21 Writing a Requester Program Recall from Section 1, Introduction to Guardian Programming, that a requester/server design has several advantages over the monolithic or unified program approach. Specifically, requesters and servers provide modularity by allowing the requester program to handle terminal I/O, while a server process provides database service.
Terminal Interface Writing a Requester Program Figure 21-1. Functions of a Requester Process VST103.VSD Terminal Interface The terminal interface deals with writing information to the terminal and reading information typed at the terminal. In its simplest form, the terminal interface consists of a series of prompts and user responses (using the WRITEREAD[X] procedure) to find out what the user wants the application to do.
Application Control Writing a Requester Program input into a specific format for processing. See Section 19, Formatting and Manipulating Character Data, for details. For the convenience of the server, the input data is usually placed in a data structure that enables the server to receive the data in known format. Similarly, the requester usually needs to perform data mapping on information sent to the requester by the server before printing it on the terminal.
File System I/O Synchronization Writing a Requester Program The requester establishes communication with the server of its choice by sending it the message that was formulated during the data-mapping phase. Usually, the requester expects a reply from the server and therefore sends the message to the server using the WRITEREAD[X] procedure. If no reply data is expected, you can use the WRITE[X] procedure. Section 6, Communicating With Processes, provides details on how to do this.
Sync-Depth Writing a Requester Program with nonretryable I/Os that are not audited (protected by TMF transactions). Synchronization is done by the participating processes; however, the file system might automate the path-failure handling for the requester. Note that processes that open $RECEIVE and other files are considered both requesters and servers in the context of this discussion. • • A process is a server while processing a message received through $RECEIVE.
Sync-Depth in Practice Writing a Requester Program Sync-Depth in Practice The following is the sequence of events that occurs for a path error retry, which is performed automatically if the sync depth value is greater than 0. • • The requester sends an I/O to the server. The file system increments the sync ID for the file and locates the server using the process handle determined at open time. The request is then sent to the server process.
File Sync Block Checkpoints (Example) Writing a Requester Program File Sync Block Checkpoints (Example) Assume that a requester is a process pair. The process pair opens a disk file or a process with sync depth value of 3. This causes the server to allocate three status blocks for the saved results with the opener control block. In its main loop, the requester performs a single CHECKPOINT call, including the file sync block. The call to CHECKPOINT is followed by three WRITE operations on the file.
File Sync Block Checkpoints (Example) Writing a Requester Program • • Nowait-depth to either 0 or 1, depending on your choice of waited or no-waited I/O. Sync-depth to 1. Sync-depth can be set to 0, but this requires the requester to handle path retries. If the requester is a process pair, it is important to ensure that any I/O that is resent on a takeover is using the same sync ID as the original request. Requester process pairs might also use sync depth values greater than 1 to optimize checkpointing.
Writing a Requester Program: An Example Writing a Requester Program • • • • • • The requesters’ primary process handle The requesters’ backup process handle The file numbers the requester used for the opens (each open has a distinct file number, although normally the backup’s file number is the same as the primary’s file number) The sync-depth value of the open The last sync ID value received Buffers to store responses (sync ID, error code, reply size, and reply data) The primary and backup process hand
User Interface Writing a Requester Program • Queries the status of an existing order to find out who placed the order, when the order was placed, and whether the order has been shipped.
Application Overview Writing a Requester Program The Orders File The orders file contains one order record for each item ordered by a customer.
Application Overview Writing a Requester Program process obtains the information from the inventory file and returns it to the requester. • • If the user requests to process an order, the requester sends a message to the process-order server ($SER2). This server uses the information it receives to update the inventory level in the inventory file and to create an order record and put it in the orders file.
Application Overview Writing a Requester Program -SET BLOCK 2048 -SET REC 240 -SET IBLOCK 2048 -SET KEYLEN 10 -SHOW TYPE K EXT ( 1 PAGES, 1 PAGES ) REC 240 BLOCK 2048 IBLOCK 2048 KEYLEN 10 KEYOFF 0 MAXEXTENTS 16 -CREATE \SYS.$APPLS.DATA.ORDERS CREATED - \SYS.$APPLS.DATA.
Coding the Requester Program Writing a Requester Program 21 Writing a Requester Program Coding the Requester Program The requester program shown at the end of this section consists of several procedures, as shown in Figure 21-5. Figure 21-5. Relationship Between Major Procedures in the Requester Program VST107.VSD In addition to the procedures shown in Figure 21-5, the error handling procedures FILE^ERRORS and FILE^ERRORS^NAME provide file-system error handling for most of the other procedures.
Coding the Requester Program Writing a Requester Program GET^COMMAND passes the result back to the requester procedure which calls the appropriate procedure in response to the user’s selection as follows: • • • • To read a part record, it calls the READ^PART procedure To process an order, it calls the PROCESS^ORDER procedure To read an order record, it calls the READ^ORDER procedure To stop the requester, it calls the EXIT^PROGRAM procedure After executing the READ^PART, PROCESS^ORDER, or READ^ORDER pr
Coding the Requester Program Writing a Requester Program The application-control phase sends the 10-digit number string to the $SER1 server process and waits for the response. For an existing part number, the server returns a data structure containing the part record and the READ^PART procedure displays the information on the terminal. Date information contained in the returned data structure is in the form of a 48-bit numeric timestamp.
Coding the Requester Program Writing a Requester Program If the server process successfully processes the order, then the reply record contains the new stock level on hand and an order record number for the newly created order. This number is 28 digits long and is made up of a timestamp and the part number. The PROCESS^ORDER procedure displays the order number on the terminal.
Coding the Requester Program Writing a Requester Program The Code for the Sample Requester Program The rest of this section lists the code for the sample requester program. ?INSPECT, SYMBOLS, NOCODE !NOLIST, SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?NOLIST, SOURCE $TOOLS.ZTOOLD04.ZSYSTAL ?LIST !--------------!Literals: !--------------LITERAL MAXFLEN = ZSYS^VAL^LEN^FILENAME; LITERAL BUFSIZE = 512; !---------------!Data structures !---------------!Startup message data structure: STRUCT .
Coding the Requester Program Writing a Requester Program !Message to send to $SER1 to request inventory information !about a specified item: STRUCT PART^REQUEST; BEGIN STRING PART^NUMBER[0:9]; !10-digit part number END; !Message to send to $SER2 to process an order: STRUCT .
Coding the Requester Program Writing a Requester Program !Data structure send to $SER3 to request information about a !specified order: STRUCT .ORDER^QUERY; BEGIN STRING ORDER^NUMBER[0:27]; END; !Message returned by $SER1. It contains a part record: STRUCT .
Coding the Requester Program Writing a Requester Program !Message returned by $SER3. It contains the order record !the corresponds to the order number sent in the request !to $SER3: STRUCT .
Coding the Requester Program Writing a Requester Program !-----------------------------------------------------------! Here are a few DEFINEs to make it a little easier to ! format and print messages.
Coding the Requester Program Writing a Requester Program !-----------------------------------------------------------! Procedure for displaying file system error numbers on the ! terminal. The parameters are the file name and its length ! and the error number. This procedure is used when the ! file is not open, so there is no file number for it. ! FILE^ERRORS is used when the file is open. !-----------------------------------------------------------PROC FILE^ERRORS^NAME(FNAME:LEN,ERROR); STRING .
Coding the Requester Program Writing a Requester Program !----------------------------------------------------------! Procedure to write a message on the terminal and check ! for any error. If there is an error, this procedure ! attempts to write a message about the error and then ! stops the program. !----------------------------------------------------------PROC WRITE^LINE(BUF,LEN); STRING .
Coding the Requester Program Writing a Requester Program !-----------------------------------------------------------! Procedure to read a part record from the $SER1 process. ! This procedure prompts the user for a part number, which it ! checks for validity before sending it to the $SER1 server ! process. The $SER1 process returns the corresponding part ! record (if there is one) from the inventory file and then ! this procedure prints the record on the terminal.
Coding the Requester Program Writing a Requester Program ! ! Check if part number all numeric.
Coding the Requester Program Writing a Requester Program ! Print two blank lines on the terminal: PRINT^BLANK; PRINT^BLANK; ! Print returned information on the terminal: PART^REC ':=' SBUFFER FOR ($LEN(PART^REC)); PRINT^STR("INVENTORY PROFILE: "); ! Print the part number: PRINT^STR("Part number: " & PART^REC.PART^NUMBER FOR 10); ! Print the part description: PRINT^STR("Part description: " & PART^REC.
Coding the Requester Program Writing a Requester Program ! Print the supplier's name: PRINT^STR("Supplier Name: " & PART^REC.SUPPLIER FOR 24); ! Print date when last order placed with supplier: CALL CONTIME(DATE^AND^TIME,PART^REC.ORDER^PLACED[0], PART^REC.ORDER^PLACED[1],PART^REC.
Coding the Requester Program Writing a Requester Program !-----------------------------------------------------------! Procedure to process an order. The procedure puts together ! a request for an order from information entered in response ! to a series of prompts.
Coding the Requester Program Writing a Requester Program !---------------------------! Prompt for and process the ! quantity requested !---------------------------! Repeat until valid quantity entered: REPEAT^QTY: ! Prompt for the quantity required: PRINT^BLANK; START^LINE; PUT^STR("Enter Quantity: "); CALL WRITEREADX(TERM^NUM,SBUFFER, @S^PTR '-' @SBUFFER,BUFSIZE,COUNT^READ); ! Check that input is numeric: I := 0; WHILE I < COUNT^READ DO BEGIN IF SBUFFER[I] < "0" OR SBUFFER[I] > "9" THEN BEGIN PRINT^
Coding the Requester Program Writing a Requester Program ! ! Check that there is enough stock on hand to satisfy the order: IF ORDER^REQUEST.QTY^ORDERED > PART^REC.QUANTITY^ON^HAND THEN BEGIN PRINT^BLANK; START^LINE; PUT^STR("Current Stock on Hand is Only "); PUT^INT(PART^REC.
Coding the Requester Program Writing a Requester Program ! ! If name contains nonalphabetic characters, prompt user to reenter name: I := 0; WHILE I < COUNT^READ DO BEGIN IF SBUFFER[I] < "A" OR SBUFFER[I] > "z" OR (SBUFFER[I] > "Z" AND SBUFFER[I] < "a") THEN BEGIN PRINT^BLANK; PRINT^STR("Name Must Be Alphabetic "); PRINT^STR("Please Enter an Alphabetic Last Name"); GOTO REPEAT^LASTNAME; END; I := I + 1; END; ! Put last name in order-request message: ORDER^REQUEST.NAME.
Coding the Requester Program Writing a Requester Program ! ! If first name contains nonalphabetic characters, prompt user to reenter name: I := 0; WHILE I < COUNT^READ DO BEGIN IF SBUFFER[I] < "A" OR SBUFFER[I] > "z" OR (SBUFFER[I] > "Z" AND SBUFFER[I] < "a") THEN BEGIN PRINT^STR("Name Must Be Alphabetic "); PRINT^STR("Please Enter an Alphabetic First Name "); GOTO REPEAT^FIRSTNAME; END; I := I + 1; END; ! Put first name in order-request message: ORDER^REQUEST.NAME.
Coding the Requester Program Writing a Requester Program ! ! If middle initial is nonalphabetic, and not blank, prompt user to reenter middle initial: IF COUNT^READ = 1 THEN BEGIN IF SBUFFER[0] < "A" OR SBUFFER[0] > "z" OR (SBUFFER[0] > "Z" AND SBUFFER[0] < "a") AND SBUFFER[0] <> " " THEN BEGIN PRINT^BLANK; PRINT^STR("Middle Initial Must Be Alphabetic "); PRINT^STR("Please Enter an Alphabetic Character"); GOTO REPEAT^INITIAL; END; END; ! Put middle initial in order-request message: ORDER^REQUEST.NAME.
Coding the Requester Program Writing a Requester Program ! Put street address in order-request message: ORDER^REQUEST.
Coding the Requester Program Writing a Requester Program !---------------------------! Prompt for and process the ! customer's zip code !---------------------------! Repeat until valid zip code entered: REPEAT^ZIP: ! Prompt user for zip code: PRINT^BLANK; START^LINE; PUT^STR("Enter Zip Code: "); CALL WRITEREADX(TERM^NUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,COUNT^READ); ! ! If zip code does not have exactly 7 characters, prompt user to issue another zip code: IF COUNT^READ <> 7 THEN BEGIN PRINT^BLANK;
Coding the Requester Program Writing a Requester Program ! ! If any of the last five characters of the zip code is nonnumeric, reenter the zip code: I := 2; WHILE I < 7 DO BEGIN IF SBUFFER[I] < "0" OR SBUFFER[I] > "9" THEN BEGIN PRINT^BLANK; PRINT^STR("Last Five Characters Must Be Numeric"); PRINT^STR("Please Enter Numeric Characters "); GOTO REPEAT^ZIP; END; I := I + 1; END; ! Put zip code in order-request message: ORDER^REQUEST.
Coding the Requester Program Writing a Requester Program ! Check that credit-card number is all numeric: I := 0; WHILE I < 16 DO BEGIN IF SBUFFER[I] < "0" OR SBUFFER[I] > "9" THEN BEGIN PRINT^BLANK; PRINT^STR("Credit Card Number Must Be All Numeric"); PRINT^STR("Please Enter Valid Credit-Card Number"); GOTO REPEAT^CCN; END; I := I + 1; END; ! Put credit-card number in order-request message: ORDER^REQUEST.
Coding the Requester Program Writing a Requester Program ! Copy reply from server into ORDER^REPLY structure: ORDER^REPLY ':=' SBUFFER FOR $LEN(ORDER^REPLY); ! If stock depleted since checking, inform user and return: IF ORDER^REPLY.QUANTITY^ON^HAND < 0 THEN BEGIN PRINT^BLANK; PRINT^STR("Insufficient Stock for this Order "); RETURN; END; ! ! Prepare the order number for printing in blocks of 6 characters separated by spaces: PRINT^BLANK; START^LINE; PUT^STR("Order Number is: "); PUT^STR(ORDER^REPLY.
Coding the Requester Program Writing a Requester Program !-----------------------------------------------------------! Procedure to read an order record from the $SER3 process. ! This procedure prompts the user for an order number, which ! it checks for validity before sending it to the $SER3 ! server process. The $SER3 process returns the order record ! (if there is one) from the corresponding the order file and ! then this procedure prints the record on the terminal.
Coding the Requester Program Writing a Requester Program ! ! Check whether order number all numeric. another order number if not: Request I := 0; WHILE I < 28 DO BEGIN IF SBUFFER[I] < "0" OR SBUFFER[I] > "9" THEN BEGIN ! Print diagnostic for failed test: PRINT^BLANK; PRINT^STR("Order Number Must Be Numeric"); PRINT^STR("Please Type Another Order Number"); PRINT^BLANK; GOTO REPEAT; END; I := I + 1; END; !-----------------------------! Get the order record from ! the server.
Coding the Requester Program Writing a Requester Program !--------------------------------! Print the contents of the order ! record on the terminal. !--------------------------------PRINT^BLANK; PRINT^BLANK; ! Copy returned information into ORDER^REC structure: ORDER^REC ':=' SBUFFER FOR ($LEN(ORDER^REC)); PRINT^STR("ORDER RECORD INFORMATION"); ! ! Print the order number in groups of 6 characters, separated by spaces: PRINT^STR("Order Number: " & ORDER^REC.ORDER^NUMBER[0] FOR 6 & " " & ORDER^REC.
Coding the Requester Program Writing a Requester Program ! Print the part number: PRINT^BLANK; PRINT^STR("Part Number: " & ORDER^REC.PART^NUMBER FOR 10); ! Print the part description: PRINT^STR("Part description: " & ORDER^REC.PART^DESC FOR 48); ! Print the quantity ordered: START^LINE; PUT^STR("Quantity Ordered: "); PUT^INT(ORDER^REC.QTY^ORDERED); PRINT^LINE; ! Print date ordered: CALL CONTIME(DATE^AND^TIME,ORDER^REC.DATE^ORDERED[0], ORDER^REC.DATE^ORDERED[1], ORDER^REC.
Coding the Requester Program Writing a Requester Program ! Print date shipped to customer: START^LINE; PUT^STR("Date Shipped: "); IF ORDER^REC.DATE^SHIPPED = 0 THEN PUT^STR("Order Not Yet Shipped ") ELSE BEGIN CALL CONTIME(DATE^AND^TIME,PART^REC.SHIPMENT^DUE[0], PART^REC.SHIPMENT^DUE[1], PART^REC.
Coding the Requester Program Writing a Requester Program !-----------------------------------------------------------! Procedure opens a server process. Prompts the user to try ! again if the open fails. !-----------------------------------------------------------PROC OPEN^SERVER(PROCESS^NAME,PROCESS^NAMELEN,SERVER^NUM); STRING .PROCESS^NAME; INT PROCESS^NAMELEN; INT .
Coding the Requester Program Writing a Requester Program !-----------------------------------------------------------! Procedure handles creating and opening servers. If the ! server already exists it calls OPEN^SERVER to open it. If ! it does not exist, it creates the server and sends it the ! standard process initialization sequence. !-----------------------------------------------------------PROC CREATE^AND^OPEN^SERVER(SERVER^NUM,SERVER^OBJECT^NAME, OBJFILE^NAMELEN,PROCESS^NAME, PROCESS^NAMELEN); INT .
Coding the Requester Program Writing a Requester Program ! Create process: ERROR := PROCESS_CREATE_( SERVER^OBJECT^NAME:OBJFILE^NAMELEN, !library^filename:library^file^len!, !swap^filename:swap^file^len!, !ext^swap^file^name:ext^swap^len!, !priority!, !processor!, !process^handle!, ERROR^DETAIL, ZSYS^VAL^PCREATOPT^NAMEINCALL, PROCESS^NAME:PROCESS^NAMELEN); IF ERROR <> 0 THEN BEGIN PRINT^STR("Unable to create server process"); CALL PROCESS_STOP_; END; ! Open the new server process: CALL OPEN^SERVER(PROC
Coding the Requester Program Writing a Requester Program OTHERWISE -> BEGIN ! Unexpected error return from PROCESS_GETPAIRINFO_: PRINT^STR("Unexpected error "); END; END; END; !-----------------------------------------------------------! Procedure to process an invalid command. The procedure ! informs the user that the selection was other than "r," ! "p," "q," or "x.
Coding the Requester Program Writing a Requester Program !----------------------------------------------------------! Procedure to initialize the program. It calls ! INITIALIZER to save the Startup message. It opens the ! terminal using the IN file from the Startup message and ! calls CREATE^AND^OPEN^SERVER to create and open each of ! the server processes. !----------------------------------------------------------PROC INIT; BEGIN STRING .
Coding the Requester Program Writing a Requester Program ! Open $SER2, create it if it does not already exist: SERVER^NAME ':=' "$SER2" -> @S^PTR; SERVERLEN := @S^PTR '-' @SERVER^NAME; OBJECT^FILE ':=' "=SER2" -> @S^PTR; OBJFILELEN := @S^PTR '-' @OBJECT^FILE; CALL CREATE^AND^OPEN^SERVER(SERV2^NUM,OBJECT^FILE, OBJFILELEN,SERVER^NAME,SERVERLEN); ! Open $SER3, create it if it does not already exist: SERVER^NAME ':=' "$SER3" -> @S^PTR; SERVERLEN := @S^PTR '-' @SERVER^NAME; OBJECT^FILE ':=' "=SER3" -> @S^P
Coding the Requester Program Writing a Requester Program ! Call the function selected by user: CASE CMD OF BEGIN "r", "R" -> CALL READ^PART; "p", "P" -> CALL PROCESS^ORDER; "q", "Q" -> CALL READ^ORDER; "x", "X" -> CALL EXIT^PROGRAM; OTHERWISE -> CALL INVALID^COMMAND; END; END; END; Guardian Programmer’s Guide — 421922-014 21 - 51
22 Writing a Server Program This section describes programming techniques that are useful when writing server programs. With Section 21, Writing a Requester Program, this section summarizes many of the techniques and procedures described earlier in this manual. You should be familiar with the information contained in Sections 1 through 20 before reading this section.
Receive-Depth Writing a Server Program Figure 22-1. Single-Threaded and Multithreaded Servers VST108.VSD Receive-Depth The receive-depth parameter is used by servers for opening $RECEIVE. This parameter determines how many times the server can call READUPDATE before it has to call REPLY. Receive-depth value Description 0 Indicates that no calls to READUPDATE can be made. The server must use READ to receive messages.
Context-Free Servers Writing a Server Program Note that COBOL85 programs do not support multithreaded servers. You cannot open $RECEIVE with receive-depth value greater than 1 unless you use the ENTER verb to call the file system FILE_OPEN_ procedure directly. (This is not recommended, because it might adversely interfere with the COBOL85 RTL I/O mechanisms.) See the COBOL85 Manual for details.
Getting Message Information Writing a Server Program BEGIN INT PROCESS^HANDLE [0:9]; !process handle of opener INT RESERVED^HANDLE [0:9]; !reserved, filled with -1; this ! field is required for OPENER_LOST_ INT FILE^NUMBER; !file number used by opener INT RESERVED^FILE^NUMBER; !for use with NonStop pairs END; An opener table entry can have other fields defined in addition to those shown above.
Adding a Requester to the Opener Table Writing a Server Program information about the message.
Checking a Request Against the Opener Table Writing a Server Program ERROR^NUMBER); END; Checking a Request Against the Opener Table If the message received on $RECEIVE is a user message, then you need to check that the sender of the message has the server open. To do this, you scan each entry of the opener table looking for a match with the process handle and file number of the message sender. If no match is found, then you should reject the user request with error number 60.
Deleting a Requester From the Opener Table Writing a Server Program When the Requester Closes the Server When a requester process closes a server, the server receives a Close message (system message -104). On receipt of this message, the server must delete the corresponding entry from the opener table. It does this by finding the process handle and file number in the opener table and then deleting the entry by writing -1 over each word.
Writing a Server Program: An Example Writing a Server Program !Check for lost openers: INDEX := -1; DO BEGIN STATUS := OPENER_LOST_(BUFFER:COUNT^READ, OPENER^TABLE[1], INDEX, MAX^OPENERS, $LEN(OPENER^TABLE[1])); IF STATUS = 6 THEN NUMBER^OF^OPENERS := NUMBER^OF^OPENERS - 1; END UNTIL STATUS = 0 OR STATUS = 2 OR STATUS = 3 OR STATUS = 7; . . !Process other system messages. . .
Application Overview Writing a Server Program • • • The unit price of the item The name of the supplier If an order has been placed with the supplier, the quantity ordered and the expected delivery date The Orders File The orders file contains one order record for each item ordered by a customer.
The Part-Query Server ($SER1) Writing a Server Program Figure 22-2. Server Processes in the Example Application VST109.VSD The requester chooses the server to send a request to depending on the function requested by the user: • • • If the user requests to query a part record, then the requester obtains the part record by sending a request to the part-query server ($SER1). The $SER1 process obtains the information from the inventory file and returns it to the requester.
The Part-Query Server ($SER1) Writing a Server Program Figure 22-3. Relationship Between Major Procedures in the Part-Query Server VST110.VSD The following paragraphs describe the major procedures in detail. The SERVER Procedure The SERVER procedure is the main procedure for the part-query server.
The Part-Query Server ($SER1) Writing a Server Program On return from either the PROCESS^SYSTEM^MESSAGE or PROCESS^USER^REQUEST procedure, the SERVER procedure replies to the message. For a user message, the reply consists of a part record or an error indication. For a system message, the reply consists of an error indication: 0 for a successful operation or some positive number for an unsuccessful operation.
The Part-Query Server ($SER1) Writing a Server Program message as a key to the inventory file to access the desired record. If the record exists, then the record is returned to the SERVER procedure with an error condition of zero. If the record does not exist, then the file-system error number is returned without a part record. The Code for the Part-Query Server ($SER1) The code for the part-query server program appears on the following pages. ?INSPECT, SYMBOLS, NOCODE ?NOLIST, SOURCE $SYSTEM.ZSYSDEFS.
The Part-Query Server ($SER1) Writing a Server Program !Message received from requester. STRUCT PART^REQUEST; BEGIN STRING PART^NUMBER[0:9]; END; Contains a part number: !10-digit part number !Message returned to requester. Contains part record !information obtained from the inventory file: STRUCT .
The Part-Query Server ($SER1) Writing a Server Program !-----------------------!Other global variables: !-----------------------STRING .S^PTR; !pointer to end of string INT TERM^NUM; !file number for terminal INT .BUFFER[0:BUFSIZE/2 - 1];!I/O buffer STRING .SBUFFER := @BUFFER[0] '<<' 1; !string pointer to I/O ! buffer INT REPLY^ERROR; !error value returned to ! requester INT INV^FNUM; !file number for inventory ! file INT REPLY^LEN; !length of reply buffer INT RECV^NUM; !file number for $RECEIVE INT .
The Part-Query Server ($SER1) Writing a Server Program !-----------------------------------------------------------! Here are a few DEFINEs to make it a little easier to ! format and print messages.
The Part-Query Server ($SER1) Writing a Server Program !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name and its length ! and the error number. This procedure is used when the ! file is not open, so there is no file number for it. ! ! The procedure also stops the program after displaying the ! error message.
The Part-Query Server ($SER1) Writing a Server Program !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and error number are determined from the file number, ! and FILE^ERRORS^NAME is then called to display the ! information. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
The Part-Query Server ($SER1) Writing a Server Program !-----------------------------------------------------------! Procedure to process a request for a part record. This ! procedure checks that the process that sent the message is ! in the opener table before retrieving the part record from ! the inventory file using the key supplied in the part ! number.
The Part-Query Server ($SER1) Writing a Server Program ! ! ! If unable to position to the requested key, return the error number. This error occurs when the key is not in the inventory file.
The Part-Query Server ($SER1) Writing a Server Program ! ! Put the process handle into the opener table at the first empty location and increment the count of openers: I := 1; WHILE I <= MAX^OPENERS DO BEGIN J := 0; COUNT := 0; WHILE J <= (ZSYS^VAL^PHANDLE^WLEN - 1) DO BEGIN IF OPENER^TABLE.OCB[I].PROCESS^HANDLE[J] = -1 THEN COUNT := COUNT + 1; J := J + 1; END; IF COUNT = ZSYS^VAL^PHANDLE^WLEN THEN BEGIN OPENER^TABLE.OCB[I] ':=' RECEIVE^INFO[6] FOR ZSYS^VAL^PHANDLE^WLEN; OPENER^TABLE.OCB[I].
The Part-Query Server ($SER1) Writing a Server Program !-----------------------------------------------------------! Procedure to process a Close system message. It removes ! the requester from the opener table. !-----------------------------------------------------------PROC PROCESS^CLOSE^MESSAGE; BEGIN INT INT INT ! ! ! I; J; COUNT; Check that the closing process is in the opener table.
The Part-Query Server ($SER1) Writing a Server Program !-----------------------------------------------------------! Procedure to process a system message other than Open or ! Close. !-----------------------------------------------------------PROC PROCESS^OTHER^MESSAGE; BEGIN INT INDEX, STATUS; INDEX := -1; DO BEGIN STATUS := OPENER_LOST_( BUFFER:COUNT^READ, OPENER^TABLE.OCB[1], INDEX, MAX^OPENERS, $LEN( OPENER^TABLE.OCB[1] )); IF STATUS = 6 THEN OPENER^TABLE.CURRENT^COUNT := OPENER^TABLE.
The Part-Query Server ($SER1) Writing a Server Program !-----------------------------------------------------------! Procedure to save the Startup message. !-----------------------------------------------------------PROC START^IT(RUCB,START^DATA,MESSAGE,LENGTH, MATCH) VARIABLE; INT .RUCB, .START^DATA, .MESSAGE, LENGTH, MATCH; BEGIN ! ! Copy the Startup message into the START^UP^MESSAGE structure and save the message length: START^UP^MESSAGE.
The Part-Query Server ($SER1) Writing a Server Program ! ! Open $RECEIVE with a receive depth of 1 and to accept system messages (the default): RECV^NAME ':=' "$RECEIVE" -> @S^PTR; RECV^DEPTH := 1; ERROR := FILE_OPEN_(RECV^NAME:@S^PTR '-' @RECV^NAME, RECV^NUM, !access!, !exclusion!, !nowait^depth!, RECV^DEPTH); ! ! Instruct the operating system to send status change messages for processors in both local and remote systems.
The Part-Query Server ($SER1) Writing a Server Program !-----------------------------------------------------------! Main procedure calls INIT to perform initialization and ! then goes into a loop in which it reads the $RECEIVE file. ! It calls the appropriate procedure depending on whether the ! message read was a system message, a user message, or ! whether the read operation generated an error.
The Part-Query Server ($SER1) Writing a Server Program ! ! For any other error return, call the FILE^ERRORS procedure: OTHERWISE -> CALL FILE^ERRORS(RECV^NUM); END; ! Reply to the message: CALL REPLYX(SBUFFER, REPLY^LEN, !count^written!, !message^tag!, REPLY^ERROR); END; END; Guardian Programmer’s Guide — 421922-014 22 - 27
The Process-Order Server ($SER2) Writing a Server Program 22 Writing a Server Program The Process-Order Server ($SER2) Figure 22-4 shows the function of each procedure in the process-order server and the relationships among the procedures. Figure 22-4. Relationship Between Major Procedures in the Process-Order Server VST111.VSD As you can see from Figure 22-4, the structure of the process-order server is similar to that of the part-query server.
The Process-Order Server ($SER2) Writing a Server Program with the new (negative) stock level; the inventory file does not get updated because the requester process uses the negative number to reject the order request. • If there is enough stock on hand to satisfy the request, then the PROCESS^USER^REQUEST procedure creates an order record out of the information sent by the requester, adds the date of the order to the structure, and then writes the new record to the orders file.
The Process-Order Server ($SER2) Writing a Server Program STRING PARAM[0:529]; END; INT MESSAGE^LEN; !parameter string !length of Startup message !Message received from requester. !request information: STRUCT .ORDER^REQUEST; BEGIN STRUCT NAME; BEGIN STRING LAST[0:19]; STRING FIRST[0:19]; STRING INITIAL[0:1]; END; STRING ADDRESS[0:47]; STRING CITY[0:23]; STRING ZIP[0:7]; STRING CCN[0:15]; STRING PART^NUMBER[0:9]; STRING PART^DESC[0:47]; INT QTY^ORDERED; END; !Record to access orders file.
The Process-Order Server ($SER2) Writing a Server Program END; ! supplier !Message returned to requester. Contains the new stock !level and the new order number: STRUCT .ORDER^REPLY; BEGIN INT QUANTITY^ON^HAND; STRING ORDER^NUMBER[0:27]; END; !Record to access inventory file. !about a part record: STRUCT .
The Process-Order Server ($SER2) Writing a Server Program !-----------------------!Other global variables: !-----------------------STRING .S^PTR; !pointer to end of string INT TERM^NUM; !file number for terminal INT .BUFFER[0:BUFSIZE/2 - 1];!I/O buffer STRING .
The Process-Order Server ($SER2) Writing a Server Program !-----------------------------------------------------------! Here are a few DEFINEs to make it a little easier to ! format and print messages.
The Process-Order Server ($SER2) Writing a Server Program !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name and its length ! and the error number. This procedure is used when the ! file is not open, so there is no file number for it. ! ! The procedure also stops the program after displaying the ! error message.
The Process-Order Server ($SER2) Writing a Server Program !--------------------------------------------------------! Procedure to write a message on the terminal and check ! for any error. If there is an error, this procedure ! attempts to write a message about the error and then ! stops the program. !--------------------------------------------------------PROC WRITE^LINE(BUF,LEN); STRING .
The Process-Order Server ($SER2) Writing a Server Program !-----------------------------------------------------------! Procedure to process an order request. This procedure ! checks that the process that sent the message is in the ! opener table before updating the part record with the new ! quantity on hand and creating an order record and writing ! it to the orders file.
The Process-Order Server ($SER2) Writing a Server Program ! Requester not in opener table: BEGIN REPLY^LEN := 0; REPLY^ERROR := 60; RETURN END; I := I + 1; END; ! Proceed because the requester is in the opener table. !----------------------------!Update inventory record with !new quantity on hand and !prepare the reply structure !with new quantity information !----------------------------! ! Copy user message from requester into data structure: ORDER^REQUEST.NAME.
The Process-Order Server ($SER2) Writing a Server Program ! ! Check that there is still enough stock on hand to satisfy the order: ORDER^REPLY.QUANTITY^ON^HAND := PART^REC.QUANTITY^ON^HAND - ORDER^REQUEST.QTY^ORDERED; ! ! If not enough stock, unlock the record and return with negative stock indication: IF ORDER^REPLY.
The Process-Order Server ($SER2) Writing a Server Program ! ! ! ! ! ! Create an order number based on a Julian timestamp and the part number. The INTERPRETTIMESTAMP procedure converts the timestamp into a Gregorian date and time, which subsequent calls to NUMOUT convert into strings. Note that in the year part, the first 2 digits are truncated: J^TIME := JULIANTIMESTAMP; JD^NUMBER := INTERPRETTIMESTAMP(J^TIME,J^DATE^AND^TIME); L := 0; BASE := 10; WIDTH := 2; WHILE L < 6 DO BEGIN CALL NUMOUT(ORDER^RECORD.
The Process-Order Server ($SER2) Writing a Server Program ! Write the order record to the orders file: CALL WRITEX(ORD^FNUM,ORDER^RECORD,$LEN(ORDER^RECORD)); IF <> THEN BEGIN CALL FILE_GETINFO_(ORD^FNUM,REPLY^ERROR); RETURN; END; ! Complete the order reply: ORDER^REPLY.ORDER^NUMBER ':=' ORDER^RECORD.
The Process-Order Server ($SER2) Writing a Server Program !-----------------------------------------------------------! Procedure to process an Open system message (-103). It ! places the process handle of the requester in the opener ! table, if there is room. If the table is full, it ! rejects the open. !-----------------------------------------------------------PROC PROCESS^OPEN^MESSAGE; BEGIN INT I; INT J; INT COUNT; ! ! Check if opener table full.
The Process-Order Server ($SER2) Writing a Server Program !-----------------------------------------------------------! Procedure to process a Close system message. This ! procedure removes the requester from the opener table. !-----------------------------------------------------------PROC PROCESS^CLOSE^MESSAGE; BEGIN INT INT INT ! ! ! I; J; COUNT; Check that the closing process is in the opener table.
The Process-Order Server ($SER2) Writing a Server Program !---------------------------------------------------------! Procedure to process a system message other than Open or ! Close. !-----------------------------------------------------------PROC PROCESS^OTHER^MESSAGE; BEGIN INT INDEX, STATUS, COUNT^READ; INDEX := -1; DO BEGIN STATUS := OPENER_LOST_( BUFFER:COUNT^READ, OPENER^TABLE.OCB[1], INDEX, MAX^OPENERS, $LEN( OPENER^TABLE.OCB[1] )); IF STATUS = 6 THEN OPENER^TABLE.CURRENT^COUNT := OPENER^TABLE.
Writing a Server Program The Process-Order Server ($SER2) !-----------------------------------------------------------! Procedure to save the Startup message. !-----------------------------------------------------------PROC START^IT(RUCB,START^DATA,MESSAGE,LENGTH, MATCH) VARIABLE; INT .RUCB, .START^DATA, .MESSAGE, LENGTH, MATCH; BEGIN ! ! Copy the Startup message into the START^UP^MESSAGE structure and save the message length: START^UP^MESSAGE.
The Process-Order Server ($SER2) Writing a Server Program !-----------------------------------------------------------! Procedure to perform initialization. It calls INITIALIZER ! to read the Startup message then opens the IN file, the ! inventory file, and $RECEIVE, and then initializes the ! opener table. !-----------------------------------------------------------PROC INIT; BEGIN STRING INT STRING STRING INT STRING INT INT INT INT ! .TERM^NAME[0:MAXFLEN TERMLEN; .RECV^NAME[0:MAXFLEN .
The Process-Order Server ($SER2) Writing a Server Program ! Open the orders file: ORD^FNAME ':=' "=ORD^FNAME" -> @S^PTR; ORD^FLEN := @S^PTR '-' @ORD^FNAME; ERROR := FILE_OPEN_(ORD^FNAME:ORD^FLEN,ORD^FNUM); IF ERROR <> 0 THEN CALL FILE^ERRORS^NAME(ORD^FNAME:ORD^FLEN,ERROR); ! Open the inventory file: INV^FNAME ':=' "=INV^FNAME" -> @S^PTR; INV^FLEN := @S^PTR '-' @INV^FNAME; ERROR := FILE_OPEN_(INV^FNAME:INV^FLEN,INV^FNUM); IF ERROR <> 0 THEN CALL FILE^ERRORS^NAME(INV^FNAME:INV^FLEN,ERROR); ! Initialize
The Process-Order Server ($SER2) Writing a Server Program !-----------------------------------------------------------! Main procedure calls INIT to perform initialization and ! then goes into a loop in which it reads the $RECEIVE file. ! It calls the appropriate procedure depending on whether the ! message read was a system message, a user message, or ! whether the read operation generated an error.
The Order-Query Server ($SER3) Writing a Server Program ! ! For any other error return, call the FILE^ERRORS procedure: OTHERWISE -> CALL FILE^ERRORS(RECV^NUM); END; ! Reply to the message: CALL REPLYX(SBUFFER, REPLY^LEN, !count^written!, !message^tag!, REPLY^ERROR); END; END; The Order-Query Server ($SER3) Figure 22-5 shows the function of each procedure in the order-query server and the relationships among the procedures. Figure 22-5.
The Order-Query Server ($SER3) Writing a Server Program The user message read by the SERVER procedure contains a 28-digit order number used to refer to the desired order record. If the requester process is in the opener table, then the PROCESS^USER^REQUEST procedure uses the 28-digit order number provided in the message read by the SERVER procedure as a primary key to the orders file.
The Order-Query Server ($SER3) Writing a Server Program STRING PARAM[0:529]; END; INT MESSAGE^LEN; !parameter string !length of Startup message !Message received from requester. STRUCT ORDER^QUERY; BEGIN STRING ORDER^NUMBER[0:27]; END; Contains a part number: !28-digit order number !Message returned to requester. Contains order record !information obtained from the orders file: STRUCT .
Writing a Server Program The Order-Query Server ($SER3) END; END; Guardian Programmer’s Guide — 421922-014 22 - 51
The Order-Query Server ($SER3) Writing a Server Program !-----------------------!Other global variables: !-----------------------STRING .S^PTR; !pointer to end of string INT TERM^NUM; !file number for terminal INT .BUFFER[0:BUFSIZE/2 - 1];!I/O buffer STRING .
The Order-Query Server ($SER3) Writing a Server Program ! Print the line: DEFINE PRINT^LINE = CALL WRITE^LINE(SBUFFER,@S^PTR '-' @SBUFFER) #; ! Print a blank line: DEFINE PRINT^BLANK = CALL WRITE^LINE(SBUFFER,0) #; ! Print a string: DEFINE PRINT^STR(S) = BEGIN START^LINE; PUT^STR(S); PRINT^LINE; END; #; !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal.
The Order-Query Server ($SER3) Writing a Server Program !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and error number are determined from the file number ! and FILE^ERRORS^NAME is then called to display the ! information. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
The Order-Query Server ($SER3) Writing a Server Program !-----------------------------------------------------------! Procedure to process a request for an order record. This ! procedure checks that the process that sent the message is ! in the opener table before retrieving the order record from ! the orders file using the key supplied in the order ! number.
The Order-Query Server ($SER3) Writing a Server Program ! Read the record from the orders file: CALL READUPDATEX(ORD^FNUM,SBUFFER,BUFSIZE, COUNT^READ); ! ! ! If unable to position to the requested key, return the error number. This error occurs when the key is not in the orders file.
The Order-Query Server ($SER3) Writing a Server Program !-----------------------------------------------------------! Procedure to process an Open system message (-103). It ! places the process handle of the requester in the opener ! table, if there is room. If the table is full, it ! rejects the open. !-----------------------------------------------------------PROC PROCESS^OPEN^MESSAGE; BEGIN INT I; INT J; INT COUNT; ! ! Check if opener table full.
The Order-Query Server ($SER3) Writing a Server Program !-----------------------------------------------------------! Procedure to process a Close system message. This ! procedure removes the requester from the opener table. !-----------------------------------------------------------PROC PROCESS^CLOSE^MESSAGE; BEGIN INT INT INT ! ! ! I; J; COUNT; Check that the closing process is in the opener table.
The Order-Query Server ($SER3) Writing a Server Program !-----------------------------------------------------------! Procedure to process a system message other than Open or ! Close. !-----------------------------------------------------------PROC PROCESS^OTHER^MESSAGE; BEGIN INT INDEX, STATUS; INDEX := -1; DO BEGIN STATUS := OPENER_LOST_( BUFFER:COUNT^READ, OPENER^TABLE.OCB[1], INDEX, MAX^OPENERS, $LEN( OPENER^TABLE.OCB[1] )); IF STATUS = 6 THEN OPENER^TABLE.CURRENT^COUNT := OPENER^TABLE.
Writing a Server Program The Order-Query Server ($SER3) !-----------------------------------------------------------! Procedure to save the Startup message. !-----------------------------------------------------------PROC START^IT(RUCB,START^DATA,MESSAGE,LENGTH, MATCH) VARIABLE; INT .RUCB, .START^DATA, .MESSAGE, LENGTH, MATCH; BEGIN ! ! Copy the Startup message into the START^UP^MESSAGE structure and save the message length: START^UP^MESSAGE.
The Order-Query Server ($SER3) Writing a Server Program !-----------------------------------------------------------! Procedure to perform initialization. It calls INITIALIZER ! to read the Startup message then opens the IN file, the ! inventory file, and $RECEIVE, and then initializes the ! opener table. !-----------------------------------------------------------PROC INIT; BEGIN STRING INT STRING STRING INT INT INT INT ! .TERM^NAME[0:MAXFLEN - 1]; !terminal file name TERMLEN; .
The Order-Query Server ($SER3) Writing a Server Program ! Open the orders file: ORD^FNAME ':=' "=ORD^FNAME" -> @S^PTR; ORD^FLEN := @S^PTR '-' @ORD^FNAME; ERROR := FILE_OPEN_(ORD^FNAME:ORD^FLEN,ORD^FNUM); IF ERROR <> 0 THEN CALL FILE^ERRORS^NAME(ORD^FNAME:ORD^FLEN,ERROR); ! Initialize the opener table: I := 1; WHILE I <= MAX^OPENERS DO BEGIN OPENER^TABLE.OCB[I].PROCESS^HANDLE ':=' [ ZSYS^VAL^PHANDLE^WLEN * [-1] ]; OPENER^TABLE.OCB[I].
The Order-Query Server ($SER3) Writing a Server Program !-----------------------------------------------------------! Main procedure calls INIT to perform initialization and ! then goes into a loop in which it reads the $RECEIVE file. ! It calls the appropriate procedure depending on whether the ! message read was a system message, a user message, or ! whether the read operation generated an error.
The Order-Query Server ($SER3) Writing a Server Program CALL REPLYX(SBUFFER, REPLY^LEN, !count^written!, !message^tag!, REPLY^ERROR); END; END; Guardian Programmer’s Guide — 421922-014 22 - 64
23 Writing a Command-Interpreter Monitor ($CMON) A command-interpreter monitor process ($CMON) controls the operation of TACL processes. When a TACL process receives certain commands from a terminal user, it sends a request message to the $CMON process to have the request verified.
Communicating With TACL Processes Writing a Command-Interpreter Monitor ($CMON) Communicating With TACL Processes Figure 23-1 shows the relationships between the $CMON process and the TACL processes. Figure 23-1. Relationships Between TACL Processes and $CMON VST113.VSD Each TACL process opens the $CMON process the first time that it receives a command that causes a request to be sent to $CMON.
Writing a Command-Interpreter Monitor ($CMON) Communicating With TACL Processes When the user issues an ADDUSER command, the ADDUSER process sends the ADDUSER message directly to the $CMON process (it does not come from the TACL process). Similarly, the DELUSER message is received from the DELUSER program, the PASSWORD message is received from the PASSWORD program, and the REMOTEPASSWORD message from the RPASSWRD program.
Writing a Command-Interpreter Monitor ($CMON) Controlling the Configuration of a TACL Process Controlling the Configuration of a TACL Process If a user is attempting to log on to an interactive TACL process from the logged-off state, or if a noninteractive TACL process is starting, the TACL process sends a Config^msg message to the $CMON process so it can verify or change the default parameters.
Writing a Command-Interpreter Monitor ($CMON) Controlling the Configuration of a TACL Process The configuration parameters are described below: AUTOLOGOFFDELAY if greater than zero, specifies the maximum number of minutes that the TACL process is to wait at a prompt. If that time is exceeded, the TACL process automatically logs off. The default value is -1 (disabled).
Writing a Command-Interpreter Monitor ($CMON) Controlling the Configuration of a TACL Process NOCHANGEUSER if zero, the #CHANGEUSER built-in function is enabled; users can log on from a terminal at which someone is already logged on. If -1, the #CHANGEUSER built-in function is disabled; users cannot log on from an already logged on terminal. The default value is 0.
Retaining Default Values Writing a Command-Interpreter Monitor ($CMON) Retaining Default Values Your $CMON process can reply to the Config^msg message by returning a text message to the TACL process and accepting the default parameter values as they are.
Writing a Command-Interpreter Monitor ($CMON) Controlling Logon and Logoff When you reply with the Config^reply message, you need to specify every value you return. For example, if you want to change only one or two values and leave the remaining values unchanged, you must specify the current values in the fields that remain unchanged.
Controlling Logon Writing a Command-Interpreter Monitor ($CMON) Controlling Logon When a user attempts to log on to the system, the TACL process sends two messages to the $CMON process: the Prelogon^msg message and the Logon^msg message. The Prelogon^msg Message In addition to providing the user ID of the user logging on and information about the TACL process, the Prelogon^msg message provides the name of the user and information about whether the user is already logged on.
Controlling Logon Writing a Command-Interpreter Monitor ($CMON) the TACL process. The display message is typically used to give a reason for the rejection. Format of a Prelogon^reply structure: STRUCT PRELOGON^REPLY; BEGIN INT REPLYCODE; STRING REPLYTEXT[0:n]; ![0] if 0, proceed to VERIFYUSER; ! if 1, disallow logon ![1] optional display text; ! maximum length is 132 bytes END; The following code fragment checks a flag value before deciding whether to accept the prelogon request.
Controlling Logon Writing a Command-Interpreter Monitor ($CMON) information was sent in the Prelogon^msg message.
Controlling Logoff Writing a Command-Interpreter Monitor ($CMON) Controlling Logoff The TACL process sends a Logoff^msg message to $CMON whenever a user logs off either explicitly by issuing a LOGOFF command or implicitly by logging on without first logging off. This message gives $CMON the option of displaying message text on logging off. The $CMON process is not able to reject a request to log off.
Controlling Illegal Logon Writing a Command-Interpreter Monitor ($CMON) Controlling Illegal Logon After the user has failed two consecutive times to log on to the system, the TACL process sends an Illegal^logon^msg message to $CMON on each subsequent failed logon attempt. The $CMON process replies with some message text to be displayed. $CMON cannot reject the Illegal^logon^msg message.
Controlling Passwords Writing a Command-Interpreter Monitor ($CMON) The following code fragment returns a display message to the TACL process following receipt of an Illegal^logon^msg message: CALL READUPDATEX(RECV^NUM,SBUFFER,RCOUNT,BYTES^READ); IF BUFFER[0] = -53 THEN BEGIN ILLEGAL^LOGON^MSG ':=' BUFFER FOR BYTES^READ; ILLEGAL^LOGON^REPLY.REPLYTEXT ':=' "Invalid logon string: "; ILLEGAL^LOGON^REPLY.REPLYTEXT[11] ':=' [ILLEGAL^LOGON^MSG.LOGONSTRING FOR BYTES^READ - 54,0]; SCAN ILLEGAL^LOGON^REPLY.
Writing a Command-Interpreter Monitor ($CMON) When the User Requests to Change a Remote Password After processing the Password^msg message, $CMON sends a Password^reply message back to the TACL process.
Writing a Command-Interpreter Monitor ($CMON) When the User Requests to Change a Remote Password The format of the Remotepassword^msg message is as follows: Format of command-interpreter message -58 (Remotepassword^msg message): STRUCT REMOTEPASSWORD^MSG; BEGIN INT MSGCODE; ![0] INT USERID; ![1] ! INT CIPRI; ![2] ! INT CIINFILE[0:11]; ![3] ! INT CIOUTFILE[0:11]; ![15] ! INT SYSNAME[0:3]; ![27] value -58 user ID of user requesting a change of remote password initial priority of command interpreter name of
Writing a Command-Interpreter Monitor ($CMON) Controlling Process Creation The following code fragment checks a flag value to see whether remote password changes are allowed, then returns display text to the TACL process, depending on the setting of the flag: CALL READUPDATEX(RECV^NUM,SBUFFER,RCOUNT,BYTES^READ); IF BUFFER[0] = -58 THEN BEGIN IF CHANGE^REMOTE^PASSWORD = YES THEN BEGIN REMOTEPASSWORD^REPLY.REPLYCODE := 0; REMOTEPASSWORD^REPLY.
Controlling Process Creation Writing a Command-Interpreter Monitor ($CMON) The structure of the Processcreation^msg message is as follows: Format of command-interpreter message -52 (Processcreation^msg message): STRUCT BEGIN INT INT INT PROCESSCREATION^MSG; MSGCODE; USERID; CIPRI; INT CIINFILE[0:11]; INT CIOUTFILE[0:11]; INT PROGNAME[0:11]; INT PRIORITY; INT PROCESSOR; ![0] ![1] ![2] ! ![3] ! ![15] ! ![27] ![39] ! ! ![40] value -52 user ID or user logged on initial priority of command interpreter name
Controlling Process Creation Writing a Command-Interpreter Monitor ($CMON) To allow process creation, $CMON returns the Processcreation^accept structure with the reply code set to 0. This structure also contains the name of the program file to run, the priority at which the process will run, and the number of the CPU in which the process will run. $CMON can change any of these values from those that it received in the Processcreation^msg message.
Writing a Command-Interpreter Monitor ($CMON) Controlling the Priority of a New Process message containing optional display text. The structure for rejecting a processcreation request is as follows: Format of Processcreation^reject structure: STRUCT PROCESSCREATION^REJECT; BEGIN INT REPLYCODE; ![0] 1 to reject process creation STRING REPLYTEXT[0:n]; ![1] optional message to be ! displayed; maximum 132 ! bytes END; Controlling the Priority of a New Process The PROCESSCREATION^MSG.
Writing a Command-Interpreter Monitor ($CMON) Controlling the CPU of a New Process Controlling the CPU of a New Process The user’s choice of the CPU in which to run the new process is received by the $CMON process in the PROCESSCREATION^MSG.PROCESSOR variable. If the user did not specify a CPU, then this value contains -1. The $CMON process can set the CPU number in the reply to any valid process number or -1 for the CPU in which the primary process of the TACL process is running.
Writing a Command-Interpreter Monitor ($CMON) ! Controlling Change of Process Priority Do not change the program-file name: PROCESSCREATION^ACCEPT.PROGNAME ':=' PROCESSCREATION^MSG.PROGNAME FOR 12; ! Reply to the TACL process: CALL REPLYX(PROCESSCREATION^ACCEPT, $LEN(PROCESSCREATION^ACCEPT)); END; Alternatively, you can prohibit use of certain CPUs that normally perform critical processing.
Controlling Change of Process Priority Writing a Command-Interpreter Monitor ($CMON) The format of the Altpri^msg message is as follows: Format of command-interpreter message -56 (Altpri^msg message): STRUCT ALTPRI^MSG; BEGIN INT MSGCODE; INT USERID; INT CIPRI; INT CIINFILE[0:11]; INT CIOUTFILE[0:11]; INT CRTPID[0:3]; INT PROGNAME[0:11]; ![0] ![1] ! ![2] ! ![3] ! ![15] ! ![27] ! ![31] value -56 user ID of user requesting change of priority current priority of command interpreter name of command file of
Writing a Command-Interpreter Monitor ($CMON) Controlling Adding and Deleting Users The following code fragment allows the priority to be reduced but not increased: CALL READUPDATEX(RECV^NUM,SBUFFER,RCOUNT,BYTES^READ); IF BUFFER[0] = -56 THEN BEGIN ALTPRI^MSG ':=' SBUFFER FOR BYTES^READ; ERROR := PROCESS_GETINFO_(ALTPRI^MSG.PROCESS^HANDLE, !file^name:maxlen!, !file^name^len!, PRIORITY); IF ERROR <> 0 THEN BEGIN ALTPRI^REPLY.REPLYCODE = 1; END ELSE BEGIN IF PRIORITY > ALTPRI^MSG.PRIORITY THEN ALTPRI^REPLY.
Controlling Adding a User Writing a Command-Interpreter Monitor ($CMON) The format of the Adduser^msg message is as follows: Format of command-interpreter message -54 (Adduser^msg message): STRUCT ADDUSER^MSG; BEGIN INT MSGCODE; INT USERID; INT CIPRI; INT CIINFILE[0:11]; INT CIOUTFILE[0:11]; INT GROUPNAME[0:3]; INT USERNAME[0:3]; INT GROUP^ID; INT USER^ID[0:3]; ![0] ![1] ! ![2] ! ![3] ! ![15] ! ![27] ! ![31] ![35] ! ![36] ! value -54 user ID of user making the request initial priority of command interpr
Controlling Deleting a User Writing a Command-Interpreter Monitor ($CMON) The following code fragment rejects any attempt to add a user except by a super-group user (255, n): CALL READUPDATEX(RECV^NUM,SBUFFER,RCOUNT,BYTES^READ); IF BUFFER[0] = -54 THEN BEGIN REQUESTING^GROUP^ID := ADDUSER^MSG.USERID.<0:7>; IF REQUESTING^GROUP^ID = 255 THEN ADDUSER^REPLY.REPLY^CODE := 0 ELSE ADDUSER^REPLY.
Writing a Command-Interpreter Monitor ($CMON) Controlling $CMON While the System Is Running The $CMON process must respond to a Deluser^msg message with a Deluser^reply structure; the reply code must be 0 to allow the deletion or 1 to reject the deletion. The reply may also contain display text.
Writing a Command-Interpreter Monitor ($CMON) • Controlling $CMON While the System Is Running Control the CPU in which new processes can execute. This section has already described how to do this statically; your $CMON program can be written to allow CPU specification at run time. One effective way of providing run-time control is to provide a command-interface program that shall be referred to as CMONCOM.
Writing a Command-Interpreter Monitor ($CMON) Setting the Logon Display Text at Run Time Setting the Logon Display Text at Run Time The following code fragments show how a command-interface program and a $CMON process can change the logon display text at run time. Here, (plus) 50 has been chosen as the message code for a message containing logon text.
Writing a Command-Interpreter Monitor ($CMON) Setting the Logon Display Text at Run Time In the $CMON process: CALL READUPDATEX(RECV^NUM,SBUFFER,RCOUNT,BYTES^READ); IF BUFFER[0] = 50 THEN BEGIN ! Find out if sender is in group 255: CALL FILE_GETRECEIVEINFO_(INFO); P^HANDLE ':=' INFO[6] FOR 10; ERROR := PROCESS_GETINFO_(P^HANDLE, !file^name:maxlen!, !file^name^len!, !priority!, !moms^processhandle!, !hometerm:maxlen!, !hometerm^len!, !process^time!, !caid!, PAID); IF PAID.
Refusing Command-Interpreter Requests Writing a Command-Interpreter Monitor ($CMON) Refusing Command-Interpreter Requests You can write your $CMON and command-interface programs to set a flag that $CMON will check before replying to any command-interpreter message. If the flag is on, then $CMON rejects all requests. If the flag is off, then requests are processed normally.
Refusing Command-Interpreter Requests Writing a Command-Interpreter Monitor ($CMON) In the $CMON process: CALL READUPDATEX(RECV^NUM,SBUFFER,RCOUNT,BYTES^READ); IF BUFFER[0] = 61 THEN BEGIN ! Find out if sender is in group 255: CALL FILE_GETRECEIVEINFO_(INFO); P^HANDLE ':=' INFO[6] FOR 10; ERROR := PROCESS_GETINFO_(P^HANDLE, !file^name:maxlen!, !file^name^len!, !priority!, !moms^processhandle!, !hometerm:maxlen!, !hometerm^len!, !process^time!, !caid!, PAID); IF PAID.
Writing a Command-Interpreter Monitor ($CMON) ! Controlling Which CPU a Process Can Run In Accept if operator group: BEGIN LOGON^REPLY.REPLYCODE := 0; LOGON^REPLY.REPLYTEXT ':=' LOGON^TEXT FOR LOGON^TEXT^LEN; END; END ! Accept if REFUSE^ALL = NO: ELSE BEGIN LOGON^REPLY.REPLYCODE := 0; LOGON^REPLY.
Writing a Command-Interpreter Monitor ($CMON) Controlling Which CPU a Process Can Run In In the command-interface program: STRUCT BEGIN INT INT INT CPU^CHANGESTATUS^MSG; MSGCODE; PROCESSOR; STATUS; !value 62 !processor number to change status !new status; 1 for move to ! priority-response list, 0 for ! move to non-priority-response ! list END; . . !Set up the CPU^CHANGESTATUS^MSG message and send to $CMON: CPU^CHANGESTATUS^MSG.MSGCODE := 62; CPU^CHANGESTATUS^MSG.
Writing a Command-Interpreter Monitor ($CMON) Controlling Which CPU a Process Can Run In IF BUFFER[0] = -52 THEN BEGIN PROCESSCREATION^MSG ':=' SBUFFER FOR $LEN(PROCESSCREATION^MSG); !Limit process priority to 175: IF PROCESSCREATION^MSG.PRIORITY > 175 THEN PROCESSCREATION^ACCEPT.PRIORITY := 175 ELSE PROCESSCREATION^ACCEPT.PRIORITY := 0; ! Accept priority ! ! Allocate priority-response processor if priority over 150, otherwise allocate non-priority-response processor: IF PROCESSCREATION^MSG.
Writing a Command-Interpreter Monitor ($CMON) Writing a $CMON Program: An Example Writing a $CMON Program: An Example The example presented here contains the code for two different processes: • • A $CMON process A command-interface program, CMONCOM The $CMON process responds to all requests from a TACL process as well as to requests made by the command-interface program. The command-interface program makes requests for run-time control over how $CMON responds to requests made by a TACL process.
Writing a Command-Interpreter Monitor ($CMON) • Sample $CMON Program The WRITE^LINE procedure provides a convenient way of writing a line to the terminal. Procedures for Processing Requests From the Command-Interface Program • • • • • The CHANGE^LOGON^MESSAGE procedure is called when $CMON receives an RT^LOGON^MESSAGE structure from the command-interface program. This procedure updates a buffer containing logon text using the text contained in the incoming message.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program nonoperator TACL processes only if the operator has already set the REFUSE^ALL flag to inhibit further requests. • • • • • The PROCESS^LOGOFF^MSG procedure is called when $CMON receives a Logoff^msg message from a TACL process. It functions the same way as the PROCESS^LOGON^MSG procedure, except that it returns a logoff message instead of a logon message.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) The $CMON Code The code for the $CMON program follows. ?INSPECT, SYMBOLS, NOCODE, NOMAP ?NOLIST, SOURCE $SYSTEM.ZSYSDEFS.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program BEGIN INT VOLUME[0:3]; INT SUBVOL[0:3]; END; STRUCT INFILE; BEGIN INT VOLUME[0:3]; INT SUBVOL[0:3]; INT FILEID[0:3]; END; STRUCT OUTFILE; BEGIN INT VOLUME[0:3]; INT SUBVOL[0:3]; INT FILEID[0:3]; END; STRING PARAM[0:529]; END; INT MESSAGE^LEN; ?NOLIST, SOURCE $SYSTEM.SYSTEM.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) ! Print a string: DEFINE PRINT^STR(S) = BEGIN START^LINE; PUT^STR(S); PRINT^LINE; END #; !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, length, and ! error number. This procedure is mainly to be used when ! the file is not open, so there is no file number for it. ! FILE^ERRORS is to be used when the file is open.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program !----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameter is the file number. The file ! name and error number are determined from the file number ! and FILE^ERRORS^NAME is then called to do the display. ! ! FILE^ERRORS^NAME also stops the program after displaying ! the error message.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program !-----------------------------------------------------------! Procedure to process the Config^msg message. Accepts the ! current default values in all cases. !-----------------------------------------------------------PROC PROCESS^CONFIG^MSG; BEGIN STRUCT .CONFIG^TEXT^REPLY; BEGIN INT REPLYCODE; STRING REPLYTEXT[0:63]; END; ! Prepare the reply message: CONFIG^TEXT^REPLY.REPLYCODE := 1; CONFIG^TEXT^REPLY.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to process the Prelogon^msg message. This ! request is accepted in all cases, except during the period ! before shutdown.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to process a Logon^msg message. The logon is ! always accepted, except after the shutdown phase has begun. ! The logon message returned to the TACL process can be ! changed at run time.
Writing a Command-Interpreter Monitor ($CMON) ! ! Sample $CMON Program Otherwise accept the logon request and reply with the logon text: BEGIN LOGON^REPLY.REPLYCODE := 0; LOGON^REPLY.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to process a Logoff^msg message. The message is ! always accepted except during the shutdown phase. The ! display text returned to the TACL process can be changed at ! run time.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) CALL REPLYX(LOGOFF^REPLY,$LEN(LOGOFF^REPLY)); END; !-----------------------------------------------------------! Procedure for processing an Illegal^logon^msg message. ! This message is always accepted, even during the shutdown ! phase. This procedure simply returns blank display text to ! the TACL process. !-----------------------------------------------------------PROC PROCESS^ILLEGAL^LOGON^MSG; BEGIN STRUCT .
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to process a Password^msg message. This request ! is always accepted, except during the shutdown phase. !-----------------------------------------------------------PROC PROCESS^PASSWORD^MSG; BEGIN STRUCT MSG(*); BEGIN INT MSGCODE; INT USERID; INT CIPRI; INT CIINFILE[0:11]; INT CIOUTFILE[0:11]; END; !template for Password^msg ! message INT .
Writing a Command-Interpreter Monitor ($CMON) ! Sample $CMON Program Otherwise accept the request and reply with display text: BEGIN PASSWORD^REPLY.REPLYCODE := 0; PASSWORD^REPLY.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program !-----------------------------------------------------------! Procedure to process a Remotepassword^msg message. This ! request is always accepted, except during the shutdown ! phase.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program ELSE ! Otherwise accept the request and return the display text: BEGIN REMOTEPASSWORD^REPLY.REPLYCODE := 0; REMOTEPASSWORD^REPLY.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to process a Processcreation^msg message. This ! request is always accepted, except during the shutdown ! phase. This procedure assigns the process to a processor ! according to whether the process runs at a high or low ! priority.
Writing a Command-Interpreter Monitor ($CMON) ! Sample $CMON Program Blank the reply buffer for rejecting requests: PROCESSCREATION^REJECT[0] ':=' " "; PROCESSCREATION^REJECT[1] ':=' PROCESSCREATION^REJECT[0] FOR 63; ! Extract the group ID of the requesting TACL process: REQUESTING^GROUPID := PROCESSCREATION^MSG.USERID.<0:7>; ! ! If shutting the system down, allow only requests from the operator group: IF REFUSE^ALL AND (REQUESTING^GROUPID <> 255) THEN BEGIN PROCESSCREATION^REJECT.
Writing a Command-Interpreter Monitor ($CMON) ! ! Sample $CMON Program There is no priority processor available, use a nonpriority-response processor: BEGIN NONPRIORITY^CPU := NONPRIORITY^CPU + 1; IF NONPRIORITY^CPU = (TOP^CPU^NUMBER + 1) THEN NONPRIORITY^CPU := 0; PROCESSCREATION^ACCEPT.PROCESSOR := NONPRIORITY^CPU; END; ! Next priority processor found: IF CPU^LIST[PRIORITY^CPU] = 1 THEN PROCESSCREATION^ACCEPT.
Writing a Command-Interpreter Monitor ($CMON) ! Do not change the program-file name: PROCESSCREATION^ACCEPT.PROGNAME ':=' PROCESSCREATION^MSG.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to process an Altpri^msg message. This request ! is rejected during the shutdown phase. Otherwise, the ! request is accepted only if it reduces the priority of ! the process.
Writing a Command-Interpreter Monitor ($CMON) ! Sample $CMON Program If accepting the request: BEGIN ! ! Allow priority change only if operator group attempting to reduce priority: CALL PROCESS_GETINFO_(ALTPRI^MSG.PHANDLE, !file^name:maxlen!, !file^name^len!, PRIORITY); IF (PRIORITY > ALTPRI^MSG.PRIORITY) OR (REQUESTING^GROUPID = 255) THEN ALTPRI^REPLY.REPLYCODE := 0 ELSE BEGIN ALTPRI^REPLY.REPLYCODE := 1; ALTPRI^REPLY.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to process an Adduser^msg message. This request ! is rejected during the shutdown phase. The request is ! accepted only if the request comes from a super-group ! user (255, n).
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program ELSE ! ! Otherwise, accept request only if originating from operator group. BEGIN IF REQUESTING^GROUPID = 255 THEN ADDUSER^REPLY.REPLYCODE := 0 ELSE BEGIN ADDUSER^REPLY.REPLYCODE := 1; ADDUSER^REPLY.
Sample $CMON Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to process a Deluser^msg message. This request ! is rejected during the shutdown phase. The request is ! accepted only if the request comes from a super-group ! user (255, n).
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program ELSE ! Otherwise, accept the request only if from operator group: BEGIN IF REQUESTING^GROUPID = 255 THEN DELUSER^REPLY.REPLYCODE := 0 ELSE BEGIN DELUSER^REPLY.REPLYCODE := 1; DELUSER^REPLY.REPLYTEXT ':=' "Must be operator"; END; END; ! Send reply to TACL process: CALL REPLYX(DELUSER^REPLY,$LEN(DELUSER^REPLY)); END; !-----------------------------------------------------------! Procedure to process a Change^logon^message.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program !-----------------------------------------------------------! Procedure to process a Change^logoff^message. This ! procedure takes the logoff display text from the input ! message and puts it in the LOGOFF^TEXT array to be read by ! the PROCESS^LOGOFF^MSG procedure when a user tries to log ! off.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program !-----------------------------------------------------------! Procedure responds to an RT^shutdown^message. This ! procedure sets a flag that prohibits $CMON from accepting ! any further requests from nonoperator TACL ! processes. !-----------------------------------------------------------PROC REJECT^REQUESTS; BEGIN STRUCT MSG(*); !template for RT^shutdown^message BEGIN INT MSGCODE; STRING SHUTDOWNTEXT[0:63]; END; INT .
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program !-----------------------------------------------------------! Procedure to respond to a CPU^changestatus^message. This ! procedure extracts a processor number and status from the ! incoming message and updates the status of the processor ! accordingly in the CPU^LIST array.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program !-----------------------------------------------------------! Procedure to save the Startup message in the global data ! area. !-----------------------------------------------------------PROC SAVE^STARTUP^MESSAGE(RUCB,START^DATA,MESSAGE, LENGTH,MATCH)VARIABLE; INT INT INT INT INT .RUCB; .START^DATA; .MESSAGE; LENGTH; MATCH; BEGIN ! Copy the Startup message into the CI^STARTUP structure: CI^STARTUP.
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program !-----------------------------------------------------------! Procedure to read the Startup message, and open the IN ! file, and open the $RECEIVE file. This procedure also ! initializes the CPU^LIST array. !-----------------------------------------------------------PROC INIT; BEGIN STRING .RECV^FILE[0:MAXFLEN - 1]; INT RECVLEN; STRING .
Writing a Command-Interpreter Monitor ($CMON) CPU^LIST[1] := 1; END; Guardian Programmer’s Guide — 421922-014 23 - 68 Sample $CMON Program
Writing a Command-Interpreter Monitor ($CMON) Sample $CMON Program !-----------------------------------------------------------! Main procedure performs initialization, then goes into a ! loop in which it reads the $RECEIVE file and then calls the ! appropriate procedure depending on whether the message read ! was a system message, the message read was a user message, ! or the read generated an error.
Writing a Command-Interpreter Monitor ($CMON) ! ! Sample Command-Interface Program For a user message, Select a procedure depending on the results of the read operation: 0 -> BEGIN CASE BUFFER[0] OF BEGIN -60 -> CALL PROCESS^CONFIG^MSG; -59 -> CALL PROCESS^PRELOGON^MSG; -50 -> CALL PROCESS^LOGON^MSG; -51 -> CALL PROCESS^LOGOFF^MSG; -53 -> CALL PROCESS^ILLEGAL^LOGON^MSG; -57 -> CALL PROCESS^PASSWORD^MSG; -58 -> CALL PROCESS^REMOTEPASSWORD^MSG; -52 -> CALL PROCESS^PROCESSCREATION^MSG; -56 -> CALL PROCESS^A
Writing a Command-Interpreter Monitor ($CMON) • • • • • • • • • Sample Command-Interface Program The CHANGE^LOGON^MESSAGE procedure is called when the operator chooses to change the logon message by selecting 1 from the menu. This procedure prompts the operator for the new logon text before sending an RT^logon^message to $CMON. The CHANGE^LOGOFF^MESSAGE procedure is called when the operator chooses to change the logoff message by selecting 2 from the menu.
Sample Command-Interface Program Writing a Command-Interpreter Monitor ($CMON) ?INSPECT, SYMBOLS, NOCODE, NOMAP ?NOLIST, SOURCE $SYSTEM.ZSYSDEFS.ZSYSTAL ?LIST !Literals: LITERAL TOP^CPU^NUMBER = 5; LITERAL BUFSIZE = 512; LITERAL MAXFLEN = ZSYS^VAL^LEN^FILENAME; LITERAL ABEND = 1; !highest CPU number on system !size in bytes of I/O buffer !maximum file-name length !Global variables: STRING .SBUFFER[0:BUFSIZE]; INT TERMNUM; INT CMONNUM; STRING .
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !-----------------------------------------------------------! The following DEFINEs help formatting and printing text.
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !-----------------------------------------------------------! Procedure for displaying file-system error numbers on the ! terminal. The parameters are the file name, name length, ! and error number. This procedure is mainly to be used when ! the file is not open, when there is no file number for it. ! File^ERRORS should be used when the file is open. ! ! The procedure also stops the program after displaying ! the error message.
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !-----------------------------------------------------------! Procedure to write a message on the terminal and check ! for any error. If there is an error, the procedure tries ! to write a message about the error and the program is ! stopped. !-----------------------------------------------------------PROC WRITE^LINE(BUF,LEN); STRING .
Sample Command-Interface Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to generate an RT^logon^message. This procedure ! prompts the operator for the logon display text, creates ! the RT^logon^message and sends it to the $CMON process. !-----------------------------------------------------------PROC CHANGE^LOGON^MESSAGE; BEGIN STRUCT .
Sample Command-Interface Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to generate a Change^logoff^message. This ! procedure prompts the operator for the logoff display text, ! and then sends the new text to the $CMON procedure. !-----------------------------------------------------------PROC CHANGE^LOGOFF^MESSAGE; BEGIN STRUCT .
Sample Command-Interface Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure generates an RT^shutdown^message. This procedure ! prompts the operator for the shutdown display text, puts ! it into the message, and sends the message to $CMON. !-----------------------------------------------------------PROC REJECT^REQUESTS; BEGIN STRUCT .
Sample Command-Interface Program Writing a Command-Interpreter Monitor ($CMON) !-----------------------------------------------------------! Procedure to generate an RT^start^message and send it to ! the $CMON process. !-----------------------------------------------------------PROC ACCEPT^REQUESTS; BEGIN STRUCT RT^START^MESSAGE; BEGIN INT MSGCODE; END; INT ! ! !structure to send to $CMON BYTES^READ; Set message code in message structure for the restart message: RT^START^MESSAGE.
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !-----------------------------------------------------------! Procedure to generate a CPU^changestatus^message. This ! procedure prompts the operator for a processor number and then ! for a status (priority or nonpriority) to assign to that ! processor. Finally, this procedure sends the ! CPU^changestatus^message to $CMON. !-----------------------------------------------------------PROC CHANGE^CPU^STATUS; BEGIN STRING .
Writing a Command-Interpreter Monitor ($CMON) ! Sample Command-Interface Program Obtain the new priority for the processor from the operator: DO BEGIN SBUFFER ':=' ["Enter new status: 1 for priority,", "0 for non-priority: "] -> @S^PTR; CALL WRITEREADX(TERMNUM,SBUFFER,@S^PTR '-' @SBUFFER, BUFSIZE,BYTES^READ); IF <> THEN CALL FILE^ERRORS(TERMNUM); SBUFFER[BYTES^READ] := 0; @NEXT^ADR := DNUMIN(SBUFFER,NUMBER,10,STATUS); END UNTIL STATUS = 0 AND @NEXT^ADR = $XADR(SBUFFER[BYTES^READ]) AND ($INT(NUMBER) = 1 O
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !----------------------------------------------------------! Procedure to prompt the user for the next function to ! perform: ! ! "1" to change the logon message ! "2" to change the logoff message ! "3" to prohibit requests during shutdown ! "4" to re-enable requests ! "5" to change the processor status ! "x" to exit the program ! ! The selection made is returned as the result of the call.
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !-----------------------------------------------------------! Procedure opens the $CMON process. Prompts the user to try ! again if the open fails. !-----------------------------------------------------------PROC OPEN^CMON(PROCESS^NAME,PROCESS^NAMELEN,SERVER^NUM); STRING .PROCESS^NAME; INT PROCESS^NAMELEN; INT .
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !-----------------------------------------------------------! Procedure handles creating and opening a $CMON process. If ! $CMON already exists it calls OPEN^CMON to open it. If ! $CMON does not exist, it creates the process and sends it ! the standard process initialization sequence.
Writing a Command-Interpreter Monitor ($CMON) ! Sample Command-Interface Program Create process: ERROR := PROCESS_CREATE_( CMON^OBJECT^NAME:OBJFILE^NAMELEN, !library^file:length!, !swap^file:length!, !ext^swap^file:length!, !priority!, !processor!, !process^handle!, !error^detail!, ZSYS^VAL^PCREATOPT^NAMEINCALL, PROCESS^NAME:PROCESS^NAMELEN); IF ERROR <> 0 THEN BEGIN PRINT^STR("Unable to create $CMON"); CALL PROCESS_STOP_; END; ! Open the new $CMON process: CALL OPEN^CMON(PROCESS^NAME,PROCESS^NAMELEN,
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !-----------------------------------------------------------! Procedure to save the Startup message in the global ! data area. !-----------------------------------------------------------PROC SAVE^STARTUP^MESSAGE(RUCB,START^DATA,MESSAGE, LENGTH,MATCH)VARIABLE; INT .RUCB; INT .START^DATA; INT .MESSAGE; INT LENGTH; INT MATCH; BEGIN ! Save Startup message in CI^STARTUP structure: CI^STARTUP.
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !-----------------------------------------------------------! Procedure to read the Startup message and open the terminal ! and $CMON files. !-----------------------------------------------------------PROC INIT; BEGIN STRING .PROGNAME[0:MAXFLEN - 1]; INT PROGNAME^LEN; STRING .PROCESS^NAME[0:MAXFLEN - 1]; INT PROCESS^NAME^LEN; STRING .
Writing a Command-Interpreter Monitor ($CMON) Sample Command-Interface Program !-----------------------------------------------------------! Main procedure performs initialization, then goes into a ! loop in which it reads the $RECEIVE file and then calls the ! appropriate procedure depending on whether the message read ! was a system message, the message used was a user message, ! or the read generated an error.
Writing a Command-Interpreter Monitor ($CMON) Debugging a TACL Monitor ($CMON) Debugging a TACL Monitor ($CMON) Replacing a standard $CMON with an untested program being debugged can lead to unacceptable delays and inconvenience to the user community. You should therefore name the TACL monitor program you are developing something other than $CMON and follow the guidelines described in this subsection for debugging and testing.
Writing a Command-Interpreter Monitor ($CMON) == A TACL Macro for Debugging and Testing a $CMON Program Structure for receiving the reply to a Logon^msg message: [#DEF loggedon^reply STRUCT BEGIN INT reply^code; CHAR reply^text (0:131); END; ] == == == == Structure for receiving a reply to a Config^msg message. cmon:configreply^text is used when $CMON accepts the default parameters.
Writing a Command-Interpreter Monitor ($CMON) == A TACL Macro for Debugging and Testing a $CMON Program Open $CMON socket: #SET cmon^process^name $CMOO == Temporary name of == $CMON process #SET cmon^process^sock == .
Writing a Command-Interpreter Monitor ($CMON) Procedure for Debugging and Testing a TACL Monitor ($CMON) Procedure for Debugging and Testing a TACL Monitor ($CMON) The following steps describe the recommended way to debug or test a $CMON program. 1. Start your $CMON program and give it a name other than $CMON. Start it under debug if desired: 1> RUN[D] ZCMON/NAME $CMOO/ See the Inspect Manual or the Debug Manual for general debugging information. 2.
24 Writing a Terminal Simulator Using the interprocess-communication features described in Section 6, Communicating With Processes, you can write a program that simulates a terminal. A user process can communicate with this terminal-simulation process as though it were a real terminal. This user process is typically a requester in an application designed according to the requester/server model.
Specifying Device Subtype 30 Writing a Terminal Simulator Specifying Device Subtype 30 A terminal-simulation process must have a device subtype of 30. The device subtype of a process is an attribute that is stored in the object file. By default, the subtype is 0; however, you can specify some value for this attribute when you compile or bind the program.
Assigning a Name to the Terminal-Simulation Process Writing a Terminal Simulator Assigning a Name to the Terminal-Simulation Process A terminal-simulation process must be a named process. You assign a name to the process when you call the PROCESS_CREATE_ procedure to create the process or type the TACL RUN command. The following example creates a process named “$T1.
Writing a Terminal Simulator Specifying How to Process System Messages default value 0 of the options parameter in the FILE_OPEN_ call that opens $RECEIVE: FILE^NAME ':=' "$RECEIVE" -> @S^PTR; LENGTH := @S^PTR '-' @FILE^NAME; OPTIONS.<15> := 0; ERROR := FILE_OPEN_(FILE^NAME:LENGTH, RECV^NUM); See Section 6, Communicating With Processes, for more information about opening $RECEIVE.
Allowing the Requester to Call SETPARAM Writing a Terminal Simulator If the terminal-simulation process allows requesters to specify the last-params parameter, the process must remember the previous parameter values for SETMODE functions. Furthermore, the process must return this information to the requester when responding to Setmode messages. Reading and responding to Setmode messages is discussed in detail later in this section.
Processing I/O Requests Writing a Terminal Simulator 6 indicates that a file-system message was received, so the message should be processed as a system message. The following code fragment checks the message read from $RECEIVE to determine whether it is a system message or a user message: CALL READUPDATE(RECV^NUM,BUFFER,RCOUNT); CALL FILE_GETINFO_(RECV^NUM,ERROR); IF ERROR = 6 THEN... !system message IF ERROR = 0 THEN...
Processing I/O Requests Writing a Terminal Simulator Immediately after reading a message from $RECEIVE, the terminal-simulation process should call FILE_GETRECEIVEINFO_ to check the value returned for the I/O type. The meanings of the I/O type values that can be returned are as follows: 0 The most recent message read from $RECEIVE was not an I/O request. The terminal-simulation process should process the message as a system message. 1 The most recent message read from $RECEIVE was a WRITE[X] request.
Processing I/O Requests Writing a Terminal Simulator . . CALL READUPDATEX(RECV^NUM,SBUFFER,RCOUNT,BYTES^READ); CALL FILE_GETINFO_(RECV^NUM,ERROR); CALL FILE_GETRECEIVEINFO_(RECEIVE^INFO); IF ERROR = 6 THEN !system message BEGIN . .
Processing System Messages Writing a Terminal Simulator ! ! I/O is a WRITEREAD[X]; write message to terminal, wait for response, write response back to requester: 3 -> BEGIN ERROR^RETURN := 0; WCOUNT := BYTES^READ; RCOUNT := BYTES^TO^RETURN; CALL WRITEREADX(TERM^NUM,SBUFFER,WCOUNT,RCOUNT, BYTES^READ); IF <> THEN CALL FILE_GETINFO_(TERM^NUM,ERROR^RETURN); CALL REPLYX(SBUFFER, BYTES^READ, !count^written!, !message^tag!, ERROR^RETURN); END; !Any other I/O type is unexpected: OTHERWISE -> BEGIN . .
Processing Control Messages Writing a Terminal Simulator Processing Control Messages When the requester calls the Control procedure, a Control message is sent to the terminal-simulation process. The message has the following format: Structure of the Control message (system message -32): sysmsg[0] = -32 sysmsg[1] = the operation parameter of the CONTROL call sysmsg[2] = the param parameter of the CONTROL call A requester can perform many terminal-related operations by calling the CONTROL procedure.
Processing Setparam Messages Writing a Terminal Simulator SETMODE function. The terminal-simulation process must respond to system message -33 with a message in the following format: Structure of -33 reply message: replymsg[0] = -33 replymsg[1] = previous value of param1, if requested replymsg[2] = previous value of param2, if requested A requester can perform many terminal-related functions by calling the SETMODE procedure.
Processing Device-Type Information Requests Writing a Terminal Simulator Processing Device-Type Information Requests A requester process can call a procedure such as FILE_GETINFO[BYNAME]_ to determine the device type of the terminal-simulation process. When another process requests device-type information from the terminal-simulation process, system message -106 is sent to the terminal-simulation process.
Managing the BREAK Key Writing a Terminal Simulator BEGIN !Set device type to 6 and subtype to 30: REPLY^TO^106.MESSAGE^NUMBER := -106; REPLY^TO^106.DEVICE^TYPE := 6; REPLY^TO^106.DEVICE^SUBTYPE := 30; REPLY^TO^106.RESERVED[0] := -1; REPLY^TO^106.RESERVED[1] := -1; REPLY^TO^106.RESERVED[2] := -1; REPLY^TO^106.
Tracking the BREAK Owner Writing a Terminal Simulator Tracking the BREAK Owner The terminal-simulation process must know the BREAK owner so that it can send Break-on-device messages to it.
Sending Break-on-Device Messages Writing a Terminal Simulator When the user presses the BREAK key, the terminal simulator might enter BREAK mode, depending on the value of the param2 parameter on the last call to SETMODE function 11 or word 1 of the param-array parameter passed to the last call to SETPARAM function 3. Sending Break-on-Device Messages The terminal-simulation process can send Break-on-device messages to requesters.
25 Debugging, Trap Handling, and Signal Handling This section deals with fixing problems that might occur with compiled code. Such problems typically are coding errors that produce unexpected results or prevent the program from continuing to execute. Coding errors are not the only possible cause of problems: a lack of a system resource (such as memory) can also prevent normal process execution. Such situations are reported as traps to TNS and accelerated programs and as signals to native programs.
Debugging, Trap Handling, and Signal Handling Getting a Process Into the Debug State be done in previous RVUs on the NonStop server using Debug or Inspect.
Debugging, Trap Handling, and Signal Handling • • • • • • • • Getting a Process Into the Debug State Using the DEBUG procedure Using the PROCESS_DEBUG_ procedure Using the PROCESS_CREATE_ or PROCESS_LAUNCH_ procedure Using the RUND command (or the TACL run option DEBUG) Using the DEBUG command (from the TACL prompt) Using the Visual Inspect application (TNS/R and TNS/E environments only) Using the Native Inspect application (TNS/E environment only) Specifying a breakpoint The following paragraphs descri
Debugging, Trap Handling, and Signal Handling Getting a Process Into the Debug State You can accomplish the same thing using the PROCESS_LAUNCH_ procedure. The difference is that the debug options bits are passed to PROCESS_LAUNCH_ in a field in a structure instead of in a separate parameter.
Debugging, Trap Handling, and Signal Handling Getting a Process Into the Debug State Using the DEBUG Command You can put a process into the Debug state by entering the DEBUG command. You need to supply the CPU number and PIN of the process you want to debug. Typical use of this command is as follows: 1. Run a process with the RUN command: > RUN OBJFILE 2. Press the BREAK key to return the command-interpreter prompt. 3.
Debugging, Trap Handling, and Signal Handling Specifying the Debugging Environment You can also specify a home terminal on which you want Native Inspect to run: TACL> debug $myuproc, term $ztn10.#pthef Native Inspect is used to debug native processes; it does not support process debugging of TNS processes. See the Native Inspect Manual for further information. Specifying a Breakpoint You can put a process into the Debug state by specifying a breakpoint, as follows: 1.
Debugging, Trap Handling, and Signal Handling Specifying the Debugging Environment Switching Debuggers During a Debugging Session The native debuggers Inspect, Visual Inspect, and Native Inspect (in the TNS/E environment only), allow you to switch from one debugger to an other while debugging a process.
Debugging, Trap Handling, and Signal Handling Specifying the Debugging Environment The next example also establishes the Inspect program as the default debugger while specifying that a saveabend file should be created: > SET INSPECT SAVEABEND The last example establishes Debug as the default debugger: > SET INSPECT OFF The environment set up by the SET INSPECT command can be overridden by a RUN command, a Binder command, or a TAL compiler directive.
Debugging, Trap Handling, and Signal Handling Handling Trap Conditions To specify a saveabend file for the process, set bit 13 to 1. You can accomplish the same things using the PROCESS_LAUNCH_ procedure. The difference is that the debug options bits are passed to PROCESS_LAUNCH_ in a field in a structure instead of in a separate parameter.
Debugging, Trap Handling, and Signal Handling Handling Trap Conditions Table 25-1. Summary of Trap Conditions Trap Number 0 1 2 3 4 5 8 11 12 13 Cause of Trap Invalid address reference Instruction failure Arithmetic overflow Stack overflow Process loop-timer timeout Invalid call from process with PIN greater than 255 Signal (Under very unusual circumstances, a signal is delivered to a TNS process and appears as a trap 8.
Debugging, Trap Handling, and Signal Handling • Setting Up a Trap Handler Choose to handle traps with your own trap-handling code. The ARMTRAP procedure allows you to specify the address of your trap handler. The rest of this section describes how to write your own trap handler. • Disable all trap handlers, including Inspect and Debug. In this case, your process abnormally terminates if a trap condition occurs. The ARMTRAP procedure allows you to disable traps. Note.
Processing a Trap Debugging, Trap Handling, and Signal Handling Figure 25-1. Trap Handler Data Stack When Trap Occurs VST115.VSD Processing a Trap The code for processing a trap depends on what you want to do for a given trap condition. See Writing a Trap Handler: Examples at the end of this section for an example of how to process a trap due to arithmetic overflow.
Exiting a Trap Handler Debugging, Trap Handling, and Signal Handling Figure 25-2. Trap Handler Data Stack After Storage Allocation VST116.VSD Exiting a Trap Handler Once you have processed the trap condition, you might want to exit from your trap handler and return to your application.
Debugging, Trap Handling, and Signal Handling Disabling Trap Handling Exiting After an Arithmetic Overflow Trap Condition If the cause of the trap was an arithmetic overflow condition, then you must reset the arithmetic overflow bit in the ENV register (bit 10) before exiting the trap handler. Exiting After a System Code Trap Another case to be aware of is when exiting the trap handler after a trap condition that occurred when system code was being executed.
Debugging, Trap Handling, and Signal Handling Trap Handling on Native Systems Trap Handling on Native Systems Special restrictions apply to trap handlers that execute on native systems. You should observe the following rules: • Trap P variable The TNS trap P variable is only approximate for a process running in accelerated mode. You should not use it to inspect the code area and determine the failing instruction.
Debugging, Trap Handling, and Signal Handling Writing a Trap Handler: Examples Writing a Trap Handler: Examples The following program shows an example of a trap handler that displays the contents of the P register when an arithmetic overflow occurs. After displaying the P register, the trap handler returns to the application. If any trap condition other than arithmetic overflow occurs, then the trap handler calls the DEBUG procedure.
Debugging, Trap Handling, and Signal Handling ! Writing a Trap Handler: Examples Enter here on trap: TRAP: ! Save registers R0 through R7 and allocate local storage: CODE(PUSH %777; ADDS LOCALS); ! ! Call DEBUG if trap not an arithmetic overflow condition: IF TRAPNUM <> 2 THEN CALL DEBUG; ! ! Format and print the message on the home terminal with the P register value displayed in octal: SBUF ':=' "ARITHMETIC OVERFLOW AT %"; CALL NUMOUT(SBUF[24], PREG, 8, 6); CALL WRITE(TERM^NUM,WBUF,30); IF <> THEN
Debugging, Trap Handling, and Signal Handling ! Writing a Trap Handler: Examples Open home terminal: CALL PROCESS_GETINFO_(!process^handle!, !file^name!, !file^name^len!, !priority!, !moms^processhandle!, TERM^NAME:MAXLEN, LEN); CALL FILE_OPEN_(TERM^NAME:LEN,TERM^NUM); IF <> THEN CALL DEBUG; ! ! Set up an arithmetic overflow condition to cause the trap: J := 0; I := I/J; END; In the following example, a trap causes the current code sequence to be abandoned and an alternate code sequence executed inste
Debugging, Trap Handling, and Signal Handling Writing a Trap Handler: Examples ! Global declarations for trap handling INT saved_L, ! destination when trap occurs: L, saved_E, ! ENV (stack-marker form, with space index), saved_P, ! P, saved_S; ! S STRUCT trapframe_template(*); BEGIN ! stack frame layout of trap handler INT spaceid,trapnum,S,P,E,L,R[0:-1]; END; STRUCT .trapstate(trapframe_template); ! data from most ! recent trap INT .
Debugging, Trap Handling, and Signal Handling Handling Signals ! One or more sequences like the following ! surround code that might trap. IF TRAP_GUARD THEN ! TRAP_GUARD will later return True BEGIN ! to execute this THEN clause, if a ! trap occurs in the ELSE clause. . . ! code to handle the trap contingency . END ELSE ! TRAP_GUARD initially returns False BEGIN ! to execute this ELSE clause. . . ! code that might trap .
Debugging, Trap Handling, and Signal Handling • • About Signals Signals functions in the POSIX.1 standard. These are the signals functions provided in the Open System Services (OSS) application program interface (API). These functions are all available in C and most are available in pTAL. HP signals extensions to the POSIX.1 standard. These procedures are written especially for applications that focus on handling signals indicating conditions known as traps in TNS processes.
Comparing Traps and Signals Debugging, Trap Handling, and Signal Handling pending. The order in which pending signals are delivered to a process once they are unblocked is unspecified. Default Signal Settings in Guardian Processes When a Guardian native process is created, the process signal mask is initialized so that no signal is blocked. If a signal is delivered to the process for which the default action is to terminate the process, process termination remains the default action.
Debugging, Trap Handling, and Signal Handling When Would You Use a Signal Handler? When Would You Use a Signal Handler? If your applications have trap handlers to handle trap conditions in TNS processes, you should write signal handlers to handle the equivalent signals in native processes. Signal handlers are also helpful in applications that must run all the time with minimal, if any, operator intervention, such as a print spooler.
Standard Signals Functions Debugging, Trap Handling, and Signal Handling Table 25-3. Signals Functions That Conform to the POSIX.1 Standard (page 1 of 2) C Function pTAL Procedure Description abort() No equivalent Terminates the calling process by sending it a SIGABRT signal. alarm() No equivalent Sets or changes a timer that expires at a specified time in the future. When the timer expires, the SIGALRM signal is generated. kill() No equivalent Sends a signal to a process.
Standard Signals Functions Debugging, Trap Handling, and Signal Handling Table 25-3. Signals Functions That Conform to the POSIX.1 Standard (page 2 of 2) C Function pTAL Procedure Description siglongjmp() SIGLONGJMP_ Performs a nonlocal goto. It is often called from a signal handler to return to the main loop of a program instead of returning from the handler.
Debugging, Trap Handling, and Signal Handling Using Standard Signals Functions Using Standard Signals Functions There are many ways to use the standard signals functions in your application programs. For information about writing standard, portable signal handlers, see commercial texts on UNIX programming. The following discussion provides considerations for using some of the standard signals functions and the sequence in which you might use them.
HP Extensions Debugging, Trap Handling, and Signal Handling Considerations for Using the Jump Functions The jump functions can be valuable tools in your application program. However, if the program changes the values of variables that are local to the procedure containing the call to setjmp(), the program cannot depend upon the values of the local variables being preserved.
Debugging, Trap Handling, and Signal Handling Using HP Extensions Using HP Extensions The HP signals extensions are provided as migration and convenience tools that allow native processes to catch signals corresponding to trap conditions in TNS processes. The signals extensions provide shortcuts to the same basic functions as provided by the standard signal interfaces. If you are concerned about conforming to the POSIX.1 standard and application portability, you should use the standard signals functions.
Debugging, Trap Handling, and Signal Handling Interoperability Considerations SIGACTION_SUPPLANT_() sets the process signal mask so that all signals that can be blocked are blocked from delivery. Signals that can be deferred, which are those sent to a process by itself and those generated by timers, remain pending until the process exits the subsystem. Nondeferrable signals, which are generated by the system to indicate a run-time error in the process, are delivered to the signal handler.
Debugging, Trap Handling, and Signal Handling Examples SIGJMP_BUF_DEF( ENV ); INT TERMNUM; PROC MYHANDLER (SIGNO, SIG_INFO, SIG_CONTEXTP); INT(32) SIGNO; !signal number delivered to this handler SIGINFO_T SIG_INFO; !NULL INT .EXT SIG_CONTEXTP( UCONTEXT_T );!pointer to saved !process execution !context BEGIN STRING BUF [0:40]; STRING .
Debugging, Trap Handling, and Signal Handling Examples --SIGSETJMP_ returns 0 (zero) if called directly and --returns a nonzero value if returning from a call --to SIGLONGJMP_. IF SIGSETJMP_( ENV, 1D ) = 0D THEN BEGIN --Code that could generate a signal that is caught by --MYHANDLER. i := 3/i; ! SIGFPE generated here is caught by ! MYHANDLER END ELSE BEGIN --This is the return location for SIGLONGJMP_, which is --called from MYHANDLER after dealing with the signal.
Debugging, Trap Handling, and Signal Handling Examples the main() function, and jumps to the location of sigsetjmp() with a return value of 1. */ siglongjmp (env, 1); } main () { int i = 0; if (SIGACTION_INIT_ (myHandler)) /* install the signal */ /* handler */ ; /* Code to handle errors returned by */ /* SIGACTION_INIT_() */ /* sigsetjmp() returns 0 (zero) if called directly and returns a nonzero value if returning from a call to siglongjmp().
Debugging, Trap Handling, and Signal Handling Examples information provided in sig_contextP. 7. Unblocks all signals by clearing the process signal mask in the jump buffer using the SIGJMP_MASKSET_() function. 8. Restores the process execution context (including the process signal mask) and jumps to the location established by the call to setjmp(), using the siglongjmp() function. 9.
Debugging, Trap Handling, and Signal Handling Examples } } while ((error = HIST_GETPRIOR_ (&hws)) == HIST_OK); else printf ("HIST_INIT_ error: %d. Unable to trace\n", error); /* if error != HIST_DONE ... */ SIGJMP_MASKSET_ (jmpEnv, (sigset_t *) 0); /* unblock the signals */ /* Restore the process execution context (including the process signal mask) and jump to the location of the setjmp() call in worker().
Examples Debugging, Trap Handling, and Signal Handling result = 0; /* 0 means success */ } else { /* Error path: entered via siglongjmp() called in localHandler().
Debugging, Trap Handling, and Signal Handling Examples /* localHandler(). */ /* Code to deal with errors returned from worker(). */ } /* Back once again in the domain of globalHandler() installed by SIGACTION_INIT_(). */ /* SIGFPE generated here is caught by globalHandler() */ divider ( 5, 0 ); /* If traps are enabled, we shouldn't get here. */ printf( "main: second call to divider() failed to generate signal\n" ); } else { /* The siglongjmp() call in globalHandler() gets used here.
26 Synchronizing Processes One or more processes executing concurrently on a HP system might need to share a particular resource. Most commonly, the shared resource is an area of memory, such as an extended data segment. This sharing can result in conflicts and possible errors. Consider, for example, two processes, A and B, running concurrently in the same CPU, both of which are attempting to increment a variable in a shared memory area.
Synchronizing Processes How Binary Semaphores Work How Binary Semaphores Work Using binary semaphores, a programmer can maximize parallelism (the degree to which processes are able to execute concurrently) among concurrent processes while avoiding conflicts over shared resources. A binary semaphore consists of a global entity called a lock and an associated group of waiting processes called a wait group. Figure 26-1 illustrates the binary semaphore concept. Figure 26-1. Binary Semaphore VST130.
Synchronizing Processes How Binary Semaphores Work Table 26-1. Process Synchronization Not Executing t0 A,B,C,D t1 A Executing Noncritical Section Executing Critical Section B D C D A,B,C C,D A B A,C B t2 t3 t4 D t5 A,B,C,D Waiting At t0, the four processes have not been started.
Synchronizing Processes Summary of Guardian Binary Semaphore Procedures Summary of Guardian Binary Semaphore Procedures HP provides Guardian procedure calls to implement the binary semaphore capability. The procedures are callable from programs written in Transaction Application Language (TAL) and C/C++. The binary semaphore procedure calls are summarized in Table 26-1.
Synchronizing Processes Using the Binary Semaphore Procedure Calls Using the Binary Semaphore Procedure Calls This subsection describes the steps involved in using binary semaphores. Following is a summary of these steps: 1. First, the binary semaphore must be created (call BINSEM_CREATE_). 2. After the binary semaphore is created, all processes that will access the binary semaphore must open it (call BINSEM_OPEN_).
Synchronizing Processes Opening a Binary Semaphore In this example, a binary semaphore is created with security level 0, which permits all processes in the same CPU to access the semaphore. Opening a Binary Semaphore All processes that will access a binary semaphore must first open the binary semaphore. The BINSEM_CREATE_ call creates and opens a binary semaphore; all other processes besides the process that calls BINSEM_CREATE_ must call BINSEM_OPEN_.
Synchronizing Processes Unlocking a Binary Semaphore not wait indefinitely for a lock. If the timeout value is reached before the process is selected to receive the lock, the process is awakened and EAGAIN is returned from BINSEM_LOCK. When the process currently holding the lock unlocks the binary semaphore, the wait group is checked for waiting processes.
Synchronizing Processes Forcing a Lock on a Binary Semaphore BINSEM_ISMINE_ returns a nonzero value if the SEMID is valid and the calling process currently has that binary semaphore locked. Forcing a Lock on a Binary Semaphore Under certain conditions you may want to force a lock on a binary semaphore that is currently locked by another process. For example, a process holding a lock may have entered an infinite loop or some other unresponsive state.
Synchronizing Processes Binary Semaphore Interface Declarations BINSEM_OPEN_. These handles can be represented either as a structure or as an array[10] of short int. Guardian procedures declared in CEXTDECS use the array form. Some interfaces use a NSK_PHandle structure type, defined in the kphandl.h header file. The kbinsem.h file can work either way. By default, kbinsem.h uses the structure. Thus a program could contain: #include NSK_PHandle myPhandle; ...
Synchronizing Processes Binary Semaphore Example Binary Semaphore Example Following is a simple TAL example illustrating the use of the binary semaphore procedure calls. This example can be used as a template for creating other programs that use binary semaphores. The example assumes that two processes share a segment containing a shared structure. One process executes the PRIMARY procedure and the other process executes the SECONDARY procedure.
Synchronizing Processes Procedure USERESOURCE Procedure USERESOURCE Procedure USERESOURCE locks the binary semaphore, uses the shared resource, and unlocks the binary semaphore. The procedure is called by the primary process and the secondary process. Procedure USERESOURCE is as follows: PROC USERESOURCE (SEMID); INT(32) SEMID; BEGIN INT ERROR; ERROR := BINSEM_LOCK_ (SEMID, -1D); IF (ERROR <> 0) THEN CALL DEBUG; ... Use the shared resource...
Synchronizing Processes Procedure SECONDARY Procedure SECONDARY Procedure SECONDARY is executed by the secondary process. That process shares the segment created by the primary process, and passes to SECONDARY a pointer to the shared structure. It opens the binary semaphore, locks the binary semaphore, uses the shared resource, unlocks the binary semaphore, and closes the binary semaphore. The main process handle and the semaphore ID are picked up from the shared structure.
Synchronizing Processes BINSEM_GETSTATS_ and BINSEM_STAT_VERSION_ Example { /* Create a BinSem */ err = BINSEM_CREATE_(&semID, 2); if(err !=BINSEM_RET_OK) { printf("BINSEM_CREATE_ failed with status %d\n", err); return(-1); } /* Unlock BinSem Created */ err = BINSEM_UNLOCK_(semID); if (err) printf("BINSEM_UNLOCK_ failed with %d\n", err); /* A real program would have application logic, including locking and unlocking one or more binary semaphores. It might also create or open additional semaphores. ...
Synchronizing Processes BINSEM_GETSTATS_ and BINSEM_STAT_VERSION_ Example return 0; } The binsemc example program can be compiled for various data models with the following commands, and then executed: • • • ccomp /in binsemc/ obinsem; symbols, nolist, runnable,& extensions ccomp /in binsemc/ obinsem; symbols, nolist, runnable,& extensions, systype oss, ILP32 ccomp /in binsemc/ obinsem; symbols, nolist, runnable,& extensions, systype oss, LP64 Guardian Programmer’s Guide — 421922-014 26 - 14
27 Fault-Tolerant Programming in C The term “fault-tolerant” means that a single failure does not cause processing to stop. At the hardware level, redundant hardware and duplication of paths allow systems to tolerate a single-component failure. In many cases, multiple-component failures can also be tolerated as long as they do not share common paths.
Summary of Active Backup Processing Fault-Tolerant Programming in C An active backup program executes as a primary and backup process pair running the same program file. The primary and backup processes communicate through interprocess communication. The primary process sends critical data to the backup process.
What the Programmer Must Do Fault-Tolerant Programming in C If the primary process or CPU fails, the backup process takes over execution from the failed primary process and becomes the new primary process. First, it creates a new backup process. (If the failure is caused by CPU failure, the new backup is created either immediately in another CPU or when the failing CPU is brought back online.
Programming Tasks Fault-Tolerant Programming in C Programming Tasks Once you have developed a strategy for updating state information and defined a protocol for interprocess communication, you can begin coding your active backup program. Compaq provides extensions to the C language that support active backup programming. These language extensions are summarized later in this section under C Extensions That Support Active Backup Programming.
C Extensions That Support Active Backup Programming Fault-Tolerant Programming in C C Extensions That Support Active Backup Programming C provides several functions that you can use to perform various tasks required for active backup programming. This subsection provides a brief overview of these functions. These functions are available for TNS and accelerated programs as well as for TNS/R native programs.
Opening a File With a Specified Sync Depth Fault-Tolerant Programming in C After receiving messages sent by __ns_start_backup, the backup process automatically does the following: • • Processes the startup, assign, and param messages in the same manner as the primary process. For any of the standard files stdin, stdout, and stderr the primary opened during its initialization, performs backup opens for the same files.
Retrieving File State Information in the Primary Process Fault-Tolerant Programming in C be open concurrently by both the primary and backup processes. The backup process performs a backup open of a file by calling the __ns_backup_fopen function. Before doing a backup open of a file, the backup process must obtain certain file open state information from the primary process.
Organizing an Active Backup Program Fault-Tolerant Programming in C Organizing an Active Backup Program This subsection expands on the overview presented at the beginning of this section to explain how to put together an active backup program. Figure 27-1 shows a general structure for an active backup program. Figure 27-1.
Primary Process Organization Fault-Tolerant Programming in C application processing phases are essentially the same as for the primary process; that is, the backup process (once it becomes the new primary process) must also create a backup process and execute the application. The message-processing loop monitors and processes messages received from the primary process and CPU.
Backup Process Organization Fault-Tolerant Programming in C Backup Process Organization A process executing as the backup process proceeds as follows: Message-Processing Loop At the beginning of its execution, the backup process does the following: 1. Opens $RECEIVE so that it can receive messages from: • • The operating system, indicating that the primary process or primary CPU has failed. The primary process, containing current state information. 2.
Updating State Information Fault-Tolerant Programming in C 3. A new backup process is created within the same CPU and with the same PIN as the old primary process. 4. Before being notified of the change, a client process sends a message to what it thinks is the primary server process, but what is actually the new backup process. The backup process checks the source of incoming messages so that it can send an appropriate reply in such cases. The backup then continues executing the message-processing loop.
Updating State Information Fault-Tolerant Programming in C Figure 27-2. Updating State Information VST133.VSD The primary process sends state information to the backup process at various points during execution. Meanwhile, the backup process enters a message-processing loop in which it receives state information and failure messages. If no failure occurs, the backup updates its memory with the state information and continues in the loop.
Types of State Information Fault-Tolerant Programming in C actual techniques and procedures you use to format, send, and receive the messages containing the state information are described earlier in this section under Organizing an Active Backup Program. As a programmer, you must determine where to do the updates within your program and what information you want to include in each update.
Updating Control State Information Fault-Tolerant Programming in C Updating Control State Information Control state information is used by the backup process to determine where to take over execution from the primary process. In many cases, a single scalar value is sufficient. For example, in Figure 27-3, a simple loop reads data from a terminal and processes it. (Note that Figure 27-3 is conceptual and does not use actual C language statements.) Figure 27-3. Control State Example VST134.
Updating File State Information Fault-Tolerant Programming in C Figure 27-4. Using Switch Statement to Determine the Continuation Point VST135.VSD Updating File State Information During application processing, I/O operations might be performed. At certain points in the program, the primary process must obtain file state information and send it to the backup. File state information includes file pointers and file synchronization information.
Updating File State Information Fault-Tolerant Programming in C points. Basic file sync data can be obtained using the _ns_fget_file_state function. For key-sequenced files FILE_GETSYNCINFO_ is required. The need to prevent duplicate file operations is illustrated in the following example. A primary process completes the following write operation successfully but fails before updating state information for the backup process. Execution -> ...Update state information...
Updating File State Information Fault-Tolerant Programming in C The preceding example is changed to reflect the use of synchronization information: Execution ----->...Update control and application state resumes here ...Update sync block... err = POSITION (F1, -1D); /*position to eof*/ err = WRITE (F1, buf); ***Primary fails here*** Restart point ...Backup takes over... ...
Updating Application State Information Fault-Tolerant Programming in C For an output stream, a call to a write function may leave a partially filled, unflushed buffer. The __ns_fget_file_state function does not cause a flush; it is your responsibility to ensure that unflushed buffers are handled appropriately. Three approaches are: • • • Perform a flush operation (for example, by a call to the fflush function) before getting file state information.
Guidelines for Updating State Information Fault-Tolerant Programming in C successfully. But certain I/O operations cannot be repeated without changing the results of the program. • The tradeoff between recoverability and performance. The more update points a program has, the greater the degree of recoverability, but the lower the performance of the program.
Example of Updating State Information Fault-Tolerant Programming in C Performance Versus Recoverability In placing update points, you need to consider the tradeoff between performance and the degree of recoverability desired. For example, an application that reads and produces a summary of a file that contains hundreds of thousands of records may not require a continuation point during the read stage, because all the reads are retryable.
Example of Updating State Information Fault-Tolerant Programming in C ...Update account balance } An insufficient number of update points is added to the transaction: /*First update point*/ cnt = 1; ...Update cnt (idle state)... err = WRITEREAD (terminal,buf1,...); /*returns account_no and amount*/ /*Second update point. data*/ cnt = 2; ...Update cnt, buf1... Include control state and terminal err = POSITION (account_file, buf1.acct_no); err = READUPDATE (account_file, buf2,...); x = buf2.
Example of Updating State Information Fault-Tolerant Programming in C err = POSITION (account_file, 12345D); err = READUPDATE (account_file, buf2,...); account_no 12345 current_balance $485 returns the following: credit_limit $500 x = $485 + $10; if (x > $500) ... current_balance = x; err = WRITEUPDATE (account_file, buf2, ...
Saving State Information for Multiple Disk Updates Fault-Tolerant Programming in C /*Third update point. Include control state (cnt), data state (buf2), and file state (account_file)*/ cnt = 3; ...Update cnt, buf2, account_file... err = WRITEUPDATE (account_file, buf2, ...); err = WRITE (terminal, buf1,...); The third update point identifies the program state as “preparing to write an updated record to disk.
Sending Messages From the Primary to the Backup Fault-Tolerant Programming in C • Receiving system status messages in the backup process. The system messages tell the backup process when the primary process or CPU has failed. This subsection gives a brief overview of communication between the primary and backup processes. The procedures of the Guardian interprocess communication facility are described in detail in the Guardian Procedure Calls Reference Manual.
Fault-Tolerant Programming in C Receiving Messages in the Backup Process Once the backup process is open, the primary process can communicate with the backup process by writing messages to the file number returned by the FILE_OPEN_ call. To send a message, the primary process can use either WRITE[X] (for one-way communication only) or WRITEREAD[X] (for one-way or two-way communication). The following example sends a state message to the backup process without expecting a reply: { ...
Monitoring the Backup Process Fault-Tolerant Programming in C switch (msg_buffer.number) { case ... /*Processor failure*/ case ... /*Process deletion*/ ...Backup takes over for primary... case ... /*State information*/ ...Update control state ... ... Update application state ... ... Update file state ...
Programming Considerations Fault-Tolerant Programming in C Reading $RECEIVE You can monitor the backup process by reading the $RECEIVE file in the same manner as the backup process. The operating system sends a message to $RECEIVE if the backup process fails. Programming Considerations Following are some general considerations for writing active backup programs in C.
Run-Time Considerations Fault-Tolerant Programming in C • • • • • • The crtlnsh header file contains the declarations that support active backup programming. (The functions that support fault-tolerant programming are implemented in pTAL. By using the crtlnsh header, the appropriate TAL interface code is generated.) Functions declared in the crtlnsh header are defined in the crtlns library file. HP provides two different file-reference models: the ANSI model and the alternate model.
Fault-Tolerant Programming in C Comparison of Active Backup and Passive Backup 27 Fault-Tolerant Programming in C Comparison of Active Backup and Passive Backup For the benefit of programmers familiar with the passive backup programming techniques supported by TAL, this subsection provides an overview of the differences between active backup programming and passive backup programming.
Comparison of Active Backup and Passive Backup Fault-Tolerant Programming in C Table 27-1. Differences Between C Active Backup and TAL Passive Backup (page 1 of 2) To Perform This Task An Active Backup Program Does This A Passive Backup Program Does This Start backup process Primary calls __ns_start_backup. Primary calls PROCESS_CREATE_. Establish communication between primary and backup processes Primary opens backup, and backup opens $RECEIVE.
Comparison of Active Backup and Passive Backup Fault-Tolerant Programming in C Table 27-1. Differences Between C Active Backup and TAL Passive Backup (page 2 of 2) An Active Backup Program Does This A Passive Backup Program Does This Send data state information to backup process Primary sends data state to backup through interprocess communication. Backup must update its own memory. Primary calls CHECKPOINT[MANY][X]. Define content of data state information Application-dependent. All of memory.
Active Backup Example 1 Fault-Tolerant Programming in C Active Backup Example 1 A simple example is now used to illustrate how to structure an active backup program. The following application consists of a loop that increments a counter. /*This function does the work of the application, which is to increment a counter.
Active Backup Example 1 Fault-Tolerant Programming in C Figure 27-5.
Active Backup Example 1 Fault-Tolerant Programming in C Figure 27-6. Backup Process Functional Flow main Begin Processing is_backup Backup Primary or Backup? backup_processing Perform backup activities Primary See Figure 27-5 Read $RECEIVE Message Processing Loop Primary failure? No Update memory with current state information Yes initialize_backup Initialization Phase Create new backup primary_processing Application Processing Phase Take over primary processing See Figure 27-5 VST137.
Program Declarations Fault-Tolerant Programming in C Program Declarations The particular declarations required by an active backup program depend on the application. The following are used in this example. #include and #pragma Lines The following #include and #pragma lines provide the declarations required by the program.
Program Declarations Fault-Tolerant Programming in C #include "$system.zspidef.zspic (zspi_ddl_char8,zspi_ddl_unit)" nolist #pragma search "$system.system.cnonstop" for TNS and accelerated programs, or search "$system.system.crtlns" in the compiler command line for TNS/R native programs, causes the library containing the C functions for faulttolerant support to be bound into the program. #pragma wide specifies the wide-data model (TNS and accelerated programs only).
Program Declarations Fault-Tolerant Programming in C • • UPDATE_MESSAGE is an application-defined message that contains the current state information. In this example, the value of the counter serves as both application state and control state information: it is the data needed by the backup process to continue processing (application state), and it defines the point at which processing is to resume (control state).
Program Declarations Fault-Tolerant Programming in C #define PHANDLESIZE 10 #define NULLPHANDLE {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1} /* Primary process attribute code for PROCESS_SETINFO_ */ # define PRIMARY_ATTRIBUTE 47 /* File number of the backup process.
Creating and Starting the Backup Process Fault-Tolerant Programming in C Miscellaneous Declarations Finally, the program contains miscellaneous declarations to define the following: • • • • • • A value to be returned by the PROCESS_GETPAIRINFO_ procedure, used to determine whether the process is running as the primary or backup process. The $RECEIVE file name. The maximum file-name length. The size of the process handle, and a null process handle.
Creating and Starting the Backup Process Fault-Tolerant Programming in C communication so that the primary process can send messages to the backup process. The operating system is allowed to select the CPU in which the backup process will run. It is convenient to code these activities as separate functions, as follows: • • Function initialize_backup, which opens and starts the backup process; called by function primary_processing.
Updating State Information Fault-Tolerant Programming in C Function primary_cpu This function returns the CPU number of the primary process CPU.
Updating State Information Fault-Tolerant Programming in C error = PROCESS_GETPAIRINFO_ (/*current process*/, /*pair:maxlen*/,, /*pairlen*/, /*primary_phandle*/, backup_phandle); return (short) memcmp (backup_phandle, null_phandle, PHANDLESIZE * 2); } Function update_backup Function update_backup is called by the primary process to update state information in the backup process.
Primary and Backup Processing Fault-Tolerant Programming in C Primary and Backup Processing The original application program can now be restructured into the following three functions: • • • Function primary_processing, which performs the primary process activities: it does the work of the application and updates state information in the backup process.
Primary and Backup Processing Fault-Tolerant Programming in C Function backup_processing Function backup_processing handles the backup processing tasks, which include: • • • Open $RECEIVE so that the backup process can receive messages from the primary process and the operating system. Call MONITORCPUS to indicate to the operating system that the backup process is to be notified if the primary process CPU fails. Enter an infinite loop that reads and processes messages from $RECEIVE.
Primary and Backup Processing Fault-Tolerant Programming in C /* open $RECEIVE */ error = FILE_OPEN_ (RECEIVE_FILENAME, (short) strlen (RECEIVE_FILENAME), &receive_filenum, /*access*/, /*exclusion*/, /*nowait*/, 1 /*receive depth*/); /*Monitor the primary process processor.
Primary and Backup Processing Fault-Tolerant Programming in C /*Start a replacement backup process*/ initialize_backup (); /*Update counter with last value*/ counter = counter_state; /*Continue the work of the application*/ primary_processing (); break; case UPDATE_MESSAGE: /*Update the counter state*/ counter_state = message.msg_variants.
Primary and Backup Processing Fault-Tolerant Programming in C Function is_backup_process Function is_backup_process is called by the main program to determine whether the current process is the primary process or the backup process.
Compiling and Running the Example Fault-Tolerant Programming in C Compiling and Running the Example If creating a TNS or accelerated program, note the following before compiling, binding, and running this example: • • • The nonstoph header is included. This header contains the declarations for the C functions that support fault-tolerant programming. (These functions are implemented in TAL. By using the nonstoph header, the appropriate TAL interface code is generated.
Example With Debugging Options Fault-Tolerant Programming in C When program execution is initiated by the appropriate RUND command, the Inspect tool immediately gains control of the primary process and sends a prompt to the primary process's home terminal. At this time, you can enter Inspect commands.
Example With Debugging Options Fault-Tolerant Programming in C initialize_backup (); primary_processing (); } } Debugging Modifications to Function initialize_backup The name of the terminal for the backup process must be communicated to the backup process through the C function __ns_start_backup. Code is added to the initialize_backup function to test the number of parameters, get the terminal name, and pass it to __ns_start_backup.
Example With Debugging Options Fault-Tolerant Programming in C backup_phandle); /*Get the process name of the backup process*/ error = PROCESSHANDLE_DECOMPOSE_ (backup_phandle, /*cpu*/, /*pin*/, /*nodenumber*/, /*nodename:nmax */,, /*nlen*/, process_name, MAXNAMELEN, &process_name_len); /*Open backup process for interprocess communication*/ error = FILE_OPEN_ (process_name, process_name_len, &backup_filenum); } Function get_terminal_name Function get_terminal_name returns the home terminal name for the c
Active Backup Example 2 Fault-Tolerant Programming in C *name = terminal_p; } Compiling and Running the Debugging Example To compile the program for use with the Inspect tool, specify the symbols and inspect pragmas on the compiler command line. To run the program with the Inspect tool, first locate two terminals. Then do the following: 1. On the terminal to be used for the backup process, enter PAUSE. This allows the backup process to gain control of the terminal when it begins executing. 2.
Program Declarations Fault-Tolerant Programming in C { (void) printf ("Error: Must provide input and output " "file names.
Program Declarations Fault-Tolerant Programming in C #include nolist /* */ /* */ /* */ Note: Include the pragmas "ansistreams," "runnamed," and 'search "$system.system.crtlns"' in the compiler command line for native programs. #else /* for TNS and accelerated programs only /* #pragma ansistreams #pragma wide /* Use wide-data (32-bit integer) model */ #pragma runnamed /* A process pair should be named. */ #pragma search "$system.system.
Program Declarations Fault-Tolerant Programming in C • • • • • • • MONITORCPUS, which is called by the backup process to monitor the primary process CPU. The operating system sends the backup process a “processor down” message if the monitored CPU fails. PROCESS_GETINFO_, PROCESS_GETPAIRINFO_, and PROCESSHANDLE_DECOMPOSE_, which are used to obtain information about the primary and backup processes.
Program Declarations Fault-Tolerant Programming in C Two other elements are defined that are used by the backup process if it receives a message it does not expect: IGNORE_MESSAGE is used as a message number and is placed in the message number portion of the message structure, and OWNERSHIP_ERROR is an error value that is returned to the sender.
Program Declarations Fault-Tolerant Programming in C /*CPU_DOWN message*/ short cpu_down_info; /*PROCESS_DELETION message, variable-length fields are assumed to have zero length*/ short process_death_notification_info [40]; } msg_variants; } message_format; Miscellaneous Declarations Finally, the program contains miscellaneous declarations to define the following: • • • • • The $RECEIVE file name. The maximum file-name length. The size of the process handle, and a null process handle.
Creating and Starting the Backup Process Fault-Tolerant Programming in C Creating and Starting the Backup Process The primary process performs several activities related to initializing the backup process. Specifically, it starts the backup process and opens it for interprocess communication so that the primary process can send messages to the backup process. The operating system is allowed to select the CPU in which the backup process will run.
Primary and Backup Processing Fault-Tolerant Programming in C /*This function is called by the primary process to update the state of the backup process. It creates and sends a message to the backup process.*/ void update_backup (void) { short cond_code; short error; message_format message; /*Create update message*/ message.msgnumber = UPDATE_MESSAGE; error = __ns_fget_file_state (infile, &message.msg_variants.update_info.infile_state); error = __ns_fget_file_state (outfile, &message.msg_variants.
Primary and Backup Processing Fault-Tolerant Programming in C process, then calls primary_processing. If it is the backup process, it calls backup_processing. Function primary_processing Function primary_processing does the work of the application (which, in this case, is to execute a loop that reads numbers from an input file and writes the numbers to an output file and to the standard output). Within the loop, function update_backup is called to send file state information to the backup process.
Primary and Backup Processing Fault-Tolerant Programming in C The four message types received by the backup process are processed as follows: • • • PROCESSOR DOWN and PROCESS DELETION indicate that the primary process has failed. The backup process takes over execution as described earlier. UPDATE_OPEN contains the file open state information for the input and output files. This information is used by __ns_backup_fopen to backup open the files.
Primary and Backup Processing Fault-Tolerant Programming in C /*Monitor the primary process processor.
Primary and Backup Processing Fault-Tolerant Programming in C /*Backup open the input and output files*/ case UPDATE_OPEN: infile =__ns_backup_fopen (&message.msg_variants.open_info.input_open_info); if (infile == NULL) { (void) printf ("Error in backup_processing, " "update input open.\n"); PROCESS_STOP_(,1); /*Stop primary and backup*/ } outfile = __ns_backup_fopen (&message.msg_variants.open_info.output_open_info); if (outfile == NULL) { (void) printf("Error in backup_processing, " update output open.
Primary and Backup Processing Fault-Tolerant Programming in C Function main Execution begins with the main function. Function main does the following: • • • Calls is_backup_process to determine whether it is running as the primary process or the backup process. Function is_backup_process is described under Active Backup Example 1. If it is the backup process, calls backup_processing to perform the backup activities.
Compiling and Running the Example Fault-Tolerant Programming in C Compiling and Running the Example If creating a TNS or accelerated program, note the following before compiling, binding, and running this example: • • • • The nonstoph header is included. This file contains the C library declarations for active backup programming. (This library is implemented in TAL. By using the crtlnsh header file, the appropriate TAL interface code is generated.) #pragma search "$system.system.cnonstop" is specified.
Fault-Tolerant Programming in C Compiling and Running the Example where cfile is the C input file and output-file is the output file written by the program.
28 Using Floating-Point Formats In the G07 and later versions of the NonStop operating system, users have the option of choosing between using HP floating-point format and IEEE floating-point format in their native C and C++ programs for performing floating-point arithmetic. Choosing HP floating-point format provides compatibility with pre-G07 C and C++ applications.
Using Floating-Point Formats • • • • Building and Running IEEE Floating-Point Programs IEEE floating-point directed roundings and “sticky” flags are useful debugging tools for investigating calculations that might go wrong because of rounding problems, division by zero, or other problems. Unlike conditional flags, once sticky flags are set, they stay set until explicitly reset by the user. This capability allows the user to check a chain of computations.
Link-Time Consistency Checking Using Floating-Point Formats > NMC/IN SOURCEA, OUT $.#LIST/OBJECTA; IEEE_FLOAT > NMCPLUS /IN SOURCEB, OUT $S.#LIST/OBJECTB; VERSION2, & IEEE_FLOAT In the above example, the native C compiler processes the file SOURCEA, and the native C++ compiler processes the file SOURCEB. Note that the C++ compiler has VERSION2 specified, because IEEE floating-point format is supported only under VERSION2 C++ features.
Run-Time Consistency Checking Using Floating-Point Formats Execute the noft utility listattribute command to display the floating-point state. Use the header, all, listattribute or filehdr commands for displaying the float_lib_overrule bit. Run-Time Consistency Checking If you attempt to use a CPU that does not support IEEE floating-point format, process create error code 64, “IEEE floating-point support not available on this processor,” occurs.
Conversion Routines Using Floating-Point Formats Conversion Routines IEEE floating-point data format is incompatible with Tandem floating-point format. Conversion between Tandem and IEEE floating-point data formats requires the use of the following routines. See the Guardian Procedure Calls Reference Manual for more information about these routines.
Floating-Point Operating Mode Routines Using Floating-Point Formats • • Controlling trap handling Controlling and accessing exception flags Routine Description FP_IEEE_ROUND_GET_ Returns the IEEE floating-point rounding mode. FP_IEEE_ROUND_NEAREST is the default mode. FP_IEEE_ROUND_SET_ Sets the IEEE floating-point rounding mode. FP_IEEE_DENORM_GET_ Returns the handling of denormalized IEEE floating-point numbers. FP_IEEE_DENORMALIZATION_ ENABLE is the default mode.
Floating-Point Operating Mode Routines Using Floating-Point Formats rounding mode settings within a procedure might be affected by instruction reordering within a procedure. When evaluating IEEE floating-point expressions, the compiler uses the default floating-point modes identified above. The expressions evaluated at compile time do not affect the exception flags. See the Guardian Procedure Calls Reference Manual for details about these operating mode routines.
Floating-Point Operating Mode Routines Using Floating-Point Formats Considerations • • • • • Use 64-bit floating-point format instead of 32-bit floating-point format for greater precision, especially when doing intermediate calculations. Use the default operating modes for rounding and trap handling. IEEE floating-point values cannot be stored in SQL/MP databases. You cannot compile a module that uses both IEEE floating-point and embedded SQL.
A Mixed Data Model Programming The Guardian personality supports only 32-bit processes. Mixed Data Model programming is a technique through which we can create both 32-bit and 64-bit pointers for 32-bit processes. Mixed mode programming is the model in which 32-bit programs can allocate 64-bit segments using the SEGMENT_ALLOCATE64_ procedure and access these segments using 64-bit pointers. Note. Mixed mode programming uses 64-bit pointers to access large segments created by SEGMENT_ALLOCATE64_.
Mixed Data Model Programming • Allocating a 64-bit Segment PROC64ADDR - The 64-bit address of a procedure Additionally, new built-ins are enabled by __EXT64: • • • • • • • • $EXT64ADDR_TO_EXTADDR - converts 64-bit address values to 32-bit extaddr address values; no checking is performed to see if the 32-bit address value is valid. $EXT64ADDR_TO_EXTADDR_OV - converts 64-bit address values to 32-bit EXTADDR address values. If the address cannot be represented in 32-bits, an overflow trap occurs.
Mixed Data Model Programming Dynamic Memory Allocation in 64-bit Segments notation it is bit <3>. SEGMENT_GETINFO_ assigns -1 to base-address for a 64-bit segment; it assigns -1 to segment-size if the size exceeds 31 bits. • SEGMENT_RESIZE64_ A 64-bit version of the existing RESIZESEGMENT procedure. Additional procedures recognizing 64-bit segments are: • ADDRESS_DELIMIT64_ A 64-bit version of the existing ADDRESS_DELIMIT_ procedure.
Mixed Data Model Programming Data Scanning and Movement within 64-bit Segments For more information on pool routines, see Guardian Procedure Calls Reference Manual. The POOL64_* routines are available in H06.20, J06.09 and subsequent RVUs. All TNS/E C/C++ compilers support the necessary 64-bit addressing constructs. The epTAL compiler supporting 64-bit addressing is available as of H06.23 and J06.12; the constructs must be enabled using the __EXT64 directive.
Mixed Data Model Programming • • • • Socket I/O to/from 64-bit Segments FILE_WRITEREAD64_ FILE_WRITEUPDATE64_ FILE_WRITEUPDATEUNLOCK64_ FILENAME_FINDNEXT64_ Socket I/O to/from 64-bit Segments Socket I/O can also be performed directly to and from 64-bit segments using the following Guardian Socket calls: • • • • • • • • • • • send64_() sendto64_() recv64_() recvfrom64_() send_nw64_() send_nw2_64_() sendto_nw64_() t_sendto_nw64_() recv_nw64_() recvfrom_nw64_() t_recvfrom_nw64_() OSS I/O to/from 64-bit
Mixed Data Model Programming Debugging Programs with 64-bit Segments Debugging Programs with 64-bit Segments Both Visual Inspect and eInspect may be used to debug a running program that has 64-bit segments. Snapshot files from programs that have 64-bit segments have a format that differs from those of programs without such segments. Snapshots of programs with 64-bit segments may not be examined using Visual Inspect but must rather be analyzed using eInspect. In RVUs prior to H06.24 and J06.
Mixed Data Model Programming Examples C Example: #include #include #include #include #include #include nolist nolist nolist nolist nolist
Mixed Data Model Programming Examples The following is a similar program written in epTAL: ?__EXT64 ?SETTOG _64BIT_CALLS ?COLUMNS 79 !Global variables: STRUCT CI_STARTUP; !Startup message BEGIN INT MSGCODE; STRUCT DEFAULT; BEGIN INT VOLUME[0:3]; INT SUBVOLUME[0:3]; END; STRUCT INFILE; BEGIN INT VOLUME[0:3]; INT SUBVOL[0:3]; INT FNAME[0:3]; END; STRUCT OUTFILE; BEGIN INT VOLUME[0:3]; INT SUBVOL[0:3]; INT FNAME[0:3]; END; STRING PARAM[0:529]; END; INT FNO; !OUT FILE NUMBER ?NOLIST ?SOURCE KMEM( SEGMENT_PROC
Mixed Data Model Programming Examples LITERAL POOLSIZE = 1024F * 1024F * 1024F; CALL INITIALIZER( !rucb!, !passthru!, START_IT ); OPEN( CI_STARTUP.OUTFILE.
Glossary absolute pathname. An Open System Services (OSS) pathname that begins with a slash (/) character and is resolved beginning with the root directory. Contrast with relative pathname. accelerate. To speed up emulated execution of a TNS object file by applying the Accelerator for TNS/R system execution or the Object Code Accelerator (OCA) for TNS/E system execution before running the object file. accelerated mode. See TNS accelerated mode. accelerated object code.
alternate key Glossary alternate key. A sequence of characters other than the primary key used as an alternate index to records in a key-sequenced file. alternate-key file. A key-sequenced file that provides the relationship between alternate keys and primary keys. ancestor. The process that is notified when a named process or process pair is deleted. The ancestor is usually the process that created the named process or process pair. ANSI. The American National Standards Institute. APE.
BREAK mode Glossary BREAK mode. A mode of process execution where a process gains exclusive access to a terminal when the BREAK key is pressed. BREAK mode is established using SETPARAM function 3 or SETMODE function 11. BREAK owner. The process that receives the Break-on-device message when the BREAK key is pressed. The establishment of BREAK ownership is achieved using SETPARAM function 3 or SETMODE function 11. breakpoint.
client application Glossary client application. An application that requests a service from a shared memory. Execution of remote procedure calls is an example of a client application. clock averaging. The technique used to keep CPU clocks synchronized. code file. See object code file. code segment. A segment that contains executable instructions of a program or library to be executed plus related information.
completion code Glossary completion code. A value used to return information about a process to its ancestor process when the process is deleted. This value is returned in the Process deletion message, system message -101. complex instruction-set computing (CISC). A CPU architecture based on a large instruction set, characterized by numerous addressing modes, multicycle machine instructions, and many special-purpose instructions. Contrast with reduced instructionset computing (RISC). concurrency control.
data space Glossary data space. The area of virtual memory reserved for user data and system data. DCT. See destination control table (DCT). deadlock. A situation in which two processes or two transactions cannot continue because they are each waiting for the other to release a lock. DEFINE. An HP Tandem Advanced Command Language (TACL) command you can use to specify a named set of attributes and values to pass to a process. DEFINE name.
elapsed time Glossary typically contains source program or script code, documentation, or program output. Open System Services (OSS) functions can open an EDIT file only for reading. elapsed time. Time as measured by the CPU clock, independent of the state of any process. eld utility. A utility that collects, links, and modifies code and data blocks from one or more position-independent code (PIC) object files to produce a target TNS/E native object file. See also ld utility and nld utility. ELF.
exclusion mode Glossary mode. A TNS/E system also has three execution modes: TNS/E native mode using TNS/E native compilers and Intel® Itanium® instructions, emulated TNS execution in TNS interpreted mode, and emulated TNS execution in TNS accelerated mode. exclusion mode. The attribute of a lock that determines whether any process except the lock holder can access the locked data. executable object file. See program file. explicit DLL. See explicit dynamic-link library (explicit DLL).
file code Glossary file code. An integer value assigned to a file for application-dependent purposes, typically identifying the kind of information the file contains. file control block (FCB). (1) A data structure automatically created and managed by the file system that contains a collection of information about a given file. (2) A data structure on the user’s data stack used by SIO to access SIO files.
file number Glossary file number. An integer that represents a particular instance of an open file. A file number is returned by the FILE_OPEN_ or OPEN procedure and is used in all subsequent input/output procedures to reference the file. Internally, the file number is an index into the file table. file serial number. A number that uniquely identifies a file within its file system. file system. In the Open System Services (OSS) environment, a collection of files and file attributes.
godmother Glossary godmother. Seejob ancestor. GPLDEFS file. A file containing TAL DEFINEs used to allocate space for SIO data structures. Greenwich mean time (GMT). The mean solar time for the meridian at Greenwich, England. Gregorian date. A date specified according to the common calendar using the month of the year (January through December), the day of the month, and the year A.D. graphical user interface (GUI). A user interface that offers point-and-click access to program functions. group list.
HP NonStop™ operating system Glossary group-name.user-name pair of values. For example, the structured view of the super ID is (255, 255). The Open System Services (OSS) environment normally uses the scalar view of this user ID, also known as the UID, which is the value (group-number * 256) + user-number. For example, the scalar view of the super ID is (255 * 256) + 255 = 65535. HP NonStop™ operating system. The operating system for HP NonStop systems.
import control Glossary import control. The characteristic of a loadfile that determines from which other loadfiles it can import symbol definitions. The programmer sets a loadfile’s import control at link time. That import control can be localized, globalized, or semiglobalized. A loadfile’s import control governs the way the linker and loader construct that loadfile’s searchList and affects the search only for symbols required by that loadfile. import library.
Intel® Itanium® instruction region loading Glossary it into the TNS object file. A TNS object file that contains an Itanium instruction region can execute in accelerated mode on TNS/E systems. Contrast with native. Intel® Itanium® instruction region loading. Mapping the Itanium instructions and any tables necessary at runtime into memory from the Itanium instruction region of a TNS object file, performed when deemed necessary by the TNS emulation software on a TNS/E system. Intel® Itanium® word.
Kernel-Managed Swap Facility (KMSF) Glossary fabrics in G-series release version updates (RVUs). The $ZZKRN Kernel subsystem manager process is started and managed by the $ZPM persistence manager process. Kernel-Managed Swap Facility (KMSF). A facility by which the operating system manages virtual memory using swap files that it owns. Each CPU has at least one kernel-managed swap file that provides the swap space needs of all of its processes. key-sequenced file.
local civil time (LCT) Glossary TNS Emulation Library. The program’s calls to its local copy of these routines are faster and more compact than DLL calls to the external library. local civil time (LCT). Wall clock time in the current time zone, including any compensation for daylight saving time. local standard time (LST). The time of day in the local time zone excluding any compensation made for daylight saving time. logical device. (1) An addressable device, independent of its physical environment.
memory manager Glossary as they would be if the object file were running in TNS interpreted mode or on a TNS system. Most source statement boundaries are memory-exact points. Complex statements might contain several such points: at each function call, privileged instruction, and embedded assignment. Contrast with register-exact point and nonexact point. memory manager. An HP NonStop™ operating system process that implements the paging scheme for virtual memory.
mirrored disk Glossary mirrored disk. A pair of identical disk drives that are used together as a single volume. One drive is considered the primary and the other is called the backup or the mirror. Each byte of data written to the primary drive is also written to the backup drive; if the primary drive fails, the backup can continue operations. mom. A process that is notified when certain other processes are deleted.
native signal Glossary native signal. SeeTNS/R native signal and TNS/E native signal. nld utility. A utility that collects, links, and modifies code and data blocks from one or more object files to produce a target TNS/R native object file. See also eld utility and ld utility. node. A system of one or more CPU modules. Typically, a node is linked with other nodes to form a network. node name. The portion of a file name that identifies the system through which the file can be accessed. noft utility.
noninteractive mode Glossary noninteractive mode. A mode of operation that usually involves a command file (an EDIT file that contains a series of commands). Contrast with interactive mode. nowait I/O. An operation with an I/O device or process where the issuing process does not wait for the I/O operation to finish. Contrast with waited I/O. object code accelerator (OCA). See TNS Object Code Accelerator (OCA). object code file. A file containing compiled machine instructions for one or more routines.
one-way communication Glossary it may be incomplete and require linking with other object files before execution. See also TNS object file, TNS/R native object file, and TNS/E native object file. one-way communication. A form of interprocess communication where the sender of a message (the requester) does not expect any data in the reply from the receiver of the message (the server). Contrast with two-way communication. open system.
page Glossary page. A unit of virtual storage for disks and CPU memory. The size of a disk page is 2048 bytes. The size of a memory page varies depending on the CPU type and the software release. page mode. A mode of communication between a terminal and its I/O process in which the terminal stores up to a full page of data (1920 bytes) in its own memory before sending the page to the I/O process. Contrast with conversational mode. PAID. See process access ID (PAID). PARAM.
process Glossary process. A program that has been submitted to the operating system for execution. Multiple submissions of the same program run multiple processes. process access ID (PAID). A user ID used to determine whether a process can make requests to the system, for example to open a file, stop another process, and so on.
process sequence number Glossary process sequence number. A subpart of a process file name that allows the process to be identified over time. process time. The amount of time that a process has spent in the active substate. process timer. A clock that measures process execution time. processor clock. A hardware timer on each CPU module that keeps CPU time; the number of microseconds since cold load. processor time. The time represented by a CPU clock. program. See program file. program file.
relative file Glossary where the program might switch into or from TNS mode or TNS interpreted mode are register-exact points. Contrast with memory manager and nonexact point. relative file. A file in which each new record is stored at the relative record location specified by its primary key, and whose primary key is a relative record number. Records can be updated or deleted. Contrast with key-sequenced file and entrysequenced file. relative pathname.
run unit control block (RUCB) Glossary run unit control block (RUCB). A data structure used by the INITIALIZER procedure to specify to SIO the name of the common FCB and the number other SIO FCBs to be created. secondary extent. A contiguous area of disk storage allocated to a file. A file is made up of one or more extents; the first extent is the primary extent, and other extents are secondary extents. The secondary extents are all the same size for a specific file.
shared run-time library (SRL) Glossary shared run-time library (SRL). An object file that the operating system links to a program file at run time. See also TNS/R native shared run-time library (TNS/R native SRL). signal. A means by which a process can be notified of or affected by an event occurring in the system. Some signals are used to notify a process when an error not related to input or output has occurred. See also TNS/R native signal and OSS signal. Contrast with trap. signal handler.
superuser Glossary The super ID has the set of special permissions called appropriate privileges. In the Guardian environment, the structured view of the super ID, which is (255, 255), is most commonly used. In the Open System Services (OSS) environment, the scalar view of the super ID, which is 65535, is most commonly used. superuser. See super ID. SUT. See site update tape (SUT). swap files. The disk files to and from which information is copied during swapping.
system process Glossary system process. (1) A privileged process that comes into existence at system-load time and exists continuously for a given configuration for as long as the CPU remains operable. (2) An HP NonStop™ operating system process, such as the memory manager, the monitor, and the input/output (I/O) control processes. The files containing system processes are invoked by ALLPROCESSORS paragraph entries.
TNS code segment identifier Glossary TNS code segment identifier. A seven-bit value in which the most significant two bits encode a code space (user code, user library, system code, or system library) and the five remaining bits encode a code segment index in the range 0 through 31. TNS code segment index. A value in the range 0 through 31 that indexes a code segment within the current user code, user library, system code, or system library space. This value can be encoded in five bits. TNS code space.
TNS library Glossary TNS library. A single, optional, TNS-compiled loadfile associated with one or more application loadfiles. If a user library has its own global or static variables, it is called a TNS shared run-time library (TNS SRL). Otherwise it is called a User Library (UL). TNS mode. The operational environment in which TNS instructions execute by inline interpretation. See also accelerated mode. TNS object code.
TNS system library Glossary TNS system library. A collection of HP-supplied TNS-compiled routines available to all TNS processes. There is no per-program or per-process customization of this library. All routines are immediately available to a new process. No dynamic loading of code or creation of instance data segments is involved. See also HP NonStop™ operating system. TNS user data segment. In a TNS process, the segment at virtual address zero. Its length is limited to 128 kilobytes.
TNS/E native user library Glossary TNS/E native user library. A user library available to TNS/E native processes in both the Guardian and Open System Services (OSS) environments. A TNS/E native user library is implemented as a TNS/E native dynamic-link library (DLL). TNSVU. A tool used to browse through TNS object files that have been accelerated by the TNS Object Code Accelerator (OCA). TNSVU displays Intel® Itanium® code in addition to TNS code. Total Information Manager (TIM).
trap handler Glossary trap handler. A location in a program where execution begins if a trap occurs. A process can specify a trap handler by a call to the Guardian ARMTRAP procedure. two-way communication. A form of interprocess communication in which the sender of a message (requester process) expects data in the reply from the receiver (server process). Contrast with one-way communication. UID. A nonnegative integer that uniquely identifies a user within a node.
user process Glossary user process. A process whose primary purpose is to solve a user’s problem. A user process is not essential to the availability of a CPU. A user process is created only when the user explicitly creates it. Contrast with system process. vertical form control (VFC) table. A table in the memory of a 5515, 5516, or 5518 printer that is used to control vertical forms movement. VFC table. See vertical form control (VFC) table. virtual memory.
Index Numbers 32-bit ELF format 1-22 48-bit timestamps converting to Gregorian date and time 18-14 definition 18-3 obtaining 18-14 working with 18-13/18-14 5515 matrix line printer, communicating with 11-2/11-9, 11-19/11-49 5516 matrix line printer, communicating with 11-2/11-9, 11-19/11-49 5518 matrix line printer, communicating with 11-2/11-9, 11-19/11-49 5573 Laser-LX printer, communicating with 11-2/11-19, 11-29/11-49 5573D Laser-LX printer, communicating with 11-2/11-19, 11-29/11-49 5574 laser printer,
B Index Assign message example 8-10 introduction to 8-8/8-10 processing 8-12/8-13 receiving 8-12, 16-46 sending 16-46 structure of 8-9 ASSIGN^BLOCKLENGTH operation 15-18/15-19 ASSIGN^FILECODE operation 15-17 ASSIGN^FILENAME operation 15-67 ASSIGN^LOGICALFILENAME operation 15-10 ASSIGN^OPENACCESS operation 15-15/15-16, 15-38 ASSIGN^OPENEXCLUSION operation 15-16, 15-16 ASSIGN^PRIMARYEXTENTSIZE operation 15-18 ASSIGN^RECORDLENGTH operation 15-17 ASSIGN^SECONDARYEXTENTSIZE operation 15-18 Asterisk (*) 13-3/13
C Index BREAK mode 10-27/10-36 BREAK ownership releasing 10-26, 15-56 taking 10-25/10-26, 15-55/15-56 BREAKMESSAGE_SEND_ procedure 24-15 Breakpoints 25-6 Break-on-device message 24-15 Buffered mode end-of-file marks 12-19 errors, recovering from 12-85/12-86 example of 12-19/12-20 introduction to 12-16/12-17 invoking 12-17/12-18 labeled tape 12-29 revoking 12-18 streaming devices 12-19 unlabeled tape 12-74 C C and C++, calling Guardian procedures from 1-19 Cache flushes, minimizing 5-13 CAID 16-10/16-11 C
C Index Code spaces 16-2, 16-5/16-7 Command interpreter See TACL Command interpreter messages number -1 (Startup) example 8-5 introduction to 8-3/8-4 processing 8-6/8-8 reading 8-6 See also Startup message structure of 8-4 terminal files 10-4/10-5 number -2 (Assign) example 8-10 introduction to 8-8/8-10 processing 8-12/8-13 reading 8-12 See also Assign message structure of 8-9 number -20 (Wakeup) 8-19/8-20 number -21 (Display) 8-20/8-21 number -3 (Param) example 8-11 introduction to 8-8/8-11 processing 8-
D Index CONTROL procedure 2-23 device operations 9-4 interprocess communication 6-2 magnetic tape operations 12-2/12-3 operation-1 printers 11-3, 11-21 terminals 10-3, 10-17/10-18 operation-10 12-3, 12-8/12-10 operation-11 10-3, 11-3 operation-12 10-3, 11-3 operation-2 12-2, 12-13 operation-24 12-3 operation-26 12-18 operation-27 5-33/5-35 operation-3 12-2, 12-10 operation-4 12-2, 12-10 operation-5 12-2, 12-11 operation-6 12-2, 12-11 operation-7 12-2, 12-5 operation-8 12-2, 12-5/12-10 operation-9 12-2, 12
D Index DEFINE attributes errors 7-13 restoring 7-13 setting 7-12/7-13 DEFINE errors number-2057 7-14 number-2058 7-14 number-2059 7-14 DEFINE mode 7-10 DEFINE names, resolving 13-9/13-11 DEFINEADD procedure 7-1, 7-14/7-15, 12-22 DEFINEDELETE procedure 7-1, 7-15 DEFINEDELETEALL procedure 7-1, 7-15 DEFINEMODE procedure 7-11, 12-22, 12-70 DEFINERESTORE procedure 7-1, 7-13, 7-15 DEFINERESTOREWORK procedure 7-1, 7-16/7-17 DEFINERESTOREWORK2 procedure 7-1, 7-16/7-17 DEFINEs adding 7-11/7-17 attributes 7-8/7-10
D Index DENSITY attribute labeled tape 12-27 unlabeled tape 12-72 Destination control table (DCT) 16-2, 16-11/16-12 DEVICE attribute labeled tape 12-27 unlabeled tape 12-71 Device files home terminal getting the name of 9-3 opening 2-17 I/O with 2-23 naming 2-6, 9-2/9-3 opening 2-16 See also Magnetic tape See also Printers See also Terminals Device mode labeled tape 12-28 unlabeled tape 12-73 Device names searching for 13-23/13-24 syntax of 2-6/2-7 Device subtypes 16-35 Device subtypes, subtype 30 24-2 De
E Index Dollar sign ($) in device name 2-7 in process name 2-7 in volume name 2-5 DST transition table 18-29/18-36 Duplicate keys 5-8 E E register 17-4 EBCDIC attribute 12-25 Edit descriptor 19-6/19-7 EDIT file segment (EFS) 14-7 EDIT files accessing with SIO 15-37/15-39 appending to with IOEdit 14-14 closing with IOEdit 14-19 compressing with IOEdit 14-18 creating for IOEdit 14-9/14-10 deleting lines with IOEdit 14-16/14-17 file pointer positioning 14-12/14-13 initializing for IOEdit 14-10 inserting lin
F Index Errors DEFINE See DEFINE errors file system See File system errors nonretryable 2-28 process creation 16-27/16-28 retryable 2-28 SIO See SIO errors Exact positioning 5-49 Exclusion mode, files See Files, exclusion mode Exclusion mode, SIO 15-16, 15-67/15-71 Execution modes, TNS/R 1-23 EXPIRATION attribute 12-27 EXTDECS file 1-19 EXTDECS0 file 1-18, 1-19 EXTDECS1 file 1-19 Extend access 3-1 Extended data segments 1-9 allocating 17-23 bounds checking 17-34, 17-38 deallocating 17-42 extensible 17-23,
F Index File control blocks, SIO See FCBs, SIO File names 1-4 comparing 13-20/13-21 default values 2-5, 13-8 device 2-6 disk location-independent 2-9 permanent 2-4/2-6 temporary 2-6 editing 13-18/13-20 extracting a part of 13-15/13-16 extracting a prefix from 13-16 extracting a suffix from 13-16 extracting pieces of 13-15/13-18 extracting pieces without implicit resolution 13-16/13-17 fully qualified 2-5, 13-7/13-13 manipulating 13-1/13-41 matching complete match 13-31/13-32 examples 13-33 incomplete matc
F Index File system (continued) path errors 10-38/10-39, 11-30, 12-52, 12-88, 15-65 printers 11-29/11-31 retryable 2-28 terminals 10-36/10-41 File System I/O Synchronization 21-4 FILEID attribute 12-24 FILEINFO command 3-2/3-3 FILENAME_COMPARE_ procedure 13-1, 13-20/13-21 FILENAME_DECOMPOSE_ procedure file-name part 13-15/13-16 file-name prefix 13-16 file-name suffix 13-16 introduction to 13-1, 13-15 no-defaults flag 13-16/13-17 process descriptor 13-17/13-18 FILENAME_EDIT_ procedure 13-1, 13-18/13-20 FIL
F Index File-name patterns introduction to 13-3/13-4 matching 13-21/13-33 scanning 13-6 searching for 13-21/13-33 validating 13-6 FILE_ALTERLIST_ procedure 5-15 FILE_CLOSE_ procedure all files 2-29 magnetic tape 12-2, 12-84 printers 11-2, 11-4/11-5 processes 6-2 terminals 10-2, 10-10 unstructured files 5-13 $RECEIVE 6-13 FILE_CREATELIST_ procedure alternate-key file 5-82/5-85 disk files 2-11 entry-sequenced files 5-31/5-32 extent sizes, specifying 2-13 key-sequenced files 5-47/5-48 partitioned file 5-78/5
G Index Flat segments allocating 17-24 introduction to 17-19/17-23 referencing data in 17-32/17-33 Form feed printers 11-21/11-25 terminals 10-17/10-18 FORMATCONVERT procedure 19-1/19-34 FORMATCONVERTX procedure 19-1/19-34 FORMATDATA procedure 19-1/19-34 FORMATDATAX procedure 19-1/19-34 Formatter 19-1/19-34 using in TNS/R native programs 19-2 Formatting buffer control 19-11/19-13 data descriptors 19-7 edit descriptors 19-6/19-7 format-directed input 19-4/19-6 format-directed output 19-3/19-4 example 19-19
I Index HIST_INIT_ procedure 25-23, 25-32 Home terminal getting the name of 9-3 opening 2-17, 10-6 relationship of process with 16-14 specifying 16-31/16-32 H-series debugging tools 25-1 I IBM Kanji character set 19-50 IBM Kanji Mixed character set 19-50 Illegal logon, controlling from $CMON 23-13/23-14 Illegal^logon^msg message 23-13/23-14 Illegal^logon^reply structure 23-13/23-14 Implicit DLLs 16-4, 16-5 IN file 9-3 and the SYSTEM command 8-2 contents of 8-4 opening 10-4/10-5 with SIO 15-7/15-10 INCREM
J Index IOEdit appending to files 14-14 closing files 14-19 compressing files 14-18 creating files 14-9/14-10 current-record pointer 14-11 deleting lines 14-16/14-17 EDIT file segment 14-7 EFS 14-7 file full errors 14-16 file pointer positioning 14-12/14-13 initializing files 14-10 inserting lines 14-15 introduction to 1-7, 14-1 line backspacing 14-17 line numbers 14-4/14-5 next-record pointer 14-11 nowait I/O with 14-18 opening files 14-8/14-10 packed line format 14-5/14-7 reading 14-11/14-17 record numb
K Index K Kanji character set 19-49 Kernel Managed Swap Facility Glossary-15 Kernel Managed Swap Facility (KMSF) requesting space guarantee from 16-34/16-35 Key compaction 5-47 Key compression 5-47 Key length 5-47 Key offset 5-47 Key specifier 5-7 KEYPOSITION procedure approximate positioning 5-49 exact positioning 5-49 generic key positioning 5-50 Key-sequenced files See also Queue files Key-sequenced files altering attributes of 5-51 alternate key access 5-72/5-76 example 5-74/5-76 approximate positioni
M Index Labeled tape (continued) tape density, specifying 12-27 tape file, specifying 12-24 tape volume, specifying 12-24 writing to multiple-file tape 12-33/12-38 writing to multiple-volume file 12-38/12-42 writing to only file 12-29/12-33 writing to, concepts of 12-13/12-14 labeled tape opening 12-4 LABELEDTAPESUPPORT procedure 12-21 LABELS attribute labeled tape 12-23 unlabeled tape 12-70 Laser printers, communicating with 11-2/11-19, 11-29/11-49 LCT definition 18-2 obtaining from GMT timestamp 18-11 L
M Index Magnetic tape positioning 12-4/12-11 reading, concepts of 12-11/12-13 rewinding 12-10/12-11 See also Labeled tape See also Unlabeled tape writing to, concepts of 12-13/12-14 Magnetic tape files 2-3 Main procedure TNS 17-4 TNS/R native 17-9 Main stack 1-8, 16-8, 16-9, 17-6 bounds checking 17-15/17-16 sizing 17-13/17-15 use for procedure calls 17-9 Main stack, TNS/E 16-9, 17-13 MAP CLASS DEFINEs example of 7-3/7-4 FILE attribute 2-10 purpose 2-10, 7-2 Matrix line printers, communicating with 11-2/11
N Index Multibyte characters analyzing strings of 19-52/19-53 blank characters 19-54 case shifting 19-55 checking support for 19-51/19-52 determining the default set 19-52 example program 19-56/19-65 introduction to 19-49/19-51 size of 19-54/19-55 special symbols, testing for 19-55/19-56 trimming fragments of 19-54 Multithreaded server 22-1/22-2 MYPROCESSTIME procedure 18-19 N Named processes creating 2-14 relationship with 16-11/16-12 Native Inspect 25-2 introduction 25-5 specifying the debugging enviro
P Index OPENER_LOST_ procedure 22-7/22-8 OPEN^FILE procedure ABORT^OPENERR flag 15-61/15-62 ABORT^XFERERR flag 15-62/15-63 block buffering 15-25/15-26 creating and opening files 15-24/15-25 EDIT files 15-38 flag values, setting 15-23/15-24 interactive read prompt, setting 15-29 introduction to 15-2 long writes 15-30/15-31 MUSTBENEW flag 15-25 nowait I/O 15-40 opening files 15-24 padding records before writing 15-33/15-34 printing 15-36 purging data 15-26 redirecting error messages 15-61 suppressing error
P Index Physical-block length, SIO 15-18/15-19 PIN 2-8, 16-3 POOL_CHECK_ procedure 17-1, 17-48 POOL_DEFINE_ procedure 17-1, 17-43/17-45 POOL_GETINFO_ procedure 17-1, 17-48 POOL_GETSPACE_ procedure 17-1, 17-46/17-47 POOL_PUTSPACE_ procedure 17-1, 17-47 POOL_RESIZE_ procedure 17-1, 17-47/17-48 POSITION procedure 2-21, 2-22, 5-17, 5-32/5-33 POSITIONEDIT procedure 14-1, 14-12 PostScript selecting for 5577 printer 11-10/11-11 Pound sign in qualifier device name 2-7 process name 2-8 in temporary file ID 2-6 Pre
P Index Process file names (continued) unnamed processes 2-8 Process file segment 17-6 Process file segment (PFS) sizing 16-32/16-33 Process files introduction to 2-4 opening 2-17/2-18 Process handles converting to process file names 16-63/16-64 converting to process strings 16-64 getting information from 16-62 introduction to 16-3 manipulating 16-61/16-64 obtaining from process file names 16-62/16-63 Process identification number (PIN) 2-8, 16-3 Process identifiers 16-2/16-3 Process names See Process fil
P Index I/O with (continued) job ID, specifying 16-35/16-36 low PIN 16-28/16-31 main and priv stack, sizing 17-13 managing 1-8, 16-1/16-64 named creating 2-14, 16-23/16-25 process file name for 2-7 opening 2-17/2-18, 6-4/6-5 organization of 16-4, 16-5 persistent 1-15 PFS, sizing 16-32/16-33 priority of 16-15/16-17 processor, specifying 16-35 propagating DEFINEs 16-36/16-37 receiving messages from 6-11/6-16, 15-46, 15-48/15-51 replying to messages 6-11/6-16, 6-21, 15-46 runnable state 16-18/16-19, 16-53 sa
Q Index PROCESS_GETINFOLIST_ procedure introduction to 16-1 multiple processes 16-57/16-58 single process 16-56/16-57 PROCESS_GETINFO_ procedure getting the home terminal name 2-17, 9-3, 10-6 how to use 16-54/16-56 introduction to 16-1 process timing information 18-19 terminals 10-2 PROCESS_GETPAIRINFO_ procedure 16-58 how to use 16-59 PROCESS_LAUNCH_ completion message 16-22, 16-26/16-27 PROCESS_LAUNCH_ procedure creating processes detailed discussion 16-19/16-20 Debug state 25-4 introduction to 16-1 mem
R Index $RECEIVE (continued) two-way communication 6-11 READUPDATEX procedure effect on record pointers 2-22 extended data segments 17-35/17-36 key-sequenced files 5-49/5-51 relative files 5-17 $RECEIVE 2-24, 2-25 concurrent message processing 6-18 introduction 6-3 system messages 6-27 two-way communication 6-11 READX procedure devices 2-23 effect on record pointers 2-18/2-22 entry-sequenced files 5-32/5-33 extended data segments 17-35/17-36 introduction to 2-18 key-sequenced files 5-49/5-51 magnetic tape
S Index REPLYX procedure extended data segments 17-35/17-36 introduction to 6-3 multiple concurrent messages 6-21 one-and-a-half-way communication 2-25 two-way communication 2-24, 6-11/6-12 Requester process 1-10/1-15 application control 21-3/21-4 data mapping 21-2/21-3 example 6-32/6-51, 21-9/21-51 field validation 21-2 functions of 21-1/21-4 selecting a server 21-3/21-4 terminal interface 21-2 transaction control 21-4 writing a program for 21-1/21-51 Requester/server model 1-10/1-15 adding functions to
S Index Sequential Input/Output (SIO) See SIO Server process 1-10/1-15 concurrent message processing 6-17/6-22 context-free 22-3 example 6-51/6-63, 22-8/22-64 functions of 22-1/22-3 monitor 1-15 monitor process 1-15 multithreaded 22-1/22-2 opener table See Opener table processing system messages 22-4/22-8 single message handling 6-10/6-16 single-threaded 22-1/22-2 writing a program for 22-1/22-64 ServerWare Storage Management Foundation (SMF) 2-9 SET INSPECT command 25-7/25-8 SETJMP_ procedure/C function
S Index SETMODE procedure (continued) terminal functions 10-2, 10-3 SETMODENOWAIT procedure device functions 9-4 interprocess communication 6-3 magnetic tape functions 12-2 printer functions 11-2 terminal functions 10-2 Setparam message 6-26, 6-30, 24-11 SETPARAM procedure device functions 9-4 function-3 10-25/10-27, 10-30, 10-31 interprocess communication 6-3 terminal functions 10-2 SETSTOP procedure 16-1, 16-51/16-52 SETSYSTEMCLOCK procedure 18-27/18-28 SETTIME command 18-28 Setting timers elapsed time
S Index SIGACTION_INIT_ procedure/C function 25-27/25-36 SIGACTION_RESTORE_ procedure/C function 25-27/25-36 SIGACTION_SUPPLANT_ procedure/C function 25-27/25-36 SIGADDSET_ procedure/C function 25-24, 25-26 SIGDELSET_ procedure/C function 25-24, 25-26 SIGEMPTYSET_ procedure/C function 25-24, 25-26 SIGFILLSET_ procedure/C function 25-24, 25-26 SIGISMEMBER_ procedure/C function 25-24 SIGJMP_MASKSET_ procedure/C function 25-27/25-36 SIGLONGJMP_ procedure/C function 25-25, 25-26, 25-29/25-36 Signal defined Gl
S Index SIO (continued) replying to interprocess messages 15-46 sending interprocess messages no reply 15-48 reply expected 15-46 system messages 15-52/15-54 two-way communication, example of 15-47 writing a program for 15-3 $RECEIVE file, using for 15-7/15-10, 15-66, 15-67, 15-67 SIO errors fatal 15-61/15-63 introduction to 15-60 I/O errors 15-62/15-63 open errors 15-61/15-62 redirecting 15-61 retryable 15-63/15-65 suppressing error-message reporting 15-60/15-61 SIO files access mode 15-15/15-16, 15-67/1
S Index SRL data segments 17-6 SRL (shared run-time library) 16-5, 16-5/16-7 Stack frames 17-9 Stack pointer 17-9 Stacks TNS/R and TNS/E 16-9, 17-13 Stack, TNS user data 17-2 Starting state 16-18/16-19 Startup message example 8-5 example of sending and receiving 16-39/16-46 introduction to 8-3/8-5 opening terminal files 10-4/10-5 processing 8-6/8-8 reading 8-6 structure of 8-4 Startup sequence introduction to 1-7, 2-10, 8-2/8-3 sending and receiving 16-38/16-46 timing out 8-16 with INITIALIZER 8-3/8-15 wi
T Index System messages (continued) -103 (Open) 6-26, 6-29, 15-53, 22-5/22-6 -104 (Close) 6-26, 6-29, 15-53, 22-7 -105 (Break-on-device) 24-15 -110 (Loss of communication with a network node) 15-53 -110 (Node down) 22-7/22-8 -2 (CPU down) 15-53, 22-7/22-8 -22 (Time signal) 18-16/18-17 -26 (Process time signal) 18-17/18-18 -32 (Control) 6-26, 6-30, 24-10 -33 (Setmode) 6-26, 6-30, 24-10/24-11 -34 (Resetsync) 6-26, 6-30 -35 (Controlbuf) 6-26, 6-30 -37 (Setparam) 6-26, 6-30, 24-11 -40 (Request for device-type
T Index Terminal simulator (continued) system messages 24-3/24-5, 24-9/24-13 Terminals accessing 10-1/10-10 block mode See Page mode BREAK key 10-23/10-36 BREAK mode 10-27/10-36 BREAK ownership 15-55/15-59 releasing 10-26 taking 10-25/10-26 closing 10-10 communicating with 10-1/10-41 conversational mode See Conversational mode echo mode 10-9 errors 10-36/10-41 form feed 10-17/10-18 interrupt characters See Interrupt characters I/O with 1-6, 2-23, 10-6/10-8 line spacing 10-16/10-17 line-termination charact
U Index Two-way communication example of 6-13, 6-31/6-63, 15-47 introduction to 2-24, 6-10 opening $RECEIVE 6-10 receiving messages 6-11, 15-46 replying to messages 6-11/6-12, 15-46 returning data 6-11 returning errors 6-11 sending messages 15-46 U Underlining text 11-18, 11-27 Unified program 1-10 Unlabeled tape accessing 12-70/12-74 appending to last file 12-78/12-79 appending to only file 12-76/12-77 backward spacing by files 12-5/12-10 backward spacing by records 12-8/12-10 blocking records 12-72 buf
V Index User data segment 1-8 bounds checking 17-15, 17-15/17-16 managing 17-2/17-15 sizing 16-32, 17-3 upper 64K bytes 17-6/17-15 User data stack 1-8, 16-8, 17-3/17-6 User library 16-5 overview 16-4, 16-5 User library file 16-33 Users adding 23-24/23-26 deleting 23-26/23-27 V VERSION attribute 12-28 Vertical forms control (VFC) table 11-21/11-25 VFC table 11-21/11-25 Virtual memory 16-4, 16-5 introduction to 1-8 managing 1-8 pools 1-9, 17-42/17-48 Visual Inspect introduction 25-1, 25-5 specifying the de
Z Index WRITEX procedure devices 2-23 effect on record pointers 2-18/2-21 entry-sequenced files 5-32/5-33 extended data segments 17-35/17-36 introduction to 2-18 key-sequenced files 5-49/5-51 magnetic tape 12-2, 12-13/12-14 printers 11-2 relative files 5-17 terminals 10-2, 10-6 writing messages 6-4, 6-6/6-7 Write-only access, files 3-1 WRITE^FILE procedure 15-2 long writes 15-30/15-31 printers 15-37 replying to interprocess messages 15-46 sending messages, no reply 15-48 writing records 15-29 Z ZSYS* fil
Special Characters Index $RECEIVE closing 6-13 getting message information 6-19/6-21 introduction to 1-6, 1-11, 2-4, 2-24 opening 6-10, 6-15, 6-17 queuing messages on 6-8/6-9 reading messages from 6-18 SIO allocating 15-66 initializing FCB for 15-7/15-10, 15-67 naming 15-67 * symbol 13-3/13-4 <, condition-code-less-than (CCL) 2-27 =, condition-code-equal (CCE) 2-27 =_DEFAULTS DEFINE 2-5, 7-3, 7-5/7-6 >, condition-code-greater-than (CCG) 2-27 ? symbol 13-3/13-4 \ symbol, in node name 2-5 __ns_backup_fopen
Special Characters Index Guardian Programmer’s Guide —421922-014 Index -38