1 Introduction
2 Ground Rules

Building a File System
3 File Systems
4 File Content Data Structure
5 Allocation Cluster Manager
6 Exceptions and Emancipation
7 Base Classes, Testing, and More
8 File Meta Data
9 Native File Class
10 Our File System
11 Allocation Table
12 File System Support Code
13 Initializing the File System
14 Contiguous Files
15 Rebuilding the File System
16 Native File System Support Methods
17 Lookups, Wildcards, and Unicode, Oh My
18 Finishing the File System Class

The Init Program
19 Hardware Abstraction and UOS Architecture
20 Init Command Mode
21 Using Our File System
22 Hardware and Device Lists
23 Fun with Stores: Partitions
24 Fun with Stores: RAID
25 Fun with Stores: RAM Disks
26 Init wrap-up

The Executive
27 Overview of The Executive
28 Starting the Kernel
29 The Kernel
30 Making a Store Bootable
31 The MMC
32 The HMC
33 Loading the components
34 Using the File Processor
35 Symbols and the SSC
36 The File Processor and Device Management
37 The File Processor and File System Management
38 Finishing Executive Startup

Users and Security
39 Introduction to Users and Security
40 More Fun With Stores: File Heaps
41 File Heaps, part 2
42 SysUAF
43 TUser
44 SysUAF API

Terminal I/O
45 Shells and UCL
46 UOS API, the Application Side
47 UOS API, the Executive Side
48 I/O Devices
49 Streams
50 Terminal Output Filters
51 The TTerminal Class
52 Handles
53 Putting it All Together
54 Getting Terminal Input
55 QIO
56 Cooking Terminal Input
57 Putting it all together, part 2
58 Quotas and I/O

UCL
59 UCL Basics
60 Symbol Substitution
61 Command execution
62 Command execution, part 2
63 Command Abbreviation
64 ASTs
65 Expressions, Part 1
66 Expressions, Part 2: Support code
67 Expressions, part 3: Parsing
68 SYS_GETJPIW and SYS_TRNLNM
69 Expressions, part 4: Evaluation

UCL Lexical Functions
70 PROCESS_SCAN
71 PROCESS_SCAN, Part 2
72 TProcess updates
73 Unicode revisted
74 Lexical functions: F$CONTEXT
75 Lexical functions: F$PID
76 Lexical Functions: F$CUNITS
77 Lexical Functions: F$CVSI and F$CVUI
78 UOS Date and Time Formatting
79 Lexical Functions: F$CVTIME
80 LIB_CVTIME
81 Date/Time Contexts
82 SYS_GETTIM, LIB_Get_Timestamp, SYS_ASCTIM, and LIB_SYS_ASCTIM
83 Lexical Functions: F$DELTA_TIME
84 Lexical functions: F$DEVICE
85 SYS_DEVICE_SCAN
86 Lexical functions: F$DIRECTORY
87 Lexical functions: F$EDIT and F$ELEMENT
88 Lexical functions: F$ENVIRONMENT
89 SYS_GETUAI
90 Lexical functions: F$EXTRACT and F$IDENTIFIER
91 LIB_FAO and LIB_FAOL
92 LIB_FAO and LIB_FAOL, part 2
93 Lexical functions: F$FAO
94 File Processing Structures
95 Lexical functions: F$FILE_ATTRIBUTES
96 SYS_DISPLAY
97 Lexical functions: F$GETDVI
98 Parse_GetDVI
99 GetDVI
100 GetDVI, part 2
101 GetDVI, part 3
102 Lexical functions: F$GETJPI
103 GETJPI
104 Lexical functions: F$GETSYI
105 GETSYI
106 Lexical functions: F$INTEGER, F$LENGTH, F$LOCATE, and F$MATCH_WILD
107 Lexical function: F$PARSE
108 FILESCAN
109 SYS_PARSE
110 Lexical Functions: F$MODE, F$PRIVILEGE, and F$PROCESS
111 File Lookup Service
112 Lexical Functions: F$SEARCH
113 SYS_SEARCH
114 F$SETPRV and SYS_SETPRV
115 Lexical Functions: F$STRING, F$TIME, and F$TYPE
116 More on symbols
117 Lexical Functions: F$TRNLNM
118 SYS_TRNLNM, Part 2
119 Lexical functions: F$UNIQUE, F$USER, and F$VERIFY
120 Lexical functions: F$MESSAGE
121 TUOS_File_Wrapper
122 OPEN, CLOSE, and READ system services

UCL Commands
123 WRITE
124 Symbol assignment
125 The @ command
126 @ and EXIT
127 CRELNT system service
128 DELLNT system service
129 IF...THEN...ELSE
130 Comments, labels, and GOTO
131 GOSUB and RETURN
132 CALL, SUBROUTINE, and ENDSUBROUTINE
133 ON, SET {NO}ON, and error handling
134 INQUIRE
135 SYS_WRITE Service
136 OPEN
137 CLOSE
138 DELLNM system service
139 READ
140 Command Recall
141 RECALL
142 RUN
143 LIB_RUN
144 The Data Stream Interface
145 Preparing for execution
146 EOJ and LOGOUT
147 SYS_DELPROC and LIB_GET_FOREIGN

CUSPs and utilities
148 The I/O Queue
149 Timers
150 Logging in, part one
151 Logging in, part 2
152 System configuration
153 SET NODE utility
154 UUI
155 SETTERM utility
156 SETTERM utility, part 2
157 SETTERM utility, part 3
158 AUTHORIZE utility
159 AUTHORIZE utility, UI
160 AUTHORIZE utility, Access Restrictions
161 AUTHORIZE utility, Part 4
162 AUTHORIZE utility, Reporting
163 AUTHORIZE utility, Part 6
164 Authentication
165 Hashlib
166 Authenticate, Part 7
167 Logging in, part 3
168 DAY_OF_WEEK, CVT_FROM_INTERNAL_TIME, and SPAWN
169 DAY_OF_WEEK and CVT_FROM_INTERNAL_TIME
170 LIB_SPAWN
171 CREPRC
172 CREPRC, Part 2
173 COPY
174 COPY, part 2
175 COPY, part 3
176 COPY, part 4
177 LIB_Get_Default_File_Protection and LIB_Substitute_Wildcards
178 CREATESTREAM, STREAMNAME, and Set_Contiguous
179 Help Files
180 LBR Services
181 LBR Services, Part 2
182 LIBRARY utility
183 LIBRARY utility, Part 2
184 FS Services
185 FS Services, Part 2
186 Implementing Help
187 HELP
188 HELP, Part 2
189 DMG_Get_Key and LIB_Put_Formatted_Output
190 LIBRARY utility, Part 3
191 Shutting Down UOS
192 SHUTDOWN
193 WAIT
194 SETIMR
195 WAITFR and Scheduling
196 REPLY, OPCOM, and Mailboxes
197 REPLY utility
198 Mailboxes
199 BRKTHRU

Glossary/Index


Downloads

GetDVI, part 2

In this article, we continue our look at the long GETDVI system call.

                    DVI_DEVLOCKNAM : begin
                                         S := '' ;
                                         I := 1 ;
                                         if( Kernel.Node_Name <> '' ) then
                                         begin
                                             S := Kernel.Node_Name ;
                                             if( S <> '' ) then
                                             begin
                                                 S := S + '$' ;
                                                 I := 2 ;
                                             end ;
                                         end ;
                                         S := S + copy( This_Device_Name, I, 1024 ) ;
                                         if( Write_String( S ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
This item returns a unique name for a device. On a given system, each device has a unique name. On clusters however, multiple systems could have devices with the same name (for instance "DISKA0:"). Thus, this function will prefix the device name with the system's cluster node name (if it has been defined) and a dollar sign. Note that we remove the underscore at the start of the device name in this case. For instance "MYCOMPUTER$DISKA0:".

                    DVI_DEVNAM : begin
                                     if( Write_String( This_Device_Name ) = UE_Error ) then
                                     begin
                                         exit ;
                                     end ;
                                 end ;
This item simply returns the device name that we constructed near the beginning of this method.

                    DVI_DEVSTS : begin
                                     if( Write_Integer( Device.Updated_Info.Status ) = UE_Error ) then
                                     begin
                                         exit ;
                                     end ;
                                 end ;
This item returns the device status flags.

                    DVI_DEVTYPE : begin
                                      S := copy( This_Device_Name, 1, 5 ) ;
                                      if( S = '_DISK' ) then
                                      begin
                                          Res := DT_Generic_Disk ; // Disk device
                                      end else
                                      if( S = '_TAPE' ) then
                                      begin
                                          Res := DT_Generic_Tape ;
                                      end else
                                      if( S = '_TERM' ) then
                                      begin
                                          Res := DT_Ttyunkn ; // Terminal
                                      end else
                                      if( S = '_NULL' ) then
                                      begin
                                          Res := DT_Null ; // Miscellaneous device
                                      end ;
                                      if( Write_Integer( Res ) = UE_Error ) then
                                      begin
                                          exit ;
                                      end ;
                                  end ;
This item returns the device type. On UOS, this isn't much different than the device class. Again, VMS device types are DEC hardware-specific, so we only return codes for generic disks, generic tapes, null devices, and unknown terminals.

                    DVI_ERASE_ON_DELETE : begin
                                              Res := 0 ;
                                              if( Device.FS <> nil ) then
                                              begin
                                                  if( Device.FS.DSM <> 0 ) then
                                                  begin
                                                      Res := 1 ;
                                                  end ;
                                              end ;
                                              if( Write_Integer( Res ) = UE_Error ) then
                                              begin
                                                  exit ;
                                              end ;
                                          end ;
VMS has one form of erase-on-delete. UOS supports multiple forms of erase-on-delete. This code doesn't return the type of erase, it simply returns 1 if there is any form of erase-on-delete for this device.

                    DVI_EXISTS : begin
                                     if( Write_Integer( 1 ) = UE_Error ) then 
                                     begin
                                         exit ; // If we get here, the device exists
                                     end ;
                                 end ;
We handled DVI_EXISTS above for the case when the specified device doesn't exist. Thus, if we get here, the device exists so we return 1.

                    DVI_FC_HBA_FIRMWARE_REV : begin
                                                  if( Write_String( Device.Revision ) = UE_Error ) then
                                                  begin
                                                      exit ;
                                                  end ;
                                              end ;
                    DVI_FIRMWARE_REV : begin
                                           if( Write_String( Device.Revision ) = UE_Error ) then
                                           begin
                                               exit ;
                                           end ;
                                       end ;
                    DVI_SCSI_DEVICE_FIRMWARE_REV : begin
                                                       if( Write_String( Device.Revision ) = UE_Error ) then
                                                       begin
                                                           exit ;
                                                       end ;
                                                   end ;
These items return the firmware revision for a device. DVI_FC_HBA_FIRMWARE_REV is hardware-specific whereas DVI_FIRMWARE_REV is generic. DVI_SCSI_DEVICE_FIRMWARE_REV refers to SCSI-hardware. I'm not sure why VMS has these three different codes rather than just the generic one. In any case we support all three for compatibility sake and return the device revision in all cases.

                    DVI_FREEBLOCKS : begin
                                         Res := 0 ;
                                         if( Device.FS <> nil ) then
                                         begin
                                             Res := Device.FS.Store.SpaceAvail div 
                                                 Device.FS.Store.Min_Storage ;
                                         end ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
This item returns the free space on a store. We return 0 for anything that is not a file system. Note that VMS returns the number of free 512-byte blocks, whereas UOS returns the number of free clusters, whatever size those are.

                    DVI_FULLDEVNAM : begin
                                         I := 1 ;
                                         if( Device.Allocation_Class > 0 ) then
                                         begin
                                             S := '$' + inttostr( Device.Allocation_Class - 1 ) + '$' ;
                                         end else
                                         if( Kernel.Node_Name <> '' ) then
                                         begin
                                             S := Kernel.Node_Name ;
                                             if( S <> '' ) then
                                             begin
                                                 S := S + '$' ;
                                                 I := 2 ;
                                             end ;
                                         end ;
                                         S := S + copy( This_Device_Name, I, 1024 ) ;
                                         if( Write_String( S ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
This item returns the fully-qualified device name. This operates much like the DVI_DEVLOCKNAM item except that if an allocation class has been defined for the device, that value is used instead of the cluster node name.

                    DVI_HARDLINKS_SUPPORTED : begin
                                                  Res := 0 ;
                                                  if( Device.FS <> nil ) then
                                                  begin
                                                      if( Device.FS.Hardlinks ) then
                                                      begin
                                                          Res := 1 ;
                                                      end ;
                                                  end ;
                                                  if( Write_Integer( Res ) = UE_Error ) then //TODO
                                                  begin
                                                      exit ;
                                                  end ;
                                              end ;
The item returns 1 if a file system supports hard links, and 0 otherwise. Since this only applies to file systems, we only check if the device has a file system. In that case, we ask the file system if it supports them. If so, we return 1.

                    DVI_LOCKID : begin
                                     if( Write_Integer( 0 ) = UE_Error ) then //TODO
                                     begin
                                         exit ;
                                     end ;
                                 end ;
Devices and files can be locked. We will be discussing the subject of locking and the lock manager in the future. For now, we always return 0, which indicates no lock exists for the device.

                    DVI_LOGVOLNAM : begin
                                        if( Write_String( '' ) = UE_Error ) then //TODO
                                        begin
                                            exit ;
                                        end ;
                                    end ;
This item returns the device logical name. This is defined by the MOUNT command, which we will cover in a future article. For now, we return a null string.

                    DVI_MAXBLOCK : begin
                                       Res := 0 ;
                                       if( Device.FS <> nil ) then
                                       begin
                                           Res := Device.FS.Store.Max_Storage div 
                                               Device.Fs.Store.Min_Storage - 1 ;
                                       end ;
                                       if( Write_Integer( Res ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                   end ;
This item returns the maximum block on the device. It only applies to file systems. On VMS, this is the number of 512-byte blocks (minus 1). For UOS, this is the total store capacity divided by the minimum allocation amount - in other words, this is the number of store clusters (minus 1).

                    DVI_MAXFILES : begin
                                       Res := 0 ;
                                       if( Device.FS <> nil ) then
                                       begin
                                           Res := Device.FS.Max_Files ;
                                       end ;
                                       if( Write_Integer( Res ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                   end ;
Some file systems have a limit on the number of files that can exist on that file system (the VMS file system is one of these). Since this is based on the file system, we query the file system for the value. Note that 0 indicates that there is no inherent limit, as well as indicating that the device has no file system at all. Because the default UOS file system has no limit, it returns 0.

                    DVI_MEDIA_ID : begin
                                       if( Write_Integer( 0 ) = UE_Error ) then //TODO
                                       begin
                                           exit ;
                                       end ;
                                   end ;
                    DVI_MEDIA_NAME, DVI_MEDIA_TYPE :
                        begin
                            S := copy( This_Device_Name, 2, 4 ) ;
                            if( Write_String( S ) = UE_Error ) then
                            begin
                                exit ;
                            end ;
                        end ;
These items are VMS-specific and, to be honest, I haven't decided how (or if) we will handle the DVI_MEDIA_ID item. For the other items, we return the device category ("DISK", "TERM", etc). This is analogous to VMS, but not exactly the same. For VMS, this is VAX/Alpha hardware-specific and simply don't apply to UOS. If UOS ever runs on VAX/Alpha hardware, then we might want to revisit this.

                    DVI_MOUNT_TIME : begin
                                         if( Write_Integer( Device.Last_Mount_Time ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
This item returns the timestamp of the time when the device was last mounted. 0 indicates that the device has not been mounted.

                    DVI_MOUNTCNT : begin
                                       if( Write_Integer( Device.Mounts ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                   end ;
This item returns the number of times the device has been mounted since UOS started up.

                    DVI_NOSHARE_MOUNTED : begin
                                              if( Write_Integer( 0 ) = UE_Error ) then //TODO
                                              begin
                                                  exit ;
                                              end ;
                                          end ;
This item has to do with device mount options, which we will discuss in the future.

                    DVI_OPCNT : begin
                                    Res := Device.Operation_Count ;
                                    if( Write_Integer( Res ) = UE_Error ) then
                                    begin
                                        exit ;
                                    end ;
                                end ;
                    DVI_TRANSCNT : begin
                                       Res := Device.Operation_Count ;
                                       if( Write_Integer( Res ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                   end ;
These items return the operation counts for the device. Here I must confess that I'm not sure what the difference is between these two on VMS. On UOS they both do the same thing.

                    DVI_OWNUIC : begin
                                     Res := Device.Owner.Owner ;
                                     if( Write_Integer( Res ) = UE_Error ) then
                                     begin
                                         exit ;
                                     end ;
                                 end ;
This item returns the UIC of the device owner (or 0 if there is no owner).

                    DVI_PID : begin
                                  Res := Device.Owner_PID ;
                                  if( Write_Integer( Res ) = UE_Error ) then
                                  begin
                                      exit ;
                                  end ;
                              end ;
This item returns the process ID of the device owner (or 0 if not owned).

                    DVI_PROT_SUBSYSTEM_ENABLED : begin
                                                     if( Write_Integer( 0 ) = UE_Error ) then //TODO
                                                     begin
                                                         exit ;
                                                     end ;
                                                 end ;
This item indicates where a protection subsystem is enabled. This is a subject for a future article.

                    DVI_QLEN : begin
                                   if( Write_Integer( Device.Queue_Length ) = UE_Error ) then
                                   begin
                                       exit ;
                                   end ;
                               end ;
This item indicates the length of the operation queue for the device. This is specifically the queue length of the driver and/or hardware - not of the File Processor's I/O queue.

                    DVI_RECSIZ : begin
                                     Res := Device.Buffer_Size ;
                                     if( Write_Integer( Res ) = UE_Error ) then
                                     begin
                                         exit ;
                                     end ;
                                 end ;
This item returns the device's record size.

                    DVI_REFCNT : begin
                                     Res := 0 ; // TODO
                                     if( Write_Integer( Res ) = UE_Error ) then
                                     begin
                                         exit ;
                                     end ;
                                 end ;
This item returns the number of file handles currently referencing this device. We will cover this in a future article.

                    DVI_REMOTE_DEVICE : begin
                                            S := copy( This_Device_Name, 2, 5 ) ;
                                            Res := 0 ;
                                            if( ( S = 'DISKZ' ) or ( S = 'TERMZ' ) ) then // Remote devices
                                            begin
                                                Res := 1 ;
                                            end ;
                                            if( Write_Integer( Res ) = UE_Error ) then
                                            begin
                                                exit ;
                                            end ;
                                        end ;
This item returns 1 if the device is a remote device. This can be easily determined by device controller. All remote devices have a "Z" (controller 25), which never corresponds to a local physical device. Thus DISKZ and TERMZ device names indicate a remote device.

                    DVI_SERIALNUM : begin
                                        if( Write_String( Device.Serial_Number ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
This item returns the device serial number.

                    DVI_SERVED_DEVICE : begin
                                            Res := 0 ; // TODO
                                            if( Write_Integer( Res ) = UE_Error ) then
                                            begin
                                                exit ;
                                            end ;
                                        end ;
This item indicates whether or not the device is served. We will cover this in the future.

                    DVI_SPLDEVNAME : begin
                                         if( Write_String( '' ) = UE_Error ) then //TODO
                                         begin
                                             exit ;
                                         end ;
                                     end ;
This item returns the spooled device name. This is something we will cover in the future when we cover spoolers.

                    DVI_STS : begin
                                  if( Write_Integer( Device.Status ) = UE_Error ) then
                                  begin
                                      exit ;
                                  end ;
                              end ;
This item returns the device status flags. Note: these don't necessarily match the VMS flags. It should be noted that there are two sets of device flags: the hardware flags and the software flags. This returns the software device flags.

                    DVI_TT_ACCPORNAM : begin
                                           if( Write_String( '' ) = UE_Error ) then //TODO
                                           begin
                                               exit ;
                                           end ;
                                       end ;
                    DVI_TT_ALTYPEAHD : begin
                                           Res := TT_Flag( TT2_M_ALTYPEAHD ) ;
                                           if( Write_Integer( Res ) = UE_Error ) then
                                           begin
                                               exit ;
                                           end ;
                                       end ;
                    DVI_TT_ANSICRT : begin
                                         Res := TT_Flag( TT2_M_ANSICRT ) ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_APP_KEYPAD : begin
                                            Res := TT_Flag( TT2_M_APP_KEYPAD ) ;
                                            if( Write_Integer( Res ) = UE_Error ) then
                                            begin
                                                exit ;
                                            end ;
                                        end ;
                    DVI_TT_AUTOBAUD : begin
                                          Res := TT_Flag( TT2_M_AUTOBAUD ) ;
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_AVO : begin
                                     Res := TT_Flag( TT2_M_AVO ) ;
                                     if( Write_Integer( Res ) = UE_Error ) then
                                     begin
                                         exit ;
                                     end ;
                                 end ;
                    DVI_TT_BLOCK : begin
                                       Res := TT_Flag( TT2_M_BLOCK ) ;
                                       if( Write_Integer( Res ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                   end ;
                    DVI_TT_BRDCSTMBX : begin
                                           Res := TT_Flag( TT2_M_BRDCSTMBX ) ;
                                           if( Write_Integer( Res ) = UE_Error ) then
                                           begin
                                               exit ;
                                           end ;
                                       end ;
                    DVI_TT_CHARSET : begin
                                         Res := 0 ; // TODO
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_CRFILL : begin
                                        Res := 0 ;
                                        if( Device.Terminal <> nil ) then
                                        begin
                                            Res := Device.Terminal.CRFill ;
                                        end ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_CS_HANGUL : begin
                                           Res := 0 ; // TODO
                                           if( Write_Integer( Res ) = UE_Error ) then
                                           begin
                                               exit ;
                                           end ;
                                       end ;
                    DVI_TT_CS_HANYU : begin
                                          Res := 0 ; // TODO
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_CS_HANZI : begin
                                          Res := 0 ; // TODO
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_CS_KANA : begin
                                         Res := 0 ; // TODO
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_CS_KANJI : begin
                                          Res := 0 ; // TODO
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_CS_THAI : begin
                                         Res := 0 ; // TODO
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_DECCRT : begin
                                        Res := TT_Flag( TT2_M_DECCRT ) ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_DECCRT2 : begin
                                         Res := TT_Flag( TT2_M_DECCRT2 ) ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_DECCRT3 : begin
                                         Res := TT_Flag( TT2_M_DECCRT3 ) ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_DECCRT4 : begin
                                         Res := TT_Flag( TT2_M_DECCRT4 ) ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_DIALUP : begin
                                        Res := TT_Flag( TT2_M_DIALUP ) ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_DISCONNECT : begin
                                            Res := TT_Flag( TT2_M_DISCONNECT ) ;
                                            if( Write_Integer( Res ) = UE_Error ) then
                                            begin
                                                exit ;
                                            end ;
                                        end ;
                    DVI_TT_DMA : begin
                                     Res := TT_Flag( TT2_M_DMA ) ;
                                     if( Write_Integer( Res ) = UE_Error ) then
                                     begin
                                         exit ;
                                     end ;
                                 end ;
                    DVI_TT_DRCS : begin
                                      Res := TT_Flag( TT2_M_DRCS ) ;
                                      if( Write_Integer( Res ) = UE_Error ) then
                                      begin
                                          exit ;
                                      end ;
                                  end ;
                    DVI_TT_EDIT : begin
                                      Res := TT_Flag( TT2_M_EDIT ) ;
                                      if( Write_Integer( Res ) = UE_Error ) then
                                      begin
                                          exit ;
                                      end ;
                                  end ;
                    DVI_TT_EDITING : begin
                                         Res := TT_Flag( TT2_M_EDITING ) ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_EIGHTBIT : begin
                                          Res := TT_Flag( TT_M_EIGHTBIT ) ;
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_ESCAPE : begin
                                        Res := TT_Flag( TT_M_ESCAPE ) ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_FALLBACK : begin
                                          Res := TT_Flag( TT2_M_FALLBACK ) ;
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_HALFDUP : begin
                                         Res := TT_Flag( TT_M_HALFDUP ) ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_HANGUP : begin
                                        Res := TT_Flag( TT2_M_NOHANGUP ) ;
                                        if( Res = 1 ) then
                                        begin
                                            Res := 0 ;
                                        end else
                                        begin
                                            Res := 1 ;
                                        end ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_HOSTSYNC : begin
                                          Res := TT_Flag( TT_M_HOSTSYNC ) ;
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_INSERT : begin
                                        Res := TT_Flag( TT2_M_INSERT ) ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_LFFILL : begin
                                        Res := 0 ;
                                        if( Device.Terminal <> nil ) then
                                        begin
                                            Res := Device.Terminal.LFFill ;
                                        end ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_LOCALECHO : begin
                                           Res := TT_Flag( TT2_M_LOCALECHO ) ;
                                           if( Write_Integer( Res ) = UE_Error ) then
                                           begin
                                               exit ;
                                           end ;
                                       end ;
                    DVI_TT_LOWER : begin
                                       Res := TT_Flag( TT_M_UPPERCASE ) ;
                                       if( Res = 1 ) then
                                       begin
                                           Res := 0 ;
                                       end else
                                       begin
                                           Res := 1 ;
                                       end ;
                                       if( Write_Integer( Res ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                   end ;
                    DVI_TT_MBXDSABL : begin
                                          Res := TT_Flag( TT_M_MBXDSABL ) ;
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_MECHFORM : begin
                                          Res := TT_Flag( TT_M_MECHFORM ) ;
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_MECHTAB : begin
                                         Res := TT_Flag( TT_M_MECHTAB ) ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_MODEM : begin
                                       Res := TT_Flag( TT_M_MODEM ) ;
                                       if( Write_Integer( Res ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                   end ;
                    DVI_TT_MODHANGUP : begin
                                           Res := TT_Flag( TT2_M_MODHANGUP ) ;
                                           if( Write_Integer( Res ) = UE_Error ) then
                                           begin
                                               exit ;
                                           end ;
                                       end ;
                    DVI_TT_NOBRDCST : begin
                                          Res := TT_Flag( TT_M_NOBRDCST ) ;
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_NOECHO : begin
                                        Res := TT_Flag( TT_M_NOECHO ) ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_NOTYPEAHD : begin
                                           Res := TT_Flag( TT_M_NOTYPEAHD ) ;
                                           if( Write_Integer( Res ) = UE_Error ) then
                                           begin
                                               exit ;
                                           end ;
                                       end ;
                    DVI_TT_OPER : begin
                                      Res := 0 ;
                                      if( ( Device.Updated_Info.Flags and DEV_V_OPR ) <> 0 ) then
                                      begin
                                          if( Device.Terminal <> nil ) then
                                          begin
                                              Res := 1 ;
                                          end ;
                                      end ;
                                      if( Write_Integer( Res ) = UE_Error ) then
                                      begin
                                          exit ;
                                      end ;
                                  end ;
                    DVI_TT_PAGE : begin
                                      Res := 0 ;
                                      if( Device.Terminal <> nil ) then
                                      begin
                                          Res := Device.Terminal.Char_Rows ;
                                      end ;
                                      if( Write_Integer( Res ) = UE_Error ) then
                                      begin
                                          exit ;
                                      end ;
                                  end ;
                    DVI_TT_PASTHRU : begin
                                         Res := TT_Flag( TT2_M_PASTHRU ) ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_PHYDEVNAM : begin
                                           if( Write_String( This_Device_Name ) = UE_Error ) then
                                           begin
                                               exit ;
                                           end ;
                                           // TODO: Handle remote devices
                                       end ;
                    DVI_TT_PRINTER : begin
                                         Res := TT_Flag( TT2_M_PRINTER ) ;
                                         if( Write_Integer( Res ) = UE_Error ) then
                                         begin
                                             exit ;
                                         end ;
                                     end ;
                    DVI_TT_READSYNC : begin
                                          Res := TT_Flag( TT_M_READSYNC ) ;
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_REGIS : begin
                                       Res := TT_Flag( TT2_M_REGIS ) ;
                                       if( Write_Integer( Res ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                    end ;
                    DVI_TT_REMOTE : begin
                                        Res := TT_Flag( TT_M_REMOTE ) ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_SCOPE : begin
                                       Res := TT_Flag( TT_M_SCOPE ) ;
                                       if( Write_Integer( Res ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                   end ;
                    DVI_TT_SECURE : begin
                                        Res := TT_Flag( TT2_M_SECURE ) ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_SETSPEED : begin
                                          Res := TT_Flag( TT2_M_SETSPEED ) ;
                                          if( Write_Integer( Res ) = UE_Error ) then
                                          begin
                                              exit ;
                                          end ;
                                      end ;
                    DVI_TT_SIXEL : begin
                                       Res := TT_Flag( TT2_M_SIXEL ) ;
                                       if( Write_Integer( Res ) = UE_Error ) then
                                       begin
                                           exit ;
                                       end ;
                                   end ;
                    DVI_TT_SYSPWD : begin
                                        Res := TT_Flag( TT2_M_SYSPWD ) ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_TTSYNC : begin
                                        Res := TT_Flag( TT_M_TTSYNC ) ;
                                        if( Write_Integer( Res ) = UE_Error ) then
                                        begin
                                            exit ;
                                        end ;
                                    end ;
                    DVI_TT_WRAP : begin
                                      Res := TT_Flag( TT_M_WRAP ) ;
                                      if( Write_Integer( Res ) = UE_Error ) then
                                      begin
                                          exit ;
                                      end ;
                                  end ;
These items return information about a terminal. For the most part, these simply return 0 or 1 depending upon the value of a given terminal flag (obtained via the TT_Flag). There are a some special cases to note.

DVI_TT_ACCPORNAM is something we will handle in the future.

DVI_TT_CHARSET, DVI_TT_CS_HANGUL, DVI_TT_CS_HANYU, DVI_TT_CS_HANZI, DVI_TT_CS_KANA, DVI_TT_CS_KANJI, and DVI_TT_CS_THAI have to do with the terminal's character set. We will adress this subject in the future.

DVI_TT_CRFILL and DVI_TT_LFFILL differ from VMS in that they are merely flags on VMS, whereas on UOS they are integer values indicating the amount of fill to send after carriage returns or Line Feeds.

DVI_TT_HANGUP and DVI_TT_LOWER are the inverse of the corresponding terminal flags, so we return 1 if the given flag is clear and 0 if the flag is set.

DVI_TT_OPER is based on the device flags rather than the terminal flags and only terminals apply to this flag.

DVI_TT_PAGE returns the terminal width rather than a flag.

DVI_TT_PHYDEVNAM returns the device name of the terminal. There is a situation that we will handle in the future: for remote terminals, the physical device name would be the name of the device through which the remote terminal connects (in most cases, a network device). For now, this is simply the device name.

                    DVI_UNIT : begin
                                   S := copy( This_Device_Name, 7, 1024 ) ;
                                   setlength( S, system.length( S ) - 1 ) ; // Trim colon
                                   while( pos( S[ system.length( S ) ], '0123456789' ) = 0 ) do
                                   begin
                                       setlength( S, system.length( S ) - 1 ) ; // Trim letrers
                                   end ;
                                   Res := strtoint( S ) ;
                                   if( Write_Integer( Res ) = UE_Error ) then
                                   begin
                                       exit ;
                                   end ;
                               end ;
This item returns the unit number of the device. This is easily pulled from the device name. The unit number will start at position 7 in the name. We trim off the colon at the end of the device name. Then we trim any letters that may be at the end (indicating a partition or session).

                    DVI_VPROT : begin
                                    if( Write_Integer( Device.Owner.Protections ) = UE_Error ) then
                                    begin
                                        exit ;
                                    end ;
                                end ;
This item returns the protection mask for the device.

                    DVI_WWID : begin
                                   if( Write_String( '' ) = UE_Error ) then //TODO
                                   begin
                                       exit ;
                                   end ;
                               end ;
This item returns the World Wide Identifier for the device. This applies to Fibre Channel devices, network interfaces, and remote devices. On VMS it applied only to Fibre Channel. WWID will be addressed in the future.

                    else begin
                             Generate_Exception( UOSErr_Bad_Parameter ) ;
                             exit ;
                         end ;
                end ; // case Descriptor.Item_Code
                Offset := Offset + sizeof( Descriptor ) ; // Move to next descriptor
            end ; // for Loop := 0 to Length
        finally
            MMC.UnMap_Pages( 0, Buffer, Length * sizeof( Descriptor ) ) ;
        end ;
    finally
        MMC.Unlock_Pages( PID, Buffer, Length * sizeof( Descriptor ) ) ;

        // Clean up...
        if( Temp_Handle ) then
        begin
            Resource.Free ;
        end ;
    end ;
end ; // TUOS_FiP.Get_Device_Info
The remainder of the routine is the same as when we first covered it.

    function TT_Flag( Flag : int64 ) : int64 ;

    begin
        Result := 0 ;
        if( Device.Terminal <> nil ) then
        begin
            if( ( Device.Terminal.Terminal_Flags and Flag ) <> 0 ) then
            begin
                Result := 1 ;
            end ;
        end ;
    end ;
This local function checks that the device is a terminal and, if so, sees if the passed flag is set. If not set or not a terminal, we return 0.

    function Write_Integer( I : int64 ) : integer ;

    var Len : int64 ;

    begin
        setlength( Temp, Descriptor.Buffer_Length ) ;
        Res := I ;
        Len := Descriptor.Buffer_Length ;
        if( Len > sizeof( I ) ) then
        begin
            Len := sizeof( I ) ;
        end ;
        move( Res, PChar( Temp )[ 0 ], Len ) ;
        SRB.Buffer := Descriptor.Buffer_Address ;
        SRB.Length := Len ;
        Result := Set_User_String( Kernel, PID, SRB, Temp ) ;
        if( Result = UE_Error ) then
        begin
            if( MMC.Last_Error = nil ) then
            begin
                Generate_Exception( UOSErr_Memory_Address_Error ) ;
            end ;
            exit ;
        end ;
        if( Descriptor.Return_Length_Address <> 0 ) then
        begin
            Result := Write_User_int64( Kernel, PID, Descriptor.Return_Length_Address, Len ) ;
            if( Result = UE_Error ) then
            begin
                if( MMC.Last_Error = nil ) then
                begin
                    Generate_Exception( UOSErr_Memory_Address_Error ) ;
                end ;
                exit ;
            end ;
        end ;
    end ; // Write_Integer
This local function writes an integer value to the user address space. First, we set a string to the length of the return buffer specified in the current descriptor. If it is longer than the size of a 64-bit integer, we adjust the length. Then we move the data from the passed integer into the string and then construct a TSRB structure that points to the buffer. Then we use Set_User_String to write the data. Note that we use strings here as merely binary buffers. If there is an error, we exit. If there is a return length address in the descriptor, we write the length written to that address.

    function Write_String( S : string ) : integer ;

    begin
        if( system.length( S ) > Descriptor.Buffer_Length ) then
        begin
            setlength( S, Descriptor.Buffer_Length ) ;
        end ;
        SRB.Buffer := Descriptor.Buffer_Address ;
        SRB.Length := system.length( S ) ;
        Result := Set_User_String( Kernel, PID, SRB, S ) ;
        if( Result = UE_Error ) then
        begin
            if( MMC.Last_Error = nil ) then
            begin
                Generate_Exception( UOSErr_Memory_Address_Error ) ;
            end ;
            exit ;
        end ;
        if( Descriptor.Return_Length_Address <> 0 ) then
        begin
            Result := Write_User_int64( Kernel, PID, Descriptor.Return_Length_Address, 
                system.length( S ) ) ;
            if( Result = UE_Error ) then
            begin
                if( MMC.Last_Error = nil ) then
                begin
                    Generate_Exception( UOSErr_Memory_Address_Error ) ;
                end ;
                exit ;
            end ;
        end ;
    end ; // Write_String
This local function writes a string value to the user address space. Much like the Write_Integer function above, we construct a TSRB structure and then use Set_User_String to write the data. We exit on error and then attempt to write the actual length to the return length address, if it is provided in the current descriptor.

In the next article, we will look at various other changes that have been made in relation to this system call.

 

Copyright © 2020 by Alan Conroy. This article may be copied in whole or in part as long as this copyright is included.