Device type interface (PostScript device plugins)
This page applies to Harlequin v13.1r0 and later; and to Harlequin MultiRIP but not Harlequin Core
A device type is specified by a DEVICETYPE
, a structure defined in the supplied header file swdevice.h
. This is a C structure that contains the device type number, flags, and so on, and pointers to the functions that implement that device type.
The structure definition is:
typedef {
int32 devicenumber; int32 devicetypeflags; int32 sizeof_private; int32 tickle_control;
int32 (*tickle_proc)(struct DeviceType * dev, int32 recursion); int32 (*last_error)(DEVICELIST * dev) ;
int32 (*device_init)(DEVICELIST * dev);
int32 (*open_file)(DEVICELIST * dev, uint8 * filename, int32 openflags) ;
int32 (*read_file)(DEVICELIST * dev, int32 DEVICE_FILEDESCRIPTOR, uint8 * buff, int32 len) ;
int32 (*write_file)(DEVICELIST * dev, int32 DEVICE_FILEDESCRIPTOR, uint8 * buff, int32 len) ;
int32 (*close_file)(DEVICELIST * dev, int32 DEVICE_FILEDESCRIPTOR) ; int32 (*abort_file)(DEVICELIST * dev, int32 DEVICE_FILEDESCRIPTOR) ;
int32 (*seek_file)(DEVICELIST * dev, int32 DEVICE_FILEDESCRIPTOR, Hq32x2 * destn, int32 flags) ;
int32 (*bytes_file)(DEVICELIST * dev, int32 DEVICE_FILEDESCRIPTOR, Hq32x2 * bytes, int32 reason) ;
int32 (*status_file)(DEVICELIST * dev, uint8 * filename, STAT * statbuff); void* (*start_file_list)(DEVICELIST * dev, uint8 * pattern);
int32 (*next_file)(DEVICELIST * dev, void ** handle, uint8 * pattern, FILEENTRY *entry);
int32 (*end_file_list)(DEVICELIST * dev, void * handle);
int32 (*rename_file)(DEVICELIST * dev, uint8 * file1, uint8 * file2); int32 (*delete_file)(DEVICELIST * dev, uint8 * filename);
int32 (*set_param)(DEVICELIST * dev, DEVICEPARAM * param) ; int32 (*start_param)(DEVICELIST * dev);
int32 (*get_param)(DEVICELIST * dev, DEVICEPARAM * param) ; int32 (*status_device)(DEVICELIST * dev, DEVSTAT * devstat); int32 (*device_dismount)(DEVICELIST * dev);
int32 (*device_buffersize)(DEVICELIST * dev);
int32 (*ioctl_call)(DEVICELIST * dev, int32 fileDescriptor, int32 opcode, int32 arg);
int32 (*spare)(void);
} DEVICETYPE ;
Device routines must take certain actions to ensure that they do not block; that is, it must not put itself into a position where it is waiting for an external event to occur without being able to return control to the RIP.
Particular care should be taken to ensure that the calls in the device type that implement the filenameforall
and status
PostScript language operators start_file_list
, next_file
, end_file_list
, and status_file
return sensible results, since if the %progress%
device is enabled, filenameforall
will potentially search for files on all devices. If it intentionally should not find any files, it must not produce a fault in doing so, since this might cause the PostScript language job to abort.
devicenumber (DEVICETYPE)
int32 devicenumber
This holds the device type number, as Instantiating PostScript plugin devices . There must be only one device type struct with any one device type number.
devicetypeflags (DEVICETYPE)
int32 devicetypeflags
This field specifies if the device is device‐relative and if it can be written to, and selects one of two sizes of buffer to be used for I/O with this device type. The value of the field is the inclusive‐OR of the selected flags.
The flags (defined in swdevice.h
) are:
| Devices of this type take filenames |
| Devices of this type can be written to |
| Devices of this type do not need a large buffer |
| Output for this device will be line‐buffered |
DEVICEWRITABLE
means only that writing may be appropriate; some files within the device may still
not be writable. For instance, a device type that implemented access to a floppy disk would almost certainly set DEVICEWRITABLE
, but one would not be able to write to a floppy disk that was itself write‐ protected.
DEVICESMALLBUFF
is only a hint to the interpreter that it need not use a large buffer for read or write, since the amount of data transferred per call is unlikely to be large; it does not imply any particular buffer size. The only guarantee about buffer sizes is that the size of read requests will always be at least 1024 bytes. Devices can also specify their required buffer size see device_buffersize determine file buffer size (DEVICETYPE)
.
DEVICELINEBUFF
indicates that output will only be buffered until an end‐of‐line is seen, rather than until the buffer is full. This is to make a device type suitable for interactive operation.
sizeof_private (DEVICETYPE)
int32 sizeof_private
With a single exception (see Harlequin RIP SDK Programmer's Reference Manual, Chapter 8 ‐
The PageBuffer Device ) devices are not permitted themselves to allocate memory from the RIP memory. However, this field can be used to request the RIP to allocate memory when setting up the device, and this memory can then be used by the device.
When a new device is set up by the RIP, it allocates sizeof_private
bytes of memory and zeroes it. If no per‐device memory is required for a particular device type, sizeof_private
should be set to 0.
tickle_control (DEVICETYPE)
Tickle functions are no longer supported. This field should now be set to 0
.
tickle_proc (DEVICETYPE)
Tickle functions are no longer supported. This field should now be set to NULL
.
If an implementation needs to do some processing on a regular basis, for example to send more out‐ put to the device or a keep‐alive message to maintain an open communication channel, it can use the timer library to set up a regular call to a function to do the processing.
last_error (DEVICETYPE)
int32 last_error (DEVICELIST * dev)
This must return the last error produced by any of the other device routines. If called at all, it is called immediately following the call to the routine that produced the error (that is, no other device routines will be called in between).
It must return one of the following values (defined in swdevice.h
):
DeviceNoError DeviceInvalidAccess DeviceIOError DeviceLimitCheck DeviceUndefined DeviceUnregistered DeviceInterrupted DeviceVMError DeviceReOutput DeviceNotReady DeviceCancelPage DeviceReOutputPageBuffer DeviceTimeout
The circumstances under which each one should be returned are described in the individual routine descriptions. The last six errors should only be returned by the %pagebuffer%
device.
device_init initialize a device (DEVICETYPE)
int32 device_init (DEVICELIST * dev)
This routine is called when the DeviceType
parameter is set for a given device (with setdevparam
, described in set_param set a device parameter (DEVICETYPE)
). This is done once for each device, and will be the first call to any of the device routines for that device.
It should return 0
if the device was initialized successfully, and -1
if it could not be initialized. Note that not successfully initializing may be a fatal error in some circumstances; for example, the %os%
device must function for the RIP to work correctly.
The %os%
device is always the first to be initialized with this call.
open_file open a file on the device (DEVICETYPE)
int 32 open_file (DEVICELIST * dev, uint8 * filename, int32 openflags)
For a device‐relative device, filename
gives a null‐terminated name. For a single‐file device, the file‐ name is undefined and should be ignored.
The openflags
parameter is one or more of the following ORʹd together. They are defined in
swdevice.h
:
SW_RDONLY SW_WRONLY SW_RDWR SW_APPEND SW_CREAT SW_TRUNC SW_EXCL
The return value is -1
if the file could not be opened, or the descriptor (an integer greater than or equal to zero) if it succeeds. This descriptor is passed to other calls for this device to indicate which file the operation is intended for.
Descriptors have to be unique only for each device; different devices (even of the same device type) can use the same number for different files without confusion.
last_error
should be one of:
| If the filename is invalid for the device, or the file does not exist and the value of |
| If the requested access status values, set by |
| If the file cannot be opened because to do so would exceed some implementation‐defined limit on the number of open files. |
| Other errors should cause |
The PostScript language model of files is of a stream of bytes; that is, there is no record structure or distinction between binary and text files. The implementation of the open_file
call must open the file in the appropriate way to support this.
read_file read data from a file on the device (DEVICETYPE)
int32 read_file (DEVICELIST * dev, int32 descriptor, uint8 * buff, int32 len)
The descriptor will be one returned by a previous open_file
on this device. The bytes read should be stored in ascending addresses starting at buff
. Unless the device type specifies an explicit size (as described in device_buffersize determine file buffer size (DEVICETYPE)
), the space available in
buff (len)
is guaranteed to be at least 1024 bytes, although the read routine can return fewer if appropriate.
The routine should return the number of bytes added to the buffer, or -1
if some error occurred. Returning 0
(zero) indicates that the end of file has been reached.
If an error occurs, the RIP will not assume anything about the contents of the buffer. Thus, it is safe to put bytes into this buffer even if an error occurs at some point before all the bytes have been read.
If an error occurs during the read, the routine should return -1
and last_error
should return
DeviceIOError
.
Note that some implementations may need to close the file if the file is deleted (see delete_file remove a file (DEVICETYPE) ) and so an error may need to be returned even if there is no error in the underlying file system.
write_file write data to a file on the device (DEVICETYPE)
write_file (DEVICELIST * dev, int32 descriptor, uint8 * buff, int32 len)
The descriptor
will be one returned by a previous open_file
on this device. The bytes to be written are stored in ascending addresses starting at buff
. The number of bytes to write (len
) can be any number greater than or equal to 0 (zero).
The routine should return the number of bytes successfully written, or ‐1 if an error occurred. If an error occurs, last_error
should return DeviceIOError
. When the returned number (of bytes actually written) is anything less than the number requested, it is also treated as a DeviceIOError
.
Note that some implementations may need to close the file if the file is deleted (see delete_file remove a file (DEVICETYPE) ) and so an error may need to be returned even if there is no error in the underlying file system.
close_file close a file descriptor on a device (DEVICETYPE)
int32 close_file (DEVICELIST * dev, int32 descriptor)
This breaks the connection between a descriptor and the underlying file. After a descriptor has been closed, the descriptor's value is free to be returned by a future open
call on this device.
Any data waiting to be written on that file should be written before this call returns. Note that the RIP itself buffers data read from or written to a file, and so the device implementation normally need not (and indeed should not) also implement a buffering scheme.
The value returned should be 0
(zero), if the close operation was successful, or -1
if some error occurred.
Note that some implementations may need to close the file if the file is deleted (with delete_file
, described in delete_file remove a file (DEVICETYPE)
) and so an error may need to be returned even if there is no error in the underlying file system.
abort_file close a file descriptor abnormally (DEVICETYPE)
int32 abort_file (DEVICELIST * dev, int32 descriptor)
This routine, like close_file
, breaks the connection between a descriptor and the underlying file. After a descriptor has been closed, the descriptor's value is free to be returned by a future open
call on this device.
This function is called when the file is being closed due to some abnormal condition. The implication is that something went wrong, and that the data being read, or already written, will not be used. Thus, it is not necessary, nor indeed desirable, to flush any data waiting to be written. If the file was created when opened for writing, it might be appropriate to delete the file after closing it in this routine.
In general, this routine should close a file as quickly as possible and do any consequent tidying up. If it succeeds, it should return 0
; otherwise, it should return -1
.
seek_file seek to a specified point in a file on the device (DEVICETYPE)
int32 seek_file (DEVICELIST * dev, int32 descriptor, Hq32x2 * destn, int32 flags)
Some devices will be able to support positionable files that is, there is a current file position associated with the open file and the next read
or write
will read from, or write to, bytes at that point, and this current file position can be set.
Other devices will have non‐positionable files or perhaps some files on the device will be positionable and some non‐positionable. If the current file position cannot be set for whatever reason, this routine should return -1
.
The flags
parameter indicates how the destn
parameter should be interpreted. If flags
is SW_SET
(the constants for flags are defined in the file swdevice.h
), the current file position should be set to destn
bytes from the start of the file. If flags
is SW_INCR
, the current file position should be changed by destn
bytes, and if flags
is SW_XTND
, the current file position should be set to the length of the file plus destn
.
If this results in the current file position being beyond the current end of the file, the file is open for writing, and the file permissions allow it to be extended, then file should be extended to the current
file position. Red Book does not specify the values to be set in any bytes between the old and new lengths of the file, but Harlequin specifies that such bytes should read as zero.
Calls with a destn
of zero have additional semantics, and some such calls should be supported, even if arbitrary seeks on the file are not supported.
| If this succeeds, then arbitrary seeks on the file are supported. |
| Return current file position. This feature is often supportable even if other seeks are not. |
| Flush the file, discarding any remaining input. This may not always require reading the remaining input for example, a network connection could be shut down instead. |
bytes_file return bytes available for an open file (DEVICETYPE)
int32 bytes_file (DEVICELIST * dev, int32 descriptor, Hq32x2 * bytes, int32 reason)
If the number of bytes available is not known, but it is possible that there are some, then 0
should be returned. Bytes available should be -1
only when the file has reached EOF (and last_error
should then return DeviceIOError
) or when some other error occurs, such as when the file is open for writing only.
The value of reason
is one of:
SW_BYTES_AVAIL_REL (0)
SW_BYTES_TOTAL_ABS (1)
where SW_BYTES_AVAIL_REL
will give the answer as bytes immediately available after the current position, and SW_BYTES_TOTAL_ABS
gives the total extent of the file in bytes.
status_file return status information (DEVICETYPE)
int32 status_file (DEVICELIST * dev, uint8 * filename, STAT * statbuff)
This routine returns information about a named file on the device. If the file exists, the fields in the structure pointed to by the statbuff
argument should be set appropriately and 0
returned. If the file does not exist, or some error occurred when retrieving the information on the file, -1
should be returned. In the latter case, the values of the fields are undefined, and last_error
should return DeviceIOError
.
The pages
field should be set to the storage space occupied by the file, in implementation‐dependent units. The page size used must be 1024 bytes, even if this is not the unit used by the underlying operating system (if any).
The bytes
field should be set to the length of the file in bytes.
The referenced
field should be set to the time of the last read or write operation to that file. The created
field should be set to the time that the file was created.
Suitable default values should be substituted for either time if they are not supported by the underlying file system.
The time units and origin are not defined by Red Book : the only legal interpretation of these values by a PostScript language program is to assume that a larger value means a later time. This implies that
the value should not overflow a signed 32-bit integer in the near future; say the next twenty years, which in turn implies the time measurement cannot be too fine grained.
The plugin must implement a real time clock, where the times should be measured in seconds since 00:00:00 (midnight), 1st Jan. 1970. If additionally, the implementation supports time zones, this should be in measured in the GMT time zone. This is the same as the UNIX definition.
start_file_list begin listing files (DEVICETYPE)
void* start_file_list (DEVICELIST * dev, uint8 * pattern)
This routine is called as part of the implementation of the PostScript language operator filenameforall
. pattern
is the template string as passed to filenameforall
. This routine should do any initializing necessary such that successive calls to next_file
(described in next_file return next matching file (DEVICETYPE)
) will return filenames matching that template.
The pointer returned by this function will be passed to the first call to next_file
. The implementation of next_file
should use this pointer to identify where in the enumeration of files matching this pat‐ tern it has reached. This is necessary because it is possible to have recursive calls to filenameforall
, so nested filename enumerations must be supported.
If for some reason the enumeration cannot be started, the routine should return NULL
. One reason for this could be that the nesting level of filename enumerations is too deep, and in this case last_error
should return DeviceLimitCheck
. Other errors should return DeviceIOError
.
next_file return next matching file (DEVICETYPE)
int32 next_file (DEVICELIST * dev, void ** handle, uint8 * pattern,FILEENTRY *entry)
handle
is a pointer to the value returned from the call to start_file_list
. It should be used to identify the current point in this enumeration, and can be modified by next_file
if necessary.
pattern
is a pointer to the same template passed to start_file_list
for this enumeration. entry
is a pointer to a FILEENTRY
structure. If there is another match for the pattern, a pointer to the filename should be stored in the name
field (there is no need to null‐terminate it, but remember that it cannot be a string on the C stack), and namelength
set to the length of that filename in bytes, excluding any terminating NULL character. FileNameMatch
should then be returned. If the filename is too long, FileNameRangeCheck
should be returned.
If there are no more matches, FileNameNoMatch
should be returned. If an error occurred while looking for a match, FileNameError
should be returned. The last_error
routine will then be called to deter‐ mine the error.
The names FileNameMatch
, FileNameRangeCheck
, FileNameNoMatch
, FileNameError
are defined in
swdevice.h
.
Note: If an underlying directory does not have permission for enumeration of its contents, do not treat this as an error. The routine should just ignore those directories and continue the search for matches.
There are two routines implemented by the RIP that can be used in the implementation of next_file
. These are:
SwPatternMatch
returns non‐zero if the NUL‐terminated string matches the NUL‐terminated pattern, or zero if the string does not match the pattern. SwLengthPatternMatch
is similar but the pattern and string are given by start address and length, and may not be NUL‐terminated.
end_file_list end the listing of files (DEVICETYPE)
int32 end_file_list (DEVICELIST * dev, void * handle)
This can be called at any time after start_file_list
that is, it is possible that not all matches have been asked for. It should do any necessary tidying up since a particular file enumeration has finished. Which file enumeration should be deduced from handle
, the value returned by start_file_list
as possibly modified by intervening calls to next_file
.
If successful, this routine should return 0
. If an error occurs then -1
should be returned, and
last_error
should return DeviceIOError
.
rename_file rename a file (DEVICETYPE)
int32 rename_file (DEVICELIST * dev, uint8 * file1, uint8 * file2)
This routine renames the file file1
with the new name file2
. If the rename succeeds, then it should return 0
. If the rename fails -1
should be returned.
If no file is named file1
then last_error
should return DeviceUndefined
. If the device does not sup‐ port renaming, or the particular filenames are not permitted to be changed, then last_error
should return DeviceInvalidAccess
. If some other error occurs, then last_error
should return DeviceIOError
.
Whether an error is returned if a file with name file2
already exists is implementation defined. If possible, the routine should not remove the filename file1
until it is known that the rename will succeed.
delete_file remove a file (DEVICETYPE)
int32 delete_file (DEVICELIST * dev, uint8 * filename)
This routine removes the file with the given name. If the file has been opened it is implementation‐ dependent whether any further operations can be done with the open file. This permits the file to be closed in the underlying operating system, if necessary, before deleting this file.
If successful, this routine should return 0
. If an error occurs, then -1
should be returned.
If no file is named file1
then last_error
should return DeviceUndefined
. If the device does not sup‐ port deletion, or the particular files cannot be deleted, then last_error
should return DeviceInvalidAccess
. If some other error occurs, then last_error
should return DeviceIOError
.
set_param set a device parameter (DEVICETYPE)
int32 set_param (DEVICELIST * dev, DEVICEPARAM * param)
The PostScript language operator setdevparams
will call this device routine. The param
argument specifies the device parameter and its value in the following structure:
typedef struct { uint8 *paramname ; int32 paramnamelen ; int32 type ;
union {
int32 intval ; int32 boolval ; uint8 *strval; float floatval ;
struct DEVICEPARAM *compobval;
} paramval; int32 strvallen ;
} DEVICEPARAM ;
The strings paramname
and strval
are not
null‐terminated. The strings are stored in the RIP internal memory which may be re‐used; therefore, if the device needs to keep referring to the string, it must copy it into its own static memory. The type
field is one of the following values (defined in swdevice.h
):
ParamBoolean ParamInteger ParamString ParamFloat ParamArray ParamDict ParamNull
These correspond to the PostScript language type of the value for one of the keys in the dictionary passed to setdevparams
, except that values that are PostScript language name objects are passed to this routine as if they were strings.
compobval
is a pointer to an array of device parameters.
The following device parameters are valid for all devices, and are not passed to the device. They are handled internally by the RIP.
/Password (set_param)
string
See the description of setdevparams
in Red Book
.
/DeviceType (set_param)
integer
Sets the device type pointer in the device structure to point to the device type specified by the number.
/Enable (set_param)
boolean
If set to true
, permits operations on that device. An invalidaccess
error is generated if the device's type has not been set yet.
The first call to setdevparams
after creating a device with devmount
must set the device type. This is to prevent attempts to do anything with the device before the device routines are installed. An invalidaccess
error occurs if the /DeviceType
is missing on the first call. The device_init
routine of the device is called when the DeviceType
parameter is set. (device_init initialize a device (DEVICETYPE)
describes device_init
.)
The routine returns a code to indicate the success or failure of setting the parameter. The following values (defined in swdevice.h
) can be returned:
ParamAccepted ParamTypeCheck ParamRangeCheck ParamConfigError ParamIgnored ParamError
ParamAccepted
means that the parameter has been set successfully. This does not necessarily mean the exact value was used, or that the parameter will read back with the value just set: these things are implementation defined. ParamTypeCheck
means the value was not one of the right types for the parameter of that name. ParamRangeCheck
indicates that the value was unreasonable for that device parameter. ParamConfigError
indicates that the value was reasonable, but not achievable with this implementation. If the parameter name is not one of those supported by this device, ParamIgnored
should be returned. ParamError
means that some other error occurred for example, this error might be that configuring underlying hardware failed. If ParamError
is returned, last_error
will be called to determine what error occurred.
start_param begin the listing the device parameters (DEVICETYPE)
int32 start_param (DEVICELIST * dev)
This routine returns the number of parameters that the device wishes to have returned by the PostScript language operator currentdevparams
. This returned number need not be the same as the number of device parameter names recognized by the device, because some device parameters can be ‘write‐only', or a parameter may only be returned once a value has been set for it. The number excludes the parameters that are handled by the interpreter itself (/Password
, /DeviceType
, /Enable
). Following this call, get_param
will be called multiple times (with a NULL paramname
) to get each of the parameters.
If there are no such device parameters, 0
(zero) should be returned. If an error occurs, -1
should be returned.
get_param return (next) device parameter (DEVICETYPE)
int32 get_param (DEVICELIST * dev, DEVICEPARAM * param)
This routine performs two functions. If, when called, the paramname
field of the param
structure is NULL
, then the next parameter name and value should be returned through the DEVICEPARAM
structure. If the field is not NULL
then it points to a parameter name. The length of the name is then in the paramnamelen
field. The value of this parameter should be returned through the DEVICEPARAM
structure.
If in the second case the parameter name is not recognized the routine should return ParamIgnored
, otherwise it should return ParamAccepted
. If an error occurs, ParamError
should be returned and last_error
will be called to determine what error that was.
If not returning ParamIgnored
, the type
field must be set to one of ParamBoolean
, ParamInteger
, ParamString
, or ParamFloat
. The appropriate union
field should be set to the value of the parameter. If a string value is returned, the strval
field should point to the device's local copy of the string, and strvallen
is set to the length of the string not including any terminating NULL character. The inter‐ preter will copy the string into its own storage.
If returning the next parameter (paramname
is NULL
on function entry) the paramname
field should be set to point to the name of the parameter being returned, and the paramnamelen
field set to the length of that name not including any terminating NULL character. The value should be returned in the same way as above.
status_device return the status of the device itself (DEVICETYPE)
int32 status_device (DEVICELIST * dev, DEVSTAT * devstat)
This routine returns information on the device: size
is the current size of the device and free
is the amount of space available on the device. The interpretation of these is device‐dependent, but for a device that supports files such as a hard disk, size
should be the total size of the device, and free
should be the space left that future operations can consume. The sizes are measured in units of 1024 bytes as described in status_file return status information (DEVICETYPE)
.
This routine should return 0
(zero) if successful or -1
if an error occurred (and then last_error
will be called to determine the type of error)
device_dismount remove a device (DEVICETYPE)
int32 device_dismount (DEVICELIST * dev)
This routine is called when a device is removed from the current list of devices. All files open when the RIP unmounted the device will have been closed. The routine should perform any required tasks, such as freeing any resources used by that device.
This routine should return 0
(zero) if successful or -1
if an error occurred (and then last_error
will be called to determine the type of error).
device_buffersize determine file buffer size (DEVICETYPE)
int32 device_buffersize (DEVICELIST * dev)
This routine is called when a file is opened on the device. It should return the size of the buffer that the device requires for best operation. If the device_type
structure has NULL
in this function slot, or if the specified routine returns a number less than zero, then the DEVICESMALLBUFF
flag is examined to determine the size of the buffer.
ioctl_call provide arbitrarily extended functionality (DEVICETYPE)
int32 ioctl_call (DEVICELIST * dev, int32 fileDescriptor, int32 opcode, int32 arg)
This function provides for arbitrary, extended functionality to be implemented in certain device types, but is not used for plugins.